From 540376a8a04ec3fd5aa6c725cf47464593dee704 Mon Sep 17 00:00:00 2001 From: Carlisia Date: Thu, 14 May 2020 07:34:16 -0700 Subject: [PATCH 01/34] kubebuilder init - minimalist version Signed-off-by: Carlisia --- Makefile | 38 +++++++++- PROJECT | 3 + config/certmanager/certificate.yaml | 26 +++++++ config/certmanager/kustomization.yaml | 5 ++ config/certmanager/kustomizeconfig.yaml | 16 +++++ config/default/kustomization.yaml | 70 +++++++++++++++++++ config/default/manager_auth_proxy_patch.yaml | 25 +++++++ config/default/manager_webhook_patch.yaml | 23 ++++++ config/default/webhookcainjection_patch.yaml | 15 ++++ config/manager/kustomization.yaml | 2 + config/manager/manager.yaml | 39 +++++++++++ config/prometheus/kustomization.yaml | 2 + config/prometheus/monitor.yaml | 16 +++++ .../rbac/auth_proxy_client_clusterrole.yaml | 7 ++ config/rbac/auth_proxy_role.yaml | 13 ++++ config/rbac/auth_proxy_role_binding.yaml | 12 ++++ config/rbac/auth_proxy_service.yaml | 14 ++++ config/rbac/kustomization.yaml | 12 ++++ config/rbac/leader_election_role.yaml | 32 +++++++++ config/rbac/leader_election_role_binding.yaml | 12 ++++ config/rbac/role_binding.yaml | 12 ++++ config/webhook/kustomization.yaml | 6 ++ config/webhook/kustomizeconfig.yaml | 25 +++++++ config/webhook/service.yaml | 12 ++++ go.mod | 5 +- go.sum | 8 +++ 26 files changed, 448 insertions(+), 2 deletions(-) create mode 100644 PROJECT create mode 100644 config/certmanager/certificate.yaml create mode 100644 config/certmanager/kustomization.yaml create mode 100644 config/certmanager/kustomizeconfig.yaml create mode 100644 config/default/kustomization.yaml create mode 100644 config/default/manager_auth_proxy_patch.yaml create mode 100644 config/default/manager_webhook_patch.yaml create mode 100644 config/default/webhookcainjection_patch.yaml create mode 100644 config/manager/kustomization.yaml create mode 100644 config/manager/manager.yaml create mode 100644 config/prometheus/kustomization.yaml create mode 100644 config/prometheus/monitor.yaml create mode 100644 config/rbac/auth_proxy_client_clusterrole.yaml create mode 100644 config/rbac/auth_proxy_role.yaml create mode 100644 config/rbac/auth_proxy_role_binding.yaml create mode 100644 config/rbac/auth_proxy_service.yaml create mode 100644 config/rbac/kustomization.yaml create mode 100644 config/rbac/leader_election_role.yaml create mode 100644 config/rbac/leader_election_role_binding.yaml create mode 100644 config/rbac/role_binding.yaml create mode 100644 config/webhook/kustomization.yaml create mode 100644 config/webhook/kustomizeconfig.yaml create mode 100644 config/webhook/service.yaml diff --git a/Makefile b/Makefile index baaf1a9498..9690683a09 100644 --- a/Makefile +++ b/Makefile @@ -85,7 +85,7 @@ IMAGE ?= $(REGISTRY)/$(BIN)-$(GOARCH) # If you want to build all binaries, see the 'all-build' rule. # If you want to build all containers, see the 'all-containers' rule. # If you want to build AND push all containers, see the 'all-push' rule. -all: +all: generate manifests @$(MAKE) build @$(MAKE) build BIN=velero-restic-restore-helper @@ -304,3 +304,39 @@ serve-docs: # a new versioned docs site. The full process is documented in site/README-JEKYLL.md. gen-docs: @hack/gen-docs.sh + +# Kubebuilder +# Produce CRDs that work back to Kubernetes 1.11 (no version conversion) +CRD_OPTIONS ?= "crd:trivialVersions=true" + +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +# Generate manifests e.g. CRD, RBAC etc. +manifests: controller-gen + $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases + +# Generate code +generate: controller-gen + $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + +# find or download controller-gen +# download controller-gen if necessary +controller-gen: +ifeq (, $(shell which controller-gen)) + @{ \ + set -e ;\ + CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\ + cd $$CONTROLLER_GEN_TMP_DIR ;\ + go mod init tmp ;\ + go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.2.5 ;\ + rm -rf $$CONTROLLER_GEN_TMP_DIR ;\ + } +CONTROLLER_GEN=$(GOBIN)/controller-gen +else +CONTROLLER_GEN=$(shell which controller-gen) +endif diff --git a/PROJECT b/PROJECT new file mode 100644 index 0000000000..4fd590919a --- /dev/null +++ b/PROJECT @@ -0,0 +1,3 @@ +domain: io +repo: github.com/vmware-tanzu/velero +version: "2" diff --git a/config/certmanager/certificate.yaml b/config/certmanager/certificate.yaml new file mode 100644 index 0000000000..58db114fa0 --- /dev/null +++ b/config/certmanager/certificate.yaml @@ -0,0 +1,26 @@ +# The following manifests contain a self-signed issuer CR and a certificate CR. +# More document can be found at https://docs.cert-manager.io +# WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for +# breaking changes +apiVersion: cert-manager.io/v1alpha2 +kind: Issuer +metadata: + name: selfsigned-issuer + namespace: system +spec: + selfSigned: {} +--- +apiVersion: cert-manager.io/v1alpha2 +kind: Certificate +metadata: + name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize + dnsNames: + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize diff --git a/config/certmanager/kustomization.yaml b/config/certmanager/kustomization.yaml new file mode 100644 index 0000000000..bebea5a595 --- /dev/null +++ b/config/certmanager/kustomization.yaml @@ -0,0 +1,5 @@ +resources: +- certificate.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/config/certmanager/kustomizeconfig.yaml b/config/certmanager/kustomizeconfig.yaml new file mode 100644 index 0000000000..90d7c313ca --- /dev/null +++ b/config/certmanager/kustomizeconfig.yaml @@ -0,0 +1,16 @@ +# This configuration is for teaching kustomize how to update name ref and var substitution +nameReference: +- kind: Issuer + group: cert-manager.io + fieldSpecs: + - kind: Certificate + group: cert-manager.io + path: spec/issuerRef/name + +varReference: +- kind: Certificate + group: cert-manager.io + path: spec/commonName +- kind: Certificate + group: cert-manager.io + path: spec/dnsNames diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml new file mode 100644 index 0000000000..b9e0a79206 --- /dev/null +++ b/config/default/kustomization.yaml @@ -0,0 +1,70 @@ +# Adds namespace to all resources. +namespace: velero-system + +# Value of this field is prepended to the +# names of all resources, e.g. a deployment named +# "wordpress" becomes "alices-wordpress". +# Note that it should also match with the prefix (text before '-') of the namespace +# field above. +namePrefix: velero- + +# Labels to add to all resources and selectors. +#commonLabels: +# someName: someValue + +bases: +- ../crd +- ../rbac +- ../manager +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- ../webhook +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. +#- ../certmanager +# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. +#- ../prometheus + +patchesStrategicMerge: + # Protect the /metrics endpoint by putting it behind auth. + # If you want your controller-manager to expose the /metrics + # endpoint w/o any authn/z, please comment the following line. +- manager_auth_proxy_patch.yaml + +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- manager_webhook_patch.yaml + +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. +# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. +# 'CERTMANAGER' needs to be enabled to use ca injection +#- webhookcainjection_patch.yaml + +# the following config is for teaching kustomize how to do var substitution +vars: +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. +#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR +# objref: +# kind: Certificate +# group: cert-manager.io +# version: v1alpha2 +# name: serving-cert # this name should match the one in certificate.yaml +# fieldref: +# fieldpath: metadata.namespace +#- name: CERTIFICATE_NAME +# objref: +# kind: Certificate +# group: cert-manager.io +# version: v1alpha2 +# name: serving-cert # this name should match the one in certificate.yaml +#- name: SERVICE_NAMESPACE # namespace of the service +# objref: +# kind: Service +# version: v1 +# name: webhook-service +# fieldref: +# fieldpath: metadata.namespace +#- name: SERVICE_NAME +# objref: +# kind: Service +# version: v1 +# name: webhook-service diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml new file mode 100644 index 0000000000..77e743d1c1 --- /dev/null +++ b/config/default/manager_auth_proxy_patch.yaml @@ -0,0 +1,25 @@ +# This patch inject a sidecar container which is a HTTP proxy for the +# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: kube-rbac-proxy + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0 + args: + - "--secure-listen-address=0.0.0.0:8443" + - "--upstream=http://127.0.0.1:8080/" + - "--logtostderr=true" + - "--v=10" + ports: + - containerPort: 8443 + name: https + - name: manager + args: + - "--metrics-addr=127.0.0.1:8080" + - "--enable-leader-election" diff --git a/config/default/manager_webhook_patch.yaml b/config/default/manager_webhook_patch.yaml new file mode 100644 index 0000000000..738de350b7 --- /dev/null +++ b/config/default/manager_webhook_patch.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert diff --git a/config/default/webhookcainjection_patch.yaml b/config/default/webhookcainjection_patch.yaml new file mode 100644 index 0000000000..7e79bf9955 --- /dev/null +++ b/config/default/webhookcainjection_patch.yaml @@ -0,0 +1,15 @@ +# This patch add annotation to admission webhook config and +# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + name: mutating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + name: validating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml new file mode 100644 index 0000000000..5c5f0b84cb --- /dev/null +++ b/config/manager/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- manager.yaml diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml new file mode 100644 index 0000000000..b6c85a52d5 --- /dev/null +++ b/config/manager/manager.yaml @@ -0,0 +1,39 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: system +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system + labels: + control-plane: controller-manager +spec: + selector: + matchLabels: + control-plane: controller-manager + replicas: 1 + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - command: + - /manager + args: + - --enable-leader-election + image: controller:latest + name: manager + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + terminationGracePeriodSeconds: 10 diff --git a/config/prometheus/kustomization.yaml b/config/prometheus/kustomization.yaml new file mode 100644 index 0000000000..ed137168a1 --- /dev/null +++ b/config/prometheus/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- monitor.yaml diff --git a/config/prometheus/monitor.yaml b/config/prometheus/monitor.yaml new file mode 100644 index 0000000000..9b8047b760 --- /dev/null +++ b/config/prometheus/monitor.yaml @@ -0,0 +1,16 @@ + +# Prometheus Monitor Service (Metrics) +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + control-plane: controller-manager + name: controller-manager-metrics-monitor + namespace: system +spec: + endpoints: + - path: /metrics + port: https + selector: + matchLabels: + control-plane: controller-manager diff --git a/config/rbac/auth_proxy_client_clusterrole.yaml b/config/rbac/auth_proxy_client_clusterrole.yaml new file mode 100644 index 0000000000..7d62534c5f --- /dev/null +++ b/config/rbac/auth_proxy_client_clusterrole.yaml @@ -0,0 +1,7 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: metrics-reader +rules: +- nonResourceURLs: ["/metrics"] + verbs: ["get"] diff --git a/config/rbac/auth_proxy_role.yaml b/config/rbac/auth_proxy_role.yaml new file mode 100644 index 0000000000..618f5e4177 --- /dev/null +++ b/config/rbac/auth_proxy_role.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: proxy-role +rules: +- apiGroups: ["authentication.k8s.io"] + resources: + - tokenreviews + verbs: ["create"] +- apiGroups: ["authorization.k8s.io"] + resources: + - subjectaccessreviews + verbs: ["create"] diff --git a/config/rbac/auth_proxy_role_binding.yaml b/config/rbac/auth_proxy_role_binding.yaml new file mode 100644 index 0000000000..48ed1e4b85 --- /dev/null +++ b/config/rbac/auth_proxy_role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: proxy-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/config/rbac/auth_proxy_service.yaml b/config/rbac/auth_proxy_service.yaml new file mode 100644 index 0000000000..6cf656be14 --- /dev/null +++ b/config/rbac/auth_proxy_service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + name: controller-manager-metrics-service + namespace: system +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml new file mode 100644 index 0000000000..66c28338fe --- /dev/null +++ b/config/rbac/kustomization.yaml @@ -0,0 +1,12 @@ +resources: +- role.yaml +- role_binding.yaml +- leader_election_role.yaml +- leader_election_role_binding.yaml +# Comment the following 4 lines if you want to disable +# the auth proxy (https://github.com/brancz/kube-rbac-proxy) +# which protects your /metrics endpoint. +- auth_proxy_service.yaml +- auth_proxy_role.yaml +- auth_proxy_role_binding.yaml +- auth_proxy_client_clusterrole.yaml diff --git a/config/rbac/leader_election_role.yaml b/config/rbac/leader_election_role.yaml new file mode 100644 index 0000000000..eaa79158fb --- /dev/null +++ b/config/rbac/leader_election_role.yaml @@ -0,0 +1,32 @@ +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: leader-election-role +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create diff --git a/config/rbac/leader_election_role_binding.yaml b/config/rbac/leader_election_role_binding.yaml new file mode 100644 index 0000000000..eed16906f4 --- /dev/null +++ b/config/rbac/leader_election_role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: leader-election-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml new file mode 100644 index 0000000000..8f2658702c --- /dev/null +++ b/config/rbac/role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/config/webhook/kustomization.yaml b/config/webhook/kustomization.yaml new file mode 100644 index 0000000000..9cf26134e4 --- /dev/null +++ b/config/webhook/kustomization.yaml @@ -0,0 +1,6 @@ +resources: +- manifests.yaml +- service.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/config/webhook/kustomizeconfig.yaml b/config/webhook/kustomizeconfig.yaml new file mode 100644 index 0000000000..25e21e3c96 --- /dev/null +++ b/config/webhook/kustomizeconfig.yaml @@ -0,0 +1,25 @@ +# the following config is for teaching kustomize where to look at when substituting vars. +# It requires kustomize v2.1.0 or newer to work properly. +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + - kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + +namespace: +- kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true +- kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true + +varReference: +- path: metadata/annotations diff --git a/config/webhook/service.yaml b/config/webhook/service.yaml new file mode 100644 index 0000000000..31e0f82959 --- /dev/null +++ b/config/webhook/service.yaml @@ -0,0 +1,12 @@ + +apiVersion: v1 +kind: Service +metadata: + name: webhook-service + namespace: system +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + control-plane: controller-manager diff --git a/go.mod b/go.mod index 57799c6b45..5a404ca938 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/vmware-tanzu/velero -go 1.14 +go 1.13 require ( cloud.google.com/go v0.46.2 // indirect @@ -16,11 +16,14 @@ require ( github.com/gobwas/glob v0.2.3 github.com/gofrs/uuid v3.2.0+incompatible github.com/golang/protobuf v1.3.2 + github.com/googleapis/gnostic v0.3.1 // indirect github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd github.com/hashicorp/go-plugin v0.0.0-20190610192547-a1bc61569a26 github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 // indirect github.com/joho/godotenv v1.3.0 github.com/kubernetes-csi/external-snapshotter/v2 v2.1.0 + github.com/onsi/ginkgo v1.11.0 // indirect + github.com/onsi/gomega v1.8.1 // indirect github.com/pkg/errors v0.8.1 github.com/prometheus/client_golang v1.0.0 github.com/robfig/cron v0.0.0-20170309132418-df38d32658d8 diff --git a/go.sum b/go.sum index 5256daaee5..47e630ff21 100644 --- a/go.sum +++ b/go.sum @@ -103,6 +103,7 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docker/spdystream v0.0.0-20170912183627-bc6354cbbc29 h1:llBx5m8Gk0lrAaiLud2wktkX/e8haX7Ru0oVfQqtZQ4= github.com/docker/spdystream v0.0.0-20170912183627-bc6354cbbc29/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/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M= @@ -212,6 +213,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= +github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -306,9 +309,13 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.2 h1:uqH7bpe+ERSiDa34FDOF7RikN6RzXgduUF8yarlZp94= github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34= +github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= 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/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= @@ -502,6 +509,7 @@ 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-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= From ef80469d8c4339a4f56f4e4ba25e7982f7faa8aa Mon Sep 17 00:00:00 2001 From: Carlisia Date: Thu, 14 May 2020 07:43:01 -0700 Subject: [PATCH 02/34] Add back main.go, apparently kb needs it Signed-off-by: Carlisia --- go.mod | 4 +--- go.sum | 24 ++++++++++++++++++- main.go | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 main.go diff --git a/go.mod b/go.mod index 5a404ca938..7d2b87ee2d 100644 --- a/go.mod +++ b/go.mod @@ -16,14 +16,11 @@ require ( github.com/gobwas/glob v0.2.3 github.com/gofrs/uuid v3.2.0+incompatible github.com/golang/protobuf v1.3.2 - github.com/googleapis/gnostic v0.3.1 // indirect github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd github.com/hashicorp/go-plugin v0.0.0-20190610192547-a1bc61569a26 github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 // indirect github.com/joho/godotenv v1.3.0 github.com/kubernetes-csi/external-snapshotter/v2 v2.1.0 - github.com/onsi/ginkgo v1.11.0 // indirect - github.com/onsi/gomega v1.8.1 // indirect github.com/pkg/errors v0.8.1 github.com/prometheus/client_golang v1.0.0 github.com/robfig/cron v0.0.0-20170309132418-df38d32658d8 @@ -42,4 +39,5 @@ require ( k8s.io/client-go v0.17.4 k8s.io/klog v1.0.0 k8s.io/utils v0.0.0-20191218082557-f07c713de883 // indirect + sigs.k8s.io/controller-runtime v0.5.2 ) diff --git a/go.sum b/go.sum index 47e630ff21..e1cba1b54a 100644 --- a/go.sum +++ b/go.sum @@ -126,7 +126,10 @@ github.com/go-ini/ini v1.28.2 h1:drmmYv7psRpoGZkPtPKKTB+ZFSnvmwCMfNj5o1nLh2Y= github.com/go-ini/ini v1.28.2/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54= +github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= @@ -237,6 +240,7 @@ github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKe github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= @@ -390,8 +394,11 @@ go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qL go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -403,6 +410,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/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-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -475,6 +483,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220220014-0732a990476f h1:72l8qCJ1nGxMGH26QVBVIxKd/D34cfGt0OvrPtpemyY= golang.org/x/sys v0.0.0-20191220220014-0732a990476f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -511,6 +520,8 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= +gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= @@ -543,8 +554,9 @@ google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= @@ -560,6 +572,7 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl 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= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= @@ -569,23 +582,30 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI= +k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4= k8s.io/api v0.17.4 h1:HbwOhDapkguO8lTAE8OX3hdF2qp8GtpC9CW/MQATXXo= k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA= +k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs= k8s.io/apiextensions-apiserver v0.17.4 h1:ZKFnw3cJrGZ/9s6y+DerTF4FL+dmK0a04A++7JkmMho= k8s.io/apiextensions-apiserver v0.17.4/go.mod h1:rCbbbaFS/s3Qau3/1HbPlHblrWpFivoaLYccCffvQGI= k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= k8s.io/apimachinery v0.17.1-beta.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= +k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= k8s.io/apimachinery v0.17.4 h1:UzM+38cPUJnzqSQ+E1PY4YxMHIzQyCg29LOoGfo79Zw= k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= +k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo= k8s.io/apiserver v0.17.4/go.mod h1:5ZDQ6Xr5MNBxyi3iUZXS84QOhZl+W7Oq2us/29c0j9I= k8s.io/cli-runtime v0.17.4 h1:ZIJdxpBEszZqUhydrCoiI5rLXS2J/1AF5xFok2QJ9bc= k8s.io/cli-runtime v0.17.4/go.mod h1:IVW4zrKKx/8gBgNNkhiUIc7nZbVVNhc1+HcQh+PiNHc= k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k= +k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI= k8s.io/client-go v0.17.4 h1:VVdVbpTY70jiNHS1eiFkUt7ZIJX3txd29nDxxXH4en8= k8s.io/client-go v0.17.4/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc= k8s.io/code-generator v0.0.0-20191121015212-c4c8f8345c7e/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= +k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= k8s.io/code-generator v0.17.4/go.mod h1:l8BLVwASXQZTo2xamW5mQNFCe1XPiAesVq7Y1t7PiQQ= k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc= +k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs= k8s.io/component-base v0.17.4/go.mod h1:5BRqHMbbQPm2kKu35v3G+CpVq4K0RJKC7TRioF0I9lE= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= @@ -605,6 +625,8 @@ modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03 modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +sigs.k8s.io/controller-runtime v0.5.2 h1:pyXbUfoTo+HA3jeIfr0vgi+1WtmNh0CwlcnQGLXwsSw= +sigs.k8s.io/controller-runtime v0.5.2/go.mod h1:JZUwSMVbxDupo0lTJSSFP5pimEyxGynROImSsqIOx1A= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= diff --git a/main.go b/main.go new file mode 100644 index 0000000000..eb0115b46f --- /dev/null +++ b/main.go @@ -0,0 +1,72 @@ +/* + + +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 main + +import ( + "flag" + "os" + + "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + // +kubebuilder:scaffold:imports +) + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") +) + +func init() { + _ = clientgoscheme.AddToScheme(scheme) + + // +kubebuilder:scaffold:scheme +} + +func main() { + var metricsAddr string + var enableLeaderElection bool + flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") + flag.BoolVar(&enableLeaderElection, "enable-leader-election", false, + "Enable leader election for controller manager. "+ + "Enabling this will ensure there is only one active controller manager.") + flag.Parse() + + ctrl.SetLogger(zap.New(zap.UseDevMode(true))) + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + MetricsBindAddress: metricsAddr, + Port: 9443, + LeaderElection: enableLeaderElection, + LeaderElectionID: "059bfcb5.io", + }) + if err != nil { + setupLog.Error(err, "unable to start manager") + os.Exit(1) + } + + // +kubebuilder:scaffold:builder + + setupLog.Info("starting manager") + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) + } +} From ce3e4b13a8c4e9cbdd20a46905d20abb26b793c1 Mon Sep 17 00:00:00 2001 From: Carlisia Date: Thu, 14 May 2020 09:11:12 -0700 Subject: [PATCH 03/34] Tweak makefile to accomodate kubebuilder expectations Signed-off-by: Carlisia --- Makefile | 2 +- PROJECT | 4 + api/v1/backupstoragelocation_types.go | 148 +++++++ api/v1/groupversion_info.go | 36 ++ api/v1/zz_generated.deepcopy.go | 172 ++++++++ config/crd/bases/velero.io_backups.yaml | 417 ++++++++++++++++++ .../velero.io_backupstoragelocations.yaml | 134 ++++++ .../bases/velero.io_deletebackuprequests.yaml | 72 +++ .../crd/bases/velero.io_downloadrequests.yaml | 93 ++++ .../crd/bases/velero.io_podvolumebackups.yaml | 161 +++++++ .../bases/velero.io_podvolumerestores.yaml | 144 ++++++ .../bases/velero.io_resticrepositories.yaml | 88 ++++ config/crd/bases/velero.io_restores.yaml | 188 ++++++++ config/crd/bases/velero.io_schedules.yaml | 374 ++++++++++++++++ .../bases/velero.io_serverstatusrequests.yaml | 84 ++++ .../velero.io_volumesnapshotlocations.yaml | 73 +++ config/crd/kustomization.yaml | 21 + config/crd/kustomizeconfig.yaml | 17 + ...cainjection_in_backupstoragelocations.yaml | 8 + .../webhook_in_backupstoragelocations.yaml | 17 + .../backupstoragelocation_editor_role.yaml | 24 + .../backupstoragelocation_viewer_role.yaml | 20 + .../velero_v1_backupstoragelocation.yaml | 7 + main.go | 3 + pkg/apis/velero/v1/zz_generated.deepcopy.go | 57 +-- 25 files changed, 2308 insertions(+), 56 deletions(-) create mode 100644 api/v1/backupstoragelocation_types.go create mode 100644 api/v1/groupversion_info.go create mode 100644 api/v1/zz_generated.deepcopy.go create mode 100644 config/crd/bases/velero.io_backups.yaml create mode 100644 config/crd/bases/velero.io_backupstoragelocations.yaml create mode 100644 config/crd/bases/velero.io_deletebackuprequests.yaml create mode 100644 config/crd/bases/velero.io_downloadrequests.yaml create mode 100644 config/crd/bases/velero.io_podvolumebackups.yaml create mode 100644 config/crd/bases/velero.io_podvolumerestores.yaml create mode 100644 config/crd/bases/velero.io_resticrepositories.yaml create mode 100644 config/crd/bases/velero.io_restores.yaml create mode 100644 config/crd/bases/velero.io_schedules.yaml create mode 100644 config/crd/bases/velero.io_serverstatusrequests.yaml create mode 100644 config/crd/bases/velero.io_volumesnapshotlocations.yaml create mode 100644 config/crd/kustomization.yaml create mode 100644 config/crd/kustomizeconfig.yaml create mode 100644 config/crd/patches/cainjection_in_backupstoragelocations.yaml create mode 100644 config/crd/patches/webhook_in_backupstoragelocations.yaml create mode 100644 config/rbac/backupstoragelocation_editor_role.yaml create mode 100644 config/rbac/backupstoragelocation_viewer_role.yaml create mode 100644 config/samples/velero_v1_backupstoragelocation.yaml diff --git a/Makefile b/Makefile index 9690683a09..2397e51651 100644 --- a/Makefile +++ b/Makefile @@ -85,7 +85,7 @@ IMAGE ?= $(REGISTRY)/$(BIN)-$(GOARCH) # If you want to build all binaries, see the 'all-build' rule. # If you want to build all containers, see the 'all-containers' rule. # If you want to build AND push all containers, see the 'all-push' rule. -all: generate manifests +all-targets: all @$(MAKE) build @$(MAKE) build BIN=velero-restic-restore-helper diff --git a/PROJECT b/PROJECT index 4fd590919a..88609cdaad 100644 --- a/PROJECT +++ b/PROJECT @@ -1,3 +1,7 @@ domain: io repo: github.com/vmware-tanzu/velero +resources: +- group: velero + kind: BackupStorageLocation + version: v1 version: "2" diff --git a/api/v1/backupstoragelocation_types.go b/api/v1/backupstoragelocation_types.go new file mode 100644 index 0000000000..595bbb827e --- /dev/null +++ b/api/v1/backupstoragelocation_types.go @@ -0,0 +1,148 @@ +/* +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 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// BackupStorageLocationSpec defines the desired state of a Velero BackupStorageLocation +type BackupStorageLocationSpec struct { + // Provider is the provider of the backup storage. + Provider string `json:"provider"` + + // Config is for provider-specific configuration fields. + // +optional + Config map[string]string `json:"config,omitempty"` + + StorageType `json:",inline"` + + // AccessMode defines the permissions for the backup storage location. + // +optional + AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"` + + // BackupSyncPeriod defines how frequently to sync backup API objects from object storage. A value of 0 disables sync. + // +optional + // +nullable + BackupSyncPeriod *metav1.Duration `json:"backupSyncPeriod,omitempty"` +} + +// BackupStorageLocationStatus defines the observed state of BackupStorageLocation +type BackupStorageLocationStatus struct { + // Phase is the current state of the BackupStorageLocation. + // +optional + Phase BackupStorageLocationPhase `json:"phase,omitempty"` + + // LastSyncedTime is the last time the contents of the location were synced into + // the cluster. + // +optional + // +nullable + LastSyncedTime *metav1.Time `json:"lastSyncedTime,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. + // + // Deprecated: this field is no longer updated or used for detecting changes to + // the location's contents and will be removed entirely in v2.0. + // +optional + LastSyncedRevision types.UID `json:"lastSyncedRevision,omitempty"` + + // AccessMode is an unused field. + // + // Deprecated: there is now an AccessMode field on the Spec and this field + // will be removed entirely as of v2.0. + // +optional + AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:storageversion +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="Backup Storage Location status such as Available/Unavailable" + +// BackupStorageLocation is the Schema for the backupstoragelocations API +type BackupStorageLocation struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec BackupStorageLocationSpec `json:"spec,omitempty"` + Status BackupStorageLocationStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// BackupStorageLocationList contains a list of BackupStorageLocation +type BackupStorageLocationList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []BackupStorageLocation `json:"items"` +} + +func init() { + SchemeBuilder.Register(&BackupStorageLocation{}, &BackupStorageLocationList{}) +} + +// StorageType represents the type of storage that a backup location uses. +// ObjectStorage must be non-nil, since it is currently the only supported StorageType. +type StorageType struct { + ObjectStorage *ObjectStorageLocation `json:"objectStorage"` +} + +// ObjectStorageLocation specifies the settings necessary to connect to a provider's object storage. +type ObjectStorageLocation struct { + // Bucket is the bucket to use for object storage. + Bucket string `json:"bucket"` + + // Prefix is the path inside a bucket to use for Velero storage. Optional. + // +optional + Prefix string `json:"prefix,omitempty"` + + // CACert defines a CA bundle to use when verifying TLS connections to the provider. + // +optional + CACert []byte `json:"caCert,omitempty"` +} + +// BackupStorageLocationPhase is the lifecyle phase of a Velero BackupStorageLocation. +// +kubebuilder:validation:Enum=Available;Unavailable +type BackupStorageLocationPhase string + +const ( + // BackupStorageLocationPhaseAvailable means the location is available to read and write from. + BackupStorageLocationPhaseAvailable BackupStorageLocationPhase = "Available" + + // BackupStorageLocationPhaseUnavailable means the location is unavailable to read and write from. + BackupStorageLocationPhaseUnavailable BackupStorageLocationPhase = "Unavailable" +) + +// BackupStorageLocationAccessMode represents the permissions for a BackupStorageLocation. +// +kubebuilder:validation:Enum=ReadOnly;ReadWrite +type BackupStorageLocationAccessMode string + +const ( + // BackupStorageLocationAccessModeReadOnly represents read-only access to a BackupStorageLocation. + BackupStorageLocationAccessModeReadOnly BackupStorageLocationAccessMode = "ReadOnly" + + // BackupStorageLocationAccessModeReadWrite represents read and write access to a BackupStorageLocation. + BackupStorageLocationAccessModeReadWrite BackupStorageLocationAccessMode = "ReadWrite" +) + +// TODO(2.0): remove the AccessMode field from BackupStorageLocationStatus. +// TODO(2.0): remove the LastSyncedRevision field from BackupStorageLocationStatus. diff --git a/api/v1/groupversion_info.go b/api/v1/groupversion_info.go new file mode 100644 index 0000000000..9bb6d76c27 --- /dev/null +++ b/api/v1/groupversion_info.go @@ -0,0 +1,36 @@ +/* +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 contains API Schema definitions for the velero v1 API group +// +kubebuilder:object:generate=true +// +groupName=velero.io +package v1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "velero.io", Version: "v1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..14c8056b1c --- /dev/null +++ b/api/v1/zz_generated.deepcopy.go @@ -0,0 +1,172 @@ +// +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 controller-gen. DO NOT EDIT. + +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupStorageLocation) DeepCopyInto(out *BackupStorageLocation) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocation. +func (in *BackupStorageLocation) DeepCopy() *BackupStorageLocation { + if in == nil { + return nil + } + out := new(BackupStorageLocation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BackupStorageLocation) 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 *BackupStorageLocationList) DeepCopyInto(out *BackupStorageLocationList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]BackupStorageLocation, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocationList. +func (in *BackupStorageLocationList) DeepCopy() *BackupStorageLocationList { + if in == nil { + return nil + } + out := new(BackupStorageLocationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BackupStorageLocationList) 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 *BackupStorageLocationSpec) DeepCopyInto(out *BackupStorageLocationSpec) { + *out = *in + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.StorageType.DeepCopyInto(&out.StorageType) + if in.BackupSyncPeriod != nil { + in, out := &in.BackupSyncPeriod, &out.BackupSyncPeriod + *out = new(metav1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocationSpec. +func (in *BackupStorageLocationSpec) DeepCopy() *BackupStorageLocationSpec { + if in == nil { + return nil + } + out := new(BackupStorageLocationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupStorageLocationStatus) DeepCopyInto(out *BackupStorageLocationStatus) { + *out = *in + if in.LastSyncedTime != nil { + in, out := &in.LastSyncedTime, &out.LastSyncedTime + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocationStatus. +func (in *BackupStorageLocationStatus) DeepCopy() *BackupStorageLocationStatus { + if in == nil { + return nil + } + out := new(BackupStorageLocationStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectStorageLocation) DeepCopyInto(out *ObjectStorageLocation) { + *out = *in + if in.CACert != nil { + in, out := &in.CACert, &out.CACert + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectStorageLocation. +func (in *ObjectStorageLocation) DeepCopy() *ObjectStorageLocation { + if in == nil { + return nil + } + out := new(ObjectStorageLocation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StorageType) DeepCopyInto(out *StorageType) { + *out = *in + if in.ObjectStorage != nil { + in, out := &in.ObjectStorage, &out.ObjectStorage + *out = new(ObjectStorageLocation) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageType. +func (in *StorageType) DeepCopy() *StorageType { + if in == nil { + return nil + } + out := new(StorageType) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/velero.io_backups.yaml b/config/crd/bases/velero.io_backups.yaml new file mode 100644 index 0000000000..876d46f452 --- /dev/null +++ b/config/crd/bases/velero.io_backups.yaml @@ -0,0 +1,417 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: backups.velero.io +spec: + group: velero.io + names: + kind: Backup + listKind: BackupList + plural: backups + singular: backup + scope: Namespaced + validation: + openAPIV3Schema: + description: Backup is a Velero resource that respresents the capture of Kubernetes + cluster state at a point in time (API objects and associated volume state). + 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: BackupSpec defines the specification for a Velero backup. + properties: + excludedNamespaces: + description: ExcludedNamespaces contains a list of namespaces that are + not included in the backup. + items: + type: string + nullable: true + type: array + excludedResources: + description: ExcludedResources is a slice of resource names that are + not included in the backup. + items: + type: string + nullable: true + type: array + hooks: + description: Hooks represent custom behaviors that should be executed + at different phases of the backup. + properties: + resources: + description: Resources are hooks that should be executed when backing + up individual instances of a resource. + items: + description: BackupResourceHookSpec defines one or more BackupResourceHooks + that should be executed based on the rules defined for namespaces, + resources, and label selector. + properties: + excludedNamespaces: + description: ExcludedNamespaces specifies the namespaces to + which this hook spec does not apply. + items: + type: string + nullable: true + type: array + excludedResources: + description: ExcludedResources specifies the resources to + which this hook spec does not apply. + items: + type: string + nullable: true + type: array + includedNamespaces: + description: IncludedNamespaces specifies the namespaces to + which this hook spec applies. If empty, it applies to all + namespaces. + items: + type: string + nullable: true + type: array + includedResources: + description: IncludedResources specifies the resources to + which this hook spec applies. If empty, it applies to all + resources. + items: + type: string + nullable: true + type: array + labelSelector: + description: LabelSelector, if specified, filters the resources + to which this hook spec applies. + nullable: true + 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 + name: + description: Name is the name of this hook. + type: string + post: + description: PostHooks is a list of BackupResourceHooks to + execute after storing the item in the backup. These are + executed after all "additional items" from item actions + are processed. + items: + description: BackupResourceHook defines a hook for a resource. + properties: + exec: + description: Exec defines an exec hook. + properties: + command: + description: Command is the command and arguments + to execute. + items: + type: string + minItems: 1 + type: array + container: + description: Container is the container in the pod + where the command should be executed. If not specified, + the pod's first container is used. + type: string + onError: + description: OnError specifies how Velero should + behave if it encounters an error executing this + hook. + enum: + - Continue + - Fail + type: string + timeout: + description: Timeout defines the maximum amount + of time Velero should wait for the hook to complete + before considering the execution a failure. + type: string + required: + - command + type: object + required: + - exec + type: object + type: array + pre: + description: PreHooks is a list of BackupResourceHooks to + execute prior to storing the item in the backup. These are + executed before any "additional items" from item actions + are processed. + items: + description: BackupResourceHook defines a hook for a resource. + properties: + exec: + description: Exec defines an exec hook. + properties: + command: + description: Command is the command and arguments + to execute. + items: + type: string + minItems: 1 + type: array + container: + description: Container is the container in the pod + where the command should be executed. If not specified, + the pod's first container is used. + type: string + onError: + description: OnError specifies how Velero should + behave if it encounters an error executing this + hook. + enum: + - Continue + - Fail + type: string + timeout: + description: Timeout defines the maximum amount + of time Velero should wait for the hook to complete + before considering the execution a failure. + type: string + required: + - command + type: object + required: + - exec + type: object + type: array + required: + - name + type: object + nullable: true + type: array + type: object + includeClusterResources: + description: IncludeClusterResources specifies whether cluster-scoped + resources should be included for consideration in the backup. + nullable: true + type: boolean + includedNamespaces: + description: IncludedNamespaces is a slice of namespace names to include + objects from. If empty, all namespaces are included. + items: + type: string + nullable: true + type: array + includedResources: + description: IncludedResources is a slice of resource names to include + in the backup. If empty, all resources are included. + items: + type: string + nullable: true + type: array + labelSelector: + description: LabelSelector is a metav1.LabelSelector to filter with + when adding individual objects to the backup. If empty or nil, all + objects are included. Optional. + nullable: true + 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 + snapshotVolumes: + description: SnapshotVolumes specifies whether to take cloud snapshots + of any PV's referenced in the set of objects included in the Backup. + nullable: true + type: boolean + storageLocation: + description: StorageLocation is a string containing the name of a BackupStorageLocation + where the backup should be stored. + type: string + ttl: + description: TTL is a time.Duration-parseable string describing how + long the Backup should be retained for. + type: string + volumeSnapshotLocations: + description: VolumeSnapshotLocations is a list containing names of VolumeSnapshotLocations + associated with this backup. + items: + type: string + type: array + type: object + status: + description: BackupStatus captures the current status of a Velero backup. + properties: + completionTimestamp: + description: CompletionTimestamp records the time a backup was completed. + Completion time is recorded even on failed backups. Completion time + is recorded before uploading the backup object. The server's time + is used for CompletionTimestamps + format: date-time + nullable: true + type: string + 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 + log file in object storage. + type: integer + expiration: + description: Expiration is when this Backup is eligible for garbage-collection. + format: date-time + nullable: true + type: string + formatVersion: + description: FormatVersion is the backup format version, including major, + minor, and patch version. + type: string + phase: + description: Phase is the current state of the Backup. + enum: + - New + - FailedValidation + - InProgress + - Completed + - PartiallyFailed + - Failed + - Deleting + type: string + progress: + description: Progress contains information about the backup's execution + progress. Note that this information is best-effort only -- if Velero + fails to update it during a backup for any reason, it may be inaccurate/stale. + properties: + itemsBackedUp: + description: ItemsBackedUp is the number of items that have actually + been written to the backup tarball so far. + type: integer + totalItems: + description: TotalItems is the total number of items to be backed + up. This number may change throughout the execution of the backup + due to plugins that return additional related items to back up, + the velero.io/exclude-from-backup label, and various other filters + that happen as items are processed. + type: integer + type: object + startTimestamp: + description: StartTimestamp records the time a backup was started. Separate + from CreationTimestamp, since that value changes on restores. The + server's time is used for StartTimestamps + format: date-time + nullable: true + type: string + validationErrors: + description: ValidationErrors is a slice of all validation errors (if + applicable). + items: + type: string + nullable: true + type: array + version: + description: 'Version is the backup format major version. Deprecated: + Please see FormatVersion' + type: integer + volumeSnapshotsAttempted: + description: VolumeSnapshotsAttempted is the total number of attempted + volume snapshots for this backup. + type: integer + volumeSnapshotsCompleted: + description: VolumeSnapshotsCompleted is the total number of successfully + completed volume snapshots for this backup. + type: integer + warnings: + description: 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. + type: integer + type: object + type: object + version: v1 + versions: + - name: v1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/velero.io_backupstoragelocations.yaml b/config/crd/bases/velero.io_backupstoragelocations.yaml new file mode 100644 index 0000000000..9e650168b3 --- /dev/null +++ b/config/crd/bases/velero.io_backupstoragelocations.yaml @@ -0,0 +1,134 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: backupstoragelocations.velero.io +spec: + additionalPrinterColumns: + - JSONPath: .status.phase + description: Backup Storage Location status such as Available/Unavailable + name: Phase + type: string + group: velero.io + names: + kind: BackupStorageLocation + listKind: BackupStorageLocationList + plural: backupstoragelocations + singular: backupstoragelocation + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + description: BackupStorageLocation is the Schema for the backupstoragelocations + 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: BackupStorageLocationSpec defines the desired state of a Velero + BackupStorageLocation + properties: + accessMode: + description: AccessMode defines the permissions for the backup storage + location. + enum: + - ReadOnly + - ReadWrite + type: string + backupSyncPeriod: + description: BackupSyncPeriod defines how frequently to sync backup + API objects from object storage. A value of 0 disables sync. + nullable: true + type: string + config: + additionalProperties: + type: string + description: Config is for provider-specific configuration fields. + type: object + objectStorage: + description: ObjectStorageLocation specifies the settings necessary + to connect to a provider's object storage. + properties: + bucket: + description: Bucket is the bucket to use for object storage. + type: string + caCert: + description: CACert defines a CA bundle to use when verifying TLS + connections to the provider. + format: byte + type: string + prefix: + description: Prefix is the path inside a bucket to use for Velero + storage. Optional. + type: string + required: + - bucket + type: object + provider: + description: Provider is the provider of the backup storage. + type: string + required: + - objectStorage + - provider + type: object + status: + description: BackupStorageLocationStatus defines the observed state of BackupStorageLocation + properties: + accessMode: + description: "AccessMode is an unused field. \n Deprecated: there is + now an AccessMode field on the Spec and this field will be removed + entirely as of v2.0." + enum: + - ReadOnly + - ReadWrite + type: string + lastSyncedRevision: + description: "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. \n Deprecated: this field is no longer + updated or used for detecting changes to the location's contents and + will be removed entirely in v2.0." + type: string + lastSyncedTime: + description: LastSyncedTime is the last time the contents of the location + were synced into the cluster. + format: date-time + nullable: true + type: string + phase: + description: Phase is the current state of the BackupStorageLocation. + enum: + - Available + - Unavailable + type: string + type: object + type: object + version: v1 + versions: + - name: v1 + served: true + storage: true + - name: v1 + served: true + storage: false +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/velero.io_deletebackuprequests.yaml b/config/crd/bases/velero.io_deletebackuprequests.yaml new file mode 100644 index 0000000000..01503bf05b --- /dev/null +++ b/config/crd/bases/velero.io_deletebackuprequests.yaml @@ -0,0 +1,72 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: deletebackuprequests.velero.io +spec: + group: velero.io + names: + kind: DeleteBackupRequest + listKind: DeleteBackupRequestList + plural: deletebackuprequests + singular: deletebackuprequest + scope: Namespaced + validation: + openAPIV3Schema: + description: DeleteBackupRequest is a request to delete one or more backups. + 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: DeleteBackupRequestSpec is the specification for which backups + to delete. + properties: + backupName: + type: string + required: + - backupName + type: object + status: + description: DeleteBackupRequestStatus is the current status of a DeleteBackupRequest. + properties: + errors: + description: Errors contains any errors that were encountered during + the deletion process. + items: + type: string + nullable: true + type: array + phase: + description: Phase is the current state of the DeleteBackupRequest. + enum: + - New + - InProgress + - Processed + type: string + type: object + type: object + version: v1 + versions: + - name: v1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/velero.io_downloadrequests.yaml b/config/crd/bases/velero.io_downloadrequests.yaml new file mode 100644 index 0000000000..491bd11d65 --- /dev/null +++ b/config/crd/bases/velero.io_downloadrequests.yaml @@ -0,0 +1,93 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: downloadrequests.velero.io +spec: + group: velero.io + names: + kind: DownloadRequest + listKind: DownloadRequestList + plural: downloadrequests + singular: downloadrequest + scope: Namespaced + validation: + openAPIV3Schema: + description: DownloadRequest is a request to download an artifact from backup + object storage, such as a backup log file. + 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: DownloadRequestSpec is the specification for a download request. + properties: + target: + description: Target is what to download (e.g. logs for a backup). + properties: + kind: + description: Kind is the type of file to download. + enum: + - BackupLog + - BackupContents + - BackupVolumeSnapshots + - BackupResourceList + - RestoreLog + - RestoreResults + type: string + name: + description: Name is the name of the kubernetes resource with which + the file is associated. + type: string + required: + - kind + - name + type: object + required: + - target + type: object + status: + description: DownloadRequestStatus is the current status of a DownloadRequest. + properties: + downloadURL: + description: DownloadURL contains the pre-signed URL for the target + file. + type: string + expiration: + description: Expiration is when this DownloadRequest expires and can + be deleted by the system. + format: date-time + nullable: true + type: string + phase: + description: Phase is the current state of the DownloadRequest. + enum: + - New + - Processed + type: string + type: object + type: object + version: v1 + versions: + - name: v1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/velero.io_podvolumebackups.yaml b/config/crd/bases/velero.io_podvolumebackups.yaml new file mode 100644 index 0000000000..4dfbdc4f03 --- /dev/null +++ b/config/crd/bases/velero.io_podvolumebackups.yaml @@ -0,0 +1,161 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: podvolumebackups.velero.io +spec: + group: velero.io + names: + kind: PodVolumeBackup + listKind: PodVolumeBackupList + plural: podvolumebackups + singular: podvolumebackup + 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/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: PodVolumeBackupSpec is the specification for a PodVolumeBackup. + properties: + backupStorageLocation: + description: BackupStorageLocation is the name of the backup storage + location where the restic repository is stored. + type: string + node: + description: Node is the name of the node that the Pod is running on. + type: string + pod: + description: Pod is a reference to the pod containing the volume to + be backed up. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + repoIdentifier: + description: RepoIdentifier is the restic repository identifier. + type: string + tags: + additionalProperties: + type: string + description: Tags are a map of key-value pairs that should be applied + to the volume backup as tags. + type: object + volume: + description: Volume is the name of the volume within the Pod to be backed + up. + type: string + required: + - backupStorageLocation + - node + - pod + - repoIdentifier + - volume + type: object + status: + description: PodVolumeBackupStatus is the current status of a PodVolumeBackup. + properties: + completionTimestamp: + description: CompletionTimestamp records the time a backup was completed. + Completion time is recorded even on failed backups. Completion time + is recorded before uploading the backup object. The server's time + is used for CompletionTimestamps + format: date-time + nullable: true + type: string + message: + description: Message is a message about the pod volume backup's status. + type: string + path: + description: Path is the full path within the controller pod being backed + up. + type: string + phase: + description: Phase is the current state of the PodVolumeBackup. + enum: + - New + - InProgress + - Completed + - Failed + type: string + progress: + description: Progress holds the total number of bytes of the volume + and the current number of backed up bytes. This can be used to display + progress information about the backup operation. + properties: + bytesDone: + format: int64 + type: integer + totalBytes: + format: int64 + type: integer + type: object + snapshotID: + description: SnapshotID is the identifier for the snapshot of the pod + volume. + type: string + startTimestamp: + description: StartTimestamp records the time a backup was started. Separate + from CreationTimestamp, since that value changes on restores. The + server's time is used for StartTimestamps + format: date-time + nullable: true + type: string + type: object + type: object + version: v1 + versions: + - name: v1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/velero.io_podvolumerestores.yaml b/config/crd/bases/velero.io_podvolumerestores.yaml new file mode 100644 index 0000000000..0a8d377dbe --- /dev/null +++ b/config/crd/bases/velero.io_podvolumerestores.yaml @@ -0,0 +1,144 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: podvolumerestores.velero.io +spec: + group: velero.io + names: + kind: PodVolumeRestore + listKind: PodVolumeRestoreList + plural: podvolumerestores + singular: podvolumerestore + 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/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: PodVolumeRestoreSpec is the specification for a PodVolumeRestore. + properties: + backupStorageLocation: + description: BackupStorageLocation is the name of the backup storage + location where the restic repository is stored. + type: string + pod: + description: Pod is a reference to the pod containing the volume to + be restored. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + repoIdentifier: + description: RepoIdentifier is the restic repository identifier. + type: string + snapshotID: + description: SnapshotID is the ID of the volume snapshot to be restored. + type: string + volume: + description: Volume is the name of the volume within the Pod to be restored. + type: string + required: + - backupStorageLocation + - pod + - repoIdentifier + - snapshotID + - volume + type: object + status: + description: PodVolumeRestoreStatus is the current status of a PodVolumeRestore. + properties: + completionTimestamp: + description: CompletionTimestamp records the time a restore was completed. + Completion time is recorded even on failed restores. The server's + time is used for CompletionTimestamps + format: date-time + nullable: true + type: string + message: + description: Message is a message about the pod volume restore's status. + type: string + phase: + description: Phase is the current state of the PodVolumeRestore. + enum: + - New + - InProgress + - Completed + - Failed + type: string + progress: + description: Progress holds the total number of bytes of the snapshot + and the current number of restored bytes. This can be used to display + progress information about the restore operation. + properties: + bytesDone: + format: int64 + type: integer + totalBytes: + format: int64 + type: integer + type: object + startTimestamp: + description: StartTimestamp records the time a restore was started. + The server's time is used for StartTimestamps + format: date-time + nullable: true + type: string + type: object + type: object + version: v1 + versions: + - name: v1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/velero.io_resticrepositories.yaml b/config/crd/bases/velero.io_resticrepositories.yaml new file mode 100644 index 0000000000..822477c3da --- /dev/null +++ b/config/crd/bases/velero.io_resticrepositories.yaml @@ -0,0 +1,88 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: resticrepositories.velero.io +spec: + group: velero.io + names: + kind: ResticRepository + listKind: ResticRepositoryList + plural: resticrepositories + singular: resticrepository + 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/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: ResticRepositorySpec is the specification for a ResticRepository. + properties: + backupStorageLocation: + description: BackupStorageLocation is the name of the BackupStorageLocation + that should contain this repository. + type: string + maintenanceFrequency: + description: MaintenanceFrequency is how often maintenance should be + run. + 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 + contains pod volume backups for. + type: string + required: + - backupStorageLocation + - maintenanceFrequency + - resticIdentifier + - volumeNamespace + type: object + status: + description: ResticRepositoryStatus is the current status of a ResticRepository. + properties: + lastMaintenanceTime: + description: LastMaintenanceTime is the last time maintenance was run. + format: date-time + nullable: true + type: string + message: + description: Message is a message about the current status of the ResticRepository. + type: string + phase: + description: Phase is the current state of the ResticRepository. + enum: + - New + - Ready + - NotReady + type: string + type: object + type: object + version: v1 + versions: + - name: v1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/velero.io_restores.yaml b/config/crd/bases/velero.io_restores.yaml new file mode 100644 index 0000000000..8d4b802f84 --- /dev/null +++ b/config/crd/bases/velero.io_restores.yaml @@ -0,0 +1,188 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: restores.velero.io +spec: + group: velero.io + names: + kind: Restore + listKind: RestoreList + plural: restores + singular: restore + scope: Namespaced + validation: + openAPIV3Schema: + description: Restore is a Velero resource that represents the application of + resources from a Velero backup to a target 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: RestoreSpec defines the specification for a Velero restore. + properties: + backupName: + description: BackupName is the unique name of the Velero backup to restore + from. + type: string + excludedNamespaces: + description: ExcludedNamespaces contains a list of namespaces that are + not included in the restore. + items: + type: string + nullable: true + type: array + excludedResources: + description: ExcludedResources is a slice of resource names that are + not included in the restore. + items: + type: string + nullable: true + type: array + includeClusterResources: + description: IncludeClusterResources specifies whether cluster-scoped + resources should be included for consideration in the restore. If + null, defaults to true. + nullable: true + type: boolean + includedNamespaces: + description: IncludedNamespaces is a slice of namespace names to include + objects from. If empty, all namespaces are included. + items: + type: string + nullable: true + type: array + includedResources: + description: IncludedResources is a slice of resource names to include + in the restore. If empty, all resources in the backup are included. + items: + type: string + nullable: true + type: array + labelSelector: + description: LabelSelector is a metav1.LabelSelector to filter with + when restoring individual objects from the backup. If empty or nil, + all objects are included. Optional. + nullable: true + 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 + namespaceMapping: + additionalProperties: + type: string + description: 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. + type: object + restorePVs: + description: RestorePVs specifies whether to restore all included PVs + from snapshot (via the cloudprovider). + nullable: true + type: boolean + scheduleName: + description: 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. + type: string + required: + - backupName + type: object + status: + description: RestoreStatus captures the current status of a Velero restore + properties: + errors: + description: Errors is a count of all error messages that were generated + during execution of the restore. The actual errors are stored in object + storage. + type: integer + failureReason: + description: FailureReason is an error that caused the entire restore + to fail. + type: string + phase: + description: Phase is the current state of the Restore + enum: + - New + - FailedValidation + - InProgress + - Completed + - PartiallyFailed + - Failed + type: string + validationErrors: + description: ValidationErrors is a slice of all validation errors (if + applicable) + items: + type: string + nullable: true + type: array + warnings: + description: Warnings is a count of all warning messages that were generated + during execution of the restore. The actual warnings are stored in + object storage. + type: integer + type: object + type: object + version: v1 + versions: + - name: v1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/velero.io_schedules.yaml b/config/crd/bases/velero.io_schedules.yaml new file mode 100644 index 0000000000..ab594cae42 --- /dev/null +++ b/config/crd/bases/velero.io_schedules.yaml @@ -0,0 +1,374 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: schedules.velero.io +spec: + group: velero.io + names: + kind: Schedule + listKind: ScheduleList + plural: schedules + singular: schedule + scope: Namespaced + validation: + openAPIV3Schema: + description: Schedule is a Velero resource that represents a pre-scheduled or + periodic Backup that should be run. + 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: ScheduleSpec defines the specification for a Velero schedule + properties: + schedule: + description: Schedule is a Cron expression defining when to run the + Backup. + type: string + template: + description: Template is the definition of the Backup to be run on the + provided schedule + properties: + excludedNamespaces: + description: ExcludedNamespaces contains a list of namespaces that + are not included in the backup. + items: + type: string + nullable: true + type: array + excludedResources: + description: ExcludedResources is a slice of resource names that + are not included in the backup. + items: + type: string + nullable: true + type: array + hooks: + description: Hooks represent custom behaviors that should be executed + at different phases of the backup. + properties: + resources: + description: Resources are hooks that should be executed when + backing up individual instances of a resource. + items: + description: BackupResourceHookSpec defines one or more BackupResourceHooks + that should be executed based on the rules defined for namespaces, + resources, and label selector. + properties: + excludedNamespaces: + description: ExcludedNamespaces specifies the namespaces + to which this hook spec does not apply. + items: + type: string + nullable: true + type: array + excludedResources: + description: ExcludedResources specifies the resources + to which this hook spec does not apply. + items: + type: string + nullable: true + type: array + includedNamespaces: + description: IncludedNamespaces specifies the namespaces + to which this hook spec applies. If empty, it applies + to all namespaces. + items: + type: string + nullable: true + type: array + includedResources: + description: IncludedResources specifies the resources + to which this hook spec applies. If empty, it applies + to all resources. + items: + type: string + nullable: true + type: array + labelSelector: + description: LabelSelector, if specified, filters the + resources to which this hook spec applies. + nullable: true + 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 + name: + description: Name is the name of this hook. + type: string + post: + description: PostHooks is a list of BackupResourceHooks + to execute after storing the item in the backup. These + are executed after all "additional items" from item + actions are processed. + items: + description: BackupResourceHook defines a hook for a + resource. + properties: + exec: + description: Exec defines an exec hook. + properties: + command: + description: Command is the command and arguments + to execute. + items: + type: string + minItems: 1 + type: array + container: + description: Container is the container in the + pod where the command should be executed. + If not specified, the pod's first container + is used. + type: string + onError: + description: OnError specifies how Velero should + behave if it encounters an error executing + this hook. + enum: + - Continue + - Fail + type: string + timeout: + description: Timeout defines the maximum amount + of time Velero should wait for the hook to + complete before considering the execution + a failure. + type: string + required: + - command + type: object + required: + - exec + type: object + type: array + pre: + description: PreHooks is a list of BackupResourceHooks + to execute prior to storing the item in the backup. + These are executed before any "additional items" from + item actions are processed. + items: + description: BackupResourceHook defines a hook for a + resource. + properties: + exec: + description: Exec defines an exec hook. + properties: + command: + description: Command is the command and arguments + to execute. + items: + type: string + minItems: 1 + type: array + container: + description: Container is the container in the + pod where the command should be executed. + If not specified, the pod's first container + is used. + type: string + onError: + description: OnError specifies how Velero should + behave if it encounters an error executing + this hook. + enum: + - Continue + - Fail + type: string + timeout: + description: Timeout defines the maximum amount + of time Velero should wait for the hook to + complete before considering the execution + a failure. + type: string + required: + - command + type: object + required: + - exec + type: object + type: array + required: + - name + type: object + nullable: true + type: array + type: object + includeClusterResources: + description: IncludeClusterResources specifies whether cluster-scoped + resources should be included for consideration in the backup. + nullable: true + type: boolean + includedNamespaces: + description: IncludedNamespaces is a slice of namespace names to + include objects from. If empty, all namespaces are included. + items: + type: string + nullable: true + type: array + includedResources: + description: IncludedResources is a slice of resource names to include + in the backup. If empty, all resources are included. + items: + type: string + nullable: true + type: array + labelSelector: + description: LabelSelector is a metav1.LabelSelector to filter with + when adding individual objects to the backup. If empty or nil, + all objects are included. Optional. + nullable: true + 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 + snapshotVolumes: + description: SnapshotVolumes specifies whether to take cloud snapshots + of any PV's referenced in the set of objects included in the Backup. + nullable: true + type: boolean + storageLocation: + description: StorageLocation is a string containing the name of + a BackupStorageLocation where the backup should be stored. + type: string + ttl: + description: TTL is a time.Duration-parseable string describing + how long the Backup should be retained for. + type: string + volumeSnapshotLocations: + description: VolumeSnapshotLocations is a list containing names + of VolumeSnapshotLocations associated with this backup. + items: + type: string + type: array + type: object + required: + - schedule + - template + type: object + status: + description: ScheduleStatus captures the current state of a Velero schedule + properties: + lastBackup: + description: LastBackup is the last time a Backup was run for this Schedule + schedule + format: date-time + nullable: true + type: string + phase: + description: Phase is the current phase of the Schedule + enum: + - New + - Enabled + - FailedValidation + type: string + validationErrors: + description: ValidationErrors is a slice of all validation errors (if + applicable) + items: + type: string + type: array + type: object + type: object + version: v1 + versions: + - name: v1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/velero.io_serverstatusrequests.yaml b/config/crd/bases/velero.io_serverstatusrequests.yaml new file mode 100644 index 0000000000..731fa4ae64 --- /dev/null +++ b/config/crd/bases/velero.io_serverstatusrequests.yaml @@ -0,0 +1,84 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: serverstatusrequests.velero.io +spec: + group: velero.io + names: + kind: ServerStatusRequest + listKind: ServerStatusRequestList + plural: serverstatusrequests + singular: serverstatusrequest + scope: Namespaced + validation: + openAPIV3Schema: + description: ServerStatusRequest is a request to access current status information + about the Velero server. + 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: ServerStatusRequestSpec is the specification for a ServerStatusRequest. + type: object + status: + description: ServerStatusRequestStatus is the current status of a ServerStatusRequest. + properties: + phase: + description: Phase is the current lifecycle phase of the ServerStatusRequest. + enum: + - New + - Processed + type: string + plugins: + description: Plugins list information about the plugins running on the + Velero server + items: + description: PluginInfo contains attributes of a Velero plugin + properties: + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + nullable: true + type: array + processedTimestamp: + description: ProcessedTimestamp is when the ServerStatusRequest was + processed by the ServerStatusRequestController. + format: date-time + nullable: true + type: string + serverVersion: + description: ServerVersion is the Velero server version. + type: string + type: object + type: object + version: v1 + versions: + - name: v1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/velero.io_volumesnapshotlocations.yaml b/config/crd/bases/velero.io_volumesnapshotlocations.yaml new file mode 100644 index 0000000000..a45b0ea79f --- /dev/null +++ b/config/crd/bases/velero.io_volumesnapshotlocations.yaml @@ -0,0 +1,73 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: volumesnapshotlocations.velero.io +spec: + group: velero.io + names: + kind: VolumeSnapshotLocation + listKind: VolumeSnapshotLocationList + plural: volumesnapshotlocations + singular: volumesnapshotlocation + scope: Namespaced + validation: + openAPIV3Schema: + description: VolumeSnapshotLocation is a location where Velero stores volume + snapshots. + 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: VolumeSnapshotLocationSpec defines the specification for a + Velero VolumeSnapshotLocation. + properties: + config: + additionalProperties: + type: string + description: Config is for provider-specific configuration fields. + type: object + provider: + description: Provider is the provider of the volume storage. + type: string + required: + - provider + type: object + status: + description: VolumeSnapshotLocationStatus describes the current status of + a Velero VolumeSnapshotLocation. + properties: + phase: + description: VolumeSnapshotLocationPhase is the lifecyle phase of a + Velero VolumeSnapshotLocation. + enum: + - Available + - Unavailable + type: string + type: object + type: object + version: v1 + versions: + - name: v1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml new file mode 100644 index 0000000000..8f3656ec54 --- /dev/null +++ b/config/crd/kustomization.yaml @@ -0,0 +1,21 @@ +# This kustomization.yaml is not intended to be run by itself, +# since it depends on service name and namespace that are out of this kustomize package. +# It should be run by config/default +resources: +- bases/velero.io_backupstoragelocations.yaml +# +kubebuilder:scaffold:crdkustomizeresource + +patchesStrategicMerge: +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. +# patches here are for enabling the conversion webhook for each CRD +#- patches/webhook_in_backupstoragelocations.yaml +# +kubebuilder:scaffold:crdkustomizewebhookpatch + +# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. +# patches here are for enabling the CA injection for each CRD +#- patches/cainjection_in_backupstoragelocations.yaml +# +kubebuilder:scaffold:crdkustomizecainjectionpatch + +# the following config is for teaching kustomize how to do kustomization for CRDs. +configurations: +- kustomizeconfig.yaml diff --git a/config/crd/kustomizeconfig.yaml b/config/crd/kustomizeconfig.yaml new file mode 100644 index 0000000000..6f83d9a94b --- /dev/null +++ b/config/crd/kustomizeconfig.yaml @@ -0,0 +1,17 @@ +# This file is for teaching kustomize how to substitute name and namespace reference in CRD +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: CustomResourceDefinition + group: apiextensions.k8s.io + path: spec/conversion/webhookClientConfig/service/name + +namespace: +- kind: CustomResourceDefinition + group: apiextensions.k8s.io + path: spec/conversion/webhookClientConfig/service/namespace + create: false + +varReference: +- path: metadata/annotations diff --git a/config/crd/patches/cainjection_in_backupstoragelocations.yaml b/config/crd/patches/cainjection_in_backupstoragelocations.yaml new file mode 100644 index 0000000000..2b989b19dd --- /dev/null +++ b/config/crd/patches/cainjection_in_backupstoragelocations.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: backupstoragelocations.velero.io diff --git a/config/crd/patches/webhook_in_backupstoragelocations.yaml b/config/crd/patches/webhook_in_backupstoragelocations.yaml new file mode 100644 index 0000000000..8851a00174 --- /dev/null +++ b/config/crd/patches/webhook_in_backupstoragelocations.yaml @@ -0,0 +1,17 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: backupstoragelocations.velero.io +spec: + conversion: + strategy: Webhook + webhookClientConfig: + # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, + # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/config/rbac/backupstoragelocation_editor_role.yaml b/config/rbac/backupstoragelocation_editor_role.yaml new file mode 100644 index 0000000000..56ee2eab4e --- /dev/null +++ b/config/rbac/backupstoragelocation_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit backupstoragelocations. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: backupstoragelocation-editor-role +rules: +- apiGroups: + - velero.io + resources: + - backupstoragelocations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - velero.io + resources: + - backupstoragelocations/status + verbs: + - get diff --git a/config/rbac/backupstoragelocation_viewer_role.yaml b/config/rbac/backupstoragelocation_viewer_role.yaml new file mode 100644 index 0000000000..c82adae7ba --- /dev/null +++ b/config/rbac/backupstoragelocation_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view backupstoragelocations. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: backupstoragelocation-viewer-role +rules: +- apiGroups: + - velero.io + resources: + - backupstoragelocations + verbs: + - get + - list + - watch +- apiGroups: + - velero.io + resources: + - backupstoragelocations/status + verbs: + - get diff --git a/config/samples/velero_v1_backupstoragelocation.yaml b/config/samples/velero_v1_backupstoragelocation.yaml new file mode 100644 index 0000000000..0383e1b887 --- /dev/null +++ b/config/samples/velero_v1_backupstoragelocation.yaml @@ -0,0 +1,7 @@ +apiVersion: velero.io/v1 +kind: BackupStorageLocation +metadata: + name: backupstoragelocation-sample +spec: + # Add fields here + foo: bar diff --git a/main.go b/main.go index eb0115b46f..ace0b01975 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,8 @@ import ( _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/log/zap" + + velerov1 "github.com/vmware-tanzu/velero/api/v1" // +kubebuilder:scaffold:imports ) @@ -36,6 +38,7 @@ var ( func init() { _ = clientgoscheme.AddToScheme(scheme) + _ = velerov1.AddToScheme(scheme) // +kubebuilder:scaffold:scheme } diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index 7e5e8ad391..4f2cfe2a5c 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -16,13 +16,13 @@ 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 ( 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. @@ -32,7 +32,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. @@ -63,7 +62,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. @@ -88,7 +86,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. @@ -112,7 +109,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. @@ -133,7 +129,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. @@ -188,7 +183,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. @@ -289,7 +283,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. @@ -309,7 +302,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. @@ -342,7 +334,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. @@ -379,7 +370,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. @@ -399,7 +389,6 @@ func (in *BackupStorageLocationStatus) DeepCopyInto(out *BackupStorageLocationSt in, out := &in.LastSyncedTime, &out.LastSyncedTime *out = (*in).DeepCopy() } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocationStatus. @@ -419,7 +408,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. @@ -452,7 +440,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. @@ -476,7 +463,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. @@ -497,7 +483,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. @@ -517,7 +502,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. @@ -550,7 +534,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. @@ -575,7 +558,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. @@ -595,7 +577,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. @@ -611,7 +592,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. @@ -633,7 +613,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. @@ -654,7 +633,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. @@ -670,7 +648,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. @@ -690,7 +667,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. @@ -723,7 +699,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. @@ -755,7 +730,6 @@ func (in *PodVolumeBackupSpec) DeepCopyInto(out *PodVolumeBackupSpec) { (*out)[key] = val } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodVolumeBackupSpec. @@ -780,7 +754,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. @@ -796,7 +769,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. @@ -816,7 +788,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. @@ -849,7 +820,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. @@ -874,7 +844,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. @@ -899,7 +868,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. @@ -919,7 +887,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. @@ -952,7 +919,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. @@ -977,7 +943,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. @@ -997,7 +962,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. @@ -1017,7 +981,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. @@ -1050,7 +1013,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. @@ -1116,7 +1078,6 @@ func (in *RestoreSpec) DeepCopyInto(out *RestoreSpec) { *out = new(bool) **out = **in } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreSpec. @@ -1137,7 +1098,6 @@ func (in *RestoreStatus) DeepCopyInto(out *RestoreStatus) { *out = make([]string, len(*in)) copy(*out, *in) } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreStatus. @@ -1157,7 +1117,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. @@ -1190,7 +1149,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. @@ -1215,7 +1173,6 @@ func (in *ScheduleList) DeepCopyObject() runtime.Object { func (in *ScheduleSpec) DeepCopyInto(out *ScheduleSpec) { *out = *in in.Template.DeepCopyInto(&out.Template) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScheduleSpec. @@ -1240,7 +1197,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. @@ -1260,7 +1216,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. @@ -1293,7 +1248,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. @@ -1317,7 +1271,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. @@ -1342,7 +1295,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. @@ -1363,7 +1315,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. @@ -1383,7 +1334,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. @@ -1416,7 +1366,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. @@ -1447,7 +1396,6 @@ func (in *VolumeSnapshotLocationSpec) DeepCopyInto(out *VolumeSnapshotLocationSp (*out)[key] = val } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotLocationSpec. @@ -1463,7 +1411,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 616f708cab62f3c6a8db649beea5c02f05eff0fd Mon Sep 17 00:00:00 2001 From: Carlisia Date: Thu, 14 May 2020 11:37:44 -0700 Subject: [PATCH 04/34] Port BSL to kubebuilder api client Signed-off-by: Carlisia --- Makefile | 13 +- api/v1/backupstoragelocation_types.go | 8 +- .../velero.io_backupstoragelocations.yaml | 3 - go.mod | 1 + go.sum | 3 + hack/crd-gen/main.go | 136 ------------ hack/update-generated-crd-code.sh | 14 +- hack/verify-generated-crd-code.sh | 29 --- main.go | 60 +----- pkg/apis/velero/v1/backup_storage_location.go | 147 ------------- pkg/apis/velero/v1/register.go | 4 +- pkg/apis/velero/v1/zz_generated.deepcopy.go | 197 +++++------------- pkg/backup/request.go | 3 +- .../backup_storage_location_builder.go | 4 +- pkg/cmd/cli/backup/create.go | 36 +++- pkg/cmd/cli/backuplocation/create.go | 32 ++- pkg/cmd/cli/backuplocation/get.go | 26 ++- pkg/cmd/cli/restic/server.go | 55 ++++- pkg/cmd/server/server.go | 117 +++++++++-- .../output/backup_storage_location_printer.go | 2 +- pkg/cmd/util/output/output.go | 9 +- pkg/cmd/velero/velero.go | 15 ++ pkg/controller/backup_controller.go | 21 +- pkg/controller/backup_controller_test.go | 11 +- pkg/controller/backup_deletion_controller.go | 35 ++-- pkg/controller/backup_sync_controller.go | 109 +++++----- pkg/controller/download_request_controller.go | 19 +- pkg/controller/gc_controller.go | 26 ++- .../pod_volume_backup_controller.go | 28 ++- .../pod_volume_restore_controller.go | 35 +++- .../restic_repository_controller.go | 16 +- pkg/controller/restore_controller.go | 22 +- .../typed/velero/v1/backupstoragelocation.go | 191 ----------------- .../v1/fake/fake_backupstoragelocation.go | 140 ------------- .../velero/v1/fake/fake_velero_client.go | 4 - .../typed/velero/v1/generated_expansion.go | 2 - .../typed/velero/v1/velero_client.go | 5 - pkg/generated/crds/doc.go | 4 - .../velero.io_backupstoragelocations.yaml | 126 ----------- .../velero.io_deletebackuprequests.yaml | 73 ------- .../manifests/velero.io_downloadrequests.yaml | 94 --------- .../manifests/velero.io_podvolumebackups.yaml | 162 -------------- .../velero.io_podvolumerestores.yaml | 145 ------------- .../velero.io_resticrepositories.yaml | 89 -------- .../crds/manifests/velero.io_restores.yaml | 189 ----------------- .../velero.io_serverstatusrequests.yaml | 85 -------- .../velero.io_volumesnapshotlocations.yaml | 74 ------- .../informers/externalversions/generic.go | 2 - .../velero/v1/backupstoragelocation.go | 89 -------- .../externalversions/velero/v1/interface.go | 7 - .../velero/v1/backupstoragelocation.go | 94 --------- .../listers/velero/v1/expansion_generated.go | 8 - pkg/install/resources.go | 39 +++- pkg/persistence/object_store.go | 3 +- pkg/restic/common.go | 26 +-- pkg/restic/config.go | 6 +- pkg/restic/repository_manager.go | 72 ++++--- 57 files changed, 621 insertions(+), 2344 deletions(-) delete mode 100644 hack/crd-gen/main.go delete mode 100755 hack/verify-generated-crd-code.sh delete mode 100644 pkg/apis/velero/v1/backup_storage_location.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/backupstoragelocation.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backupstoragelocation.go delete mode 100644 pkg/generated/crds/doc.go delete mode 100644 pkg/generated/crds/manifests/velero.io_backupstoragelocations.yaml delete mode 100644 pkg/generated/crds/manifests/velero.io_deletebackuprequests.yaml delete mode 100644 pkg/generated/crds/manifests/velero.io_downloadrequests.yaml delete mode 100644 pkg/generated/crds/manifests/velero.io_podvolumebackups.yaml delete mode 100644 pkg/generated/crds/manifests/velero.io_podvolumerestores.yaml delete mode 100644 pkg/generated/crds/manifests/velero.io_resticrepositories.yaml delete mode 100644 pkg/generated/crds/manifests/velero.io_restores.yaml delete mode 100644 pkg/generated/crds/manifests/velero.io_serverstatusrequests.yaml delete mode 100644 pkg/generated/crds/manifests/velero.io_volumesnapshotlocations.yaml delete mode 100644 pkg/generated/informers/externalversions/velero/v1/backupstoragelocation.go delete mode 100644 pkg/generated/listers/velero/v1/backupstoragelocation.go diff --git a/Makefile b/Makefile index 2397e51651..a7d2a2d0f0 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,11 @@ MANIFEST_PLATFORMS ?= amd64 ppc64le arm arm64 GIT_SHA = $(shell git rev-parse HEAD) GIT_DIRTY = $(shell git status --porcelain 2> /dev/null) +.PHONY: generate +generate-all: ## Generate code and yaml manifests + $(MAKE) generate + $(MAKE) manifests + ### ### These variables should not need tweaking. ### @@ -214,11 +219,17 @@ ifneq ($(SKIP_TESTS), 1) hack/test.sh $(WHAT) endif -verify: +verify: verify-gen ifneq ($(SKIP_TESTS), 1) @$(MAKE) shell CMD="-c 'hack/verify-all.sh'" endif +verify-gen: generate + @if !(git diff --quiet HEAD); then \ + git diff; \ + echo "files were out of date, `make generate` was run"; exit 1; \ + fi + update: @$(MAKE) shell CMD="-c 'hack/update-all.sh'" diff --git a/api/v1/backupstoragelocation_types.go b/api/v1/backupstoragelocation_types.go index 595bbb827e..4ffd60bf6c 100644 --- a/api/v1/backupstoragelocation_types.go +++ b/api/v1/backupstoragelocation_types.go @@ -21,8 +21,9 @@ import ( "k8s.io/apimachinery/pkg/types" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. +func init() { + SchemeBuilder.Register(&BackupStorageLocation{}, &BackupStorageLocation{}) +} // BackupStorageLocationSpec defines the desired state of a Velero BackupStorageLocation type BackupStorageLocationSpec struct { @@ -74,6 +75,7 @@ type BackupStorageLocationStatus struct { } // +kubebuilder:object:root=true +// +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" @@ -120,7 +122,7 @@ type ObjectStorageLocation struct { CACert []byte `json:"caCert,omitempty"` } -// BackupStorageLocationPhase is the lifecyle phase of a Velero BackupStorageLocation. +// BackupStorageLocationPhase is the lifecycle phase of a Velero BackupStorageLocation. // +kubebuilder:validation:Enum=Available;Unavailable type BackupStorageLocationPhase string diff --git a/config/crd/bases/velero.io_backupstoragelocations.yaml b/config/crd/bases/velero.io_backupstoragelocations.yaml index 9e650168b3..e10f0ae01c 100644 --- a/config/crd/bases/velero.io_backupstoragelocations.yaml +++ b/config/crd/bases/velero.io_backupstoragelocations.yaml @@ -123,9 +123,6 @@ spec: - name: v1 served: true storage: true - - name: v1 - served: true - storage: false status: acceptedNames: kind: "" diff --git a/go.mod b/go.mod index 7d2b87ee2d..fe9716fcc5 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/kubernetes-csi/external-snapshotter/v2 v2.1.0 github.com/pkg/errors v0.8.1 github.com/prometheus/client_golang v1.0.0 + github.com/prometheus/common v0.4.1 github.com/robfig/cron v0.0.0-20170309132418-df38d32658d8 github.com/sirupsen/logrus v1.4.2 github.com/smartystreets/goconvey v1.6.4 // indirect diff --git a/go.sum b/go.sum index e1cba1b54a..73cf89b9b0 100644 --- a/go.sum +++ b/go.sum @@ -58,7 +58,9 @@ github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdko github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -552,6 +554,7 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= 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= diff --git a/hack/crd-gen/main.go b/hack/crd-gen/main.go deleted file mode 100644 index edd759aef6..0000000000 --- a/hack/crd-gen/main.go +++ /dev/null @@ -1,136 +0,0 @@ -/* -Copyright 2019 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. -*/ - -// This code embeds the CRD manifests in pkg/generated/crds/manifests in -// pkg/generated/crds/crds.go. - -package main - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "log" - "os" - "text/template" -) - -// This is relative to pkg/generated/crds -const goHeaderFile = "../../../hack/boilerplate.go.txt" - -const tpl = `{{.GoHeader}} -// Code generated by crds_generate.go; DO NOT EDIT. - -package crds - -import ( - "bytes" - "compress/gzip" - "io/ioutil" - - apiextinstall "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install" - apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - "k8s.io/client-go/kubernetes/scheme" -) - -var rawCRDs = [][]byte{ -{{- range .RawCRDs }} - []byte({{ . }}), -{{- end }} -} - -var CRDs = crds() - -func crds() []*apiextv1beta1.CustomResourceDefinition { - apiextinstall.Install(scheme.Scheme) - decode := scheme.Codecs.UniversalDeserializer().Decode - var objs []*apiextv1beta1.CustomResourceDefinition - for _, crd := range rawCRDs { - gzr, err := gzip.NewReader(bytes.NewReader(crd)) - if err != nil { - panic(err) - } - bytes, err := ioutil.ReadAll(gzr) - if err != nil { - panic(err) - } - gzr.Close() - - obj, _, err := decode(bytes, nil, nil) - if err != nil { - panic(err) - } - objs = append(objs, obj.(*apiextv1beta1.CustomResourceDefinition)) - } - return objs -} -` - -type templateData struct { - GoHeader string - RawCRDs []string -} - -func main() { - headerBytes, err := ioutil.ReadFile(goHeaderFile) - if err != nil { - log.Fatalln(err) - } - - data := templateData{ - GoHeader: string(headerBytes), - } - - // This is relative to pkg/generated/crds - manifests, err := ioutil.ReadDir("manifests") - if err != nil { - log.Fatalln(err) - } - - for _, crd := range manifests { - file, err := os.Open("manifests/" + crd.Name()) - if err != nil { - log.Fatalln(err) - } - - // gzip compress manifest - var buf bytes.Buffer - gzw := gzip.NewWriter(&buf) - if _, err := io.Copy(gzw, file); err != nil { - log.Fatalln(err) - } - file.Close() - gzw.Close() - - data.RawCRDs = append(data.RawCRDs, fmt.Sprintf("%q", buf.Bytes())) - } - - t, err := template.New("crds").Parse(tpl) - if err != nil { - log.Fatalln(err) - } - - out, err := os.Create("crds.go") - if err != nil { - log.Fatalln(err) - } - - if err := t.Execute(out, data); err != nil { - log.Fatalln(err) - } -} diff --git a/hack/update-generated-crd-code.sh b/hack/update-generated-crd-code.sh index 0873ca447f..fb065acbd4 100755 --- a/hack/update-generated-crd-code.sh +++ b/hack/update-generated-crd-code.sh @@ -30,11 +30,6 @@ if [[ ! -d "${GOPATH}/src/k8s.io/code-generator" ]]; then exit 1 fi -if ! command -v controller-gen > /dev/null; then - echo "controller-gen is missing" - exit 1 -fi - ${GOPATH}/src/k8s.io/code-generator/generate-groups.sh \ all \ github.com/vmware-tanzu/velero/pkg/generated \ @@ -42,11 +37,4 @@ ${GOPATH}/src/k8s.io/code-generator/generate-groups.sh \ "velero:v1" \ --go-header-file ./hack/boilerplate.go.txt \ --output-base ../../.. \ - $@ - -controller-gen \ - crd:crdVersions=v1beta1,preserveUnknownFields=false \ - output:dir=./pkg/generated/crds/manifests \ - paths=./pkg/apis/velero/v1/... - -go generate ./pkg/generated/crds + $@ \ No newline at end of file diff --git a/hack/verify-generated-crd-code.sh b/hack/verify-generated-crd-code.sh deleted file mode 100755 index 698f8d11b6..0000000000 --- a/hack/verify-generated-crd-code.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -e -# -# Copyright 2017 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. - -HACK_DIR=$(dirname "${BASH_SOURCE}") - -${HACK_DIR}/update-generated-crd-code.sh --verify-only - -# ensure no changes to generated CRDs -if ! git diff --exit-code pkg/generated/crds/crds.go >/dev/null; then - # revert changes to state before running CRD generation to stay consistent - # with code-generator `--verify-only` option which discards generated changes - git checkout pkg/generated/crds - - echo "CRD verification - failed! Generated CRDs are out-of-date, please run 'make update'." - exit 1 -fi diff --git a/main.go b/main.go index ace0b01975..4b4f124e10 100644 --- a/main.go +++ b/main.go @@ -14,62 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -package main - -import ( - "flag" - "os" - - "k8s.io/apimachinery/pkg/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - - velerov1 "github.com/vmware-tanzu/velero/api/v1" - // +kubebuilder:scaffold:imports -) - -var ( - scheme = runtime.NewScheme() - setupLog = ctrl.Log.WithName("setup") -) - -func init() { - _ = clientgoscheme.AddToScheme(scheme) +// +kubebuilder:scaffold:imports - _ = velerov1.AddToScheme(scheme) - // +kubebuilder:scaffold:scheme -} - -func main() { - var metricsAddr string - var enableLeaderElection bool - flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") - flag.BoolVar(&enableLeaderElection, "enable-leader-election", false, - "Enable leader election for controller manager. "+ - "Enabling this will ensure there is only one active controller manager.") - flag.Parse() - - ctrl.SetLogger(zap.New(zap.UseDevMode(true))) - - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ - Scheme: scheme, - MetricsBindAddress: metricsAddr, - Port: 9443, - LeaderElection: enableLeaderElection, - LeaderElectionID: "059bfcb5.io", - }) - if err != nil { - setupLog.Error(err, "unable to start manager") - os.Exit(1) - } - - // +kubebuilder:scaffold:builder +package main - setupLog.Info("starting manager") - if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { - setupLog.Error(err, "problem running manager") - os.Exit(1) - } -} +func main() {} diff --git a/pkg/apis/velero/v1/backup_storage_location.go b/pkg/apis/velero/v1/backup_storage_location.go deleted file mode 100644 index 3d74b647e5..0000000000 --- a/pkg/apis/velero/v1/backup_storage_location.go +++ /dev/null @@ -1,147 +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 v1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" -) - -// +genclient -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// BackupStorageLocation is a location where Velero stores backup objects. -type BackupStorageLocation struct { - metav1.TypeMeta `json:",inline"` - - // +optional - metav1.ObjectMeta `json:"metadata,omitempty"` - - // +optional - Spec BackupStorageLocationSpec `json:"spec,omitempty"` - - // +optional - Status BackupStorageLocationStatus `json:"status,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// BackupStorageLocationList is a list of BackupStorageLocations. -type BackupStorageLocationList struct { - metav1.TypeMeta `json:",inline"` - - // +optional - metav1.ListMeta `json:"metadata,omitempty"` - - Items []BackupStorageLocation `json:"items"` -} - -// StorageType represents the type of storage that a backup location uses. -// ObjectStorage must be non-nil, since it is currently the only supported StorageType. -type StorageType struct { - ObjectStorage *ObjectStorageLocation `json:"objectStorage"` -} - -// ObjectStorageLocation specifies the settings necessary to connect to a provider's object storage. -type ObjectStorageLocation struct { - // Bucket is the bucket to use for object storage. - Bucket string `json:"bucket"` - - // Prefix is the path inside a bucket to use for Velero storage. Optional. - // +optional - Prefix string `json:"prefix,omitempty"` - - // CACert defines a CA bundle to use when verifying TLS connections to the provider. - // +optional - CACert []byte `json:"caCert,omitempty"` -} - -// BackupStorageLocationSpec defines the specification for a Velero BackupStorageLocation. -type BackupStorageLocationSpec struct { - // Provider is the provider of the backup storage. - Provider string `json:"provider"` - - // Config is for provider-specific configuration fields. - // +optional - Config map[string]string `json:"config,omitempty"` - - StorageType `json:",inline"` - - // AccessMode defines the permissions for the backup storage location. - // +optional - AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"` - - // BackupSyncPeriod defines how frequently to sync backup API objects from object storage. A value of 0 disables sync. - // +optional - // +nullable - BackupSyncPeriod *metav1.Duration `json:"backupSyncPeriod,omitempty"` -} - -// BackupStorageLocationPhase is the lifecyle phase of a Velero BackupStorageLocation. -// +kubebuilder:validation:Enum=Available;Unavailable -type BackupStorageLocationPhase string - -const ( - // BackupStorageLocationPhaseAvailable means the location is available to read and write from. - BackupStorageLocationPhaseAvailable BackupStorageLocationPhase = "Available" - - // BackupStorageLocationPhaseUnavailable means the location is unavailable to read and write from. - BackupStorageLocationPhaseUnavailable BackupStorageLocationPhase = "Unavailable" -) - -// BackupStorageLocationAccessMode represents the permissions for a BackupStorageLocation. -// +kubebuilder:validation:Enum=ReadOnly;ReadWrite -type BackupStorageLocationAccessMode string - -const ( - // BackupStorageLocationAccessModeReadOnly represents read-only access to a BackupStorageLocation. - BackupStorageLocationAccessModeReadOnly BackupStorageLocationAccessMode = "ReadOnly" - - // BackupStorageLocationAccessModeReadWrite represents read and write access to a BackupStorageLocation. - BackupStorageLocationAccessModeReadWrite BackupStorageLocationAccessMode = "ReadWrite" -) - -// TODO(2.0): remove the AccessMode field from BackupStorageLocationStatus. -// TODO(2.0): remove the LastSyncedRevision field from BackupStorageLocationStatus. - -// BackupStorageLocationStatus describes the current status of a Velero BackupStorageLocation. -type BackupStorageLocationStatus struct { - // Phase is the current state of the BackupStorageLocation. - // +optional - Phase BackupStorageLocationPhase `json:"phase,omitempty"` - - // LastSyncedTime is the last time the contents of the location were synced into - // the cluster. - // +optional - // +nullable - LastSyncedTime *metav1.Time `json:"lastSyncedTime,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. - // - // Deprecated: this field is no longer updated or used for detecting changes to - // the location's contents and will be removed entirely in v2.0. - // +optional - LastSyncedRevision types.UID `json:"lastSyncedRevision,omitempty"` - - // AccessMode is an unused field. - // - // Deprecated: there is now an AccessMode field on the Spec and this field - // will be removed entirely as of v2.0. - // +optional - AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"` -} diff --git a/pkg/apis/velero/v1/register.go b/pkg/apis/velero/v1/register.go index 6eb7051e81..c9e60fb9b5 100644 --- a/pkg/apis/velero/v1/register.go +++ b/pkg/apis/velero/v1/register.go @@ -20,6 +20,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + + velerov1api "github.com/vmware-tanzu/velero/api/v1" ) var ( @@ -67,7 +69,7 @@ func CustomResources() map[string]typeInfo { "PodVolumeBackup": newTypeInfo("podvolumebackups", &PodVolumeBackup{}, &PodVolumeBackupList{}), "PodVolumeRestore": newTypeInfo("podvolumerestores", &PodVolumeRestore{}, &PodVolumeRestoreList{}), "ResticRepository": newTypeInfo("resticrepositories", &ResticRepository{}, &ResticRepositoryList{}), - "BackupStorageLocation": newTypeInfo("backupstoragelocations", &BackupStorageLocation{}, &BackupStorageLocationList{}), + "BackupStorageLocation": newTypeInfo("backupstoragelocations", &velerov1api.BackupStorageLocation{}, &velerov1api.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 4f2cfe2a5c..b5ea91f3ab 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -16,13 +16,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Code generated by controller-gen. DO NOT EDIT. +// Code generated by deepcopy-gen. DO NOT EDIT. package v1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" + runtime "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -32,6 +32,7 @@ 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. @@ -62,6 +63,7 @@ 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. @@ -86,6 +88,7 @@ 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. @@ -109,6 +112,7 @@ 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. @@ -129,6 +133,7 @@ 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. @@ -183,6 +188,7 @@ 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. @@ -283,6 +289,7 @@ 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. @@ -295,112 +302,6 @@ func (in *BackupStatus) DeepCopy() *BackupStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackupStorageLocation) DeepCopyInto(out *BackupStorageLocation) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocation. -func (in *BackupStorageLocation) DeepCopy() *BackupStorageLocation { - if in == nil { - return nil - } - out := new(BackupStorageLocation) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *BackupStorageLocation) 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 *BackupStorageLocationList) DeepCopyInto(out *BackupStorageLocationList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]BackupStorageLocation, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocationList. -func (in *BackupStorageLocationList) DeepCopy() *BackupStorageLocationList { - if in == nil { - return nil - } - out := new(BackupStorageLocationList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *BackupStorageLocationList) 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 *BackupStorageLocationSpec) DeepCopyInto(out *BackupStorageLocationSpec) { - *out = *in - if in.Config != nil { - in, out := &in.Config, &out.Config - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - in.StorageType.DeepCopyInto(&out.StorageType) - if in.BackupSyncPeriod != nil { - in, out := &in.BackupSyncPeriod, &out.BackupSyncPeriod - *out = new(metav1.Duration) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocationSpec. -func (in *BackupStorageLocationSpec) DeepCopy() *BackupStorageLocationSpec { - if in == nil { - return nil - } - out := new(BackupStorageLocationSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackupStorageLocationStatus) DeepCopyInto(out *BackupStorageLocationStatus) { - *out = *in - if in.LastSyncedTime != nil { - in, out := &in.LastSyncedTime, &out.LastSyncedTime - *out = (*in).DeepCopy() - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocationStatus. -func (in *BackupStorageLocationStatus) DeepCopy() *BackupStorageLocationStatus { - if in == nil { - return nil - } - out := new(BackupStorageLocationStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DeleteBackupRequest) DeepCopyInto(out *DeleteBackupRequest) { *out = *in @@ -408,6 +309,7 @@ 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. @@ -440,6 +342,7 @@ 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. @@ -463,6 +366,7 @@ 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. @@ -483,6 +387,7 @@ 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. @@ -502,6 +407,7 @@ 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. @@ -534,6 +440,7 @@ 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. @@ -558,6 +465,7 @@ 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. @@ -577,6 +485,7 @@ 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. @@ -592,6 +501,7 @@ 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. @@ -613,6 +523,7 @@ 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. @@ -625,29 +536,10 @@ func (in *ExecHook) DeepCopy() *ExecHook { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ObjectStorageLocation) DeepCopyInto(out *ObjectStorageLocation) { - *out = *in - if in.CACert != nil { - in, out := &in.CACert, &out.CACert - *out = make([]byte, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectStorageLocation. -func (in *ObjectStorageLocation) DeepCopy() *ObjectStorageLocation { - if in == nil { - return nil - } - out := new(ObjectStorageLocation) - in.DeepCopyInto(out) - return out -} - // 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. @@ -667,6 +559,7 @@ 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. @@ -699,6 +592,7 @@ 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. @@ -730,6 +624,7 @@ func (in *PodVolumeBackupSpec) DeepCopyInto(out *PodVolumeBackupSpec) { (*out)[key] = val } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodVolumeBackupSpec. @@ -754,6 +649,7 @@ 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. @@ -769,6 +665,7 @@ 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. @@ -788,6 +685,7 @@ 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. @@ -820,6 +718,7 @@ 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. @@ -844,6 +743,7 @@ 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. @@ -868,6 +768,7 @@ 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. @@ -887,6 +788,7 @@ 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. @@ -919,6 +821,7 @@ 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. @@ -943,6 +846,7 @@ 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. @@ -962,6 +866,7 @@ 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. @@ -981,6 +886,7 @@ 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. @@ -1013,6 +919,7 @@ 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. @@ -1078,6 +985,7 @@ func (in *RestoreSpec) DeepCopyInto(out *RestoreSpec) { *out = new(bool) **out = **in } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreSpec. @@ -1098,6 +1006,7 @@ func (in *RestoreStatus) DeepCopyInto(out *RestoreStatus) { *out = make([]string, len(*in)) copy(*out, *in) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreStatus. @@ -1117,6 +1026,7 @@ 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. @@ -1149,6 +1059,7 @@ 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. @@ -1173,6 +1084,7 @@ func (in *ScheduleList) DeepCopyObject() runtime.Object { func (in *ScheduleSpec) DeepCopyInto(out *ScheduleSpec) { *out = *in in.Template.DeepCopyInto(&out.Template) + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScheduleSpec. @@ -1197,6 +1109,7 @@ 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. @@ -1216,6 +1129,7 @@ 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. @@ -1248,6 +1162,7 @@ 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. @@ -1271,6 +1186,7 @@ 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. @@ -1295,6 +1211,7 @@ 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. @@ -1307,26 +1224,6 @@ func (in *ServerStatusRequestStatus) DeepCopy() *ServerStatusRequestStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *StorageType) DeepCopyInto(out *StorageType) { - *out = *in - if in.ObjectStorage != nil { - in, out := &in.ObjectStorage, &out.ObjectStorage - *out = new(ObjectStorageLocation) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageType. -func (in *StorageType) DeepCopy() *StorageType { - if in == nil { - return nil - } - out := new(StorageType) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VolumeSnapshotLocation) DeepCopyInto(out *VolumeSnapshotLocation) { *out = *in @@ -1334,6 +1231,7 @@ 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. @@ -1366,6 +1264,7 @@ 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. @@ -1396,6 +1295,7 @@ func (in *VolumeSnapshotLocationSpec) DeepCopyInto(out *VolumeSnapshotLocationSp (*out)[key] = val } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotLocationSpec. @@ -1411,6 +1311,7 @@ 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. diff --git a/pkg/backup/request.go b/pkg/backup/request.go index bfb91452df..165ae25b6b 100644 --- a/pkg/backup/request.go +++ b/pkg/backup/request.go @@ -20,6 +20,7 @@ import ( "fmt" "sort" + velerov1apikb "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/util/collections" "github.com/vmware-tanzu/velero/pkg/volume" @@ -36,7 +37,7 @@ type itemKey struct { type Request struct { *velerov1api.Backup - StorageLocation *velerov1api.BackupStorageLocation + StorageLocation *velerov1apikb.BackupStorageLocation SnapshotLocations []*velerov1api.VolumeSnapshotLocation NamespaceIncludesExcludes *collections.IncludesExcludes ResourceIncludesExcludes *collections.IncludesExcludes diff --git a/pkg/builder/backup_storage_location_builder.go b/pkg/builder/backup_storage_location_builder.go index 9b0fa66385..6099cf9521 100644 --- a/pkg/builder/backup_storage_location_builder.go +++ b/pkg/builder/backup_storage_location_builder.go @@ -19,7 +19,7 @@ package builder import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov1api "github.com/vmware-tanzu/velero/api/v1" ) // BackupStorageLocationBuilder builds BackupStorageLocation objects. @@ -32,7 +32,7 @@ func ForBackupStorageLocation(ns, name string) *BackupStorageLocationBuilder { return &BackupStorageLocationBuilder{ object: &velerov1api.BackupStorageLocation{ TypeMeta: metav1.TypeMeta{ - APIVersion: velerov1api.SchemeGroupVersion.String(), + APIVersion: velerov1api.GroupVersion.String(), Kind: "BackupStorageLocation", }, ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index d73928c0db..90d7683cbe 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -17,14 +17,19 @@ limitations under the License. package backup import ( + "context" "fmt" "time" "github.com/spf13/cobra" "github.com/spf13/pflag" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/cache" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + + velerov1apikb "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/client" @@ -33,10 +38,23 @@ import ( "github.com/vmware-tanzu/velero/pkg/cmd/util/output" veleroclient "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" v1 "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" + + kbclient "sigs.k8s.io/controller-runtime/pkg/client" ) const DefaultBackupTTL time.Duration = 30 * 24 * time.Hour +var ( + scheme = runtime.NewScheme() +) + +func init() { + _ = clientgoscheme.AddToScheme(scheme) + + _ = velerov1api.AddToScheme(scheme) + // +kubebuilder:scaffold:scheme +} + func NewCreateCommand(f client.Factory, use string) *cobra.Command { o := NewCreateOptions() @@ -146,13 +164,29 @@ func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Facto return err } + clientConfig, err := f.ClientConfig() + if err != nil { + return err + } + + clientKB, err := kbclient.New(clientConfig, kbclient.Options{ + Scheme: scheme, + }) + if err != nil { + return err + } + // Ensure that unless FromSchedule is set, args contains a backup name if o.FromSchedule == "" && len(args) != 1 { return fmt.Errorf("a backup name is required, unless you are creating based on a schedule") } if o.StorageLocation != "" { - if _, err := o.client.VeleroV1().BackupStorageLocations(f.Namespace()).Get(o.StorageLocation, metav1.GetOptions{}); err != nil { + location := &velerov1apikb.BackupStorageLocation{} + if err := clientKB.Get(context.Background(), kbclient.ObjectKey{ + Namespace: f.Namespace(), + Name: o.StorageLocation, + }, location); err != nil { return err } } diff --git a/pkg/cmd/cli/backuplocation/create.go b/pkg/cmd/cli/backuplocation/create.go index a4dfd5ad45..337ba38693 100644 --- a/pkg/cmd/cli/backuplocation/create.go +++ b/pkg/cmd/cli/backuplocation/create.go @@ -17,6 +17,7 @@ limitations under the License. package backuplocation import ( + "context" "fmt" "strings" "time" @@ -25,14 +26,29 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" + + velerov1api "github.com/vmware-tanzu/velero/api/v1" "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" ) +var ( + scheme = runtime.NewScheme() +) + +func init() { + _ = clientgoscheme.AddToScheme(scheme) + + _ = velerov1api.AddToScheme(scheme) + // +kubebuilder:scaffold:scheme +} + func NewCreateCommand(f client.Factory, use string) *cobra.Command { o := NewCreateOptions() @@ -146,13 +162,21 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { return err } - client, err := f.Client() + clientConfig, err := f.ClientConfig() if err != nil { return err } - if _, err := client.VeleroV1().BackupStorageLocations(backupStorageLocation.Namespace).Create(backupStorageLocation); err != nil { - return errors.WithStack(err) + clientKB, err := kbclient.New(clientConfig, kbclient.Options{ + Scheme: scheme, + }) + if err != nil { + return err + } + + if err := clientKB.Create(context.Background(), backupStorageLocation, &kbclient.CreateOptions{}); err != nil { + fmt.Println("argh") + return err } fmt.Printf("Backup storage location %q configured successfully.\n", backupStorageLocation.Name) diff --git a/pkg/cmd/cli/backuplocation/get.go b/pkg/cmd/cli/backuplocation/get.go index 32102c1696..3701a63868 100644 --- a/pkg/cmd/cli/backuplocation/get.go +++ b/pkg/cmd/cli/backuplocation/get.go @@ -17,10 +17,13 @@ limitations under the License. package backuplocation import ( + "context" + "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" - api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/cmd" "github.com/vmware-tanzu/velero/pkg/cmd/util/output" @@ -36,19 +39,30 @@ func NewGetCommand(f client.Factory, use string) *cobra.Command { err := output.ValidateFlags(c) cmd.CheckError(err) - veleroClient, err := f.Client() + clientConfig, err := f.ClientConfig() + cmd.CheckError(err) + + clientKB, err := kbclient.New(clientConfig, kbclient.Options{ + Scheme: scheme, + }) cmd.CheckError(err) - var locations *api.BackupStorageLocationList + location := &veleroapiv1.BackupStorageLocation{} + var locations *veleroapiv1.BackupStorageLocationList + locations = new(veleroapiv1.BackupStorageLocationList) if len(args) > 0 { - locations = new(api.BackupStorageLocationList) for _, name := range args { - location, err := veleroClient.VeleroV1().BackupStorageLocations(f.Namespace()).Get(name, metav1.GetOptions{}) + err = clientKB.Get(context.Background(), kbclient.ObjectKey{ + Namespace: f.Namespace(), + Name: name, + }, location) cmd.CheckError(err) locations.Items = append(locations.Items, *location) } } else { - locations, err = veleroClient.VeleroV1().BackupStorageLocations(f.Namespace()).List(listOptions) + err := clientKB.List(context.Background(), locations, &kbclient.ListOptions{ + Namespace: f.Namespace(), + }) cmd.CheckError(err) } diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/restic/server.go index 2d27386eb0..40381bb11a 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/restic/server.go @@ -27,12 +27,16 @@ import ( "github.com/spf13/cobra" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/sets" kubeinformers "k8s.io/client-go/informers" corev1informers "k8s.io/client-go/informers/core/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" + ctrl "sigs.k8s.io/controller-runtime" + + velerov1apikb "github.com/vmware-tanzu/velero/api/v1" "github.com/vmware-tanzu/velero/pkg/buildinfo" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/cmd" @@ -43,8 +47,28 @@ import ( "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/logging" + + kbcache "sigs.k8s.io/controller-runtime/pkg/cache" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + + velerov1 "github.com/vmware-tanzu/velero/api/v1" +) + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") ) +func init() { + _ = clientgoscheme.AddToScheme(scheme) + + _ = velerov1.AddToScheme(scheme) + // +kubebuilder:scaffold:scheme +} + func NewServerCommand(f client.Factory) *cobra.Command { logLevelFlag := logging.LogLevelFlag(logrus.InfoLevel) formatFlag := logging.NewFormatFlag() @@ -86,6 +110,8 @@ type resticServer struct { ctx context.Context cancelFunc context.CancelFunc fileSystem filesystem.Interface + kbClient kbclient.Client + kbCache kbcache.Cache } func newResticServer(logger logrus.FieldLogger, factory client.Factory) (*resticServer, error) { @@ -130,6 +156,21 @@ func newResticServer(logger logrus.FieldLogger, factory client.Factory) (*restic ctx, cancelFunc := context.WithCancel(context.Background()) + clientConfig, err := factory.ClientConfig() + if err != nil { + return nil, err + } + + ctrl.SetLogger(zap.New(zap.UseDevMode(true))) + mgr, err := ctrl.NewManager(clientConfig, ctrl.Options{ + Scheme: scheme, + }) + if err != nil { + return nil, err + } + + // +kubebuilder:scaffold:builder + s := &resticServer{ kubeClient: kubeClient, veleroClient: veleroClient, @@ -141,6 +182,8 @@ func newResticServer(logger logrus.FieldLogger, factory client.Factory) (*restic ctx: ctx, cancelFunc: cancelFunc, fileSystem: filesystem.NewFileSystem(), + kbClient: mgr.GetClient(), + kbCache: mgr.GetCache(), } if err := s.validatePodVolumesHostPath(); err != nil { @@ -157,6 +200,12 @@ func (s *resticServer) run() { var wg sync.WaitGroup + // TODO(carlisia): how to handle this? Issues: + // - options are get informer for specific obj (below, but w/o namespace of obj name info) or for a specific kind (CRD?) + // - maybe it should go inside the controller? same above problems would apply tho + location := &velerov1apikb.BackupStorageLocation{} + bslInformer, _ := s.kbCache.GetInformer(location) + backupController := controller.NewPodVolumeBackupController( s.logger, s.veleroInformerFactory.Velero().V1().PodVolumeBackups(), @@ -165,7 +214,8 @@ func (s *resticServer) run() { s.secretInformer, s.kubeInformerFactory.Core().V1().PersistentVolumeClaims(), s.kubeInformerFactory.Core().V1().PersistentVolumes(), - s.veleroInformerFactory.Velero().V1().BackupStorageLocations(), + bslInformer, + s.kbCache, os.Getenv("NODE_NAME"), ) wg.Add(1) @@ -182,7 +232,8 @@ func (s *resticServer) run() { s.secretInformer, s.kubeInformerFactory.Core().V1().PersistentVolumeClaims(), s.kubeInformerFactory.Core().V1().PersistentVolumes(), - s.veleroInformerFactory.Velero().V1().BackupStorageLocations(), + bslInformer, + s.kbCache, os.Getenv("NODE_NAME"), ) wg.Add(1) diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 48c032b09c..3b23c1f9f7 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -34,6 +34,7 @@ import ( "github.com/spf13/cobra" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" kubeerrs "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/wait" @@ -68,6 +69,16 @@ import ( "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/restore" "github.com/vmware-tanzu/velero/pkg/util/logging" + + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + ctrl "sigs.k8s.io/controller-runtime" + + kbcache "sigs.k8s.io/controller-runtime/pkg/cache" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/manager" + + velerov1 "github.com/vmware-tanzu/velero/api/v1" ) const ( @@ -100,6 +111,10 @@ const ( defaultBackupTTL = 30 * 24 * time.Hour ) +var ( + scheme = runtime.NewScheme() +) + // list of available controllers for input validation var disableControllerList = []string{ BackupControllerKey, @@ -113,6 +128,13 @@ var disableControllerList = []string{ ServerStatusRequestControllerKey, } +func init() { + _ = clientgoscheme.AddToScheme(scheme) + + _ = velerov1.AddToScheme(scheme) + // +kubebuilder:scaffold:scheme +} + type serverConfig struct { pluginDir, metricsAddress, defaultBackupLocation string backupSyncPeriod, podVolumeOperationTimeout, resourceTerminatingTimeout time.Duration @@ -240,6 +262,9 @@ type server struct { resticManager restic.RepositoryManager metrics *metrics.ServerMetrics config serverConfig + kbClient kbclient.Client + kbCache kbcache.Cache + ctrlManager manager.Manager } func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*server, error) { @@ -294,6 +319,26 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s } } + mgr, err := ctrl.NewManager(clientConfig, ctrl.Options{ + Scheme: scheme, + }) + if err != nil { + cancelFunc() + return nil, err + } + + // logger.Debug("STARTING CACHE") + // var wgc sync.WaitGroup + // runstart := func() { + // defer wgc.Done() + // kbCache.Start(ctrl.SetupSignalHandler()) // ***this blocks + // logger.Debug("PAST CACHING insideyy") + // } + // wgc.Add(1) + // go runstart() + // wgc.Wait() + // logger.Debug("PAST CACHING OUTSIDE") + s := &server{ namespace: f.Namespace(), metricsAddress: config.metricsAddress, @@ -311,6 +356,9 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s logLevel: logger.Level, pluginRegistry: pluginRegistry, config: config, + kbClient: mgr.GetClient(), + kbCache: mgr.GetCache(), + ctrlManager: mgr, } return s, nil @@ -338,14 +386,19 @@ func (s *server) run() error { return err } - if err := s.validateBackupStorageLocations(); err != nil { - return err - } - - if _, err := s.veleroClient.VeleroV1().BackupStorageLocations(s.namespace).Get(s.config.defaultBackupLocation, metav1.GetOptions{}); err != nil { - s.logger.WithError(errors.WithStack(err)). - Warnf("A backup storage location named %s has been specified for the server to use by default, but no corresponding backup storage location exists. Backups with a location not matching the default will need to explicitly specify an existing location", s.config.defaultBackupLocation) - } + // + // if err := s.validateBackupStorageLocations(); err != nil { + // return err + // } + + // bsl := &velerov1.BackupStorageLocation{} + // if err := s.kbClient.Get(context.Background(), kbclient.ObjectKey{ + // Namespace: s.namespace, + // Name: s.config.defaultBackupLocation, + // }, bsl); err != nil { + // s.logger.WithError(errors.WithStack(err)). + // Warnf("A backup storage location named %s has been specified for the server to use by default, but no corresponding backup storage location exists. Backups with a location not matching the default will need to explicitly specify an existing location", s.config.defaultBackupLocation) + // } if err := s.initRestic(); err != nil { return err @@ -442,8 +495,10 @@ func (s *server) validateBackupStorageLocations() error { pluginManager := clientmgmt.NewManager(s.logger, s.logLevel, s.pluginRegistry) defer pluginManager.CleanupClients() - locations, err := s.veleroClient.VeleroV1().BackupStorageLocations(s.namespace).List(metav1.ListOptions{}) - if err != nil { + locations := &velerov1.BackupStorageLocationList{} + if err := s.kbClient.List(context.Background(), locations, &kbclient.ListOptions{ + Namespace: s.namespace, + }); err != nil { return errors.WithStack(err) } @@ -543,7 +598,7 @@ func (s *server) initRestic() error { secretsInformer, s.sharedInformerFactory.Velero().V1().ResticRepositories(), s.veleroClient.VeleroV1(), - s.sharedInformerFactory.Velero().V1().BackupStorageLocations(), + s.kbCache, s.kubeClient.CoreV1(), s.kubeClient.CoreV1(), s.logger, @@ -611,10 +666,10 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string backupSyncControllerRunInfo := func() controllerRunInfo { backupSyncContoller := controller.NewBackupSyncController( s.veleroClient.VeleroV1(), - s.veleroClient.VeleroV1(), + s.kbClient, s.veleroClient.VeleroV1(), s.sharedInformerFactory.Velero().V1().Backups().Lister(), - s.sharedInformerFactory.Velero().V1().BackupStorageLocations().Lister(), + s.kbCache, s.config.backupSyncPeriod, s.namespace, s.csiSnapshotClient, @@ -653,7 +708,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logLevel, newPluginManager, backupTracker, - s.sharedInformerFactory.Velero().V1().BackupStorageLocations().Lister(), + s.kbCache, s.config.defaultBackupLocation, s.config.defaultVolumesToRestic, s.config.defaultBackupTTL, @@ -693,7 +748,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.sharedInformerFactory.Velero().V1().Backups(), s.sharedInformerFactory.Velero().V1().DeleteBackupRequests().Lister(), s.veleroClient.VeleroV1(), - s.sharedInformerFactory.Velero().V1().BackupStorageLocations().Lister(), + s.kbCache, ) return controllerRunInfo{ @@ -713,7 +768,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string backupTracker, s.resticManager, s.sharedInformerFactory.Velero().V1().PodVolumeBackups().Lister(), - s.sharedInformerFactory.Velero().V1().BackupStorageLocations().Lister(), + s.kbCache, s.sharedInformerFactory.Velero().V1().VolumeSnapshotLocations().Lister(), csiVSLister, csiVSCLister, @@ -748,7 +803,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.veleroClient.VeleroV1(), restorer, s.sharedInformerFactory.Velero().V1().Backups().Lister(), - s.sharedInformerFactory.Velero().V1().BackupStorageLocations().Lister(), + s.kbCache, s.sharedInformerFactory.Velero().V1().VolumeSnapshotLocations().Lister(), s.logger, s.logLevel, @@ -769,7 +824,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger, s.sharedInformerFactory.Velero().V1().ResticRepositories(), s.veleroClient.VeleroV1(), - s.sharedInformerFactory.Velero().V1().BackupStorageLocations().Lister(), + s.kbCache, s.resticManager, s.config.defaultResticMaintenanceFrequency, ) @@ -785,7 +840,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.veleroClient.VeleroV1(), s.sharedInformerFactory.Velero().V1().DownloadRequests(), s.sharedInformerFactory.Velero().V1().Restores().Lister(), - s.sharedInformerFactory.Velero().V1().BackupStorageLocations().Lister(), + s.kbCache, s.sharedInformerFactory.Velero().V1().Backups().Lister(), newPluginManager, s.logger, @@ -872,12 +927,23 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger.WithField("informer", informer).Info("Informer cache synced") } + // instantiate cache + // go func() { + // // start the cache- this blocks on signal handlers + // } + // pass cache to controllers + // start controllers + // wait for controllers to shutdown + // once they, have the start caches (blocking call) unblocked by sending a signal asking to exit + // now that the informer caches have all synced, we can start running the controllers + for i := range controllers { controllerRunInfo := controllers[i] wg.Add(1) go func() { + s.logger.Debug("---->>> add controller: ", controllerRunInfo.controller) controllerRunInfo.controller.Run(ctx, controllerRunInfo.numWorkers) wg.Done() }() @@ -885,6 +951,19 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger.Info("Server started successfully") + s.logger.Debug("CACHING OUTSIDE") + wg.Add(1) + go func() { + defer wg.Done() + s.logger.Debug("------------->>>CACHING INSIDE<<<----------") + // +kubebuilder:scaffold:builder + if err := s.ctrlManager.Start(ctrl.SetupSignalHandler()); err != nil { // ***this blocks + s.logger.Error(err, "problem running manager") + s.logger.Debug("------------->>>CACHING INSIDE AFTERRRRRR<<<----------") + } + }() + + // wg.Done() <-ctx.Done() s.logger.Info("Waiting for all controllers to shut down gracefully") diff --git a/pkg/cmd/util/output/backup_storage_location_printer.go b/pkg/cmd/util/output/backup_storage_location_printer.go index 851bc7b737..e4aa4089a9 100644 --- a/pkg/cmd/util/output/backup_storage_location_printer.go +++ b/pkg/cmd/util/output/backup_storage_location_printer.go @@ -20,7 +20,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov1api "github.com/vmware-tanzu/velero/api/v1" ) var ( diff --git a/pkg/cmd/util/output/output.go b/pkg/cmd/util/output/output.go index 00fe5c9098..1b28f0c33e 100644 --- a/pkg/cmd/util/output/output.go +++ b/pkg/cmd/util/output/output.go @@ -29,6 +29,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/cli-runtime/pkg/printers" + kbvelerov1api "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" "github.com/vmware-tanzu/velero/pkg/util/encode" @@ -187,15 +188,15 @@ func printTable(cmd *cobra.Command, obj runtime.Object) (bool, error) { ColumnDefinitions: resticRepoColumns, Rows: printResticRepoList(obj.(*velerov1api.ResticRepositoryList)), } - case *velerov1api.BackupStorageLocation: + case *kbvelerov1api.BackupStorageLocation: table = &metav1.Table{ ColumnDefinitions: backupStorageLocationColumns, - Rows: printBackupStorageLocation(obj.(*velerov1api.BackupStorageLocation)), + Rows: printBackupStorageLocation(obj.(*kbvelerov1api.BackupStorageLocation)), } - case *velerov1api.BackupStorageLocationList: + case *kbvelerov1api.BackupStorageLocationList: table = &metav1.Table{ ColumnDefinitions: backupStorageLocationColumns, - Rows: printBackupStorageLocationList(obj.(*velerov1api.BackupStorageLocationList)), + Rows: printBackupStorageLocationList(obj.(*kbvelerov1api.BackupStorageLocationList)), } case *velerov1api.VolumeSnapshotLocation: table = &metav1.Table{ diff --git a/pkg/cmd/velero/velero.go b/pkg/cmd/velero/velero.go index db9c09b40c..1ff26d2c48 100644 --- a/pkg/cmd/velero/velero.go +++ b/pkg/cmd/velero/velero.go @@ -22,8 +22,12 @@ import ( "os" "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/klog" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + + velerov1api "github.com/vmware-tanzu/velero/api/v1" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/cmd/cli/backup" "github.com/vmware-tanzu/velero/pkg/cmd/cli/backuplocation" @@ -47,6 +51,17 @@ import ( "github.com/vmware-tanzu/velero/pkg/features" ) +var ( + scheme = runtime.NewScheme() +) + +func init() { + _ = clientgoscheme.AddToScheme(scheme) + + _ = velerov1api.AddToScheme(scheme) + // +kubebuilder:scaffold:scheme +} + func NewCommand(name string) *cobra.Command { // Load the config here so that we can extract features from it. config, err := client.LoadConfig() diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 85380144b9..9aa676c121 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -19,6 +19,7 @@ package controller import ( "bytes" "compress/gzip" + "context" "encoding/json" "fmt" "io" @@ -40,6 +41,7 @@ import ( snapshotv1beta1api "github.com/kubernetes-csi/external-snapshotter/v2/pkg/apis/volumesnapshot/v1beta1" snapshotv1beta1listers "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/listers/volumesnapshot/v1beta1" + velerov1apikb "github.com/vmware-tanzu/velero/api/v1" 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" @@ -57,6 +59,9 @@ import ( kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube" "github.com/vmware-tanzu/velero/pkg/util/logging" "github.com/vmware-tanzu/velero/pkg/volume" + + kbcache "sigs.k8s.io/controller-runtime/pkg/cache" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" ) type backupController struct { @@ -69,14 +74,14 @@ type backupController struct { backupLogLevel logrus.Level newPluginManager func(logrus.FieldLogger) clientmgmt.Manager backupTracker BackupTracker - backupLocationLister velerov1listers.BackupStorageLocationLister + kbCache kbcache.Cache defaultBackupLocation string defaultVolumesToRestic bool defaultBackupTTL time.Duration snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister defaultSnapshotLocations map[string]string metrics *metrics.ServerMetrics - newBackupStore func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) + newBackupStore func(*velerov1apikb.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) formatFlag logging.Format volumeSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister volumeSnapshotContentLister snapshotv1beta1listers.VolumeSnapshotContentLister @@ -91,7 +96,7 @@ func NewBackupController( backupLogLevel logrus.Level, newPluginManager func(logrus.FieldLogger) clientmgmt.Manager, backupTracker BackupTracker, - backupLocationLister velerov1listers.BackupStorageLocationLister, + kbCache kbcache.Cache, defaultBackupLocation string, defaultVolumesToRestic bool, defaultBackupTTL time.Duration, @@ -112,7 +117,7 @@ func NewBackupController( backupLogLevel: backupLogLevel, newPluginManager: newPluginManager, backupTracker: backupTracker, - backupLocationLister: backupLocationLister, + kbCache: kbCache, defaultBackupLocation: defaultBackupLocation, defaultVolumesToRestic: defaultVolumesToRestic, defaultBackupTTL: defaultBackupTTL, @@ -371,7 +376,11 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg } // validate the storage location, and store the BackupStorageLocation API obj on the request - if storageLocation, err := c.backupLocationLister.BackupStorageLocations(request.Namespace).Get(request.Spec.StorageLocation); err != nil { + storageLocation := &velerov1apikb.BackupStorageLocation{} + if err := c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + Namespace: request.Namespace, + Name: request.Spec.StorageLocation, + }, storageLocation); err != nil { if apierrors.IsNotFound(err) { request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("a BackupStorageLocation CRD with the name specified in the backup spec needs to be created before this backup can be executed. Error: %v", err)) } else { @@ -380,7 +389,7 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg } else { request.StorageLocation = storageLocation - if request.StorageLocation.Spec.AccessMode == velerov1api.BackupStorageLocationAccessModeReadOnly { + if request.StorageLocation.Spec.AccessMode == velerov1apikb.BackupStorageLocationAccessModeReadOnly { request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("backup can't be created because backup storage location %s is currently in read-only mode", request.StorageLocation.Name)) } diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index b746cb734c..9bb48cd948 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -35,6 +35,7 @@ import ( "k8s.io/apimachinery/pkg/version" + velerov1apikb "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/builder" @@ -136,7 +137,7 @@ func TestProcessBackupValidationFailures(t *testing.T) { tests := []struct { name string backup *velerov1api.Backup - backupLocation *velerov1api.BackupStorageLocation + backupLocation *velerov1apikb.BackupStorageLocation expectedErrs []string }{ { @@ -159,7 +160,7 @@ func TestProcessBackupValidationFailures(t *testing.T) { { name: "backup for read-only backup location fails validation", backup: defaultBackup().StorageLocation("read-only").Result(), - backupLocation: builder.ForBackupStorageLocation("velero", "read-only").AccessMode(velerov1api.BackupStorageLocationAccessModeReadOnly).Result(), + backupLocation: builder.ForBackupStorageLocation("velero", "read-only").AccessMode(velerov1apikb.BackupStorageLocationAccessModeReadOnly).Result(), expectedErrs: []string{"backup can't be created because backup storage location read-only is currently in read-only mode"}, }, } @@ -281,7 +282,7 @@ func TestDefaultBackupTTL(t *testing.T) { tests := []struct { name string backup *velerov1api.Backup - backupLocation *velerov1api.BackupStorageLocation + backupLocation *velerov1apikb.BackupStorageLocation expectedTTL metav1.Duration expectedExpiration metav1.Time }{ @@ -426,7 +427,7 @@ func TestProcessBackupCompletions(t *testing.T) { backup: defaultBackup().StorageLocation("read-write").Result(), backupLocation: builder.ForBackupStorageLocation("velero", "read-write"). Bucket("store-1"). - AccessMode(velerov1api.BackupStorageLocationAccessModeReadWrite). + AccessMode(velerov1apikb.BackupStorageLocationAccessModeReadWrite). Result(), defaultVolumesToRestic: true, expectedResult: &velerov1api.Backup{ @@ -806,7 +807,7 @@ func TestProcessBackupCompletions(t *testing.T) { metrics: metrics.NewServerMetrics(), clock: clock.NewFakeClock(now), newPluginManager: func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, - newBackupStore: func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { + newBackupStore: func(*velerov1apikb.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { return backupStore, nil }, backupper: backupper, diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 1af193e15b..b451fc582d 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -36,6 +36,7 @@ import ( kubeerrs "k8s.io/apimachinery/pkg/util/errors" "k8s.io/client-go/tools/cache" + velerov1apikb "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/features" @@ -49,6 +50,9 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/kube" + + kbcache "sigs.k8s.io/controller-runtime/pkg/cache" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" ) const resticTimeout = time.Minute @@ -64,7 +68,7 @@ type backupDeletionController struct { backupTracker BackupTracker resticMgr restic.RepositoryManager podvolumeBackupLister velerov1listers.PodVolumeBackupLister - backupLocationLister velerov1listers.BackupStorageLocationLister + kbCache kbcache.Cache snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister csiSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister csiSnapshotContentLister snapshotv1beta1listers.VolumeSnapshotContentLister @@ -72,7 +76,7 @@ type backupDeletionController struct { processRequestFunc func(*velerov1api.DeleteBackupRequest) error clock clock.Clock newPluginManager func(logrus.FieldLogger) clientmgmt.Manager - newBackupStore func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) + newBackupStore func(*velerov1apikb.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) metrics *metrics.ServerMetrics } @@ -87,7 +91,7 @@ func NewBackupDeletionController( backupTracker BackupTracker, resticMgr restic.RepositoryManager, podvolumeBackupLister velerov1listers.PodVolumeBackupLister, - backupLocationLister velerov1listers.BackupStorageLocationLister, + kbCache kbcache.Cache, snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, csiSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister, csiSnapshotContentLister snapshotv1beta1listers.VolumeSnapshotContentLister, @@ -105,7 +109,7 @@ func NewBackupDeletionController( backupTracker: backupTracker, resticMgr: resticMgr, podvolumeBackupLister: podvolumeBackupLister, - backupLocationLister: backupLocationLister, + kbCache: kbCache, snapshotLocationLister: snapshotLocationLister, csiSnapshotLister: csiSnapshotLister, csiSnapshotContentLister: csiSnapshotContentLister, @@ -214,19 +218,22 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR } // Don't allow deleting backups in read-only storage locations - location, err := c.backupLocationLister.BackupStorageLocations(backup.Namespace).Get(backup.Spec.StorageLocation) - if apierrors.IsNotFound(err) { - _, err := c.patchDeleteBackupRequest(req, 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 - } - if err != nil { + location := &velerov1apikb.BackupStorageLocation{} + if err := c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + Namespace: backup.Namespace, + Name: backup.Spec.StorageLocation, + }, location); err != nil { + if apierrors.IsNotFound(err) { + _, err := c.patchDeleteBackupRequest(req, 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 errors.Wrap(err, "error getting backup storage location") } - if location.Spec.AccessMode == velerov1api.BackupStorageLocationAccessModeReadOnly { + if location.Spec.AccessMode == velerov1apikb.BackupStorageLocationAccessModeReadOnly { _, err := c.patchDeleteBackupRequest(req, 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)) diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index 6caa6039c8..5b3ebc2d5f 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -17,18 +17,19 @@ limitations under the License. package controller import ( - "encoding/json" + "context" "time" snapshotterClientSet "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/clientset/versioned" "github.com/pkg/errors" "github.com/sirupsen/logrus" kuberrs "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/sets" "k8s.io/client-go/kubernetes" + velerov1apikb "github.com/vmware-tanzu/velero/api/v1" 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" @@ -36,31 +37,34 @@ import ( "github.com/vmware-tanzu/velero/pkg/label" "github.com/vmware-tanzu/velero/pkg/persistence" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" + + kbcache "sigs.k8s.io/controller-runtime/pkg/cache" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" ) type backupSyncController struct { *genericController - backupClient velerov1client.BackupsGetter - backupLocationClient velerov1client.BackupStorageLocationsGetter - podVolumeBackupClient velerov1client.PodVolumeBackupsGetter - backupLister velerov1listers.BackupLister - csiSnapshotClient *snapshotterClientSet.Clientset - kubeClient kubernetes.Interface - backupStorageLocationLister velerov1listers.BackupStorageLocationLister - namespace string - defaultBackupLocation string - defaultBackupSyncPeriod time.Duration - newPluginManager func(logrus.FieldLogger) clientmgmt.Manager - newBackupStore func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) + backupClient velerov1client.BackupsGetter + backupLocationClient kbclient.Client + podVolumeBackupClient velerov1client.PodVolumeBackupsGetter + backupLister velerov1listers.BackupLister + csiSnapshotClient *snapshotterClientSet.Clientset + kubeClient kubernetes.Interface + kbCache kbcache.Cache + namespace string + defaultBackupLocation string + defaultBackupSyncPeriod time.Duration + newPluginManager func(logrus.FieldLogger) clientmgmt.Manager + newBackupStore func(*velerov1apikb.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) } func NewBackupSyncController( backupClient velerov1client.BackupsGetter, - backupLocationClient velerov1client.BackupStorageLocationsGetter, + backupLocationClient kbclient.Client, podVolumeBackupClient velerov1client.PodVolumeBackupsGetter, backupLister velerov1listers.BackupLister, - backupStorageLocationLister velerov1listers.BackupStorageLocationLister, + kbCache kbcache.Cache, syncPeriod time.Duration, namespace string, csiSnapshotClient *snapshotterClientSet.Clientset, @@ -75,17 +79,17 @@ func NewBackupSyncController( logger.Infof("Backup sync period is %v", syncPeriod) c := &backupSyncController{ - genericController: newGenericController("backup-sync", logger), - backupClient: backupClient, - backupLocationClient: backupLocationClient, - podVolumeBackupClient: podVolumeBackupClient, - namespace: namespace, - defaultBackupLocation: defaultBackupLocation, - defaultBackupSyncPeriod: syncPeriod, - backupLister: backupLister, - backupStorageLocationLister: backupStorageLocationLister, - csiSnapshotClient: csiSnapshotClient, - kubeClient: kubeClient, + genericController: newGenericController("backup-sync", logger), + backupClient: backupClient, + backupLocationClient: backupLocationClient, + podVolumeBackupClient: podVolumeBackupClient, + namespace: namespace, + defaultBackupLocation: defaultBackupLocation, + defaultBackupSyncPeriod: syncPeriod, + backupLister: backupLister, + kbCache: kbCache, + csiSnapshotClient: csiSnapshotClient, + kubeClient: kubeClient, // use variables to refer to these functions so they can be // replaced with fakes for testing. @@ -101,35 +105,38 @@ func NewBackupSyncController( // 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(locations []*velerov1api.BackupStorageLocation, defaultLocationName string) []*velerov1api.BackupStorageLocation { - var result []*velerov1api.BackupStorageLocation +func orderedBackupLocations(locationList *velerov1apikb.BackupStorageLocationList, defaultLocationName string) []velerov1apikb.BackupStorageLocation { + var result []velerov1apikb.BackupStorageLocation - for i := range locations { - if locations[i].Name == defaultLocationName { + for i := range locationList.Items { + if locationList.Items[i].Name == defaultLocationName { // put the default location first - result = append(result, locations[i]) + result = append(result, locationList.Items[i]) // append everything before the default - result = append(result, locations[:i]...) + result = append(result, locationList.Items[:i]...) // append everything after the default - result = append(result, locations[i+1:]...) + result = append(result, locationList.Items[i+1:]...) return result } } - return locations + return locationList.Items } func (c *backupSyncController) run() { c.logger.Debug("Checking for existing backup storage locations to sync into cluster") - locations, err := c.backupStorageLocationLister.BackupStorageLocations(c.namespace).List(labels.Everything()) - if err != nil { + locationList := &velerov1apikb.BackupStorageLocationList{} + if err := c.kbCache.List(context.Background(), locationList, &kbclient.ListOptions{ + Namespace: c.namespace, + }); err != nil { c.logger.WithError(errors.WithStack(err)).Error("Error getting backup storage locations from lister") return } + // sync the default location first, if it exists - locations = orderedBackupLocations(locations, c.defaultBackupLocation) + locations := orderedBackupLocations(locationList, c.defaultBackupLocation) pluginManager := c.newPluginManager(c.logger) defer pluginManager.CleanupClients() @@ -162,7 +169,7 @@ func (c *backupSyncController) run() { log.Debug("Checking backup location for backups to sync into cluster") - backupStore, err := c.newBackupStore(location, pluginManager, log) + backupStore, err := c.newBackupStore(&location, pluginManager, log) if err != nil { log.WithError(err).Error("Error getting backup store for this location") continue @@ -303,24 +310,16 @@ func (c *backupSyncController) run() { c.deleteOrphanedBackups(location.Name, backupStoreBackups, log) - // update the location's last-synced time field - patch := map[string]interface{}{ - "status": map[string]interface{}{ - "lastSyncedTime": time.Now().UTC(), - }, - } - - patchBytes, err := json.Marshal(patch) - if err != nil { - log.WithError(errors.WithStack(err)).Error("Error marshaling last-synced patch to JSON") + locationUpdate := &velerov1apikb.BackupStorageLocation{} + if err = c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + Namespace: c.namespace, + Name: location.Name, + }, locationUpdate); err != nil { + log.WithError(errors.WithStack(err)).Error("Error fetching backup location for update") continue } - - if _, err = c.backupLocationClient.BackupStorageLocations(c.namespace).Patch( - location.Name, - types.MergePatchType, - patchBytes, - ); err != nil { + locationUpdate.Status.LastSyncedTime = &metav1.Time{Time: time.Now().UTC()} + if err := c.backupLocationClient.Update(context.Background(), locationUpdate); err != nil { log.WithError(errors.WithStack(err)).Error("Error patching backup location's last-synced time") continue } diff --git a/pkg/controller/download_request_controller.go b/pkg/controller/download_request_controller.go index 7bd84cdcef..fd86df2e9c 100644 --- a/pkg/controller/download_request_controller.go +++ b/pkg/controller/download_request_controller.go @@ -17,6 +17,7 @@ limitations under the License. package controller import ( + "context" "encoding/json" "time" @@ -29,7 +30,10 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/clock" "k8s.io/client-go/tools/cache" + kbcache "sigs.k8s.io/controller-runtime/pkg/cache" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" + velerov1apikb "github.com/vmware-tanzu/velero/api/v1" v1 "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" @@ -46,10 +50,10 @@ type downloadRequestController struct { downloadRequestLister velerov1listers.DownloadRequestLister restoreLister velerov1listers.RestoreLister clock clock.Clock - backupLocationLister velerov1listers.BackupStorageLocationLister + kbCache kbcache.Cache backupLister velerov1listers.BackupLister newPluginManager func(logrus.FieldLogger) clientmgmt.Manager - newBackupStore func(*v1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) + newBackupStore func(*velerov1apikb.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) } // NewDownloadRequestController creates a new DownloadRequestController. @@ -57,7 +61,7 @@ func NewDownloadRequestController( downloadRequestClient velerov1client.DownloadRequestsGetter, downloadRequestInformer velerov1informers.DownloadRequestInformer, restoreLister velerov1listers.RestoreLister, - backupLocationLister velerov1listers.BackupStorageLocationLister, + kbCache kbcache.Cache, backupLister velerov1listers.BackupLister, newPluginManager func(logrus.FieldLogger) clientmgmt.Manager, logger logrus.FieldLogger, @@ -67,7 +71,7 @@ func NewDownloadRequestController( downloadRequestClient: downloadRequestClient, downloadRequestLister: downloadRequestInformer.Lister(), restoreLister: restoreLister, - backupLocationLister: backupLocationLister, + kbCache: kbCache, backupLister: backupLister, // use variables to refer to these functions so they can be @@ -159,8 +163,11 @@ func (c *downloadRequestController) generatePreSignedURL(downloadRequest *v1.Dow return errors.WithStack(err) } - backupLocation, err := c.backupLocationLister.BackupStorageLocations(backup.Namespace).Get(backup.Spec.StorageLocation) - if err != nil { + backupLocation := &velerov1apikb.BackupStorageLocation{} + if err := c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + Namespace: backup.Namespace, + Name: backup.Spec.StorageLocation, + }, backupLocation); err != nil { return errors.WithStack(err) } diff --git a/pkg/controller/gc_controller.go b/pkg/controller/gc_controller.go index f262e2e8ab..db6cb66ef9 100644 --- a/pkg/controller/gc_controller.go +++ b/pkg/controller/gc_controller.go @@ -17,6 +17,7 @@ limitations under the License. package controller import ( + "context" "time" "github.com/pkg/errors" @@ -26,6 +27,10 @@ import ( "k8s.io/apimachinery/pkg/util/clock" "k8s.io/client-go/tools/cache" + kbcache "sigs.k8s.io/controller-runtime/pkg/cache" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" + + velerov1apikb "github.com/vmware-tanzu/velero/api/v1" 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" @@ -45,7 +50,7 @@ type gcController struct { backupLister velerov1listers.BackupLister deleteBackupRequestLister velerov1listers.DeleteBackupRequestLister deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter - backupLocationLister velerov1listers.BackupStorageLocationLister + kbCache kbcache.Cache clock clock.Clock } @@ -56,7 +61,7 @@ func NewGCController( backupInformer velerov1informers.BackupInformer, deleteBackupRequestLister velerov1listers.DeleteBackupRequestLister, deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter, - backupLocationLister velerov1listers.BackupStorageLocationLister, + kbCache kbcache.Cache, ) Interface { c := &gcController{ genericController: newGenericController("gc-controller", logger), @@ -64,7 +69,7 @@ func NewGCController( backupLister: backupInformer.Lister(), deleteBackupRequestLister: deleteBackupRequestLister, deleteBackupRequestClient: deleteBackupRequestClient, - backupLocationLister: backupLocationLister, + kbCache: kbCache, } c.syncHandler = c.processQueueItem @@ -130,15 +135,18 @@ func (c *gcController) processQueueItem(key string) error { log.Info("Backup has expired") - loc, err := c.backupLocationLister.BackupStorageLocations(ns).Get(backup.Spec.StorageLocation) - if apierrors.IsNotFound(err) { - log.Warnf("Backup cannot be garbage-collected because backup storage location %s does not exist", backup.Spec.StorageLocation) - } - if err != nil { + loc := &velerov1apikb.BackupStorageLocation{} + if err := c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + Namespace: ns, + Name: backup.Spec.StorageLocation, + }, 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) + } return errors.Wrap(err, "error getting backup storage location") } - if loc.Spec.AccessMode == velerov1api.BackupStorageLocationAccessModeReadOnly { + if loc.Spec.AccessMode == velerov1apikb.BackupStorageLocationAccessModeReadOnly { log.Infof("Backup cannot be garbage-collected because backup storage location %s is currently in read-only mode", loc.Name) return nil } diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index ae57546bd4..f470d45086 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -17,6 +17,7 @@ limitations under the License. package controller import ( + "context" "encoding/json" "fmt" "os" @@ -35,6 +36,7 @@ import ( corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" + velerov1apikb "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" @@ -42,6 +44,9 @@ import ( "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/kube" + + kbcache "sigs.k8s.io/controller-runtime/pkg/cache" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" ) type podVolumeBackupController struct { @@ -53,7 +58,7 @@ type podVolumeBackupController struct { podLister corev1listers.PodLister pvcLister corev1listers.PersistentVolumeClaimLister pvLister corev1listers.PersistentVolumeLister - backupLocationLister listers.BackupStorageLocationLister + kbCache kbcache.Cache nodeName string processBackupFunc func(*velerov1api.PodVolumeBackup) error @@ -70,7 +75,8 @@ func NewPodVolumeBackupController( secretInformer cache.SharedIndexInformer, pvcInformer corev1informers.PersistentVolumeClaimInformer, pvInformer corev1informers.PersistentVolumeInformer, - backupLocationInformer informers.BackupStorageLocationInformer, + backupLocationInformer kbcache.Informer, + kbCache kbcache.Cache, nodeName string, ) Interface { c := &podVolumeBackupController{ @@ -81,7 +87,7 @@ func NewPodVolumeBackupController( secretLister: corev1listers.NewSecretLister(secretInformer.GetIndexer()), pvcLister: pvcInformer.Lister(), pvLister: pvInformer.Lister(), - backupLocationLister: backupLocationInformer.Lister(), + kbCache: kbCache, nodeName: nodeName, fileSystem: filesystem.NewFileSystem(), @@ -95,7 +101,7 @@ func NewPodVolumeBackupController( podInformer.HasSynced, secretInformer.HasSynced, pvcInformer.Informer().HasSynced, - backupLocationInformer.Informer().HasSynced, + backupLocationInformer.HasSynced, ) c.processBackupFunc = c.processBackup @@ -228,7 +234,15 @@ func (c *podVolumeBackupController) processBackup(req *velerov1api.PodVolumeBack ) // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic - caCert, err := restic.GetCACert(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation) + location := &velerov1apikb.BackupStorageLocation{} + if err := c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + Namespace: req.Namespace, + Name: req.Spec.BackupStorageLocation, + }, location); err != nil { + return err + } + + caCert, err := restic.GetCACert(location) if err != nil { log.WithError(err).Error("Error getting caCert") } @@ -247,12 +261,12 @@ func (c *podVolumeBackupController) processBackup(req *velerov1api.PodVolumeBack // set resticCmd.Env appropriately (currently for Azure and S3 based backuplocations) var env []string if strings.HasPrefix(req.Spec.RepoIdentifier, "azure") { - if env, err = restic.AzureCmdEnv(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation); err != nil { + if env, err = restic.AzureCmdEnv(location); err != nil { return c.fail(req, errors.Wrap(err, "error setting restic cmd env").Error(), log) } resticCmd.Env = env } else if strings.HasPrefix(req.Spec.RepoIdentifier, "s3") { - if env, err = restic.S3CmdEnv(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation); err != nil { + if env, err = restic.S3CmdEnv(location); err != nil { return c.fail(req, errors.Wrap(err, "error setting restic cmd env").Error(), log) } resticCmd.Env = env diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 37f5b60600..1ccfb6112d 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -17,6 +17,7 @@ limitations under the License. package controller import ( + "context" "encoding/json" "fmt" "io/ioutil" @@ -36,7 +37,10 @@ import ( corev1informers "k8s.io/client-go/informers/core/v1" corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" + kbcache "sigs.k8s.io/controller-runtime/pkg/cache" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" + velerov1apikb "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" @@ -56,7 +60,7 @@ type podVolumeRestoreController struct { secretLister corev1listers.SecretLister pvcLister corev1listers.PersistentVolumeClaimLister pvLister corev1listers.PersistentVolumeLister - backupLocationLister listers.BackupStorageLocationLister + kbCache kbcache.Cache nodeName string processRestoreFunc func(*velerov1api.PodVolumeRestore) error @@ -73,7 +77,8 @@ func NewPodVolumeRestoreController( secretInformer cache.SharedIndexInformer, pvcInformer corev1informers.PersistentVolumeClaimInformer, pvInformer corev1informers.PersistentVolumeInformer, - backupLocationInformer informers.BackupStorageLocationInformer, + backupLocationInformer kbcache.Informer, + kbCache kbcache.Cache, nodeName string, ) Interface { c := &podVolumeRestoreController{ @@ -84,7 +89,7 @@ func NewPodVolumeRestoreController( secretLister: corev1listers.NewSecretLister(secretInformer.GetIndexer()), pvcLister: pvcInformer.Lister(), pvLister: pvInformer.Lister(), - backupLocationLister: backupLocationInformer.Lister(), + kbCache: kbCache, nodeName: nodeName, fileSystem: filesystem.NewFileSystem(), @@ -98,7 +103,7 @@ func NewPodVolumeRestoreController( podInformer.HasSynced, secretInformer.HasSynced, pvcInformer.Informer().HasSynced, - backupLocationInformer.Informer().HasSynced, + backupLocationInformer.HasSynced, ) c.processRestoreFunc = c.processRestore @@ -294,7 +299,15 @@ func (c *podVolumeRestoreController) processRestore(req *velerov1api.PodVolumeRe defer os.Remove(credsFile) // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic - caCert, err := restic.GetCACert(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation) + location := &velerov1apikb.BackupStorageLocation{} + if err := c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + Namespace: req.Namespace, + Name: req.Spec.BackupStorageLocation, + }, location); err != nil { + return err + } + + caCert, err := restic.GetCACert(location) if err != nil { log.WithError(err).Error("Error getting caCert") } @@ -346,14 +359,22 @@ func (c *podVolumeRestoreController) restorePodVolume(req *velerov1api.PodVolume // Running restic command might need additional provider specific environment variables. Based on the provider, we // set resticCmd.Env appropriately (currently for Azure and S3 based backuplocations) + location := &velerov1apikb.BackupStorageLocation{} + if err := c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + Namespace: req.Namespace, + Name: req.Spec.BackupStorageLocation, + }, location); err != nil { + return err + } + if strings.HasPrefix(req.Spec.RepoIdentifier, "azure") { - env, err := restic.AzureCmdEnv(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation) + env, err := restic.AzureCmdEnv(location) if err != nil { return c.failRestore(req, errors.Wrap(err, "error setting restic cmd env").Error(), log) } resticCmd.Env = env } else if strings.HasPrefix(req.Spec.RepoIdentifier, "s3") { - env, err := restic.S3CmdEnv(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation) + env, err := restic.S3CmdEnv(location) if err != nil { return c.failRestore(req, errors.Wrap(err, "error setting restic cmd env").Error(), log) } diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index 871077036d..21485341d1 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -17,6 +17,7 @@ limitations under the License. package controller import ( + "context" "encoding/json" "strings" "time" @@ -31,11 +32,15 @@ import ( "k8s.io/apimachinery/pkg/util/clock" "k8s.io/client-go/tools/cache" + velerov1apikb "github.com/vmware-tanzu/velero/api/v1" v1 "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" + + kbcache "sigs.k8s.io/controller-runtime/pkg/cache" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" ) type resticRepositoryController struct { @@ -43,7 +48,7 @@ type resticRepositoryController struct { resticRepositoryClient velerov1client.ResticRepositoriesGetter resticRepositoryLister velerov1listers.ResticRepositoryLister - backupLocationLister velerov1listers.BackupStorageLocationLister + backupLocationLister kbcache.Cache repositoryManager restic.RepositoryManager defaultMaintenanceFrequency time.Duration @@ -55,7 +60,7 @@ func NewResticRepositoryController( logger logrus.FieldLogger, resticRepositoryInformer velerov1informers.ResticRepositoryInformer, resticRepositoryClient velerov1client.ResticRepositoriesGetter, - backupLocationLister velerov1listers.BackupStorageLocationLister, + backupLocationLister kbcache.Cache, repositoryManager restic.RepositoryManager, defaultMaintenanceFrequency time.Duration, ) Interface { @@ -155,8 +160,11 @@ func (c *resticRepositoryController) initializeRepo(req *v1.ResticRepository, lo log.Info("Initializing restic repository") // confirm the repo's BackupStorageLocation is valid - loc, err := c.backupLocationLister.BackupStorageLocations(req.Namespace).Get(req.Spec.BackupStorageLocation) - if err != nil { + loc := &velerov1apikb.BackupStorageLocation{} + if err := c.backupLocationLister.Get(context.Background(), kbclient.ObjectKey{ + Namespace: req.Namespace, + Name: req.Spec.BackupStorageLocation, + }, loc); err != nil { return c.patchResticRepository(req, repoNotReady(err.Error())) } diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index 343af150ff..37681ce795 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -19,6 +19,7 @@ package controller import ( "bytes" "compress/gzip" + "context" "encoding/json" "fmt" "io" @@ -35,6 +36,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/tools/cache" + velerov1apikb "github.com/vmware-tanzu/velero/api/v1" 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" @@ -48,6 +50,9 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/collections" kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube" "github.com/vmware-tanzu/velero/pkg/util/logging" + + kbcache "sigs.k8s.io/controller-runtime/pkg/cache" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" ) // nonRestorableResources is a blacklist for the restoration process. Any resources @@ -80,7 +85,7 @@ type restoreController struct { restorer pkgrestore.Restorer backupLister velerov1listers.BackupLister restoreLister velerov1listers.RestoreLister - backupLocationLister velerov1listers.BackupStorageLocationLister + kbCache kbcache.Cache snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister restoreLogLevel logrus.Level defaultBackupLocation string @@ -88,7 +93,7 @@ type restoreController struct { logFormat logging.Format newPluginManager func(logger logrus.FieldLogger) clientmgmt.Manager - newBackupStore func(*api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) + newBackupStore func(*velerov1apikb.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) } func NewRestoreController( @@ -98,7 +103,7 @@ func NewRestoreController( podVolumeBackupClient velerov1client.PodVolumeBackupsGetter, restorer pkgrestore.Restorer, backupLister velerov1listers.BackupLister, - backupLocationLister velerov1listers.BackupStorageLocationLister, + kbCache kbcache.Cache, snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, logger logrus.FieldLogger, restoreLogLevel logrus.Level, @@ -115,7 +120,7 @@ func NewRestoreController( restorer: restorer, backupLister: backupLister, restoreLister: restoreInformer.Lister(), - backupLocationLister: backupLocationLister, + kbCache: kbCache, snapshotLocationLister: snapshotLocationLister, restoreLogLevel: restoreLogLevel, defaultBackupLocation: defaultBackupLocation, @@ -274,7 +279,7 @@ func (c *restoreController) processRestore(restore *api.Restore) error { type backupInfo struct { backup *api.Backup - location *api.BackupStorageLocation + location *velerov1apikb.BackupStorageLocation backupStore persistence.BackupStore } @@ -396,8 +401,11 @@ func (c *restoreController) fetchBackupInfo(backupName string, pluginManager cli return backupInfo{}, err } - location, err := c.backupLocationLister.BackupStorageLocations(c.namespace).Get(backup.Spec.StorageLocation) - if err != nil { + location := &velerov1apikb.BackupStorageLocation{} + if err := c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + Namespace: c.namespace, + Name: backup.Spec.StorageLocation, + }, location); err != nil { return backupInfo{}, errors.WithStack(err) } diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/backupstoragelocation.go b/pkg/generated/clientset/versioned/typed/velero/v1/backupstoragelocation.go deleted file mode 100644 index 1862fe7292..0000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/backupstoragelocation.go +++ /dev/null @@ -1,191 +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 v1 - -import ( - "time" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - scheme "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// BackupStorageLocationsGetter has a method to return a BackupStorageLocationInterface. -// A group's client should implement this interface. -type BackupStorageLocationsGetter interface { - BackupStorageLocations(namespace string) BackupStorageLocationInterface -} - -// BackupStorageLocationInterface has methods to work with BackupStorageLocation resources. -type BackupStorageLocationInterface interface { - Create(*v1.BackupStorageLocation) (*v1.BackupStorageLocation, error) - Update(*v1.BackupStorageLocation) (*v1.BackupStorageLocation, error) - UpdateStatus(*v1.BackupStorageLocation) (*v1.BackupStorageLocation, error) - Delete(name string, options *metav1.DeleteOptions) error - DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error - Get(name string, options metav1.GetOptions) (*v1.BackupStorageLocation, error) - List(opts metav1.ListOptions) (*v1.BackupStorageLocationList, error) - Watch(opts metav1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.BackupStorageLocation, err error) - BackupStorageLocationExpansion -} - -// backupStorageLocations implements BackupStorageLocationInterface -type backupStorageLocations struct { - client rest.Interface - ns string -} - -// newBackupStorageLocations returns a BackupStorageLocations -func newBackupStorageLocations(c *VeleroV1Client, namespace string) *backupStorageLocations { - return &backupStorageLocations{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the backupStorageLocation, and returns the corresponding backupStorageLocation object, and an error if there is any. -func (c *backupStorageLocations) Get(name string, options metav1.GetOptions) (result *v1.BackupStorageLocation, err error) { - result = &v1.BackupStorageLocation{} - err = c.client.Get(). - Namespace(c.ns). - Resource("backupstoragelocations"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of BackupStorageLocations that match those selectors. -func (c *backupStorageLocations) List(opts metav1.ListOptions) (result *v1.BackupStorageLocationList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1.BackupStorageLocationList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("backupstoragelocations"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested backupStorageLocations. -func (c *backupStorageLocations) Watch(opts metav1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("backupstoragelocations"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a backupStorageLocation and creates it. Returns the server's representation of the backupStorageLocation, and an error, if there is any. -func (c *backupStorageLocations) Create(backupStorageLocation *v1.BackupStorageLocation) (result *v1.BackupStorageLocation, err error) { - result = &v1.BackupStorageLocation{} - err = c.client.Post(). - Namespace(c.ns). - Resource("backupstoragelocations"). - Body(backupStorageLocation). - Do(). - Into(result) - return -} - -// Update takes the representation of a backupStorageLocation and updates it. Returns the server's representation of the backupStorageLocation, and an error, if there is any. -func (c *backupStorageLocations) Update(backupStorageLocation *v1.BackupStorageLocation) (result *v1.BackupStorageLocation, err error) { - result = &v1.BackupStorageLocation{} - err = c.client.Put(). - Namespace(c.ns). - Resource("backupstoragelocations"). - Name(backupStorageLocation.Name). - Body(backupStorageLocation). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *backupStorageLocations) UpdateStatus(backupStorageLocation *v1.BackupStorageLocation) (result *v1.BackupStorageLocation, err error) { - result = &v1.BackupStorageLocation{} - err = c.client.Put(). - Namespace(c.ns). - Resource("backupstoragelocations"). - Name(backupStorageLocation.Name). - SubResource("status"). - Body(backupStorageLocation). - Do(). - Into(result) - return -} - -// Delete takes name of the backupStorageLocation and deletes it. Returns an error if one occurs. -func (c *backupStorageLocations) Delete(name string, options *metav1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("backupstoragelocations"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *backupStorageLocations) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("backupstoragelocations"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched backupStorageLocation. -func (c *backupStorageLocations) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.BackupStorageLocation, err error) { - result = &v1.BackupStorageLocation{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("backupstoragelocations"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backupstoragelocation.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backupstoragelocation.go deleted file mode 100644 index 650ddc7d3d..0000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backupstoragelocation.go +++ /dev/null @@ -1,140 +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 ( - 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" -) - -// FakeBackupStorageLocations implements BackupStorageLocationInterface -type FakeBackupStorageLocations struct { - Fake *FakeVeleroV1 - ns string -} - -var backupstoragelocationsResource = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "backupstoragelocations"} - -var backupstoragelocationsKind = schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "BackupStorageLocation"} - -// Get takes name of the backupStorageLocation, and returns the corresponding backupStorageLocation object, and an error if there is any. -func (c *FakeBackupStorageLocations) Get(name string, options v1.GetOptions) (result *velerov1.BackupStorageLocation, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(backupstoragelocationsResource, c.ns, name), &velerov1.BackupStorageLocation{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.BackupStorageLocation), err -} - -// List takes label and field selectors, and returns the list of BackupStorageLocations that match those selectors. -func (c *FakeBackupStorageLocations) List(opts v1.ListOptions) (result *velerov1.BackupStorageLocationList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(backupstoragelocationsResource, backupstoragelocationsKind, c.ns, opts), &velerov1.BackupStorageLocationList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &velerov1.BackupStorageLocationList{ListMeta: obj.(*velerov1.BackupStorageLocationList).ListMeta} - for _, item := range obj.(*velerov1.BackupStorageLocationList).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 backupStorageLocations. -func (c *FakeBackupStorageLocations) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(backupstoragelocationsResource, c.ns, opts)) - -} - -// Create takes the representation of a backupStorageLocation and creates it. Returns the server's representation of the backupStorageLocation, and an error, if there is any. -func (c *FakeBackupStorageLocations) Create(backupStorageLocation *velerov1.BackupStorageLocation) (result *velerov1.BackupStorageLocation, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(backupstoragelocationsResource, c.ns, backupStorageLocation), &velerov1.BackupStorageLocation{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.BackupStorageLocation), err -} - -// Update takes the representation of a backupStorageLocation and updates it. Returns the server's representation of the backupStorageLocation, and an error, if there is any. -func (c *FakeBackupStorageLocations) Update(backupStorageLocation *velerov1.BackupStorageLocation) (result *velerov1.BackupStorageLocation, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(backupstoragelocationsResource, c.ns, backupStorageLocation), &velerov1.BackupStorageLocation{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.BackupStorageLocation), 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 *FakeBackupStorageLocations) UpdateStatus(backupStorageLocation *velerov1.BackupStorageLocation) (*velerov1.BackupStorageLocation, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(backupstoragelocationsResource, "status", c.ns, backupStorageLocation), &velerov1.BackupStorageLocation{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.BackupStorageLocation), err -} - -// Delete takes name of the backupStorageLocation and deletes it. Returns an error if one occurs. -func (c *FakeBackupStorageLocations) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(backupstoragelocationsResource, c.ns, name), &velerov1.BackupStorageLocation{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeBackupStorageLocations) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(backupstoragelocationsResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &velerov1.BackupStorageLocationList{}) - return err -} - -// Patch applies the patch and returns the patched backupStorageLocation. -func (c *FakeBackupStorageLocations) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *velerov1.BackupStorageLocation, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(backupstoragelocationsResource, c.ns, name, pt, data, subresources...), &velerov1.BackupStorageLocation{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.BackupStorageLocation), 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..da65a7771d 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,10 +32,6 @@ func (c *FakeVeleroV1) Backups(namespace string) v1.BackupInterface { return &FakeBackups{c, namespace} } -func (c *FakeVeleroV1) BackupStorageLocations(namespace string) v1.BackupStorageLocationInterface { - return &FakeBackupStorageLocations{c, namespace} -} - func (c *FakeVeleroV1) DeleteBackupRequests(namespace string) v1.DeleteBackupRequestInterface { return &FakeDeleteBackupRequests{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..caf5a4f82f 100644 --- a/pkg/generated/clientset/versioned/typed/velero/v1/generated_expansion.go +++ b/pkg/generated/clientset/versioned/typed/velero/v1/generated_expansion.go @@ -20,8 +20,6 @@ package v1 type BackupExpansion interface{} -type BackupStorageLocationExpansion interface{} - type DeleteBackupRequestExpansion interface{} type DownloadRequestExpansion 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..78893332c5 100644 --- a/pkg/generated/clientset/versioned/typed/velero/v1/velero_client.go +++ b/pkg/generated/clientset/versioned/typed/velero/v1/velero_client.go @@ -27,7 +27,6 @@ import ( type VeleroV1Interface interface { RESTClient() rest.Interface BackupsGetter - BackupStorageLocationsGetter DeleteBackupRequestsGetter DownloadRequestsGetter PodVolumeBackupsGetter @@ -48,10 +47,6 @@ func (c *VeleroV1Client) Backups(namespace string) BackupInterface { return newBackups(c, namespace) } -func (c *VeleroV1Client) BackupStorageLocations(namespace string) BackupStorageLocationInterface { - return newBackupStorageLocations(c, namespace) -} - func (c *VeleroV1Client) DeleteBackupRequests(namespace string) DeleteBackupRequestInterface { return newDeleteBackupRequests(c, namespace) } diff --git a/pkg/generated/crds/doc.go b/pkg/generated/crds/doc.go deleted file mode 100644 index 1103893f74..0000000000 --- a/pkg/generated/crds/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// Package crds embeds the controller-tools generated CRD manifests -package crds - -//go:generate go run ../../../hack/crd-gen/main.go diff --git a/pkg/generated/crds/manifests/velero.io_backupstoragelocations.yaml b/pkg/generated/crds/manifests/velero.io_backupstoragelocations.yaml deleted file mode 100644 index fe503368e2..0000000000 --- a/pkg/generated/crds/manifests/velero.io_backupstoragelocations.yaml +++ /dev/null @@ -1,126 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.2.4 - creationTimestamp: null - name: backupstoragelocations.velero.io -spec: - group: velero.io - names: - kind: BackupStorageLocation - listKind: BackupStorageLocationList - plural: backupstoragelocations - singular: backupstoragelocation - preserveUnknownFields: false - scope: Namespaced - validation: - openAPIV3Schema: - description: BackupStorageLocation is a location where Velero stores backup - 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/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: BackupStorageLocationSpec defines the specification for a Velero - BackupStorageLocation. - properties: - accessMode: - description: AccessMode defines the permissions for the backup storage - location. - enum: - - ReadOnly - - ReadWrite - type: string - backupSyncPeriod: - description: BackupSyncPeriod defines how frequently to sync backup - API objects from object storage. A value of 0 disables sync. - nullable: true - type: string - config: - additionalProperties: - type: string - description: Config is for provider-specific configuration fields. - type: object - objectStorage: - description: ObjectStorageLocation specifies the settings necessary - to connect to a provider's object storage. - properties: - bucket: - description: Bucket is the bucket to use for object storage. - type: string - caCert: - description: CACert defines a CA bundle to use when verifying TLS - connections to the provider. - format: byte - type: string - prefix: - description: Prefix is the path inside a bucket to use for Velero - storage. Optional. - type: string - required: - - bucket - type: object - provider: - description: Provider is the provider of the backup storage. - type: string - required: - - objectStorage - - provider - type: object - status: - description: BackupStorageLocationStatus describes the current status of - a Velero BackupStorageLocation. - properties: - accessMode: - description: "AccessMode is an unused field. \n Deprecated: there is - now an AccessMode field on the Spec and this field will be removed - entirely as of v2.0." - enum: - - ReadOnly - - ReadWrite - type: string - lastSyncedRevision: - description: "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. \n Deprecated: this field is no longer - updated or used for detecting changes to the location's contents and - will be removed entirely in v2.0." - type: string - lastSyncedTime: - description: LastSyncedTime is the last time the contents of the location - were synced into the cluster. - format: date-time - nullable: true - type: string - phase: - description: Phase is the current state of the BackupStorageLocation. - enum: - - Available - - Unavailable - type: string - type: object - type: object - version: v1 - versions: - - name: v1 - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/pkg/generated/crds/manifests/velero.io_deletebackuprequests.yaml b/pkg/generated/crds/manifests/velero.io_deletebackuprequests.yaml deleted file mode 100644 index 951b236555..0000000000 --- a/pkg/generated/crds/manifests/velero.io_deletebackuprequests.yaml +++ /dev/null @@ -1,73 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.2.4 - creationTimestamp: null - name: deletebackuprequests.velero.io -spec: - group: velero.io - names: - kind: DeleteBackupRequest - listKind: DeleteBackupRequestList - plural: deletebackuprequests - singular: deletebackuprequest - preserveUnknownFields: false - scope: Namespaced - validation: - openAPIV3Schema: - description: DeleteBackupRequest is a request to delete one or more backups. - 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: DeleteBackupRequestSpec is the specification for which backups - to delete. - properties: - backupName: - type: string - required: - - backupName - type: object - status: - description: DeleteBackupRequestStatus is the current status of a DeleteBackupRequest. - properties: - errors: - description: Errors contains any errors that were encountered during - the deletion process. - items: - type: string - nullable: true - type: array - phase: - description: Phase is the current state of the DeleteBackupRequest. - enum: - - New - - InProgress - - Processed - type: string - type: object - type: object - version: v1 - versions: - - name: v1 - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/pkg/generated/crds/manifests/velero.io_downloadrequests.yaml b/pkg/generated/crds/manifests/velero.io_downloadrequests.yaml deleted file mode 100644 index f0bd8debae..0000000000 --- a/pkg/generated/crds/manifests/velero.io_downloadrequests.yaml +++ /dev/null @@ -1,94 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.2.4 - creationTimestamp: null - name: downloadrequests.velero.io -spec: - group: velero.io - names: - kind: DownloadRequest - listKind: DownloadRequestList - plural: downloadrequests - singular: downloadrequest - preserveUnknownFields: false - scope: Namespaced - validation: - openAPIV3Schema: - description: DownloadRequest is a request to download an artifact from backup - object storage, such as a backup log file. - 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: DownloadRequestSpec is the specification for a download request. - properties: - target: - description: Target is what to download (e.g. logs for a backup). - properties: - kind: - description: Kind is the type of file to download. - enum: - - BackupLog - - BackupContents - - BackupVolumeSnapshots - - BackupResourceList - - RestoreLog - - RestoreResults - type: string - name: - description: Name is the name of the kubernetes resource with which - the file is associated. - type: string - required: - - kind - - name - type: object - required: - - target - type: object - status: - description: DownloadRequestStatus is the current status of a DownloadRequest. - properties: - downloadURL: - description: DownloadURL contains the pre-signed URL for the target - file. - type: string - expiration: - description: Expiration is when this DownloadRequest expires and can - be deleted by the system. - format: date-time - nullable: true - type: string - phase: - description: Phase is the current state of the DownloadRequest. - enum: - - New - - Processed - type: string - type: object - type: object - version: v1 - versions: - - name: v1 - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/pkg/generated/crds/manifests/velero.io_podvolumebackups.yaml b/pkg/generated/crds/manifests/velero.io_podvolumebackups.yaml deleted file mode 100644 index 54f5641bbb..0000000000 --- a/pkg/generated/crds/manifests/velero.io_podvolumebackups.yaml +++ /dev/null @@ -1,162 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.2.4 - creationTimestamp: null - name: podvolumebackups.velero.io -spec: - group: velero.io - names: - kind: PodVolumeBackup - listKind: PodVolumeBackupList - plural: podvolumebackups - singular: podvolumebackup - preserveUnknownFields: false - 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/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: PodVolumeBackupSpec is the specification for a PodVolumeBackup. - properties: - backupStorageLocation: - description: BackupStorageLocation is the name of the backup storage - location where the restic repository is stored. - type: string - node: - description: Node is the name of the node that the Pod is running on. - type: string - pod: - description: Pod is a reference to the pod containing the volume to - be backed up. - properties: - apiVersion: - description: API version of the referent. - type: string - fieldPath: - description: 'If referring to a piece of an object instead of an - entire object, this string should contain a valid JSON/Go field - access statement, such as desiredState.manifest.containers[2]. - For example, if the object reference is to a container within - a pod, this would take on a value like: "spec.containers{name}" - (where "name" refers to the name of the container that triggered - the event) or if no container name is specified "spec.containers[2]" - (container with index 2 in this pod). This syntax is chosen only - to have some well-defined way of referencing a part of an object. - TODO: this design is not final and this field is subject to change - in the future.' - type: string - kind: - description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - namespace: - description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' - type: string - resourceVersion: - description: 'Specific resourceVersion to which this reference is - made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' - type: string - uid: - description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' - type: string - type: object - repoIdentifier: - description: RepoIdentifier is the restic repository identifier. - type: string - tags: - additionalProperties: - type: string - description: Tags are a map of key-value pairs that should be applied - to the volume backup as tags. - type: object - volume: - description: Volume is the name of the volume within the Pod to be backed - up. - type: string - required: - - backupStorageLocation - - node - - pod - - repoIdentifier - - volume - type: object - status: - description: PodVolumeBackupStatus is the current status of a PodVolumeBackup. - properties: - completionTimestamp: - description: CompletionTimestamp records the time a backup was completed. - Completion time is recorded even on failed backups. Completion time - is recorded before uploading the backup object. The server's time - is used for CompletionTimestamps - format: date-time - nullable: true - type: string - message: - description: Message is a message about the pod volume backup's status. - type: string - path: - description: Path is the full path within the controller pod being backed - up. - type: string - phase: - description: Phase is the current state of the PodVolumeBackup. - enum: - - New - - InProgress - - Completed - - Failed - type: string - progress: - description: Progress holds the total number of bytes of the volume - and the current number of backed up bytes. This can be used to display - progress information about the backup operation. - properties: - bytesDone: - format: int64 - type: integer - totalBytes: - format: int64 - type: integer - type: object - snapshotID: - description: SnapshotID is the identifier for the snapshot of the pod - volume. - type: string - startTimestamp: - description: StartTimestamp records the time a backup was started. Separate - from CreationTimestamp, since that value changes on restores. The - server's time is used for StartTimestamps - format: date-time - nullable: true - type: string - type: object - type: object - version: v1 - versions: - - name: v1 - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/pkg/generated/crds/manifests/velero.io_podvolumerestores.yaml b/pkg/generated/crds/manifests/velero.io_podvolumerestores.yaml deleted file mode 100644 index 03b4bf0135..0000000000 --- a/pkg/generated/crds/manifests/velero.io_podvolumerestores.yaml +++ /dev/null @@ -1,145 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.2.4 - creationTimestamp: null - name: podvolumerestores.velero.io -spec: - group: velero.io - names: - kind: PodVolumeRestore - listKind: PodVolumeRestoreList - plural: podvolumerestores - singular: podvolumerestore - preserveUnknownFields: false - 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/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: PodVolumeRestoreSpec is the specification for a PodVolumeRestore. - properties: - backupStorageLocation: - description: BackupStorageLocation is the name of the backup storage - location where the restic repository is stored. - type: string - pod: - description: Pod is a reference to the pod containing the volume to - be restored. - properties: - apiVersion: - description: API version of the referent. - type: string - fieldPath: - description: 'If referring to a piece of an object instead of an - entire object, this string should contain a valid JSON/Go field - access statement, such as desiredState.manifest.containers[2]. - For example, if the object reference is to a container within - a pod, this would take on a value like: "spec.containers{name}" - (where "name" refers to the name of the container that triggered - the event) or if no container name is specified "spec.containers[2]" - (container with index 2 in this pod). This syntax is chosen only - to have some well-defined way of referencing a part of an object. - TODO: this design is not final and this field is subject to change - in the future.' - type: string - kind: - description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - namespace: - description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' - type: string - resourceVersion: - description: 'Specific resourceVersion to which this reference is - made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' - type: string - uid: - description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' - type: string - type: object - repoIdentifier: - description: RepoIdentifier is the restic repository identifier. - type: string - snapshotID: - description: SnapshotID is the ID of the volume snapshot to be restored. - type: string - volume: - description: Volume is the name of the volume within the Pod to be restored. - type: string - required: - - backupStorageLocation - - pod - - repoIdentifier - - snapshotID - - volume - type: object - status: - description: PodVolumeRestoreStatus is the current status of a PodVolumeRestore. - properties: - completionTimestamp: - description: CompletionTimestamp records the time a restore was completed. - Completion time is recorded even on failed restores. The server's - time is used for CompletionTimestamps - format: date-time - nullable: true - type: string - message: - description: Message is a message about the pod volume restore's status. - type: string - phase: - description: Phase is the current state of the PodVolumeRestore. - enum: - - New - - InProgress - - Completed - - Failed - type: string - progress: - description: Progress holds the total number of bytes of the snapshot - and the current number of restored bytes. This can be used to display - progress information about the restore operation. - properties: - bytesDone: - format: int64 - type: integer - totalBytes: - format: int64 - type: integer - type: object - startTimestamp: - description: StartTimestamp records the time a restore was started. - The server's time is used for StartTimestamps - format: date-time - nullable: true - type: string - type: object - type: object - version: v1 - versions: - - name: v1 - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/pkg/generated/crds/manifests/velero.io_resticrepositories.yaml b/pkg/generated/crds/manifests/velero.io_resticrepositories.yaml deleted file mode 100644 index 538fb2e814..0000000000 --- a/pkg/generated/crds/manifests/velero.io_resticrepositories.yaml +++ /dev/null @@ -1,89 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.2.4 - creationTimestamp: null - name: resticrepositories.velero.io -spec: - group: velero.io - names: - kind: ResticRepository - listKind: ResticRepositoryList - plural: resticrepositories - singular: resticrepository - preserveUnknownFields: false - 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/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: ResticRepositorySpec is the specification for a ResticRepository. - properties: - backupStorageLocation: - description: BackupStorageLocation is the name of the BackupStorageLocation - that should contain this repository. - type: string - maintenanceFrequency: - description: MaintenanceFrequency is how often maintenance should be - run. - 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 - contains pod volume backups for. - type: string - required: - - backupStorageLocation - - maintenanceFrequency - - resticIdentifier - - volumeNamespace - type: object - status: - description: ResticRepositoryStatus is the current status of a ResticRepository. - properties: - lastMaintenanceTime: - description: LastMaintenanceTime is the last time maintenance was run. - format: date-time - nullable: true - type: string - message: - description: Message is a message about the current status of the ResticRepository. - type: string - phase: - description: Phase is the current state of the ResticRepository. - enum: - - New - - Ready - - NotReady - type: string - type: object - type: object - version: v1 - versions: - - name: v1 - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/pkg/generated/crds/manifests/velero.io_restores.yaml b/pkg/generated/crds/manifests/velero.io_restores.yaml deleted file mode 100644 index cabfa3067a..0000000000 --- a/pkg/generated/crds/manifests/velero.io_restores.yaml +++ /dev/null @@ -1,189 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.2.4 - creationTimestamp: null - name: restores.velero.io -spec: - group: velero.io - names: - kind: Restore - listKind: RestoreList - plural: restores - singular: restore - preserveUnknownFields: false - scope: Namespaced - validation: - openAPIV3Schema: - description: Restore is a Velero resource that represents the application of - resources from a Velero backup to a target 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: RestoreSpec defines the specification for a Velero restore. - properties: - backupName: - description: BackupName is the unique name of the Velero backup to restore - from. - type: string - excludedNamespaces: - description: ExcludedNamespaces contains a list of namespaces that are - not included in the restore. - items: - type: string - nullable: true - type: array - excludedResources: - description: ExcludedResources is a slice of resource names that are - not included in the restore. - items: - type: string - nullable: true - type: array - includeClusterResources: - description: IncludeClusterResources specifies whether cluster-scoped - resources should be included for consideration in the restore. If - null, defaults to true. - nullable: true - type: boolean - includedNamespaces: - description: IncludedNamespaces is a slice of namespace names to include - objects from. If empty, all namespaces are included. - items: - type: string - nullable: true - type: array - includedResources: - description: IncludedResources is a slice of resource names to include - in the restore. If empty, all resources in the backup are included. - items: - type: string - nullable: true - type: array - labelSelector: - description: LabelSelector is a metav1.LabelSelector to filter with - when restoring individual objects from the backup. If empty or nil, - all objects are included. Optional. - nullable: true - 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 - namespaceMapping: - additionalProperties: - type: string - description: 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. - type: object - restorePVs: - description: RestorePVs specifies whether to restore all included PVs - from snapshot (via the cloudprovider). - nullable: true - type: boolean - scheduleName: - description: 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. - type: string - required: - - backupName - type: object - status: - description: RestoreStatus captures the current status of a Velero restore - properties: - errors: - description: Errors is a count of all error messages that were generated - during execution of the restore. The actual errors are stored in object - storage. - type: integer - failureReason: - description: FailureReason is an error that caused the entire restore - to fail. - type: string - phase: - description: Phase is the current state of the Restore - enum: - - New - - FailedValidation - - InProgress - - Completed - - PartiallyFailed - - Failed - type: string - validationErrors: - description: ValidationErrors is a slice of all validation errors (if - applicable) - items: - type: string - nullable: true - type: array - warnings: - description: Warnings is a count of all warning messages that were generated - during execution of the restore. The actual warnings are stored in - object storage. - type: integer - type: object - type: object - version: v1 - versions: - - name: v1 - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/pkg/generated/crds/manifests/velero.io_serverstatusrequests.yaml b/pkg/generated/crds/manifests/velero.io_serverstatusrequests.yaml deleted file mode 100644 index 4989ab948f..0000000000 --- a/pkg/generated/crds/manifests/velero.io_serverstatusrequests.yaml +++ /dev/null @@ -1,85 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.2.4 - creationTimestamp: null - name: serverstatusrequests.velero.io -spec: - group: velero.io - names: - kind: ServerStatusRequest - listKind: ServerStatusRequestList - plural: serverstatusrequests - singular: serverstatusrequest - preserveUnknownFields: false - scope: Namespaced - validation: - openAPIV3Schema: - description: ServerStatusRequest is a request to access current status information - about the Velero server. - 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: ServerStatusRequestSpec is the specification for a ServerStatusRequest. - type: object - status: - description: ServerStatusRequestStatus is the current status of a ServerStatusRequest. - properties: - phase: - description: Phase is the current lifecycle phase of the ServerStatusRequest. - enum: - - New - - Processed - type: string - plugins: - description: Plugins list information about the plugins running on the - Velero server - items: - description: PluginInfo contains attributes of a Velero plugin - properties: - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - nullable: true - type: array - processedTimestamp: - description: ProcessedTimestamp is when the ServerStatusRequest was - processed by the ServerStatusRequestController. - format: date-time - nullable: true - type: string - serverVersion: - description: ServerVersion is the Velero server version. - type: string - type: object - type: object - version: v1 - versions: - - name: v1 - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/pkg/generated/crds/manifests/velero.io_volumesnapshotlocations.yaml b/pkg/generated/crds/manifests/velero.io_volumesnapshotlocations.yaml deleted file mode 100644 index 66e2161a22..0000000000 --- a/pkg/generated/crds/manifests/velero.io_volumesnapshotlocations.yaml +++ /dev/null @@ -1,74 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.2.4 - creationTimestamp: null - name: volumesnapshotlocations.velero.io -spec: - group: velero.io - names: - kind: VolumeSnapshotLocation - listKind: VolumeSnapshotLocationList - plural: volumesnapshotlocations - singular: volumesnapshotlocation - preserveUnknownFields: false - scope: Namespaced - validation: - openAPIV3Schema: - description: VolumeSnapshotLocation is a location where Velero stores volume - snapshots. - 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: VolumeSnapshotLocationSpec defines the specification for a - Velero VolumeSnapshotLocation. - properties: - config: - additionalProperties: - type: string - description: Config is for provider-specific configuration fields. - type: object - provider: - description: Provider is the provider of the volume storage. - type: string - required: - - provider - type: object - status: - description: VolumeSnapshotLocationStatus describes the current status of - a Velero VolumeSnapshotLocation. - properties: - phase: - description: VolumeSnapshotLocationPhase is the lifecyle phase of a - Velero VolumeSnapshotLocation. - enum: - - Available - - Unavailable - type: string - type: object - type: object - version: v1 - versions: - - name: v1 - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go index 15770dcebb..b8409616d7 100644 --- a/pkg/generated/informers/externalversions/generic.go +++ b/pkg/generated/informers/externalversions/generic.go @@ -55,8 +55,6 @@ 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("backupstoragelocations"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().BackupStorageLocations().Informer()}, nil case v1.SchemeGroupVersion.WithResource("deletebackuprequests"): return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().DeleteBackupRequests().Informer()}, nil case v1.SchemeGroupVersion.WithResource("downloadrequests"): diff --git a/pkg/generated/informers/externalversions/velero/v1/backupstoragelocation.go b/pkg/generated/informers/externalversions/velero/v1/backupstoragelocation.go deleted file mode 100644 index 7e356339b4..0000000000 --- a/pkg/generated/informers/externalversions/velero/v1/backupstoragelocation.go +++ /dev/null @@ -1,89 +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 informer-gen. DO NOT EDIT. - -package v1 - -import ( - time "time" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" - v1 "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// BackupStorageLocationInformer provides access to a shared informer and lister for -// BackupStorageLocations. -type BackupStorageLocationInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1.BackupStorageLocationLister -} - -type backupStorageLocationInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewBackupStorageLocationInformer constructs a new informer for BackupStorageLocation 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 NewBackupStorageLocationInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredBackupStorageLocationInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredBackupStorageLocationInformer constructs a new informer for BackupStorageLocation 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 NewFilteredBackupStorageLocationInformer(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().BackupStorageLocations(namespace).List(options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().BackupStorageLocations(namespace).Watch(options) - }, - }, - &velerov1.BackupStorageLocation{}, - resyncPeriod, - indexers, - ) -} - -func (f *backupStorageLocationInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredBackupStorageLocationInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *backupStorageLocationInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&velerov1.BackupStorageLocation{}, f.defaultInformer) -} - -func (f *backupStorageLocationInformer) Lister() v1.BackupStorageLocationLister { - return v1.NewBackupStorageLocationLister(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..51615cfa8d 100644 --- a/pkg/generated/informers/externalversions/velero/v1/interface.go +++ b/pkg/generated/informers/externalversions/velero/v1/interface.go @@ -26,8 +26,6 @@ import ( type Interface interface { // Backups returns a BackupInformer. Backups() BackupInformer - // BackupStorageLocations returns a BackupStorageLocationInformer. - BackupStorageLocations() BackupStorageLocationInformer // DeleteBackupRequests returns a DeleteBackupRequestInformer. DeleteBackupRequests() DeleteBackupRequestInformer // DownloadRequests returns a DownloadRequestInformer. @@ -64,11 +62,6 @@ func (v *version) Backups() BackupInformer { return &backupInformer{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} -} - // DeleteBackupRequests returns a DeleteBackupRequestInformer. func (v *version) DeleteBackupRequests() DeleteBackupRequestInformer { return &deleteBackupRequestInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/generated/listers/velero/v1/backupstoragelocation.go b/pkg/generated/listers/velero/v1/backupstoragelocation.go deleted file mode 100644 index 8c6c140bd4..0000000000 --- a/pkg/generated/listers/velero/v1/backupstoragelocation.go +++ /dev/null @@ -1,94 +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" -) - -// BackupStorageLocationLister helps list BackupStorageLocations. -type BackupStorageLocationLister interface { - // List lists all BackupStorageLocations in the indexer. - List(selector labels.Selector) (ret []*v1.BackupStorageLocation, err error) - // BackupStorageLocations returns an object that can list and get BackupStorageLocations. - BackupStorageLocations(namespace string) BackupStorageLocationNamespaceLister - BackupStorageLocationListerExpansion -} - -// backupStorageLocationLister implements the BackupStorageLocationLister interface. -type backupStorageLocationLister struct { - indexer cache.Indexer -} - -// NewBackupStorageLocationLister returns a new BackupStorageLocationLister. -func NewBackupStorageLocationLister(indexer cache.Indexer) BackupStorageLocationLister { - return &backupStorageLocationLister{indexer: indexer} -} - -// List lists all BackupStorageLocations in the indexer. -func (s *backupStorageLocationLister) List(selector labels.Selector) (ret []*v1.BackupStorageLocation, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.BackupStorageLocation)) - }) - return ret, err -} - -// BackupStorageLocations returns an object that can list and get BackupStorageLocations. -func (s *backupStorageLocationLister) BackupStorageLocations(namespace string) BackupStorageLocationNamespaceLister { - return backupStorageLocationNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// BackupStorageLocationNamespaceLister helps list and get BackupStorageLocations. -type BackupStorageLocationNamespaceLister interface { - // List lists all BackupStorageLocations in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1.BackupStorageLocation, err error) - // Get retrieves the BackupStorageLocation from the indexer for a given namespace and name. - Get(name string) (*v1.BackupStorageLocation, error) - BackupStorageLocationNamespaceListerExpansion -} - -// backupStorageLocationNamespaceLister implements the BackupStorageLocationNamespaceLister -// interface. -type backupStorageLocationNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all BackupStorageLocations in the indexer for a given namespace. -func (s backupStorageLocationNamespaceLister) List(selector labels.Selector) (ret []*v1.BackupStorageLocation, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.BackupStorageLocation)) - }) - return ret, err -} - -// Get retrieves the BackupStorageLocation from the indexer for a given namespace and name. -func (s backupStorageLocationNamespaceLister) Get(name string) (*v1.BackupStorageLocation, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("backupstoragelocation"), name) - } - return obj.(*v1.BackupStorageLocation), nil -} diff --git a/pkg/generated/listers/velero/v1/expansion_generated.go b/pkg/generated/listers/velero/v1/expansion_generated.go index b57656650d..cb5c5fb8c5 100644 --- a/pkg/generated/listers/velero/v1/expansion_generated.go +++ b/pkg/generated/listers/velero/v1/expansion_generated.go @@ -26,14 +26,6 @@ type BackupListerExpansion interface{} // BackupNamespaceLister. type BackupNamespaceListerExpansion interface{} -// BackupStorageLocationListerExpansion allows custom methods to be added to -// BackupStorageLocationLister. -type BackupStorageLocationListerExpansion interface{} - -// BackupStorageLocationNamespaceListerExpansion allows custom methods to be added to -// BackupStorageLocationNamespaceLister. -type BackupStorageLocationNamespaceListerExpansion interface{} - // DeleteBackupRequestListerExpansion allows custom methods to be added to // DeleteBackupRequestLister. type DeleteBackupRequestListerExpansion interface{} diff --git a/pkg/install/resources.go b/pkg/install/resources.go index b88dbb183f..2dbe0a4ffe 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -17,18 +17,23 @@ limitations under the License. package install import ( + "io/ioutil" + "log" "time" corev1 "k8s.io/api/core/v1" rbacv1beta1 "k8s.io/api/rbac/v1beta1" + apiextinstall "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install" + apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" 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/runtime/schema" + v1api "github.com/vmware-tanzu/velero/api/v1" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/buildinfo" - "github.com/vmware-tanzu/velero/pkg/generated/crds" + "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" ) // Use "latest" if the build process didn't supply a version @@ -137,17 +142,17 @@ func Namespace(namespace string) *corev1.Namespace { } } -func BackupStorageLocation(namespace, provider, bucket, prefix string, config map[string]string, caCert []byte) *v1.BackupStorageLocation { - return &v1.BackupStorageLocation{ +func BackupStorageLocation(namespace, provider, bucket, prefix string, config map[string]string, caCert []byte) *v1api.BackupStorageLocation { + return &v1api.BackupStorageLocation{ ObjectMeta: objectMeta(namespace, "default"), TypeMeta: metav1.TypeMeta{ Kind: "BackupStorageLocation", - APIVersion: v1.SchemeGroupVersion.String(), + APIVersion: v1api.GroupVersion.String(), }, - Spec: v1.BackupStorageLocationSpec{ + Spec: v1api.BackupStorageLocationSpec{ Provider: provider, - StorageType: v1.StorageType{ - ObjectStorage: &v1.ObjectStorageLocation{ + StorageType: v1api.StorageType{ + ObjectStorage: &v1api.ObjectStorageLocation{ Bucket: bucket, Prefix: prefix, CACert: caCert, @@ -228,7 +233,25 @@ func AllCRDs() *unstructured.UnstructuredList { // Set the GVK so that the serialization framework outputs the list properly resources.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "List"}) - for _, crd := range crds.CRDs { + apiextinstall.Install(scheme.Scheme) + decode := scheme.Codecs.UniversalDeserializer().Decode + + files, err := ioutil.ReadDir("config/crd/bases") + if err != nil { + log.Fatal(err) + } + for _, f := range files { + data, err := ioutil.ReadFile("config/crd/bases/" + f.Name()) + if err != nil { + log.Fatal(err) + } + + obj, _, err := decode([]byte(data), nil, nil) + if err != nil { + log.Fatal(err) + } + + crd := obj.(*apiextv1beta1.CustomResourceDefinition) crd.SetLabels(labels()) appendUnstructured(resources, crd) } diff --git a/pkg/persistence/object_store.go b/pkg/persistence/object_store.go index 545f3f0423..d1b8dbdf7a 100644 --- a/pkg/persistence/object_store.go +++ b/pkg/persistence/object_store.go @@ -29,6 +29,7 @@ import ( "github.com/sirupsen/logrus" kerrors "k8s.io/apimachinery/pkg/util/errors" + velerov1apikb "github.com/vmware-tanzu/velero/api/v1" 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/plugin/velero" @@ -90,7 +91,7 @@ type ObjectStoreGetter interface { GetObjectStore(provider string) (velero.ObjectStore, error) } -func NewObjectBackupStore(location *velerov1api.BackupStorageLocation, objectStoreGetter ObjectStoreGetter, logger logrus.FieldLogger) (BackupStore, error) { +func NewObjectBackupStore(location *velerov1apikb.BackupStorageLocation, objectStoreGetter ObjectStoreGetter, logger logrus.FieldLogger) (BackupStore, error) { if location.Spec.ObjectStorage == nil { return nil, errors.New("backup storage location does not use object storage") } diff --git a/pkg/restic/common.go b/pkg/restic/common.go index ffb2c11677..220112e8bd 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -28,6 +28,7 @@ import ( "k8s.io/apimachinery/pkg/labels" corev1listers "k8s.io/client-go/listers/core/v1" + velerov1 "github.com/vmware-tanzu/velero/api/v1" 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" @@ -284,14 +285,9 @@ func TempCACertFile(caCert []byte, bsl string, fs filesystem.Interface) (string, return name, nil } -func GetCACert(backupLocationLister velerov1listers.BackupStorageLocationLister, namespace, bsl string) ([]byte, error) { - location, err := backupLocationLister.BackupStorageLocations(namespace).Get(bsl) - if err != nil { - return nil, errors.Wrap(err, "error getting backup storage location") - } - - if location.Spec.ObjectStorage != nil { - return location.Spec.ObjectStorage.CACert, nil +func GetCACert(loc *velerov1.BackupStorageLocation) ([]byte, error) { + if loc.Spec.ObjectStorage != nil { + return loc.Spec.ObjectStorage.CACert, nil } return nil, nil @@ -309,12 +305,7 @@ func NewPodVolumeRestoreListOptions(name string) metav1.ListOptions { // should be used when running a restic command for an Azure backend. This list is // the current environment, plus the Azure-specific variables restic needs, namely // a storage account name and key. -func AzureCmdEnv(backupLocationLister velerov1listers.BackupStorageLocationLister, namespace, backupLocation string) ([]string, error) { - loc, err := backupLocationLister.BackupStorageLocations(namespace).Get(backupLocation) - if err != nil { - return nil, errors.Wrap(err, "error getting backup storage location") - } - +func AzureCmdEnv(loc *velerov1.BackupStorageLocation) ([]string, error) { azureVars, err := getAzureResticEnvVars(loc.Spec.Config) if err != nil { return nil, errors.Wrap(err, "error getting azure restic env vars") @@ -332,12 +323,7 @@ func AzureCmdEnv(backupLocationLister velerov1listers.BackupStorageLocationListe // should be used when running a restic command for an S3 backend. This list is // the current environment, plus the AWS-specific variables restic needs, namely // a credential profile. -func S3CmdEnv(backupLocationLister velerov1listers.BackupStorageLocationLister, namespace, backupLocation string) ([]string, error) { - loc, err := backupLocationLister.BackupStorageLocations(namespace).Get(backupLocation) - if err != nil { - return nil, errors.Wrap(err, "error getting backup storage location") - } - +func S3CmdEnv(loc *velerov1.BackupStorageLocation) ([]string, error) { awsVars, err := getS3ResticEnvVars(loc.Spec.Config) if err != nil { return nil, errors.Wrap(err, "error getting aws restic env vars") diff --git a/pkg/restic/config.go b/pkg/restic/config.go index 771c5448d5..1026c5e42b 100644 --- a/pkg/restic/config.go +++ b/pkg/restic/config.go @@ -27,7 +27,7 @@ import ( "github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/pkg/errors" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov1apikb "github.com/vmware-tanzu/velero/api/v1" "github.com/vmware-tanzu/velero/pkg/persistence" ) @@ -45,7 +45,7 @@ var getAWSBucketRegion = getBucketRegion // getRepoPrefix returns the prefix of the value of the --repo flag for // restic commands, i.e. everything except the "/". -func getRepoPrefix(location *velerov1api.BackupStorageLocation) (string, error) { +func getRepoPrefix(location *velerov1apikb.BackupStorageLocation) (string, error) { var bucket, prefix string if location.Spec.ObjectStorage != nil { @@ -93,7 +93,7 @@ func getRepoPrefix(location *velerov1api.BackupStorageLocation) (string, error) // 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) { +func GetRepoIdentifier(location *velerov1apikb.BackupStorageLocation, name string) (string, error) { prefix, err := getRepoPrefix(location) if err != nil { return "", err diff --git a/pkg/restic/repository_manager.go b/pkg/restic/repository_manager.go index 6ae87f0226..176a157efd 100644 --- a/pkg/restic/repository_manager.go +++ b/pkg/restic/repository_manager.go @@ -29,6 +29,10 @@ import ( corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" + kbcache "sigs.k8s.io/controller-runtime/pkg/cache" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" + + velerov1 "github.com/vmware-tanzu/velero/api/v1" 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" @@ -79,20 +83,19 @@ type RestorerFactory interface { } type repositoryManager struct { - namespace string - veleroClient clientset.Interface - secretsLister corev1listers.SecretLister - repoLister velerov1listers.ResticRepositoryLister - repoInformerSynced cache.InformerSynced - backupLocationLister velerov1listers.BackupStorageLocationLister - backupLocationInformerSynced cache.InformerSynced - log logrus.FieldLogger - repoLocker *repoLocker - repoEnsurer *repositoryEnsurer - fileSystem filesystem.Interface - ctx context.Context - pvcClient corev1client.PersistentVolumeClaimsGetter - pvClient corev1client.PersistentVolumesGetter + namespace string + veleroClient clientset.Interface + secretsLister corev1listers.SecretLister + repoLister velerov1listers.ResticRepositoryLister + repoInformerSynced cache.InformerSynced + kbCache kbcache.Cache + log logrus.FieldLogger + repoLocker *repoLocker + repoEnsurer *repositoryEnsurer + fileSystem filesystem.Interface + ctx context.Context + pvcClient corev1client.PersistentVolumeClaimsGetter + pvClient corev1client.PersistentVolumesGetter } // NewRepositoryManager constructs a RepositoryManager. @@ -103,23 +106,22 @@ func NewRepositoryManager( secretsInformer cache.SharedIndexInformer, repoInformer velerov1informers.ResticRepositoryInformer, repoClient velerov1client.ResticRepositoriesGetter, - backupLocationInformer velerov1informers.BackupStorageLocationInformer, + kbCache kbcache.Cache, pvcClient corev1client.PersistentVolumeClaimsGetter, pvClient corev1client.PersistentVolumesGetter, log logrus.FieldLogger, ) (RepositoryManager, error) { rm := &repositoryManager{ - namespace: namespace, - veleroClient: veleroClient, - secretsLister: corev1listers.NewSecretLister(secretsInformer.GetIndexer()), - repoLister: repoInformer.Lister(), - repoInformerSynced: repoInformer.Informer().HasSynced, - backupLocationLister: backupLocationInformer.Lister(), - backupLocationInformerSynced: backupLocationInformer.Informer().HasSynced, - pvcClient: pvcClient, - pvClient: pvClient, - log: log, - ctx: ctx, + namespace: namespace, + veleroClient: veleroClient, + secretsLister: corev1listers.NewSecretLister(secretsInformer.GetIndexer()), + repoLister: repoInformer.Lister(), + repoInformerSynced: repoInformer.Informer().HasSynced, + kbCache: kbCache, + pvcClient: pvcClient, + pvClient: pvClient, + log: log, + ctx: ctx, repoLocker: newRepoLocker(), repoEnsurer: newRepositoryEnsurer(repoInformer, repoClient, log), @@ -244,8 +246,16 @@ func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error { cmd.PasswordFile = file + location := &velerov1.BackupStorageLocation{} + if err := rm.kbCache.Get(context.Background(), kbclient.ObjectKey{ + Namespace: rm.namespace, + Name: backupLocation, + }, location); err != nil { + return err + } + // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic - caCert, err := GetCACert(rm.backupLocationLister, rm.namespace, backupLocation) + caCert, err := GetCACert(location) if err != nil { return err } @@ -261,21 +271,21 @@ func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error { cmd.CACertFile = caCertFile if strings.HasPrefix(cmd.RepoIdentifier, "azure") { - if !cache.WaitForCacheSync(rm.ctx.Done(), rm.backupLocationInformerSynced) { + if !rm.kbCache.WaitForCacheSync(rm.ctx.Done()) { return errors.New("timed out waiting for cache to sync") } - env, err := AzureCmdEnv(rm.backupLocationLister, rm.namespace, backupLocation) + env, err := AzureCmdEnv(location) if err != nil { return err } cmd.Env = env } else if strings.HasPrefix(cmd.RepoIdentifier, "s3") { - if !cache.WaitForCacheSync(rm.ctx.Done(), rm.backupLocationInformerSynced) { + if !rm.kbCache.WaitForCacheSync(rm.ctx.Done()) { return errors.New("timed out waiting for cache to sync") } - env, err := S3CmdEnv(rm.backupLocationLister, rm.namespace, backupLocation) + env, err := S3CmdEnv(location) if err != nil { return err } From 09c20d5ad387c23c3b3a398a084b6af76810a86d Mon Sep 17 00:00:00 2001 From: Carlisia Date: Wed, 20 May 2020 10:19:49 -0700 Subject: [PATCH 05/34] s/cache/client bc client fetches from cache And other naming improvements Signed-off-by: Carlisia --- pkg/backup/request.go | 4 +- pkg/cmd/cli/backup/create.go | 10 +-- pkg/cmd/cli/backuplocation/create.go | 9 ++- pkg/cmd/cli/backuplocation/get.go | 8 +-- pkg/cmd/cli/restic/server.go | 34 ++++++----- pkg/cmd/server/server.go | 61 ++++++------------- pkg/controller/backup_controller.go | 19 +++--- pkg/controller/backup_controller_test.go | 16 ++--- pkg/controller/backup_deletion_controller.go | 19 +++--- pkg/controller/backup_sync_controller.go | 31 +++++----- pkg/controller/backup_sync_controller_test.go | 35 +++++------ pkg/controller/download_request_controller.go | 17 +++--- pkg/controller/gc_controller.go | 18 +++--- .../pod_volume_backup_controller.go | 50 +++++++-------- .../pod_volume_restore_controller.go | 24 ++++---- .../restic_repository_controller.go | 15 +++-- pkg/controller/restore_controller.go | 19 +++--- pkg/persistence/object_store.go | 4 +- pkg/restic/config.go | 6 +- pkg/restic/repository_manager.go | 7 +-- 20 files changed, 191 insertions(+), 215 deletions(-) diff --git a/pkg/backup/request.go b/pkg/backup/request.go index 165ae25b6b..f0709ac51f 100644 --- a/pkg/backup/request.go +++ b/pkg/backup/request.go @@ -20,7 +20,7 @@ import ( "fmt" "sort" - velerov1apikb "github.com/vmware-tanzu/velero/api/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/util/collections" "github.com/vmware-tanzu/velero/pkg/volume" @@ -37,7 +37,7 @@ type itemKey struct { type Request struct { *velerov1api.Backup - StorageLocation *velerov1apikb.BackupStorageLocation + StorageLocation *veleroapiv1.BackupStorageLocation SnapshotLocations []*velerov1api.VolumeSnapshotLocation NamespaceIncludesExcludes *collections.IncludesExcludes ResourceIncludesExcludes *collections.IncludesExcludes diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index 90d7683cbe..48cd6d9feb 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -29,7 +29,7 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" - velerov1apikb "github.com/vmware-tanzu/velero/api/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/client" @@ -39,7 +39,7 @@ import ( veleroclient "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" v1 "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" ) const DefaultBackupTTL time.Duration = 30 * 24 * time.Hour @@ -169,7 +169,7 @@ func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Facto return err } - clientKB, err := kbclient.New(clientConfig, kbclient.Options{ + clientKB, err := k8sclient.New(clientConfig, k8sclient.Options{ Scheme: scheme, }) if err != nil { @@ -182,8 +182,8 @@ func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Facto } if o.StorageLocation != "" { - location := &velerov1apikb.BackupStorageLocation{} - if err := clientKB.Get(context.Background(), kbclient.ObjectKey{ + location := &veleroapiv1.BackupStorageLocation{} + if err := clientKB.Get(context.Background(), k8sclient.ObjectKey{ Namespace: f.Namespace(), Name: o.StorageLocation, }, location); err != nil { diff --git a/pkg/cmd/cli/backuplocation/create.go b/pkg/cmd/cli/backuplocation/create.go index 337ba38693..17dacbec6f 100644 --- a/pkg/cmd/cli/backuplocation/create.go +++ b/pkg/cmd/cli/backuplocation/create.go @@ -28,14 +28,13 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" - velerov1api "github.com/vmware-tanzu/velero/api/v1" "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" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" ) var ( @@ -167,14 +166,14 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { return err } - clientKB, err := kbclient.New(clientConfig, kbclient.Options{ + clientKB, err := k8sclient.New(clientConfig, k8sclient.Options{ Scheme: scheme, }) if err != nil { return err } - if err := clientKB.Create(context.Background(), backupStorageLocation, &kbclient.CreateOptions{}); err != nil { + if err := clientKB.Create(context.Background(), backupStorageLocation, &k8sclient.CreateOptions{}); err != nil { fmt.Println("argh") return err } diff --git a/pkg/cmd/cli/backuplocation/get.go b/pkg/cmd/cli/backuplocation/get.go index 3701a63868..e57a51d471 100644 --- a/pkg/cmd/cli/backuplocation/get.go +++ b/pkg/cmd/cli/backuplocation/get.go @@ -21,7 +21,7 @@ import ( "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" "github.com/vmware-tanzu/velero/pkg/client" @@ -42,7 +42,7 @@ func NewGetCommand(f client.Factory, use string) *cobra.Command { clientConfig, err := f.ClientConfig() cmd.CheckError(err) - clientKB, err := kbclient.New(clientConfig, kbclient.Options{ + clientKB, err := k8sclient.New(clientConfig, k8sclient.Options{ Scheme: scheme, }) cmd.CheckError(err) @@ -52,7 +52,7 @@ func NewGetCommand(f client.Factory, use string) *cobra.Command { locations = new(veleroapiv1.BackupStorageLocationList) if len(args) > 0 { for _, name := range args { - err = clientKB.Get(context.Background(), kbclient.ObjectKey{ + err = clientKB.Get(context.Background(), k8sclient.ObjectKey{ Namespace: f.Namespace(), Name: name, }, location) @@ -60,7 +60,7 @@ func NewGetCommand(f client.Factory, use string) *cobra.Command { locations.Items = append(locations.Items, *location) } } else { - err := clientKB.List(context.Background(), locations, &kbclient.ListOptions{ + err := clientKB.List(context.Background(), locations, &k8sclient.ListOptions{ Namespace: f.Namespace(), }) cmd.CheckError(err) diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/restic/server.go index 40381bb11a..47fd86741a 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/restic/server.go @@ -36,7 +36,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" - velerov1apikb "github.com/vmware-tanzu/velero/api/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" "github.com/vmware-tanzu/velero/pkg/buildinfo" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/cmd" @@ -48,9 +48,8 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/logging" - kbcache "sigs.k8s.io/controller-runtime/pkg/cache" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/manager" clientgoscheme "k8s.io/client-go/kubernetes/scheme" @@ -110,8 +109,7 @@ type resticServer struct { ctx context.Context cancelFunc context.CancelFunc fileSystem filesystem.Interface - kbClient kbclient.Client - kbCache kbcache.Cache + mgr manager.Manager } func newResticServer(logger logrus.FieldLogger, factory client.Factory) (*resticServer, error) { @@ -182,8 +180,7 @@ func newResticServer(logger logrus.FieldLogger, factory client.Factory) (*restic ctx: ctx, cancelFunc: cancelFunc, fileSystem: filesystem.NewFileSystem(), - kbClient: mgr.GetClient(), - kbCache: mgr.GetCache(), + mgr: mgr, } if err := s.validatePodVolumesHostPath(); err != nil { @@ -200,11 +197,11 @@ func (s *resticServer) run() { var wg sync.WaitGroup - // TODO(carlisia): how to handle this? Issues: - // - options are get informer for specific obj (below, but w/o namespace of obj name info) or for a specific kind (CRD?) - // - maybe it should go inside the controller? same above problems would apply tho - location := &velerov1apikb.BackupStorageLocation{} - bslInformer, _ := s.kbCache.GetInformer(location) + // TODO(carlisia): how to handle the fetching of the bsl informer: + // - options are: 1) get informer for specific obj (below, but w/o namespace or obj name info because we don't know it here) or 2) for a specific kind, which would be CRD? + // - should it go here, or inside the controller? Note that neither resolves issue above + location := &veleroapiv1.BackupStorageLocation{} + bslInformer, _ := s.mgr.GetCache().GetInformer(location) backupController := controller.NewPodVolumeBackupController( s.logger, @@ -215,7 +212,7 @@ func (s *resticServer) run() { s.kubeInformerFactory.Core().V1().PersistentVolumeClaims(), s.kubeInformerFactory.Core().V1().PersistentVolumes(), bslInformer, - s.kbCache, + s.mgr.GetClient(), os.Getenv("NODE_NAME"), ) wg.Add(1) @@ -233,7 +230,7 @@ func (s *resticServer) run() { s.kubeInformerFactory.Core().V1().PersistentVolumeClaims(), s.kubeInformerFactory.Core().V1().PersistentVolumes(), bslInformer, - s.kbCache, + s.mgr.GetClient(), os.Getenv("NODE_NAME"), ) wg.Add(1) @@ -249,6 +246,15 @@ func (s *resticServer) run() { s.logger.Info("Controllers started successfully") + wg.Add(1) + go func() { + defer wg.Done() + // +kubebuilder:scaffold:builder + if err := s.mgr.Start(ctrl.SetupSignalHandler()); err != nil { // ***this blocks + s.logger.Fatal("Problem starting manager", err) + } + }() + <-s.ctx.Done() s.logger.Info("Waiting for all controllers to shut down gracefully") diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 3b23c1f9f7..7501bbd41b 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -74,11 +74,9 @@ import ( _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" ctrl "sigs.k8s.io/controller-runtime" - kbcache "sigs.k8s.io/controller-runtime/pkg/cache" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/manager" - - velerov1 "github.com/vmware-tanzu/velero/api/v1" ) const ( @@ -131,7 +129,7 @@ var disableControllerList = []string{ func init() { _ = clientgoscheme.AddToScheme(scheme) - _ = velerov1.AddToScheme(scheme) + _ = veleroapiv1.AddToScheme(scheme) // +kubebuilder:scaffold:scheme } @@ -262,9 +260,7 @@ type server struct { resticManager restic.RepositoryManager metrics *metrics.ServerMetrics config serverConfig - kbClient kbclient.Client - kbCache kbcache.Cache - ctrlManager manager.Manager + mgr manager.Manager } func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*server, error) { @@ -356,9 +352,7 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s logLevel: logger.Level, pluginRegistry: pluginRegistry, config: config, - kbClient: mgr.GetClient(), - kbCache: mgr.GetCache(), - ctrlManager: mgr, + mgr: mgr, } return s, nil @@ -391,8 +385,8 @@ func (s *server) run() error { // return err // } - // bsl := &velerov1.BackupStorageLocation{} - // if err := s.kbClient.Get(context.Background(), kbclient.ObjectKey{ + // bsl := &veleroapiv1.BackupStorageLocation{} + // if err := s.k8sclient.Get(context.Background(), k8sclient.ObjectKey{ // Namespace: s.namespace, // Name: s.config.defaultBackupLocation, // }, bsl); err != nil { @@ -495,8 +489,8 @@ func (s *server) validateBackupStorageLocations() error { pluginManager := clientmgmt.NewManager(s.logger, s.logLevel, s.pluginRegistry) defer pluginManager.CleanupClients() - locations := &velerov1.BackupStorageLocationList{} - if err := s.kbClient.List(context.Background(), locations, &kbclient.ListOptions{ + locations := &veleroapiv1.BackupStorageLocationList{} + if err := s.mgr.GetClient().List(context.Background(), locations, &k8sclient.ListOptions{ Namespace: s.namespace, }); err != nil { return errors.WithStack(err) @@ -598,7 +592,7 @@ func (s *server) initRestic() error { secretsInformer, s.sharedInformerFactory.Velero().V1().ResticRepositories(), s.veleroClient.VeleroV1(), - s.kbCache, + s.mgr.GetCache(), s.kubeClient.CoreV1(), s.kubeClient.CoreV1(), s.logger, @@ -666,10 +660,9 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string backupSyncControllerRunInfo := func() controllerRunInfo { backupSyncContoller := controller.NewBackupSyncController( s.veleroClient.VeleroV1(), - s.kbClient, + s.mgr.GetClient(), s.veleroClient.VeleroV1(), s.sharedInformerFactory.Velero().V1().Backups().Lister(), - s.kbCache, s.config.backupSyncPeriod, s.namespace, s.csiSnapshotClient, @@ -708,7 +701,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logLevel, newPluginManager, backupTracker, - s.kbCache, + s.mgr.GetClient(), s.config.defaultBackupLocation, s.config.defaultVolumesToRestic, s.config.defaultBackupTTL, @@ -748,7 +741,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.sharedInformerFactory.Velero().V1().Backups(), s.sharedInformerFactory.Velero().V1().DeleteBackupRequests().Lister(), s.veleroClient.VeleroV1(), - s.kbCache, + s.mgr.GetClient(), ) return controllerRunInfo{ @@ -768,7 +761,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string backupTracker, s.resticManager, s.sharedInformerFactory.Velero().V1().PodVolumeBackups().Lister(), - s.kbCache, + s.mgr.GetClient(), s.sharedInformerFactory.Velero().V1().VolumeSnapshotLocations().Lister(), csiVSLister, csiVSCLister, @@ -803,7 +796,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.veleroClient.VeleroV1(), restorer, s.sharedInformerFactory.Velero().V1().Backups().Lister(), - s.kbCache, + s.mgr.GetClient(), s.sharedInformerFactory.Velero().V1().VolumeSnapshotLocations().Lister(), s.logger, s.logLevel, @@ -824,7 +817,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger, s.sharedInformerFactory.Velero().V1().ResticRepositories(), s.veleroClient.VeleroV1(), - s.kbCache, + s.mgr.GetClient(), s.resticManager, s.config.defaultResticMaintenanceFrequency, ) @@ -840,7 +833,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.veleroClient.VeleroV1(), s.sharedInformerFactory.Velero().V1().DownloadRequests(), s.sharedInformerFactory.Velero().V1().Restores().Lister(), - s.kbCache, + s.mgr.GetClient(), s.sharedInformerFactory.Velero().V1().Backups().Lister(), newPluginManager, s.logger, @@ -927,23 +920,11 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger.WithField("informer", informer).Info("Informer cache synced") } - // instantiate cache - // go func() { - // // start the cache- this blocks on signal handlers - // } - // pass cache to controllers - // start controllers - // wait for controllers to shutdown - // once they, have the start caches (blocking call) unblocked by sending a signal asking to exit - - // now that the informer caches have all synced, we can start running the controllers - for i := range controllers { controllerRunInfo := controllers[i] wg.Add(1) go func() { - s.logger.Debug("---->>> add controller: ", controllerRunInfo.controller) controllerRunInfo.controller.Run(ctx, controllerRunInfo.numWorkers) wg.Done() }() @@ -951,19 +932,15 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger.Info("Server started successfully") - s.logger.Debug("CACHING OUTSIDE") wg.Add(1) go func() { defer wg.Done() - s.logger.Debug("------------->>>CACHING INSIDE<<<----------") // +kubebuilder:scaffold:builder - if err := s.ctrlManager.Start(ctrl.SetupSignalHandler()); err != nil { // ***this blocks - s.logger.Error(err, "problem running manager") - s.logger.Debug("------------->>>CACHING INSIDE AFTERRRRRR<<<----------") + if err := s.mgr.Start(ctrl.SetupSignalHandler()); err != nil { // ***this blocks + s.logger.Fatal("Problem starting manager", err) } }() - // wg.Done() <-ctx.Done() s.logger.Info("Waiting for all controllers to shut down gracefully") diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 9aa676c121..b29e6c220e 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -41,7 +41,7 @@ import ( snapshotv1beta1api "github.com/kubernetes-csi/external-snapshotter/v2/pkg/apis/volumesnapshot/v1beta1" snapshotv1beta1listers "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/listers/volumesnapshot/v1beta1" - velerov1apikb "github.com/vmware-tanzu/velero/api/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" 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" @@ -60,8 +60,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/logging" "github.com/vmware-tanzu/velero/pkg/volume" - kbcache "sigs.k8s.io/controller-runtime/pkg/cache" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client" ) type backupController struct { @@ -70,18 +69,18 @@ type backupController struct { backupper pkgbackup.Backupper lister velerov1listers.BackupLister client velerov1client.BackupsGetter + k8sClient client.Client clock clock.Clock backupLogLevel logrus.Level newPluginManager func(logrus.FieldLogger) clientmgmt.Manager backupTracker BackupTracker - kbCache kbcache.Cache defaultBackupLocation string defaultVolumesToRestic bool defaultBackupTTL time.Duration snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister defaultSnapshotLocations map[string]string metrics *metrics.ServerMetrics - newBackupStore func(*velerov1apikb.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) + newBackupStore func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) formatFlag logging.Format volumeSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister volumeSnapshotContentLister snapshotv1beta1listers.VolumeSnapshotContentLister @@ -96,7 +95,7 @@ func NewBackupController( backupLogLevel logrus.Level, newPluginManager func(logrus.FieldLogger) clientmgmt.Manager, backupTracker BackupTracker, - kbCache kbcache.Cache, + k8sClient client.Client, defaultBackupLocation string, defaultVolumesToRestic bool, defaultBackupTTL time.Duration, @@ -117,7 +116,7 @@ func NewBackupController( backupLogLevel: backupLogLevel, newPluginManager: newPluginManager, backupTracker: backupTracker, - kbCache: kbCache, + k8sClient: k8sClient, defaultBackupLocation: defaultBackupLocation, defaultVolumesToRestic: defaultVolumesToRestic, defaultBackupTTL: defaultBackupTTL, @@ -376,8 +375,8 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg } // validate the storage location, and store the BackupStorageLocation API obj on the request - storageLocation := &velerov1apikb.BackupStorageLocation{} - if err := c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + storageLocation := &veleroapiv1.BackupStorageLocation{} + if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ Namespace: request.Namespace, Name: request.Spec.StorageLocation, }, storageLocation); err != nil { @@ -389,7 +388,7 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg } else { request.StorageLocation = storageLocation - if request.StorageLocation.Spec.AccessMode == velerov1apikb.BackupStorageLocationAccessModeReadOnly { + if request.StorageLocation.Spec.AccessMode == veleroapiv1.BackupStorageLocationAccessModeReadOnly { request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("backup can't be created because backup storage location %s is currently in read-only mode", request.StorageLocation.Name)) } diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 9bb48cd948..a08d905a44 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -35,7 +35,7 @@ import ( "k8s.io/apimachinery/pkg/version" - velerov1apikb "github.com/vmware-tanzu/velero/api/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/builder" @@ -137,7 +137,7 @@ func TestProcessBackupValidationFailures(t *testing.T) { tests := []struct { name string backup *velerov1api.Backup - backupLocation *velerov1apikb.BackupStorageLocation + backupLocation *veleroapiv1.BackupStorageLocation expectedErrs []string }{ { @@ -160,7 +160,7 @@ func TestProcessBackupValidationFailures(t *testing.T) { { name: "backup for read-only backup location fails validation", backup: defaultBackup().StorageLocation("read-only").Result(), - backupLocation: builder.ForBackupStorageLocation("velero", "read-only").AccessMode(velerov1apikb.BackupStorageLocationAccessModeReadOnly).Result(), + backupLocation: builder.ForBackupStorageLocation("velero", "read-only").AccessMode(veleroapiv1.BackupStorageLocationAccessModeReadOnly).Result(), expectedErrs: []string{"backup can't be created because backup storage location read-only is currently in read-only mode"}, }, } @@ -219,8 +219,8 @@ func TestProcessBackupValidationFailures(t *testing.T) { func TestBackupLocationLabel(t *testing.T) { tests := []struct { name string - backup *velerov1api.Backup - backupLocation *velerov1api.BackupStorageLocation + backup *veleroapiv1.Backup + backupLocation *veleroapiv1.BackupStorageLocation expectedBackupLocation string }{ { @@ -282,7 +282,7 @@ func TestDefaultBackupTTL(t *testing.T) { tests := []struct { name string backup *velerov1api.Backup - backupLocation *velerov1apikb.BackupStorageLocation + backupLocation *veleroapiv1.BackupStorageLocation expectedTTL metav1.Duration expectedExpiration metav1.Time }{ @@ -427,7 +427,7 @@ func TestProcessBackupCompletions(t *testing.T) { backup: defaultBackup().StorageLocation("read-write").Result(), backupLocation: builder.ForBackupStorageLocation("velero", "read-write"). Bucket("store-1"). - AccessMode(velerov1apikb.BackupStorageLocationAccessModeReadWrite). + AccessMode(veleroapiv1.BackupStorageLocationAccessModeReadWrite). Result(), defaultVolumesToRestic: true, expectedResult: &velerov1api.Backup{ @@ -807,7 +807,7 @@ func TestProcessBackupCompletions(t *testing.T) { metrics: metrics.NewServerMetrics(), clock: clock.NewFakeClock(now), newPluginManager: func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, - newBackupStore: func(*velerov1apikb.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { + newBackupStore: func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { return backupStore, nil }, backupper: backupper, diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index b451fc582d..2c6ffc0b9b 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -36,7 +36,7 @@ import ( kubeerrs "k8s.io/apimachinery/pkg/util/errors" "k8s.io/client-go/tools/cache" - velerov1apikb "github.com/vmware-tanzu/velero/api/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/features" @@ -51,8 +51,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/kube" - kbcache "sigs.k8s.io/controller-runtime/pkg/cache" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client" ) const resticTimeout = time.Minute @@ -68,7 +67,7 @@ type backupDeletionController struct { backupTracker BackupTracker resticMgr restic.RepositoryManager podvolumeBackupLister velerov1listers.PodVolumeBackupLister - kbCache kbcache.Cache + k8sClient client.Client snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister csiSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister csiSnapshotContentLister snapshotv1beta1listers.VolumeSnapshotContentLister @@ -76,7 +75,7 @@ type backupDeletionController struct { processRequestFunc func(*velerov1api.DeleteBackupRequest) error clock clock.Clock newPluginManager func(logrus.FieldLogger) clientmgmt.Manager - newBackupStore func(*velerov1apikb.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) + newBackupStore func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) metrics *metrics.ServerMetrics } @@ -91,7 +90,7 @@ func NewBackupDeletionController( backupTracker BackupTracker, resticMgr restic.RepositoryManager, podvolumeBackupLister velerov1listers.PodVolumeBackupLister, - kbCache kbcache.Cache, + k8sClient client.Client, snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, csiSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister, csiSnapshotContentLister snapshotv1beta1listers.VolumeSnapshotContentLister, @@ -109,7 +108,7 @@ func NewBackupDeletionController( backupTracker: backupTracker, resticMgr: resticMgr, podvolumeBackupLister: podvolumeBackupLister, - kbCache: kbCache, + k8sClient: k8sClient, snapshotLocationLister: snapshotLocationLister, csiSnapshotLister: csiSnapshotLister, csiSnapshotContentLister: csiSnapshotContentLister, @@ -218,8 +217,8 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR } // Don't allow deleting backups in read-only storage locations - location := &velerov1apikb.BackupStorageLocation{} - if err := c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + location := &veleroapiv1.BackupStorageLocation{} + if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ Namespace: backup.Namespace, Name: backup.Spec.StorageLocation, }, location); err != nil { @@ -233,7 +232,7 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR return errors.Wrap(err, "error getting backup storage location") } - if location.Spec.AccessMode == velerov1apikb.BackupStorageLocationAccessModeReadOnly { + if location.Spec.AccessMode == veleroapiv1.BackupStorageLocationAccessModeReadOnly { _, err := c.patchDeleteBackupRequest(req, 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)) diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index 5b3ebc2d5f..f4ab2a323c 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -29,7 +29,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/kubernetes" - velerov1apikb "github.com/vmware-tanzu/velero/api/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" 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" @@ -38,33 +38,31 @@ import ( "github.com/vmware-tanzu/velero/pkg/persistence" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" - kbcache "sigs.k8s.io/controller-runtime/pkg/cache" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client" + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" ) type backupSyncController struct { *genericController backupClient velerov1client.BackupsGetter - backupLocationClient kbclient.Client + k8sClient client.Client podVolumeBackupClient velerov1client.PodVolumeBackupsGetter backupLister velerov1listers.BackupLister csiSnapshotClient *snapshotterClientSet.Clientset kubeClient kubernetes.Interface - kbCache kbcache.Cache namespace string defaultBackupLocation string defaultBackupSyncPeriod time.Duration newPluginManager func(logrus.FieldLogger) clientmgmt.Manager - newBackupStore func(*velerov1apikb.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) + newBackupStore func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) } func NewBackupSyncController( backupClient velerov1client.BackupsGetter, - backupLocationClient kbclient.Client, + k8sClient client.Client, podVolumeBackupClient velerov1client.PodVolumeBackupsGetter, backupLister velerov1listers.BackupLister, - kbCache kbcache.Cache, syncPeriod time.Duration, namespace string, csiSnapshotClient *snapshotterClientSet.Clientset, @@ -81,13 +79,12 @@ func NewBackupSyncController( c := &backupSyncController{ genericController: newGenericController("backup-sync", logger), backupClient: backupClient, - backupLocationClient: backupLocationClient, + k8sClient: k8sClient, podVolumeBackupClient: podVolumeBackupClient, namespace: namespace, defaultBackupLocation: defaultBackupLocation, defaultBackupSyncPeriod: syncPeriod, backupLister: backupLister, - kbCache: kbCache, csiSnapshotClient: csiSnapshotClient, kubeClient: kubeClient, @@ -105,8 +102,8 @@ func NewBackupSyncController( // 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 *velerov1apikb.BackupStorageLocationList, defaultLocationName string) []velerov1apikb.BackupStorageLocation { - var result []velerov1apikb.BackupStorageLocation +func orderedBackupLocations(locationList *veleroapiv1.BackupStorageLocationList, defaultLocationName string) []veleroapiv1.BackupStorageLocation { + var result []veleroapiv1.BackupStorageLocation for i := range locationList.Items { if locationList.Items[i].Name == defaultLocationName { @@ -127,8 +124,8 @@ func orderedBackupLocations(locationList *velerov1apikb.BackupStorageLocationLis func (c *backupSyncController) run() { c.logger.Debug("Checking for existing backup storage locations to sync into cluster") - locationList := &velerov1apikb.BackupStorageLocationList{} - if err := c.kbCache.List(context.Background(), locationList, &kbclient.ListOptions{ + locationList := &veleroapiv1.BackupStorageLocationList{} + if err := c.k8sClient.List(context.Background(), locationList, &client.ListOptions{ Namespace: c.namespace, }); err != nil { c.logger.WithError(errors.WithStack(err)).Error("Error getting backup storage locations from lister") @@ -310,8 +307,8 @@ func (c *backupSyncController) run() { c.deleteOrphanedBackups(location.Name, backupStoreBackups, log) - locationUpdate := &velerov1apikb.BackupStorageLocation{} - if err = c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + locationUpdate := &veleroapiv1.BackupStorageLocation{} + if err = c.k8sClient.Get(context.Background(), k8sclient.ObjectKey{ Namespace: c.namespace, Name: location.Name, }, locationUpdate); err != nil { @@ -319,7 +316,7 @@ func (c *backupSyncController) run() { continue } locationUpdate.Status.LastSyncedTime = &metav1.Time{Time: time.Now().UTC()} - if err := c.backupLocationClient.Update(context.Background(), locationUpdate); err != nil { + if err := c.k8sClient.Update(context.Background(), locationUpdate); err != nil { log.WithError(errors.WithStack(err)).Error("Error patching backup location's last-synced time") continue } diff --git a/pkg/controller/backup_sync_controller_test.go b/pkg/controller/backup_sync_controller_test.go index 47661e50d2..7327e79a52 100644 --- a/pkg/controller/backup_sync_controller_test.go +++ b/pkg/controller/backup_sync_controller_test.go @@ -28,6 +28,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation" core "k8s.io/client-go/testing" + veleroapiv1 "github.com/vmware-tanzu/velero/api/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" @@ -40,17 +41,17 @@ import ( velerotest "github.com/vmware-tanzu/velero/pkg/test" ) -func defaultLocationsList(namespace string) []*velerov1api.BackupStorageLocation { - return []*velerov1api.BackupStorageLocation{ +func defaultLocationsList(namespace string) []*veleroapiv1.BackupStorageLocation { + return []*veleroapiv1.BackupStorageLocation{ { ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, Name: "location-1", }, - Spec: velerov1api.BackupStorageLocationSpec{ + Spec: veleroapiv1.BackupStorageLocationSpec{ Provider: "objStoreProvider", - StorageType: velerov1api.StorageType{ - ObjectStorage: &velerov1api.ObjectStorageLocation{ + StorageType: veleroapiv1.StorageType{ + ObjectStorage: &veleroapiv1.ObjectStorageLocation{ Bucket: "bucket-1", }, }, @@ -61,10 +62,10 @@ func defaultLocationsList(namespace string) []*velerov1api.BackupStorageLocation Namespace: namespace, Name: "location-2", }, - Spec: velerov1api.BackupStorageLocationSpec{ + Spec: veleroapiv1.BackupStorageLocationSpec{ Provider: "objStoreProvider", - StorageType: velerov1api.StorageType{ - ObjectStorage: &velerov1api.ObjectStorageLocation{ + StorageType: veleroapiv1.StorageType{ + ObjectStorage: &veleroapiv1.ObjectStorageLocation{ Bucket: "bucket-2", }, }, @@ -73,17 +74,17 @@ func defaultLocationsList(namespace string) []*velerov1api.BackupStorageLocation } } -func defaultLocationsListWithLongerLocationName(namespace string) []*velerov1api.BackupStorageLocation { - return []*velerov1api.BackupStorageLocation{ +func defaultLocationsListWithLongerLocationName(namespace string) []*veleroapiv1.BackupStorageLocation { + return []*veleroapiv1.BackupStorageLocation{ { ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, Name: "the-really-long-location-name-that-is-much-more-than-63-characters-1", }, - Spec: velerov1api.BackupStorageLocationSpec{ + Spec: veleroapiv1.BackupStorageLocationSpec{ Provider: "objStoreProvider", - StorageType: velerov1api.StorageType{ - ObjectStorage: &velerov1api.ObjectStorageLocation{ + StorageType: veleroapiv1.StorageType{ + ObjectStorage: &veleroapiv1.ObjectStorageLocation{ Bucket: "bucket-1", }, }, @@ -94,10 +95,10 @@ func defaultLocationsListWithLongerLocationName(namespace string) []*velerov1api Namespace: namespace, Name: "the-really-long-location-name-that-is-much-more-than-63-characters-2", }, - Spec: velerov1api.BackupStorageLocationSpec{ + Spec: veleroapiv1.BackupStorageLocationSpec{ Provider: "objStoreProvider", - StorageType: velerov1api.StorageType{ - ObjectStorage: &velerov1api.ObjectStorageLocation{ + StorageType: veleroapiv1.StorageType{ + ObjectStorage: &veleroapiv1.ObjectStorageLocation{ Bucket: "bucket-2", }, }, @@ -115,7 +116,7 @@ func TestBackupSyncControllerRun(t *testing.T) { tests := []struct { name string namespace string - locations []*velerov1api.BackupStorageLocation + locations []*veleroapiv1.BackupStorageLocation cloudBuckets map[string][]*cloudBackupData existingBackups []*velerov1api.Backup existingPodVolumeBackups []*velerov1api.PodVolumeBackup diff --git a/pkg/controller/download_request_controller.go b/pkg/controller/download_request_controller.go index fd86df2e9c..8c123f600c 100644 --- a/pkg/controller/download_request_controller.go +++ b/pkg/controller/download_request_controller.go @@ -30,10 +30,9 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/clock" "k8s.io/client-go/tools/cache" - kbcache "sigs.k8s.io/controller-runtime/pkg/cache" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client" - velerov1apikb "github.com/vmware-tanzu/velero/api/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" v1 "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" @@ -50,10 +49,10 @@ type downloadRequestController struct { downloadRequestLister velerov1listers.DownloadRequestLister restoreLister velerov1listers.RestoreLister clock clock.Clock - kbCache kbcache.Cache + k8sClient client.Client backupLister velerov1listers.BackupLister newPluginManager func(logrus.FieldLogger) clientmgmt.Manager - newBackupStore func(*velerov1apikb.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) + newBackupStore func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) } // NewDownloadRequestController creates a new DownloadRequestController. @@ -61,7 +60,7 @@ func NewDownloadRequestController( downloadRequestClient velerov1client.DownloadRequestsGetter, downloadRequestInformer velerov1informers.DownloadRequestInformer, restoreLister velerov1listers.RestoreLister, - kbCache kbcache.Cache, + k8sClient client.Client, backupLister velerov1listers.BackupLister, newPluginManager func(logrus.FieldLogger) clientmgmt.Manager, logger logrus.FieldLogger, @@ -71,7 +70,7 @@ func NewDownloadRequestController( downloadRequestClient: downloadRequestClient, downloadRequestLister: downloadRequestInformer.Lister(), restoreLister: restoreLister, - kbCache: kbCache, + k8sClient: k8sClient, backupLister: backupLister, // use variables to refer to these functions so they can be @@ -163,8 +162,8 @@ func (c *downloadRequestController) generatePreSignedURL(downloadRequest *v1.Dow return errors.WithStack(err) } - backupLocation := &velerov1apikb.BackupStorageLocation{} - if err := c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + backupLocation := &veleroapiv1.BackupStorageLocation{} + if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ Namespace: backup.Namespace, Name: backup.Spec.StorageLocation, }, backupLocation); err != nil { diff --git a/pkg/controller/gc_controller.go b/pkg/controller/gc_controller.go index db6cb66ef9..03a0b811b9 100644 --- a/pkg/controller/gc_controller.go +++ b/pkg/controller/gc_controller.go @@ -27,16 +27,14 @@ import ( "k8s.io/apimachinery/pkg/util/clock" "k8s.io/client-go/tools/cache" - kbcache "sigs.k8s.io/controller-runtime/pkg/cache" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" - - velerov1apikb "github.com/vmware-tanzu/velero/api/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" 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" + "sigs.k8s.io/controller-runtime/pkg/client" ) const ( @@ -50,7 +48,7 @@ type gcController struct { backupLister velerov1listers.BackupLister deleteBackupRequestLister velerov1listers.DeleteBackupRequestLister deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter - kbCache kbcache.Cache + k8sClient client.Client clock clock.Clock } @@ -61,7 +59,7 @@ func NewGCController( backupInformer velerov1informers.BackupInformer, deleteBackupRequestLister velerov1listers.DeleteBackupRequestLister, deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter, - kbCache kbcache.Cache, + k8sClient client.Client, ) Interface { c := &gcController{ genericController: newGenericController("gc-controller", logger), @@ -69,7 +67,7 @@ func NewGCController( backupLister: backupInformer.Lister(), deleteBackupRequestLister: deleteBackupRequestLister, deleteBackupRequestClient: deleteBackupRequestClient, - kbCache: kbCache, + k8sClient: k8sClient, } c.syncHandler = c.processQueueItem @@ -135,8 +133,8 @@ func (c *gcController) processQueueItem(key string) error { log.Info("Backup has expired") - loc := &velerov1apikb.BackupStorageLocation{} - if err := c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + loc := &veleroapiv1.BackupStorageLocation{} + if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ Namespace: ns, Name: backup.Spec.StorageLocation, }, loc); err != nil { @@ -146,7 +144,7 @@ func (c *gcController) processQueueItem(key string) error { return errors.Wrap(err, "error getting backup storage location") } - if loc.Spec.AccessMode == velerov1apikb.BackupStorageLocationAccessModeReadOnly { + if loc.Spec.AccessMode == veleroapiv1.BackupStorageLocationAccessModeReadOnly { log.Infof("Backup cannot be garbage-collected because backup storage location %s is currently in read-only mode", loc.Name) return nil } diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index f470d45086..9f5073ee2d 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -36,7 +36,7 @@ import ( corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" - velerov1apikb "github.com/vmware-tanzu/velero/api/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" @@ -45,21 +45,22 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/kube" - kbcache "sigs.k8s.io/controller-runtime/pkg/cache" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" + k8scache "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" ) type podVolumeBackupController struct { *genericController - podVolumeBackupClient velerov1client.PodVolumeBackupsGetter - podVolumeBackupLister listers.PodVolumeBackupLister - secretLister corev1listers.SecretLister - podLister corev1listers.PodLister - pvcLister corev1listers.PersistentVolumeClaimLister - pvLister corev1listers.PersistentVolumeLister - kbCache kbcache.Cache - nodeName string + podVolumeBackupClient velerov1client.PodVolumeBackupsGetter + podVolumeBackupLister listers.PodVolumeBackupLister + secretLister corev1listers.SecretLister + podLister corev1listers.PodLister + pvcLister corev1listers.PersistentVolumeClaimLister + pvLister corev1listers.PersistentVolumeLister + backupLocationInformer k8scache.Informer + client client.Client + nodeName string processBackupFunc func(*velerov1api.PodVolumeBackup) error fileSystem filesystem.Interface @@ -75,20 +76,21 @@ func NewPodVolumeBackupController( secretInformer cache.SharedIndexInformer, pvcInformer corev1informers.PersistentVolumeClaimInformer, pvInformer corev1informers.PersistentVolumeInformer, - backupLocationInformer kbcache.Informer, - kbCache kbcache.Cache, + backupLocationInformer k8scache.Informer, + client client.Client, nodeName string, ) Interface { c := &podVolumeBackupController{ - genericController: newGenericController("pod-volume-backup", logger), - podVolumeBackupClient: podVolumeBackupClient, - podVolumeBackupLister: podVolumeBackupInformer.Lister(), - podLister: corev1listers.NewPodLister(podInformer.GetIndexer()), - secretLister: corev1listers.NewSecretLister(secretInformer.GetIndexer()), - pvcLister: pvcInformer.Lister(), - pvLister: pvInformer.Lister(), - kbCache: kbCache, - nodeName: nodeName, + genericController: newGenericController("pod-volume-backup", logger), + podVolumeBackupClient: podVolumeBackupClient, + podVolumeBackupLister: podVolumeBackupInformer.Lister(), + podLister: corev1listers.NewPodLister(podInformer.GetIndexer()), + secretLister: corev1listers.NewSecretLister(secretInformer.GetIndexer()), + pvcLister: pvcInformer.Lister(), + pvLister: pvInformer.Lister(), + backupLocationInformer: backupLocationInformer, + client: client, + nodeName: nodeName, fileSystem: filesystem.NewFileSystem(), clock: &clock.RealClock{}, @@ -234,8 +236,8 @@ func (c *podVolumeBackupController) processBackup(req *velerov1api.PodVolumeBack ) // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic - location := &velerov1apikb.BackupStorageLocation{} - if err := c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + location := &veleroapiv1.BackupStorageLocation{} + if err := c.client.Get(context.Background(), client.ObjectKey{ Namespace: req.Namespace, Name: req.Spec.BackupStorageLocation, }, location); err != nil { diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 1ccfb6112d..3df689d2d3 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -37,10 +37,10 @@ import ( corev1informers "k8s.io/client-go/informers/core/v1" corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" - kbcache "sigs.k8s.io/controller-runtime/pkg/cache" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" + k8scache "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" - velerov1apikb "github.com/vmware-tanzu/velero/api/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" @@ -60,7 +60,8 @@ type podVolumeRestoreController struct { secretLister corev1listers.SecretLister pvcLister corev1listers.PersistentVolumeClaimLister pvLister corev1listers.PersistentVolumeLister - kbCache kbcache.Cache + backupLocationInformer k8scache.Informer + client client.Client nodeName string processRestoreFunc func(*velerov1api.PodVolumeRestore) error @@ -77,8 +78,8 @@ func NewPodVolumeRestoreController( secretInformer cache.SharedIndexInformer, pvcInformer corev1informers.PersistentVolumeClaimInformer, pvInformer corev1informers.PersistentVolumeInformer, - backupLocationInformer kbcache.Informer, - kbCache kbcache.Cache, + backupLocationInformer k8scache.Informer, + client client.Client, nodeName string, ) Interface { c := &podVolumeRestoreController{ @@ -89,7 +90,8 @@ func NewPodVolumeRestoreController( secretLister: corev1listers.NewSecretLister(secretInformer.GetIndexer()), pvcLister: pvcInformer.Lister(), pvLister: pvInformer.Lister(), - kbCache: kbCache, + backupLocationInformer: backupLocationInformer, + client: client, nodeName: nodeName, fileSystem: filesystem.NewFileSystem(), @@ -299,8 +301,8 @@ func (c *podVolumeRestoreController) processRestore(req *velerov1api.PodVolumeRe defer os.Remove(credsFile) // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic - location := &velerov1apikb.BackupStorageLocation{} - if err := c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + location := &veleroapiv1.BackupStorageLocation{} + if err := c.client.Get(context.Background(), client.ObjectKey{ Namespace: req.Namespace, Name: req.Spec.BackupStorageLocation, }, location); err != nil { @@ -359,8 +361,8 @@ func (c *podVolumeRestoreController) restorePodVolume(req *velerov1api.PodVolume // Running restic command might need additional provider specific environment variables. Based on the provider, we // set resticCmd.Env appropriately (currently for Azure and S3 based backuplocations) - location := &velerov1apikb.BackupStorageLocation{} - if err := c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + location := &veleroapiv1.BackupStorageLocation{} + if err := c.client.Get(context.Background(), client.ObjectKey{ Namespace: req.Namespace, Name: req.Spec.BackupStorageLocation, }, location); err != nil { diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index 21485341d1..40fc3f491e 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -32,15 +32,14 @@ import ( "k8s.io/apimachinery/pkg/util/clock" "k8s.io/client-go/tools/cache" - velerov1apikb "github.com/vmware-tanzu/velero/api/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" v1 "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" - kbcache "sigs.k8s.io/controller-runtime/pkg/cache" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client" ) type resticRepositoryController struct { @@ -48,7 +47,7 @@ type resticRepositoryController struct { resticRepositoryClient velerov1client.ResticRepositoriesGetter resticRepositoryLister velerov1listers.ResticRepositoryLister - backupLocationLister kbcache.Cache + k8sClient client.Client repositoryManager restic.RepositoryManager defaultMaintenanceFrequency time.Duration @@ -60,7 +59,7 @@ func NewResticRepositoryController( logger logrus.FieldLogger, resticRepositoryInformer velerov1informers.ResticRepositoryInformer, resticRepositoryClient velerov1client.ResticRepositoriesGetter, - backupLocationLister kbcache.Cache, + k8sClient client.Client, repositoryManager restic.RepositoryManager, defaultMaintenanceFrequency time.Duration, ) Interface { @@ -68,7 +67,7 @@ func NewResticRepositoryController( genericController: newGenericController("restic-repository", logger), resticRepositoryClient: resticRepositoryClient, resticRepositoryLister: resticRepositoryInformer.Lister(), - backupLocationLister: backupLocationLister, + k8sClient: k8sClient, repositoryManager: repositoryManager, defaultMaintenanceFrequency: defaultMaintenanceFrequency, @@ -160,8 +159,8 @@ func (c *resticRepositoryController) initializeRepo(req *v1.ResticRepository, lo log.Info("Initializing restic repository") // confirm the repo's BackupStorageLocation is valid - loc := &velerov1apikb.BackupStorageLocation{} - if err := c.backupLocationLister.Get(context.Background(), kbclient.ObjectKey{ + loc := &veleroapiv1.BackupStorageLocation{} + if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ Namespace: req.Namespace, Name: req.Spec.BackupStorageLocation, }, loc); err != nil { diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index 37681ce795..ce445716b1 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -36,7 +36,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/tools/cache" - velerov1apikb "github.com/vmware-tanzu/velero/api/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" 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" @@ -51,8 +51,7 @@ import ( kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube" "github.com/vmware-tanzu/velero/pkg/util/logging" - kbcache "sigs.k8s.io/controller-runtime/pkg/cache" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client" ) // nonRestorableResources is a blacklist for the restoration process. Any resources @@ -85,7 +84,7 @@ type restoreController struct { restorer pkgrestore.Restorer backupLister velerov1listers.BackupLister restoreLister velerov1listers.RestoreLister - kbCache kbcache.Cache + k8sClient client.Client snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister restoreLogLevel logrus.Level defaultBackupLocation string @@ -93,7 +92,7 @@ type restoreController struct { logFormat logging.Format newPluginManager func(logger logrus.FieldLogger) clientmgmt.Manager - newBackupStore func(*velerov1apikb.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) + newBackupStore func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) } func NewRestoreController( @@ -103,7 +102,7 @@ func NewRestoreController( podVolumeBackupClient velerov1client.PodVolumeBackupsGetter, restorer pkgrestore.Restorer, backupLister velerov1listers.BackupLister, - kbCache kbcache.Cache, + k8sClient client.Client, snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, logger logrus.FieldLogger, restoreLogLevel logrus.Level, @@ -120,7 +119,7 @@ func NewRestoreController( restorer: restorer, backupLister: backupLister, restoreLister: restoreInformer.Lister(), - kbCache: kbCache, + k8sClient: k8sClient, snapshotLocationLister: snapshotLocationLister, restoreLogLevel: restoreLogLevel, defaultBackupLocation: defaultBackupLocation, @@ -279,7 +278,7 @@ func (c *restoreController) processRestore(restore *api.Restore) error { type backupInfo struct { backup *api.Backup - location *velerov1apikb.BackupStorageLocation + location *veleroapiv1.BackupStorageLocation backupStore persistence.BackupStore } @@ -401,8 +400,8 @@ func (c *restoreController) fetchBackupInfo(backupName string, pluginManager cli return backupInfo{}, err } - location := &velerov1apikb.BackupStorageLocation{} - if err := c.kbCache.Get(context.Background(), kbclient.ObjectKey{ + location := &veleroapiv1.BackupStorageLocation{} + if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ Namespace: c.namespace, Name: backup.Spec.StorageLocation, }, location); err != nil { diff --git a/pkg/persistence/object_store.go b/pkg/persistence/object_store.go index d1b8dbdf7a..311f313f7e 100644 --- a/pkg/persistence/object_store.go +++ b/pkg/persistence/object_store.go @@ -29,7 +29,7 @@ import ( "github.com/sirupsen/logrus" kerrors "k8s.io/apimachinery/pkg/util/errors" - velerov1apikb "github.com/vmware-tanzu/velero/api/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" 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/plugin/velero" @@ -91,7 +91,7 @@ type ObjectStoreGetter interface { GetObjectStore(provider string) (velero.ObjectStore, error) } -func NewObjectBackupStore(location *velerov1apikb.BackupStorageLocation, objectStoreGetter ObjectStoreGetter, logger logrus.FieldLogger) (BackupStore, error) { +func NewObjectBackupStore(location *veleroapiv1.BackupStorageLocation, objectStoreGetter ObjectStoreGetter, logger logrus.FieldLogger) (BackupStore, error) { if location.Spec.ObjectStorage == nil { return nil, errors.New("backup storage location does not use object storage") } diff --git a/pkg/restic/config.go b/pkg/restic/config.go index 1026c5e42b..8a78ba769a 100644 --- a/pkg/restic/config.go +++ b/pkg/restic/config.go @@ -27,7 +27,7 @@ import ( "github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/pkg/errors" - velerov1apikb "github.com/vmware-tanzu/velero/api/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" "github.com/vmware-tanzu/velero/pkg/persistence" ) @@ -45,7 +45,7 @@ var getAWSBucketRegion = getBucketRegion // getRepoPrefix returns the prefix of the value of the --repo flag for // restic commands, i.e. everything except the "/". -func getRepoPrefix(location *velerov1apikb.BackupStorageLocation) (string, error) { +func getRepoPrefix(location *veleroapiv1.BackupStorageLocation) (string, error) { var bucket, prefix string if location.Spec.ObjectStorage != nil { @@ -93,7 +93,7 @@ func getRepoPrefix(location *velerov1apikb.BackupStorageLocation) (string, error // GetRepoIdentifier returns the string to be used as the value of the --repo flag in // restic commands for the given repository. -func GetRepoIdentifier(location *velerov1apikb.BackupStorageLocation, name string) (string, error) { +func GetRepoIdentifier(location *veleroapiv1.BackupStorageLocation, name string) (string, error) { prefix, err := getRepoPrefix(location) if err != nil { return "", err diff --git a/pkg/restic/repository_manager.go b/pkg/restic/repository_manager.go index 176a157efd..2869ffb652 100644 --- a/pkg/restic/repository_manager.go +++ b/pkg/restic/repository_manager.go @@ -29,9 +29,6 @@ import ( corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" - kbcache "sigs.k8s.io/controller-runtime/pkg/cache" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" - velerov1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" @@ -40,6 +37,8 @@ import ( velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" "github.com/vmware-tanzu/velero/pkg/util/filesystem" + kbcache "sigs.k8s.io/controller-runtime/pkg/cache" + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" ) // RepositoryManager executes commands against restic repositories. @@ -247,7 +246,7 @@ func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error { cmd.PasswordFile = file location := &velerov1.BackupStorageLocation{} - if err := rm.kbCache.Get(context.Background(), kbclient.ObjectKey{ + if err := rm.kbCache.Get(context.Background(), k8sclient.ObjectKey{ Namespace: rm.namespace, Name: backupLocation, }, location); err != nil { From 8871dbb2a5788b03dfe53bd12ad67fd654a401e0 Mon Sep 17 00:00:00 2001 From: Carlisia Date: Wed, 20 May 2020 10:47:24 -0700 Subject: [PATCH 06/34] So, .GetAPIReader is how we bypass the cache In this case, the cache hasn't started yet Signed-off-by: Carlisia --- pkg/cmd/server/server.go | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 7501bbd41b..92b57ab404 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -380,19 +380,18 @@ func (s *server) run() error { return err } - // - // if err := s.validateBackupStorageLocations(); err != nil { - // return err - // } + if err := s.validateBackupStorageLocations(); err != nil { + return err + } - // bsl := &veleroapiv1.BackupStorageLocation{} - // if err := s.k8sclient.Get(context.Background(), k8sclient.ObjectKey{ - // Namespace: s.namespace, - // Name: s.config.defaultBackupLocation, - // }, bsl); err != nil { - // s.logger.WithError(errors.WithStack(err)). - // Warnf("A backup storage location named %s has been specified for the server to use by default, but no corresponding backup storage location exists. Backups with a location not matching the default will need to explicitly specify an existing location", s.config.defaultBackupLocation) - // } + bsl := &veleroapiv1.BackupStorageLocation{} + if err := s.mgr.GetAPIReader().Get(context.Background(), k8sclient.ObjectKey{ + Namespace: s.namespace, + Name: s.config.defaultBackupLocation, + }, bsl); err != nil { + s.logger.WithError(errors.WithStack(err)). + Warnf("A backup storage location named %s has been specified for the server to use by default, but no corresponding backup storage location exists. Backups with a location not matching the default will need to explicitly specify an existing location", s.config.defaultBackupLocation) + } if err := s.initRestic(); err != nil { return err @@ -490,7 +489,7 @@ func (s *server) validateBackupStorageLocations() error { defer pluginManager.CleanupClients() locations := &veleroapiv1.BackupStorageLocationList{} - if err := s.mgr.GetClient().List(context.Background(), locations, &k8sclient.ListOptions{ + if err := s.mgr.GetAPIReader().List(context.Background(), locations, &k8sclient.ListOptions{ Namespace: s.namespace, }); err != nil { return errors.WithStack(err) From f098482e9ab94460c96a3db0a3cc7618335dc7d4 Mon Sep 17 00:00:00 2001 From: Carlisia Date: Wed, 20 May 2020 11:51:58 -0700 Subject: [PATCH 07/34] Oh that's what this code was for... adding back We still need to embed the CRDs as binary data in the Velero binary to access the generated CRDs at runtime. Signed-off-by: Carlisia --- config/crd/crds/crds.go | 69 +++++++++++++++ config/crd/crds/doc.go | 4 + hack/crd-gen/main.go | 136 ++++++++++++++++++++++++++++++ hack/update-generated-crd-code.sh | 18 +--- pkg/install/resources.go | 26 +----- 5 files changed, 212 insertions(+), 41 deletions(-) create mode 100644 config/crd/crds/crds.go create mode 100644 config/crd/crds/doc.go create mode 100644 hack/crd-gen/main.go diff --git a/config/crd/crds/crds.go b/config/crd/crds/crds.go new file mode 100644 index 0000000000..d54d670e5f --- /dev/null +++ b/config/crd/crds/crds.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. +*/ + +// Code generated by crds_generate.go; DO NOT EDIT. + +package crds + +import ( + "bytes" + "compress/gzip" + "io/ioutil" + + apiextinstall "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install" + apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + "k8s.io/client-go/kubernetes/scheme" +) + +var rawCRDs = [][]byte{ + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xa2u\xd2\xe8k\x10\xa5\xc4W\x8f\x9a\xfes\xeb\xa7\u007fuki\xae\x9e?lы\x0fgOR\xe7\xd7pS9o\x8a\x9fљ\xcaf\xf8\twRK/\x8d>+Ћ\\xq}\x06 \xb46^P\xb7\xa3\u007f\x012\xa3\xbd5J\xa1]\xedQ\xaf\x9f\xaa-n+\xa9r\xb4\xbcBZ\xff\xf9\x1f\xd7\xff\xb4\xfe\x973\x80\xcc\"O\u007f\x90\x05:/\x8a\xf2\x1at\xa5\xd4\x19\x80\x16\x05^\xc3VdOU\xe9\xd6ϨК\xb54g\xaeČ\xd6\xda[S\x95\xd7\xd0\xfc\x10\xa6D<\xc2\x1e~\xe0\xd9ܡ\xa4\xf3?\xb6:?K\xe7\xf9\x87RUV\xa8z%\xeesR\xef+%l\xea=\x03p\x99)\xf1\x1a\xbe\xd0\x12\xa5\xc80?\x03x\x16J\xe6\x8c~XԔ\xa8?\xdem\x1e\xff\xf9>;`!B'@\x8e.\xb3\xb2\xe4qqu\x90\x0e\x04<2\xee`#\x8d\xc1\x1f\x84\xa7\xffJ\x8b\x0e\xb5w\xe0\x0f\b\x99(}e\x11\xcc\x0e~\xac\xb6h5zt\x112@\xa6*\xe7т\xf3\xc2#\b\x0f\x02J#\xb5\a\xa9\xc1\xcb\x02\xe1/\x1f\xef6`\xb6\xbf`\xe6\x1d\b\x9d\x83p\xcedRx\xcc\xe1٨\xaa\xc00\xf7\xaf\xeb\b\xb3\xb4\xa6D\xebe\xa2$\xb5\x96\xe8\xd4}G\xfb\xba\xa0\x8d\x871\x90\x93\xb0`@?\xb2\x1cspL\x14ڇ?H\a\x16\xe36\x99\x80-\xb0@C\x84\x8eH\xaf\xe1\x1e-\x01\x01w0\x95\xcaI\u009e\xd1\x12\x9d2\xb3\xd7\xf2\u007fk\xc8\x0e\xbc\xe1%\x95\xf0\x18\x99\x9b\x9a\xd4\x1e\xad\x16\x8aXV\xe1%\x13\xa2\x10o`\x91րJ\xb7\xa0\xf1\x10\xb7\x86\u007f7\x16AꝹ\x86\x83\xf7\xa5\xbb\xbe\xba\xdaK\x9f\x94%3EQi\xe9߮X\xe4\xe5\xb6\xf2ƺ\xab\x1c\x9fQ]9\xb9_\t\x9b\x1d\xa4nj\x98w%J\xb9b\xc45\xebʺ\xc8\xff!q\xdd]\xb40\xf5o$d\xce[\xa9\xf7u7\xcb\xf2(\xddI\xa8\x838\x85i\x01\xff\x86\xbc\xd4ET\xf9\xf9\xf6\xfe\xa1-j\xd2ui\xce\xd4n\xa6\xb9\x86\xf0D(\xa9wh\x03\xe3v\xd6\x14\f\x11u\x1ed\x8d\xc5TI\xd4]\xa2\xbbj[HO\x9c\xfe\x9f\n\x1d\x89\xb3Y\xc3\r\x9b\f\xd8\"TeNR\xb8\x86\x8d\x86\x1bQ\xa0\xba\x11\x0e\u007fs\xb2\x13\x85݊H:O\xf8\xb6\xa5\xeb\x0e\fԪ\xbb\x93M\x1a\xe4P\xd0\xf8\xfb\x12\xb3\x8eb\xd0\x1c\xb9\x93\x19\x8b?\xec\x8cm\fB0:\xeb\x16\xc0!\xa5\xa4\x86\xaf\x99\xaar\xcck\xb3t\xf4\xfb\x11*\xb7\xbd\xe1l\xb0\x85\xd4$?d\"I\xf7t\xf3+[$a\xf1\b(\x00\xf1P\xea\x00\x8dm\xcd\x01\a\xd0f\xd5\xf3X\xf4\xb0\x1a!x\x84])%\xb6\n\xaf\xc1\xdb\xeax\xe90OX+\xde\x06)\x91\x0e\xace\x84\xa8GG\rR2cK[\xeb\t\xd3\xe2\x0fD\x86\x831O\xd3[\xff7\x1a\xd1\xe89d|\xce\xc3\x16\x0f\xe2Y\x1a\x1b7\x1b\x8d\xed\x16\x01_1\xab<\x9fw\xdd&<\xe4r\xb7CKPʃp\xe8\x82q\x1f#\xc1\x98\x10S\xb3cl\xeb\xe1߰LX\f\xfb\x1dC\x19^\x0e\xa8\x19\x99>uC\xa3\x93X\xe7\xf2Y\xe6\x95P \xb5\xf3Bga\x1f\xa2\xc6\xe9x\x1f0\xce\xce\x1e\xb6A\xf9\x13\xceD\xfb\x8e!0\x1a\xc1X(\xc8\xe6\xf5\x87\xbaA\xf80\xbaݭp\x98\x83\tbh+\x85..\x94\xb3}i\xf4\xfar\x04pͅpB*\xb1E\x05\x0e\x15f\xde\xd8!2L35\xb4y\x1b5B\xbb\x01k\x15\x8df4\xa1mCeFa\x02\xbc\x1cdv\b\x87\x17\xc9\vC\x81ܠc\xfd\x15e\xa9ކ7\aӜ\x0emB\x85\x9b6\xa9\xccǰ\xfajݴY;״\x19\x8bץe\xcd\xfa?\x0f)\x93\xe1>Y07\xbd\x89\xef)\x98DDI\x0e\xe8f\aX\x94\xfe\xed\x12\xa4O\xbd\xe4\xe3\n\x8e\x8fF\xc9S\xaf\xfd\x87cĩ2\xbd9\x9e\xf7\x8e2\xfd+\xb9P/\xfd\x87a\x02\x1b\xfb\xfbh\xeb\x172\xe0s{\xce%\xc8]̀\xfc\x12vRy\xb4G\x9c\x98ڮ\x99\xe6į%\xc1\xfcIE\xad\x10>;ܾ\x92w䚴\xc6\"j\x1cO\r>e\U000aaec7\xe9$T\xe0\x90IZ,B \xf6\xc0\x14lz\xd8\xf3\xf9\xf8\xe5\x13\xe6\xe3D\x81%\x12\xd6\xdb\xc2\xc7#4\xdb\xcbF\x17y\xd9\x06\xa2\x93RG\x17!\xa8\xbe\x04\x01O\xf8\x16\xbc\v\n\xf1K\xb4\x82\x96\xa1\xc1\xb3\x10-rd\xcf\x02\xf5\x84o\f$\x06\xeb3s\x97\xb1>\xb4'|\x9b\x1ftD6\xc2F\xba\x98| \xfaQ\a\x13\x80#\xbd\xa5$\x03N\xb5$\v3\xb7)Xj\"RK\xd4>y{5\x9b\x9a\xec@`\xe4\x85\vL!i?\xc8r\xd1\x06\xc9t\x82C։\x94jy\x14J\xe6\xf52A\xbe7\xfa\x12\xbe\x18\xbf\xd1c\xcej\xb7ݾJ\x173\\\x9f\f\xba/\xc6sϻ\x131\xa0|2\t\xc34V!\x1d\xcc0\xed\xbf\x9d\xb1\x99\x15\xe2\xd06!ªY\"\x1dl4\xc5\x10\x81V!\xe7\x16\x16\x9b\xb2\xf6\xddVT\x8eS2\xda\xe8\x15\x1fv\xeb\xa1u\"\x89\x17\nr\x9b\v}\xb4\xea%\xc3r\x8b >й\x10f\x87\xfc\xa1\x12\x19\xe6\x90WLD\xce\u007f\t\x8f{\x99A\x81v?~\x10\xb4[I6{\xc9\xf2\x8blih'\xc9Ӓ\xa39\xb5h\x8c\xf394V\xa4\x9b\xb3c\x12kg\x06\x0e&\xbc\xc6\a\xce\xed\x83\x0fI\xf6\x1bf\xa8)\xf2\x9c\xef\x1b\x84\xba[l\xbd\x17S\xbe\u007fn\a\x94\xc2\x19W\x88\x92\xb4\xf3\xff\xe8\xa8b\xa1\xfd\u007f(\x85\xb4\xb3\x1a\xfa\x91/\x0e\x14vfƬP{\x11\x82/\x1d\x107\x9f\x85:N\x9b\x0elː\xd5@\x15\x8ea\xb3\xeby\x1a\x97\xf0r0.\x9c\x8a;\x89*\a9\xe5iQ;\u007f·\xf3˞\x8e\x9fo\xf4y8\x9e{\x1a\x9b\xce\xf2\x19\xc0F\xab78\xe7\x99\xe7_\xef\xba,\x92\xba\x05\x83\xf8\x16i\x993K\xd1\\:\xc5iZ}SA\xae\xe88\xb6\vd\xae4\xce/D\xe2\xce8\x1f2t\x1d\xe7q 74\x1d\xd3Ĝ\x10\x88]\xb8\x1d26\xdd\x03\x90!;JU\x12\x97\x1c\x0e&8{\x10\xf3\bR(\x05獎\x06\xfbx\x1e.\ax\t\x91\xb1[0\x01\x91D\xa1\xb4&C\xe7\xa6\xc4a\xd6\xf2\xce$\xdc\xead\x9b\bAEH\xb5O%\xf7R[\xea6\x12iNr\xb3o_[9@Rm\xfa\u007fZ\xccN\xc3\b\xf8*\xb6(\x84\x9e=,z\xc8݄yI\x15\"\x98\xe0\xb2\xdb}\xc5j\xbc\xd4ӋB\xf3m\x0f\xd8B\xea\r\x03\x87\x0f\xefz\x1cC2\x89x\xbaK}\x93f6d\xae;\x82n\x96\xa6\x9fr\x1fj/\a\xb4\xd8\xe1T?3\xcc\xee\x9c6\xbe\x15\x9e/#t\xc0\xe3\xc2\xc1NZ\xe7\xdbH:\xa8&\xb5\xb6\x05\xe4\xa4\x18E\xdfZ\xfb\x15!\xcaOa^+\x01t0/\xe9>-\x10dю\xf9\x1a\x04A\xee@z@\x9d\x99Js\x12\x83\x94\x94\x17\b$\r\xc6t\xf6\x90\rm\x89bSC]\x15K6\xbeb\xe9\x91z\"\xd7\xd1\x1e\xfc7!\xa72U\xa9\x9d\xc4&/\v4\xd5ġִ\x0e\x9b\x1e¼\xceEh!^eQ\x15 \n\"\xf6\"\x8a\xd2\xc9,\v\xec\xf2\x17^\x84\xf4l\xdd\t*\x9bzoH)J\x85~Y4\xb0ŝ\xb1\xac\x8bN\xe6X\x1f\x99\x91\xe7F\x83\x80\x9d\x90\xaa\xb2\x8b,\xda\t\x14]\xee\xd9G%\u007f\x1f\xa7}ɲ+\xde\xfel\x9ar\x91\xab6eUK\xbb\xd4Q\xbb\xb3\xf8\x9e.Ri%Ɍy_/)\x8a\x92\xd0o\xdfݤ\x16m\xbe\xbbI\xbd\xf6\xddM\xea\xb4\xefn\xd2w7i\xb2}w\x93\xbe\xbbI\u007fV7i\x1a\x93\x15\xe7\xad\x06\u007f\x9aY}\xf6\nu\x1c\xb1Q\xc8\xf1V\xff&T(/\xab\xcb\xdb\f\xcfi٫\x97\x03\xfa\x03\xdaT\xf8\xbc\xe2\xba\xec>\x9f\x9b\xab\xff\xc6\xccׅz$\xfcIxC\xf9\xe5d\xe9ނB\xbc\xad1\n\x85\x1e\xda\xff\xd2\xdá\xa2\x92nMb]ؑ\x8a\x12MZ\xa2\xb7\xfbT\xefMnf\xbb\x82A(ծM\x11\xb6!\xca7\xaaW\x9c-\xfd\x98)\xf8\x98.\xdb\x1c\xa7Бk\xdf%\x91\xed\x94\x18~c\nM\xd6e\x8cWcě\f\xf4\xe2\xf9ú\xfb\x8b7\xb16\x03^\xa4?\xf46\xc0E\x93\x14\xb2\xe8}\xbb82\xc9T,\xb2?\xa6\x1c\x18\vZ\xaa\xcb\xc1\xba\x98\xfa\xfdA\x9b\x9c\xf0S\x19\x82\xa2\x93\xf4mʵ_R\xbb\xf1\xd5\x15\x1bݚ\x8cA#{\xdae\xc7\xd2\x12\xd2\xe55\x19ݚ\x8b\x91CfA%\xc6ɕ\x16\xf3\xf1\xd6dU\xc5W\xd4R\xa4:\x89\xa9\x03w\xa2\x82b\x81\xcf1_-\xf1U5\x12|\x997\x81\xf5I\x95\x11\xad\xaa\x87\t\x90\xcb\xea!\x16\x90d\xae\xf6\xe1䊇\xe3*\x83\x89M\xcc\xd59\x8c\xd70L\x00\x1d\xacnXR\xb90\x01\xb3\xaeix\xc7z\x85\x99*\x85\xf7\xa9$\xfc\xb5\xbe\xe7X\xcd\xc1L\xa5\xc1\x8cg:\x85\xd5L-\xc1\xf2\n\x82\x19\xfa|e\xb5@]\x0f0\xb8\xe6\xa95\x02\xdd*\x80A\x90\v+\x03F\xee\xfe\aA.\xa8\a\x98\xb9\xf1\x1f\x04;y0NH\xc4\xe8ON\x8b\xd2\x1d\x8c\u007f\xe4'\x8d\xd3^\xe4}w\xec@pA>\x8exBȔ\xa9\xf2\x1av\u007f+\xfcP\xf1\r\xee\x1e\xd9\xc8\xf3S\x98\xacy\b\x14Myr~\x8e\xdf\t\xfd\xf0\x9e\xc1\x86\xf3Ɗ=~6Y\xeb=\xea\xd8\xfe\xbbc;\x8f\a#SSH\x9f\xea Dz\xc5֝:\xe4;\xc6,[\xf0\x0f[\xd1\x17a\xd8\xe7\xf7\xa8\xe6y\xaf&7\xf1\xf0\xf09 \xeee\x81\xebOU\b\xe4V\xa5\xb0\x0e\x89~iCaҖ\xfe<\x98\x97\x1e\xc2\xcaĝ\xfep\x8c\xafE\xce\xe1q\xb4\xb8\x18\xeb\xf0\xa26\tX\"Ӵ8>\x0e\xcfi\xf9\xa2-\xa6\x84\xc0\xc6\xec\xc6f\xf56\xd8z\xeeK\xde~\xa8hy\xaf'j\xc3\xc6y\xf8\x89\xa4\x17\xbers\x8f$yPz\xf2\x1c3\xbe\x95\xe5\x17f\x01@\x10Ɠ\xdfI\xc6\xf4V\xe7\xa1\xf9\x14On\xfa\xe3\xf9\xc1\xb1\xcd\x03R\x9cV\x13I\xc8_\x84\xab\x13h\x03\x16\xad\x01\x16\xe6\xb13@\xb00\a|F\rFs\xbe\x8c_p\x85\xe7\xee\xc7s\xfa\xf1k\vFL\xc7U\xa52\"O\x9a\x1bQK\x8f\xa8\x1f\xd8\x1e\xd9g\xb4\x17n\x14b\xe5brd`\xfbǒ\xb53\xb6\x10\xfe\x1ar\xe1q5\x00p\x81\x1d\x1b\x10)N\x1e\xcf<\xdd\xe4!A;8\xef\xcc\"\xa1TL<\x17\xe8\x9cا7\x9b/d\x8e\xf6\xa8\xe9\x90\x1b\xc8\x12EW\xacI\\v\xdf/\x86\x88Nd\x9e\xe2߀Z\fa[\xa3.\xfa:\xa7̞\"l\x1e\x18\xdfUG\xfbS\xc7G\xea\xa2\xfe\xfb\xb8G\xcd\a\x87n\xe7\x1d\xa5ǣ\xc1Gw%\xa4\xc1\r\xbc\xe4\xde\xfcE\xee\xfa!DY*\x99\x11\xb6\u007f\xfdFw \xcf\v\x1c\x87\x8bI\x9f\x81\x1d\x84\xfa\xf8\x87OXZ\xccH+\xfb\xc8\xdf)\xa4#\xdd!v\x9d\x91\x8bž[7\nt\x1f\xbdǢ\x1cXk\"\fl&\x8d\x19>\x91\x06\xf46\x90>\xeb\x94@\xc5\xcb\xfeѸo\xf1Fjo┍ԓ\xc66⪌\fЮ\x1a:\x8a\xea\xb0\xea\x1dw\xf5\",\xc5\xd2\xd3\xda\xf3\x9fq\xd0@\xa0\x11\xe7\xbfo\xa8ъ4\x12~\xbfS\xac1`Ǐ\xba\x9a϶}h\xfec\xf2\xad\xe2gڞC\xc9\x18[˼\xa5\xda\x11\x95\xd8\xd3\xe4\x00D\x96!\xc9\xee\x97\xe3/\xb6\x9d\x9f\xf3?\xe9\xa3l\xfcoft8K\xdd5\xfc\xd7\u007f\x9fAL%=&<\xa8\xf3\xef\x01\x00\x00\xff\xff\xf2Hm[\xe2N\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcXOo\xe36\x16\xbf\xfbS\xa8\x8c\x8d\xafg\xf4\xa11_\xde\xe4\xe7ƚ\x06\xad\x97-J\x1aG\xfe\xdf\xcd\r\xb0\\\x13\xd8$\x03\x82<\x1e\x13\x8c\xec\xb7(\xc0%@\xa6\x02\xbf\x95\x0e,6\x16\x1dj\u007f\xb0Q;L\x05L\x83Y\xff\x8a\xdc\x17\xb0BK\x87\x80ۚ\xa0\x04\x85\xc9\x0e\xad\a\x8b\xdcl\xb4\xfc\xbd;ف7\xf1J\xc5ls\a\xe2\x89(\xa9+\xb4\xc9p\x955u<\x11\xb5h\x8c\xd4>\xbep%Q\xf7Iwa]KO\x96\xfe-\xa0\xf3d\x9f\x02\x161\xef\xc1\x1a!4\x82y\x14\x05\xdchX\xb0\x1aՂ9\xfc\xe6\xb4\x13\xc3nJ\x94^&\xfe8]\xf7\x05\x13[\xddt\x9bIG-4\x1a\xa5\xab\x06y/N\x04:iɗ=\xf3\x18#\x00\x1ec\xa6\xebQ\xfar~{9xc\x00s\x8e\xce}4\x02\xfb\xf3\x03\xa8\xf3N\xac\x87\xadA[K\x17\v\xde \x9f@N(\x83C\x01\xda\fS\fVP\x87z\ba\n\xf7\xc8ĝV\xfbх\x1f\xad\xf4\xc3\vF\xcdE#\xc1Z\xed5_\xa2\x95F\x9cU\xf7\xfd@\xb8Szk\x9e\xa1\x8an\xab\xbd\xdaS^q{\xcd\xf3\xe1'\xbaΗ7\xd9!rp\xe4X\xca\xdc\x140\xcf1i*x\x03B:\xaax.\x1e9\xa4\x87\n8\xad\x96\xe0mx\xb5\xd2\xdc\xe8Jn\x86\xaa\x1e\x97\xf5q\xaf8{耫E\xbc\x83\x12\ry@c\xcdN\n\xb4S\xf2|YI\x9e1\x04\x9bjP%Q\t7\xd4n4v\xa0K>٭Ϛ\xec\xeeX\xf2\xd0b$\x14\xd9]\x1dz\xcaz\x0e4\x92;3;\xf4+ \x8br\xa35Y\xc9\x1b`\x9d>\xd7nh\xbc\xc1֗\x02\x8c\xc6:\xf0'\xf4\xa7\xf3C\xaf\x8bbm\x99N\x9b\bEp\x18\xb9=\x0f\xe0\x82\xcd\x008[\xa0\xbd\x8cb1'\xb1\xce\xe3\x19,\xe6\xb0\x0eZ(l\xb1P\aũ\xb3)\t#53\xee\xc4m\xb4y\xa6\xcdG\xa7\xc5\x03\xc0\xe8ԻSw@=jj\xb8\xe2ҳT\x8a\xda&\x8b\xb5\xd9\xc5/\x8a\xfe\xa0\x1eǢ\xda\xd37\x96\xa9`\xf7\xaexS\\\xfd\xc3\xd5W1穜\xa2\xb8ǝ\x1c~/\x9c\xb2y{\"\xdfzoW0\xe9\xe5sۈ\xcdl\x16\xfb|\xa2~%\x15\xb5\x8d#\xae\xde\xf5\"\xf9\xe3\xc0y\xf0\xb2\xc6\xf8\xf6~u{\xed\xe2\x977\xb5\xbc'\x87>\x93\xf9\\\x04H\x9f\x10&w\xba\xc1y\xb4#\xc6\xeel%\x1dh\x03\xca\xe8M/\x14\xd2\xc8}/\x18\v\xc9u\x8c\x05\x81ԲR\x9a\xe5[\xa67x\xf8\x96\xc9؏P\x92c\x9c\"\xed{\xc7\xc1\x1b\xa4\x1ew\x85W\xd8\xf0A\xd6\xe7\xa3\xe1\xb6'ښ\xae\xcfp\x87:\xdbR\x9d\xc6\xe4+\xb8\x1eH\xb7E\x84\x88\x9c\xd2U\u007fK\x1b\x15\xffk\x9cϱ$\xd1\xeaɃ\xb5\xa8\xfd!\xf7D\x87\x1a\xcb?\xafk\x82罟!\xc7+\xc3\x1f%\x17u\x19ɹ\x83\xa9\xc3路\x87\xb7\xfc\xc7'\xfd\x87\x89\v\x00)\xc1\x1e\x11\x99\xa3*\xcf\x1c\x129e\xd0ƣ\xf84\xfc'su\xd5\xfb\xb1\x12_\xb9ѩ9u%\xfc\xf4\xcb$\x9d\x8a\xe2\xb1\xc5A\x93\u007f\x05\x00\x00\xff\xff\xf8SƷz\x13\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96\xcfn\xe46\x0f\xc0\xef~\nb\xbf\xc3^>{\x9a\x16\x05\n\xdf\xda\xec\x1e\x82\xb6A\x90,r)z\xd0H\x9c1\x1b[RIj\xd2\xf4\xe9\v\xc9\xf6\xccx\xe2 \xdbC}\x13E\x91ԏ\u007f\xac\xaa\xae\xeb\xcaDzD\x16\n\xbe\x05\x13\t\xffR\xf4y%\xcd\xd3\x0f\xd2P\xd8\x1c\xae\xb6\xa8\xe6\xaaz\"\xefZ\xb8N\xa2a\xb8G\t\x89-~\xc2\x1dyR\n\xbe\x1aP\x8d3j\xda\n\xc0x\x1f\xd4d\xb1\xe4%\x80\r^9\xf4=r\xbdG\xdf<\xa5-n\x13\xf5\x0e\xb9x\x98\xfd\x1f\xbei\xbem\xbe\xaf\x00,c9\xfe\x85\x06\x145Cl\xc1\xa7\xbe\xaf\x00\xbc\x19\xb0\x05\x87=*n\x8d}J\x91\xf1τ\xa2\xd2\x1c\xb0G\x0e\r\x85J\"\xda\xecx\xcf!\xc5\x16N\x1b\xe3\xf9)\xa8\xf1B\x9f\x8a\xa9\x9f\x8a\xa9\xfb\xd1T\xd9\xedI\xf4\xe7\xb74~\xa1I+\xf6\x89M\xbf\x1ePQ\x10\xf2\xfb\xd4\x1b^U\xa9\x00Ć\x88-\xdc氢\xb1\xe8*\x80\x83\xe9ɕ\xfb\x8f\x81\x86\x88\xfeǻ\x9b\xc7\xef\x1el\x87\x83\x19\x85\x00\x0e\xc52Ţ\xb7\x16$\x90\x80\x81\xc9\x15h\x98\"\x80\xe0\x11\x02\xc3\x10\x18a\fG\x9a\xc9d\xe4\x10\x91\x95fD\xf9;+\x90\xa3\xec\xc2\xf9\xc7\x1cݨ\x03.\x97\x04\nh\x870%\x16\x1dH\x89\x1c\xc2\x0e\xb4#\x01\xc6\xc8(\xe8\xc7\"93\vY\xc5x\b\xdb?\xd0j\x03\x0f\xc8\xd9\bH\x17R\xefr\x1d\x1d\x90\x15\x18m\xd8{\xfa\xfbhY\xf2\xfd\xb2\xcb\xde\xe8\x9c\xc1\xf9#\xaf\xc8\xde\xf4\x99k\xc2\xff\x83\xf1\x0e\x06\xf3\x02\x8c\xd9\a$\u007ff\xad\xa8H\x03\xbff8\xe4w\xa1\x85N5J\xbb\xd9\xecI疰a\x18\x92'}ٔ¦m\xd2\xc0\xb2qx\xc0~#\xb4\xaf\rێ\x14\xad&ƍ\x89T\x97\xc0}\xe9\x88fp\xff\xe3\xa9\u007f\xe4\xe3Y\xa4\xfa\x92+A\x94\xc9\xef\x8f\xe2R\xa4or\xcf\x05:\xa6y<6\xc6\u007f\u009bE\x99\xca\xfd\xe7\x87/0;-)X2/\xb4O\xc7\xe4\x04>\x83\"\xbfC\x1e\x13\xb7\xe30\x14\x8b\xe8]\f\xe4\xb5,lO\xe8\x97\xd0%m\aR\x99\xcb/私\xeb2\x18`\x8b\x90\xa23\x8a\xae\x81\x1b\x0f\xd7f\xc0\xfe\xda\b\xfe\xe7\xd83a\xa93\xd2\xf7\xc1\x9fϳ\xa5\xe2H\xeb(\x9e\x87\xcdj\x86V\xda\xf2!\xa2\xcd9\xcb\xe0\xf2Yڑ-m\x00\xbb\xc0\xf0ܑ\xed\xe6\xb6\\\x10=6ps&^k\xd8\xfc\x8d\x06\xf2TY\xca߸,\x94<\x11\xe3\xa2\xd6\xea33\xefRP\xa3I\xfe\x15\x87rb&a\x133z\x9d\xec\x94)\xb0v\xe8k\xee\x8é\xe5\xf2ދp>\x17\x95\xf2[2\xe4\x05\x8c\u007f\x99\x8e\x81vF\xe1\x199\x97\xb8\r)\xcf\x0et\xe0\xd2\x05\xaf\tE\x87cRr\xfa\"\a\x8b\"ͅ\x16)\x0e\xaf\xa2y3\x0f\xf9˿:\xb3\xed\xb1\x05儫\xf93\xcc\xe6e\xb1\x13;#\xaf\x92\xbd\xb8\xf4]\xd6X\xe3\x8d\xe3\\\xc6\xf7\x80\x17\xb8>\r\x97^j\xb8\xc5\xe7W\xb2\x1b\u007f\xc7a\xcf(\xf2j\xebn$U~v_\xc1d\xa5\xe0.D\xa7\x17\xc4\xd5iU\xa0\xd7Ӌ\xa1l\x00H\x1ek\xee\f\xach`\xb3\x9fQ\x9f\xaa\xd8X\x8bQ\xd1\xdd^\xbe\x17>|X\xfc\xf8\xcb\xd2\x06\xefh|\xee\xc0o\xbfW\xa3Ut\x8fs\x1cY\xf8O\x00\x00\x00\xff\xff\xbe\x16\xd7\"m\t\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WO\x8fܶ\x0f\xbd\xfbS\x10\xf9\x1d\xf2+\x10{\x9a\x16\x05\x8a\xb9\xb5\x9b\x1c\x82\xa6A0\xbb\xddKуF\xe6\xd8\xeaʒ*R\xb3\xd9~\xfa\x82\x92=\u007f<\x9e\xdd\xf4й\x99\xa2\xa8\xa7\xc7GRS\xd5u]\xa9`\xee1\x92\xf1n\r*\x18\xfc\xc2\xe8䋚\x87\x1f\xa91~\xb5\u007f\xbbEVo\xab\a\xe3\xda5\xdc$b?l\x90|\x8a\x1a\xdf\xe1\xce8\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95\x98I>\x01\xb4w\x1c\xbd\xb5\x18\xeb\x0e]\U000d0db8Mƶ\x18\xf3\t\xd3\xf9\xfbo\x9b\xef\x9a\x1f*\x00\x1d1o\xbf3\x03\x12\xab!\xac\xc1%k+\x00\xa7\x06\\C\xeb\x1f\x9d\xf5\xaa\x8d\xf8WBbj\xf6h1\xfa\xc6\xf8\x8a\x02j9\xb4\x8b>\x855\x1c\x17\xca\xde\x11P\xb9̻1̦\x84\xc9+\xd6\x10\xff\xb2\xb4\xfaь\x1e\xc1\xa6\xa8\xec%\x88\xbcH\xc6uɪx\xb1\\\x01\x90\xf6\x01\xd7\xf0I`\x04\xa5\xb1\xad\x00\xf6ʚ6ߵ\x00\xf3\x01\xddO\x9f?\xdc\u007f\u007f\xab{\x1cT1\x02\xb4H:\x9a\x90\xfd\xe6\xc0\xc0\x10(\x18\x8f\x01\xf6\x87\x93A9P\x91\xcdNi\x86]\xf4\x03l\x95~Ha\x8c\t\xe0\xb7\u007f\xa2f \xf6Qu\xf8\x06(\xe9\x1e\x94D+\x8e`}\a;c\xb1\x19\xb7\x84\xe8\x03F6\x13\x8d\xf2;\x11\xd0\xc16\x03\xfcZnT|\xa0\x15\xc9 \x01\xf7\bc\xe2\xb1\x05ʷ\x05\xbf\x03\xee\rA\xc4\x10\x91\xd0\x15\x11\x9d\x84\x05qQnD\xde\xc0-F\t\x02\xd4\xfbd[\xd1\xd9\x1e#CD\xed;g\xfe>D&\xe1E\x8e\xb4\x8a\xa7LO?\xe3\x18\xa3SVr\x91\xf0\r(\xd7\u00a0\x9e bf'\xb9\x93hم\x1a\xf8\xd5G\x04\xe3v~\r=s\xa0\xf5j\xd5\x19\x9eJF\xfbaH\xce\xf0\xd3*\v\xdfl\x13\xfbH\xab\x16\xf7hWd\xbaZE\xdd\x1bF\xcd)\xe2J\x05Sg\xe0.WL3\xb4\xff\x8bc}\xd1\xeb\x13\xa4\xfc$\xea!\x8e\xc6u\as\x16\xf2U\xdeE\xc8E\x1ee[\xc1\u007f\xa4WL\xc2\xca\xe6\xfd\xed\x1dL\x87\xe6\x14\x9cs^tr\xd8FG\xe2\x85(\xe3v\x18K\xe2\xb2\xca$\"\xba6x\xe38\u007fhkН\x93Ni;\x18\xa6I\xb6\x92\x9f\x06nr\xe3\x80-B\n\xadbl\x1b\xf8\xe0\xe0F\rho\x14\xe1\u007fN\xbb0L\xb5P\xfa2\xf1\xa7\xfd\xeeܱ\xb0u0O\ri1C\xb3R\xbe\r\xa8%_B\x9a\xec3;\xa3s\t\xc0\xceGP\xc7\xca\x1eikN\xe2.\xd5f\x06\xa5b\x87|n\x9b\xa1\xb8\xcb.r\xf0c\xaf\xce[\xc8\xff\xb1\xe9\x1a\xe9\x034B(\x9d\xe1\x9bf\x16\xef\xda\xe9K\x1a]\xc40IU\xae.w\vZtR\trq\x87~\xff\xd5o˷\xe5\x1f&\x00\x95\xa3\xb8\xfcI\xb6\xe4\x03\xb6v\x06\xbaSj\x02\xa0\xb1\xa5\x19X#VFu--\xb0z\xee\xac/W\xa4șR\x9a\x89\xb7T\xf1\xa6\xb53\x9d\x9d\xc1\xeeCZ\x9b\x15J\xc6<\x1a\xf11\x8ay\x17\xc5\xc4/J\xfa\U00037c6f?I\x1f\xe2\f\xab:\x87\xeaX\x89\xf8\xd1K]w\n\xdd\xd1\xe7\t\x80\xaf\x8c\xa5\x19<\xb0\x1a\x16+\x12\x13\x80\x15*)\xa2\xadI1cI\xff\xf0x\xff\xf1w\xf3\xaa\xa1\x16\xd3 \x80uƒ\v\xb2ן\x9f\xbd\x93ێ\x01\b\xf2\x95\x936J\x84k\x16\x95\xe6\x80\xe0\xb3\"\x0f\xa1!Ȉ\x93\x00\x1f\xb7\x01\xb3\x84\xd0H\x0f\x8e\xac#O:\x9dޞX\xe0)\xa8\xc1,\xfeAU(aN\x8e\x85\x80oL\xa7\x04\x1f\xf0\x8a\\\x00G\x95\xa9\xb5\xfc\xd7V\xb2\x87`\xe2\x96\n\x03e\x00\xfbG\xea@N\xa3b\x10:\xba\x01\xd4\x02Z܀#\xde\x03:\xbd'-N\xf1%\xfcl\x1c\x81\xd4K3\x83&\x04\xebg\xd3i-C𥉉i\xdbN˰\x99F\x8f\x93\x8b.\x18秂V\xa4\xa6^\xd6\x05\xba\xaa\x91\x81\xaa\xd09\x9a\xa2\x95ET\\GW-[\xf1\x9dˎ\xed\xaf\xf74\r\x1b>6\x1f\x9c\xd4\xf5v8z\xd0I\xdcك@z\xc0\xbc,鿃\x97\x87\x18\x95\x0f\u007f\x9e?A\xbfi<\x82!\xe6\x11\xed\xdd2\xbf\x03\x9e\x81\x92zI.\x1d\xdcҙ6J$-\xac\x91:ėJI\xd2C\xd0}\xb7he\xe0\x93\xfegG>\xf0\xf9\x94p\x1b#\x16\x16\x04\x9d\x15\x18H\x94p\xaf\xe1\x16[R\xb7\xe8\xe9\xab\xc3\xce\b\xfb\x82!}\x19\xf8\xfdD3\x9c\x98\xd0\xda\x0e\xf7\x99`\xf4\x84\x0e\x82{n\xa9\xe2\xf3b\xd0x\x9d\\\xca*\x86\x00,\x8d\x03<\x9c^\xee\x89\x1d\vM~R\xd8σqX\xd3O\xa6\xda\v\xf2\x13:\xbd\x1b[\xd1k\xc5\xc9+\x85)e\xd1\xe0\xd3\xcc\x03\x91\x00\xaa_\xban\xc8Q\\\xe1\xc8\aY\xb1#\x19/\x83q\x1b\x16\xcb\xebI\x94\a\xebGA\xe7G\x1bAg\xf5\u007f0\x82\xc6\xd4\xe5\x85\x10\x1aL>\xf9hbd\xb8Nk\x8e\x02\xa3/V\xc0\x1aqv\xff,\x19\xc1ђ\x1ci\x8e\xa8\x94|\xac\x89)*\xa0\xd4}\xe4\xa5\xdc\f\xc1\x1c\xc1\xb7H\x00\x93\x80\xe1A\x9f;l8\x99\x8fG5\xfd\xe1\xf1\xbe\xcf\xc1=HY\xe7p\xb8\xe3YD\xf8YJR\xe2\x11C\xf3\xe2\xae\xd7\xf7˴M\xccH\xc1\x00\x82\x95T\xd1 \xb5\x83\xd4>\x10\x8a48\"\x12\x80\x03\xd7Q\x9e\u007f\x93\xf2ONs\xbbr\xc0X\x03\xa6\xfa\x06\u007f\x9d\xbf\u007f\x98\xfe\xc5$]GebU\x91g1\x18\xa8%\x1dn\xc0wU\x03\xe8\xd9\x04\xe9H\xcc\xf9K٢\x96K\xf2\xa1\xcc;\x90\xf3\x9f\xde~\x1e\xc3\f\xe0G〾`k\x15݀L(o\x13j\xef \xd2' \xb6\xf2`-C#\xc7\rGv\xa4l\xf0:\x1a\x1a\xf0\x99\xc0dC;\x02%\x9fi\x06W\x9cB\xf6T\xfc7G\xc3\u007f\xaeFe\xfe&\x05\xe9\x15O\xb9J\x8amk\xe6~\x10\xed\x14L\x91\xe4d]\x93\xa3q4c!\xe0\x04\xfb=\x18Ƕk\xb3' \x8a\xe53K\x89\x8eđ\u009f\xde~>\xa1\xed\x10'\x90Z\xd0\x17x\vR'T\xac\x11ߗ\xf0\x14=b\xa3\x03~\xe1}\xaa\xc6x\xd2`\xb4ڌkk\xa0\xc1\x15\x817-\xc1\x9a\x94*\x12W\x11\xb0\xc6\r\xdb\xdf\x1f\x17{\x18\x82E\x17\x86ldT\xea\xd3\xfb\xbb\xf7\xb3\xa4\x15\xbbP\x1d3)W\xb9\xa5d\xce\xc1d#UN\xf6\xc9\bG\x97\x9c#\x18\xa8\x1a\xd4#\x89\x15\"i\x89\xe8.;\xaee\xe5\xf5k\xa3\xf5\x906\xf4\xcf\b}8L\f\xbfR\x11\xbeȬH\xcb_4\xebaϟϚ\xc5\r\x82\xd3\x14(Z&L\xe5٨\x8al\xf0S\xb3\"\xb7\x92\xb4\x9e\xae\x8d{\x96\xba.\xd8\x11\x8b\xe4\t~\x1a9\xfe\xf4\xbb\xf8\xe7\x17Y\x11\x99\xf9e\xa6ĩ\xdf\xc2\x1e\xde\xc7O_mN\xcf+/\xadJ\xd7\xf3\xcc|\x0eWrH\xac\x1bY5}\x93\xb0˞\xa31ҢH)\x17\xf5櫻-\x03\xd99\xd6gS\xe4>\xb3@-\xf8\u007f/}\xe0\xf1W#\xd7\xc9\v\x82\xf4\xef\xf7w\xdfƙ;\xf9\xea\x88\x1c%\xc4\xc9'\xac\xb9\x17\f\xdfR\x92;K\xa7>\f\xa6\xf6\xc4n\x84In\xe7\\\xcc\xe4\x02\xd6G\x04\n\x85\x887\t\xa8\x1eϐ\xac36\x0f\x94\u007f\xc2\xda\x03:\x02\x84\x16-\x9f\xd33m\x8aT\xa4-J\xae\xb1\\F3_Y\x10\xa0\xb5J\x8e\x94\xd3\\\x8a3]\xcc̛\xdbZ\xac\xfd\xb8\xbd#\xa8\xa7\xd5g\xd1N\xed\xc5\x18}\xce['^\xb2\xa5\xd0\xc1\xec\x88\xea\xb1\xff\x1e\x11\xd7\x13\xb8q\x17\xc8\xecj_\xb5b\xbcu\x19\xcc`J?\x18\xb0F\fއ~6\xf8\x94\xecy\xb1{\v\x18:\u007fq\xff\x16g\xf7\xe8\xa5|\x10\xb2\x8c\xc8\x15~I\aW\x19\xe6\x8e\xc3{\xa8sGx{\xb4F\xdf\xefp܄\xc1\x9e\xb0\xb4.\xe6]\x96E\"R;f\x9dK\x94\x8a\x04\xf4\x97`\x87k\x8ed\xee\xcbXВSUg\x95A\xd17EY\xb5\xfe\x92牻\xe1x\xdfp\xedOJ\xec<\x89\xd8%\x8f\x98\u007fX\x1e\x96Ƶ\x18f 0P1\"PwJ\xe1B\xd1\f\x82\xeb\x0e?\x9e\f\xfd\x96\xbc\xc7\xfa|x\xfd\x9c\xe6\xa4\xfe0/\x00\\\x98.l\x1b\xc4A\x88_\xfb\xec=\x97w\xa7#-\xd8\xd0e\x91\t\xb3\xcf\xf4Q\xa9\xb8b?\xacw\xb7\xa4Q\x9f\x05\xf1\xb1\xfc\xaf\x11\x0e`\x1b\xf4\xe7\xc1y\xe4\x19c\xc1\xb3\xcdAg\xa2\abGص\x87;\x14\xf0@룱{\xfd\xe8L\xed\xc8\x1f\xbaF\xd1\xfbϑ\xb1\x05\xfc\x18\xfd\xfcb{\xf3\x06\xe7MΓ\xa01\xaa\x0fO\x13P\x81\xee\xda\x059\xb6{\xb1\t\xe4\x87I\xf8\xb8\xe7\x8f]\xc4\x0e\xb4\xbd\xd5\xfd\x15B\x92\x93\x9b\xa2\nu\xbce\xe3\x98\t\x06\x84\xf4V\xe1qWԛ\x10\x99\x04\x87\f\x87\xf4\xce[\xfb0\xb5\xe4\xe2\xa7\xd7\xdcRDm\xee\x8c\x1e\xe5\xb8}|J\x1d\xfe\xf8\xfb\x93\x8cC\xea@\xf5 \xa9\xe7\xaf\f\xe0;\x96\xff\xff\x96}\xb2\xb0z\x8d\xd67&\xdcߝ=\xed\xf9vZ\xef\xe5;\xd2\x12sW\xbc\xf7˓\xfa#\x1f\x96\xb4\xf4$7\xb88\xf4|@\x17.+\x1e\xf3\xc1\xd4\x17\xeaF\x94K\xa2\x849Yt\x18\x8e\x1d3\xde\a\xdf\x1e\xfe\x8cr\x03^\xc6k1\xe6>\x89\f\xa5V\xd7s9ajg\\\xf2\xd5c\x89\x83B0H\xfcCտE\xce\x1f\U000470e1\xddoJovo\xd1/\x8b\xfc\x1bR\xfc\x90\xcd\x12{\x9b\xe7[\xd5<\xb2\xa3!X1e'\xf1p\xf8+\xd2U\xba*\xe9\u007f\x16\x8a\xaf\x95щ\xcd\xfa\x19|\xfa<\x81|\xd7\xfa\xb1׃\a\xff\x1b\x00\x00\xff\xff=\x1a\xe4\xa9\u007f\x1b\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y͒۸\x11\xbe\xeb)\xbaf\x0f\x93\xad2\xa9\xd8\xf9\xa9\x94n\xbb3ٔ\x92\xdd\xf1\x945\xf1\xc5\xe5\x03D4EdH4\x034$+\xa9\xbc{\xaa\x01R\x12%J\xa3qe\x1d]l\x02\x8d\xc6\xd7_\xff\xa0\x81\x99dY6Q\xad\xf9\x88\xce\x1b\xb23P\xad\xc1/\x8cV\xbe|\xfe\xfc'\x9f\x1b\x9a\xae\xdf.\x91\xd5\xdbɳ\xb1z\x06w\xc135\x1f\xd0Sp\x05\xdeci\xacaCv\xd2 +\xadX\xcd&\x00\xcaZb%\xc3^>\x01\n\xb2쨮\xd1e+\xb4\xf9sX\xe22\x98Z\xa3\x8b;\xf4\xfb\xaf\u007f\x9b\xbf\xcb\xff0\x01(\x1c\xc6\xe5O\xa6AϪig`C]O\x00\xacjp\x06-\xe95աA\x87\x9eɡ\xcf\xd7X\xa3\xa3\xdc\xd0ķXȮ+G\xa1\x9d\xc1~\"-\xee\x10%k\x1eI\u007f\x8cz>$=q\xaa6\x9e\xff6:\xfd\xb3\xf1\x1cE\xda:8U\x8f\xe0\x88\xb3\xde\xd8U\xa8\x95;\x9d\x9f\x00\xf8\x82Z\x9c\xc1\x83@iU\x81z\x02\xb0V\xb5\xd1\xd1\xe0\x04\x8eZ\xb4?<\xce?\xfenQTب4\b\xd0:jѱ\xe9m\x90߁\xfbvc\x00\x1a}\xe1L\x1b5\u00ad\xa8J2\xa0\xc5a\xe8\x81+\x84\x8ev\xd4\xe0\xe36@%pe<8l\x1dz\xb4Ʌ\ajAD\x94\x05Z\xfe\x03\v\xcea\x81N\x94\x80\xaf(\xd4Z\xbc\xbcF\xc7ఠ\x955\xff\xdai\xf6\xc0\x14\xb7\xac\x15cGa\xff3\x96\xd1YU\v\t\x01߀\xb2\x1a\x1a\xb5\x05\x87\xb2\a\x04{\xa0-\x8a\xf8\x1c~!\x87`lI3\xa8\x98[?\x9bNW\x86\xfb\x80-\xa8i\x825\xbc\x9dư3\xcb\xc0\xe4\xfcT\xe3\x1a\xeb\xa97\xabL\xb9\xa22\x8c\x05\a\x87S՚,\x02\xb71^\xf3F\u007f\xe7\xba\xe8\xf6\xb7\aHy+n\xf3\xec\x8c]\xed\x86c\x14\x9d\xe5]\x82\b\x8c\a\xd5-K\xf8\xf7\xf4ʐ\xb0\xf2\xe1ϋ'\xe87\x8d.\x18r\x1e\xd9\xde/\xf3{\xe2\x85(cKt\xc9q\xa5\xa3&jD\xab[2\x96\xe3GQ\x1b\xb4C\xd2}X6\x86\xc5\xd3\xff\f\xe8Y\xfc\x93\xc3]L[X\"\x84V+F\x9d\xc3\xdc\u009dj\xb0\xbeS\x1e\u007fuڅa\x9f\t\xa5/\x13\u007fXm\x86\x82\x89\xad\xddp_\rF=t\x9cߋ\x16\vq\x98\xb0&\vMi\x8a\x98\x03P\x92\x03u\"\x9f\x1f(\x1eKN\xf9-U\xf1\x1c\xda\x05\x93S+\xfc\x99\x8a\x834?\x83\xeaDZ\x15=,)a)Q\xb1S\r>I\x1e\xa9\x04\xa8\xfb\xa5\x9b\n\x1d\xc6\x15R\x86L!\xa1D\xde0\xb9\xad\xa8\x8d\xa6\xe8\xfch\xfd(\xed\xd1P\xd2\x17\xe1?R\x17\xf4\x0eKth%\xa4S\xf6\xb7\x14k\x04+c\xfb\xd0O\xd5\x11\x98N\xd0/\x13\xda1h稆\xb3\xf5p\x14\xe8\x0f\x8f\xf3\xbe\x06\xf6\x8cv\x90\xf9xNj\x84ȯ4X\xebG\xc5Ջ\xbb\xde\xce˴M\xac\bL\xa0\xa05X࠴\x82\xb1\x9eQ\xe948\xa2\x12@\x12\xc7a'\xff&\xe5\u007fWf\xf6\xe5X\xa8\x06\x95\xce\x17\xf8\xeb\xe2\xfd\xc3\xf4/\x94\xb0\x8e\xeaTE\x81^\xd4(\xc6\x06-\xbf\x01\x1f\x8a\n\x94\x17\x13\x8cC\xbd\x90\x99\xbcQ֔\xe89\xefv@\xe7?\xbd\xfb<\xc6\x19\xc0O\xe4\x00\xbf\xa8\xa6\xad\xf1\r\x98\xc4\xf2\xae\xa0\xf5\xf1a|\"b\xa7\x0f6\x86+3n\xb8\x928\xea\f\xdeDCY=#Pgh@\xa8\xcd3\xce\xe0F2\xf8\x00\xe2\xbf%u\xfes3\xaa\xf37)EnD\xe4&\x01\u06ddY\x87\x19\xb7\aȕb`gV+t8\xcef,\xc4R\xe0\xbe\arb\xbb\xa5\x03\x05Q\xad\xf8,\xd5\x19\xd4'\x80?\xbd\xfb|\x06\xed\x90'0V\xe3\x17x\a\xc6&VZ\xd2\xdf\xe7\xf0\x14#bkY}\x91}\x8a\x8a^U\x92k\x14\xcf@\xaeə(8\x9a\xb7\xa1\xaeղ\xc6\x19\xb0\vǓgӠA\xef\xd5\xear\x1e\xfc\x92d\xd2\r\xab[\x00jI\x81wW\xac.!:\xf3o}\xe7\xf1\xeb/x\x95\xf2\x97A<\x8a\xc4X\\\xed\x92\xf2R`A\xbc\xbd\x84\xe6x\x8b\f\x1eps26\xb7\x8f\x8eV\x0e\xfd\xb1\x0f\xb2\xdeQ'\xedw\x06?\xc5\b\xb8\xda\xe0n\x83\xcb6wBPQ\xddG.\xb1\xaa\xc1\x86f\x89N\f_n\x19}\xcf@\x9f\xe8\xa77\xd4\xd8\xf3\xeeyۯ\xef\xabUR\xd4u\xf0\x85\xb2\xf1IF\xa2\x93\t\xb4\xf1m\xadN[\xf8ކx\xecIpJ\x86\xec\xe3\xa2\xcf.I\xe98\xf7\x9a;u\x84sOv\xb4#\xebS\xc1X\xfe\xe3\xefϞ\x8f\xc62\xae\x06\xa5\xb0\x9b\x15\n\u007f\x14\xfd\xffk\xddg\x0f_\xcf\xca\xf1u\xa5k1\x10}\xa9jE\xc5c5\xeb\xb0\xfc\x9c\x96\x9b\xe1&ߢҌPs4\xb4\u007f\x91\u007f\xbb\xff\x8a.ʺ\x17\xf88\x01\xc9,}\xb0y\xf7\x18Ս\xec\x0f,UH\xaf\x85\xfa\xe1\xf8\t\xfe\xe6f\xf0\xa2\x1e?\v\xb2ڤ?\x1f\xc0\xa7\xcf\x13螨>\xf68d\xf0\xbf\x01\x00\x00\xff\xffV\xe6G\x99\xbd\x18\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VMo\xe36\x13\xbe\xfbW\f\xf6=\xec\xe5\x95\xdcmQ\xa0\xf0\xadM[`\xd1$X8\x8b\\\x8a\x1e(rdOC\x91,g\xe8\xd4\xfd\xf5\x05)ɖm\xd9\x1b,P\xdd4\x9c\x8fg\x9e\xf9 \x17UU-T\xa0g\x8cLޭ@\x05¿\x05]\xfe\xe3\xfa\xe5\a\xae\xc9/w\x1f\x1a\x14\xf5a\xf1Bά\xe0.\xb1\xf8n\x8d\xecS\xd4\xf83\xb6\xe4HȻE\x87\xa2\x8c\x12\xb5Z\x00(缨,\xe6\xfc\v\xa0\xbd\x93\xe8\xad\xc5Xm\xd0\xd5/\xa9\xc1&\x915\x18K\x841\xfe\xee\x9b\xfa\xdb\xfa\xfb\x05\x80\x8eX\xcc?S\x87,\xaa\v+p\xc9\xda\x05\x80S\x1d\xae \"\v\xe9\x88\xc13\x89\x8f\x84\\\xef\xd0b\xf45\xf9\x05\a\xd49\xec&\xfa\x14Vp<\xe8\xad\aH}:\xeb\xe2h=:ڗ#K,\xbf\xcd\x1e\xdf\x13KQ\t6Ee瀔c&\xb7IV\xc5\v\x85\x1c\x80\xb5\x0f\xb8\x82nj%(\x8df\x01\xb0S\x96LI\xb9G\xe7\x03\xba\x1f?}|\xfe\xeeIo\xb1S\xbd\x10 D\x1f0\n\x8dI\xe4oR\xc0\x83\f\xc0 \xebH\xa1x\x84\xf7\xd9U\xaf\x03&\x97\f\x19d\x8b0\x10\x8f\x06\xb8\x84\x01߂l\x89!b\x88\xc8\xe8\xfa\"N\xdcBVQ\x0e|\xf3'j\xa9\xe1\tcv\x02\xbc\xf5ɚ\\\xe7\x1dF\x81\x88\xdao\x1c\xfds\xf0\xcc \xbe\x84\xb4Jp\xe0p\xfc\xc8\tF\xa7l&!\xe1\xffA9\x03\x9d\xdaC\xc4\x1c\x03\x92\x9bx+*\\Ã\x8f\b\xe4Z\xbf\x82\xadH\xe0\xd5r\xb9!\x19[V\xfb\xaeK\x8ed\xbf,\x8dGM\x12\x1fyip\x87vɴ\xa9T\xd4[\x12Ԓ\".U\xa0\xaa\x00w\xa5c\xeb\xce\xfc/\x0e\xfd\xcd\xef'He\x9f\xcb\xc6\x12\xc9m\x0e\xe2\xd2FWy\xcf]\x04Ġ\x06\xb3\x1e\xff\x91\xde,ʬ\xac\u007fy\xfa\fc\xd0R\x82S\xce\v\xdbG3>\x12\x9f\x89\"\xd7b\xec\v\xd7F\xdf\x15\x8f\xe8L\xf0\xe4\xa4\xfchK\xe8NI\xe7\xd4t$\xb9\xd2\u007f%d\xc9\xf5\xa9\xe1\xae\f.4\b)\x18%hj\xf8\xe8\xe0Nuh\xef\x14\xe3\u007fN{f\x98\xabL闉\x9f\xee\x9bSŞ\xad\x83x\\\a\xb3\x15:\x1f\xf0\xa7\x80:\x17,\xb3\x96\r\xa9%]f\x00Z\x1fA]\xe8\xd7\x13\xc7sÙ\xbfF\xe9\x97\x14\x9e\xc4G\xb5\xc1{\xaf'c~\x05\xd5Os\x16#\xac\xbc\xc3\xfaA\xc5y\xc53\xcf\x00\xb2U2\x99PQ\xe4\x0ec>\x93\xc7U\xca\v\xed*\x8f\xabSN㯥w\x9c\xde\xdf\xcc\xe5a\xc6 \xa7\xb2\xf5\xaf\xe0[A7u9\xa2l\xf0\"\x89\x98ܛA\xf6K\xf7\xa3ɭ\xd5\x12ƛ\x00\xd7g\xca#\xcfm\xb2v\xf0Ti\xdf\x05%\xd4X\x1c\a\xb9\xf5\xf1\x02\"\xf5>\xf6\xfdT\u007f\x1d\xbf;oS\x87\x87\xbb\xe1&\xf2\xe7S\xddi\x83\xf4\x82\x01DN\x01\xe2\xe9\x1d7\xfd\x86\x9e`\b\xde\f\x00\x86\xa6\xe5\x9c\xe7\x1b\xb1\xe7\xe2RēmX\xcd7\xff\x89\xc6\\G\x9d(\x9cW\xf3\xe4\xf0\x8c\xaf/.\x03Q\x92\xf8\xed련\x8f\xc4\xea\x14#:\x19\x9c\x94\x9b\xf0\xab\x16\x82U,\x93\xb1ȏ\x9c\x9bu\xbe\xbf\xd4\x1f!eW Y0\x9d\xa2W\xc5s\xf3\xd2\xfa\xd8)YA^\xedU6:;\xcfO,\xd5X\\\x81\xc4t~x}# \xb3\xda\xdc\xce\xe0\xa1\xd7\xe9\xaf\xc2\xc1\x00T\xe3\x93\\!\xb6\\\x8a7\xa8\xbd\x89(l\x15\xdf\xc6\xf3)k̕\x15\xdf\x1a\x1c]\xea\xceCT\xf0\x88\xaf\x17\xb25*s>s\x15/ޤ\xaeVp\x17\x9c7\xf53:\x13l\x89_p#\xb5\xf4\xd2\xe8E\x8d^T\u008b\xd5\x02@hm\xbc\xa0eG\u007f\x01J\xa3\xbd5J\xa1]nQ\x17oa\x8d\xeb U\x85\x96O\xc8\xe7\xef\u007f[\xfc\xae\xf8\xc3\x02\xa0\xb4\xc8ۿ\xca\x1a\x9d\x17u\xb3\x02\x1d\x94Z\x00hQ\xe3\n,:o,\xbab\x8f\n\xad)\xa4Y\xb8\x06K:lkMhVн\x88{\x92 Q\x89縝W\x94t\xfe\xbb\xfe\xea\xf7\xd2y~Ө`\x85\xea\x0e\xe3E'\xf56(a\xdb\xe5\x05\x80+M\x83+x\xa4c\x1aQb\xb5\x00\xd8\v%+\xd6!\x1el\x1aԷO\x0f\xaf\xbf\u007f)wX\x8b\xb8\bP\xa1+\xadl\x98.\v\x00ҁ\x80WV\x80Na\xa4\xc1\xef\x84\a\x8b\x8dE\x87\xda;\xf0;\x04\xd14J\x96|\n\x98Mb\t\xed\x1e\a\x1bk\xea\x8e\xd7Z\x94o\xa1\x01o@\x80\x17v\x8b\x1e\xbe\vk\xb4\x1a=:(Up\x1em\x91\xd84\xd64h\xbd\xcc\xc8\xd1ӳ\x95v\xedH\x87O\xa4d\xa4\x81\x8a\xac\x03\xa3\xa8鎱\x02\xc7\x00\x80ـ\xdfIש\xc4j\xf4\xd8\x02\x91\b\rf\xfd\x0f,}\x01/h\x89\t\xb8\x9d\t\xaa\"\x93ڣ%HJ\xb3\xd5\xf2\x9f-gG\nґJxLW\x99\x1f\xa9=Z-\x14]O\xc0k\x10\xba\x82Z\x1c\xc0\"\x9d\x01A\xf7\xb81\x89+\xe0\a\xbe\x12\xbd1+\xd8y߸\xd5\xcd\xcdV\xfa\xec\x1d\xa5\xa9력?ܰ\x8d\xcbu\xf0ƺ\x9b\n\xf7\xa8n\x9c\xdc.\x85-w\xd2c\xe9\x83\xc5\x1b\xd1\xc8%\v\xae\xd99\x8a\xba\xfaM{Y\x9fz\x92\xfa\x03\x19\x94\xf3V\xeam\xbb̶{\x12w\xb2\xe1h9q[\x94\xbf\x83\x97\x96\b\x95\xe7\xfb\x97\xaf}\xab\x92n\x889\xa3\xdd3\xb4\x0ex\x02J\xea\r\xdaxql[\xc4\x11u\xd5\x18\xa9=\xff)\x95D=\x04݅u-=\xdd\xf4\x8f\x01\x1d\x99\xae)\xe0\x8ec\x04\xac\x11BS\t\x8fU\x01\x0f\x1a\xeeD\x8d\xeaN8\xfc\xd9a'\x84ݒ =\x0f|?\xb4\r\t#Z\xedr\x8eA\x937\x94\xbc\xfb\xa5\xc1r\xe0\x19\xb4In\xb2\x1bo\x8c\x1d8?m)z,\xa7ܒ\x9e\xe8\xdb\x14\x82\x86\xebGB\xfc\xa9%#[\xa1ブ?\x06\xe4\x18\x19}\x12\xc7\xe1\xc2\xf6\xe2e\xff!\x13(\x8eV'\x11\xa4\a?J\x15*\xac\xda0\xe9f%\xbd\x1f\x91s\x16\x11R\x93\x8dS\xd4&qu\xf7\x96\x03\xa4\x98\x90\x92\xecL\xea\xc8\r\xa4f\x15'\x90\xa5Gz\xacGb\xcd\xe8\x04\x9c\x96\xc4Z\xe1\n\xbc\r\xc7g\xc7}\xc2Zq\x98\x84\"\xa7\xd1ːh\xa9\x93\x9b+Y\xf2\x95\xb5\xce\xcc`\xfc\x9apH\xd2\xdc\xc5\xccs\x19\x1a\x0f\xd3{\xb2\x1b\xa1\x83\xf7\x1d\xfa\x1dڜЖ\x9c\x9e\xab\x912]\x96L\x19e\x8d\x1d<䆥\xd1NVh\xa3c\x1e\x01\x06\x0f\x9b1\xc0A\xa9k\xf2m\x11\x94\x8fiȆ\x11\xb6\x17 \xb56F\xa1\xd0SX]\xea>\x0f#\xf2#\xabi='\x9b\x8d\xc9G\x8c\xf4\x8aQ.F|R\x1c\xb0n\xfc\xe1\x1a\x84R}\a\x14\xb6\x03\xf0\x975\xa8\v\x1d\xeb\xe1\x98\xfa\x8cc\x9dFhl\x1c}\x8c:KKt)\xb2\xfe\x1f\x00\xa6\xc4\x1a\xd5\v*,\xbd\xb1\xb3`}ߧ\x8c@Q^\xdc\u007f.\x86o\xbc\x81\x8dT\x1e-\xbcK\xbf\x1b)\xf0\xbeC\x9dp\xa2\x8aD\xeaJ\xeee\x15\x84\x1aXY\x0f\xa5\x0eL0\x16\xb4T\xd7#\x9e\x84q\xde=\xc0\x14\xfe\xc2\xc2\v\xf5\x93|\xf0T\x8a\xa5\xa7\x16\xbe\xdc\xdd\u007fPq\xe4\xba\xf6f\x06\xb6\xe3\r\x11\xb9\x9c\xbe\x18~p\x19;\xaa\x90\xa4Ś\xeb\xae\t\xce\x00_\xd9\xca:*\xd6\xf7\xf6\xf1\xcb\u0600\xe0\xb4\x11\x8d\x84\xbc\x9d\x11$\xf9D{\xbd\x94]r\"\x9e\xe4\f\xa9r\xbe\x06\x01ox\x88E6\xd5\xf1\r\x85\xd2\xcc\xc2\"\x97\xe7|\xd1ox`\xa2TqOr\x9d\xbb\x94\xf8\xbc\xe1\xe1ԫ#u\xe9\xbcT\xfdD\xbdi\x81\xa5\xe2z,\xab\xca\xdd\x15\x9eR\x92\x1eo\xa6\x85\x85yO\xcdOF\xe4B\xb1[\x00{] C\xfc\xc9E8ɾv26x3R;d\xdb\xcb\xfd\xcd+u\xaa-\xf3hQ\x0f\xfa\x1a\x1e\x8d\xa7\x9f\xfb\x0fI5\xbb\xd0\xe3\f\xda=_\f\xbaG\xe3\x99\xf6\xbf\x82$\nu! \x91\x98\rT\xc7\xd8Fz\xf5\xdb\x1f\xc7уn5\xeb7\xa3\x84tԂ\x18\x9b5\xe7\xb65\x1e\x11\x99\xd7\xc1qǢ\x8d^rD\xca\xdcg\x98\xb6\x97&]\x86\xd2\xd8\x01^'\x0e\x9a\xe1\xb9FH\xc7\u007f\xa5F,\ue26d\xb4\x12%VP\x05\x86\x80[A\xe1q+K\xa8\xd1n\xe7\xe4l(N\x9d\xbe\xba\x99H\x12\x9f\v\xee\xf6t\x16\xcaO\n;\xd5\xf4AK\xb2\xf5\x13of\xafw\xb2W\xbbL*\x0eߜ\xe0&\xb5\x17U%c\x86y:\x13\x9f\xce\xe03\xce\x19\xf1ДhEC\x96\xfd/\n\xa7l(\xff\x86FH\xeb\n\xb8偔\x9a\xbe\xd9>}\xaa<\xfa\xac\x89\xabt@\x98\uf162PO\x81C\x03*\x0e\xfc\x93,\xcdf\x94Ѯ\xe1}g\\\x8c\xe2\x1b\x89\x8a\xa7\x10Wox\xb8\xba\x1ex\x1e\xc8\xe9Pz\xf5\xa0\xafb\x92\x18\xf9A\xdb\xf0\x19\xad\x0ep\xc5ﮊQ\x12\x9cd;\x9b\x18g,\xe2䫶\xd2\xfdA4\x8d\xd4\xdb\xe3{\xbe\xcc\x16f\xec``\x03\x8fG\xa7\r\f\xa1_\x96\x0eJ\xf8\xf1qq\xcc7Q\xec\xdb\xbd\xceW\xf2\xcf-\xd9D\x1f\xd8S\x96*\xc5V\x81\xa7ױ\xe5p\xf1\xe9\xb4h\xdc\xcex\xf8f/E\x9aj\x99P5\xd6\xec\xa9\x1f\xfc\xf6\u007f\xd4ѹr\x87UPxvh\xf3\xd2#b\x86F\xaf\xa5\xbe\xb0\x05&\xb4\xda\xde貸\x81\xe6\a?)`\xe2w\xf15\xcc\xe7\xaeLZ\xf7\x97N\xf7'i\x1d\xffTd\xa5\x11Yk=\xee\xb5R\xed\xcbL\x98\xa6\xff\x02\xc0&\xba\xc0\x1b\xf8LK\x15\"\xc1\xf4\x02\xe0(2\x99\xf2F\xfc\xe2\xba@\xf5\xe1\xcb\xe6\xe1\xf7\xb4P.|'@\x8a61\xb2\xe0q5\x0e -\bx\xe0]\x80\t\xf4\x06w\x10\x0e\f\x16\x06-*G#\n\x83\xab\n\x8d\x14\xb4\t0\x01\n4R\xa72\x81\x1fD\xf2X\x16~\xaa=\xe82Ka\x8b`J\xb5\x0ec\v\xa3\v4NV4\xa2\xd6\x12\x8b\xba\xaf\x87\xe9;ڊ\x1f\x03)\t\x02Zp\a\x84\xc0NL\x99<\xb9\x00\xbd\x03w\x90\xb6\xc1\x9bI\xd2\x02\v4D(\xd0\xdb\u007f`\xe2\xd6\xf0\x15\r\x01\xa9\xb0M\xb4:\xa2\xa1}'z\xaf\xe4\xbfj\xc8\x16\x9c\xe6%3\xe10\xb0\xacjR94JdĄ\x12\xaf@\xa8\x14rq\x02\x83\xb4\x06\x94\xaa\x05\x8d\x87\xd85\xfcU\x1b\x04\xa9v\xfa\x06\x0e\xce\x15\xf6\xe6\xfaz/]\xa5\b\x89\xce\xf3RIw\xbafq\x96\xdb\xd2ic\xafS\x94\xc9\xc4\x163\xb0\x98a\xe2\xb4\x19#\xcb<\xd3}\x8b\xb3\x85#\xf4\x1c\xb0\x8a\xc1\xcf\x05\xaf\xd7lp\x12(\x90\xbd\u007f:\xc8\xe4\xe0\xa3\x0e\x92)\x86\x04\xa9F˶@\x14Ev\x1a\xdf,\xccKBXh\xda\x1c4m\xd60\xf4a\x0e\x9b\x88\xa6E\xd9Ӧ\xcdX\xd6.\x9dk\x11y#s\xe54^$Л\xb3ɯ-\xd0D`I'\x8e\xcd\x0e0/\xdc\xe9\n\xa4\xabz\xe7a\x8a,k\xe1\xf0\x8b`\xd4K\xf4aӟ\xfb\xca\xfa\xf0\n\\\xaaQ\xf8\xbff\x12;\x9b\xaf\xc1\xd7,`Ч\xf6\xbc+\x90\xbb\x9aA\xe9\x15\xecd\xe6\x90\xe3\x9ei\x14[\xaeo\x96S\xafE\x968\xafI-\x17.9\xdc\xd5\a\xab\xd9\xf1=\n\xf5\xa7\xfbX\xb9:It\x9d\xfc,d\xe0s\xb74\x98\xfb\xd3\xfc=\xeb@\xd3Ñڇ\xcf\x1f1\x9d&\x14\xc4J\xe4\xd9v>\xf4Pn/\x1f\x8e\x01\xf1\x9b\t\x01U}\xc2\xf2Y\x9a+\x10\xf0\x88'\x1f\x05\t\x05\xc4(AK\x8d\x1e$Ή\xc4\xe9\"6\x11\x8fxb@!\x03\x141?^4|{\xc4S\xdc\xc0\x1e)\t\xb3p>\xf64\xa5\x0e&\b'\x12\x96\x90\x118\x9f\xc7\x1a\x02N\xc7l\x12\x96\x98\x9b\xaaU\x9cx\xd1vk6v\x92\x9b\x8fxzg=\xc3H;\x0e\xb2\x88\xde0\x19`\xb0\xc8zT\xe5\xf7\x1eD&\xd3z)\xaf\x0f\x1b5\x15uw\xdbg\xed6\xea\n\ue7a5%\xf4T\n\x1f5\xda\xcf\xdaq\xcf7#\xacG\xffEd\xf5SY\xf5\x947\xf3D\x8fv\xda0J\xe8}\xdb\xf8\x13f\xcd*ia\xa3\xe8\xac\x14\xe8\xc2\xc9_\x86\x19/\x96\x8cR^Z\xce\x0f*\xadV\xech\xd7\x03kE\xc3\f\xecѦÝ6z\xade\xa3\xa1ҁΣvO\xbe\xc7C\xf0I\xedL$\x98BZ2QE4D\xeb\x8cp\xb8\x97\t\xe4h\xf6\b\x05\xf9\x82XnD\xdbg\xdf\x16\xcb\\lhP\xb5`\xe8\xd3\x18\x94V\xa4\xd7Q\xe3*\xf6G\f\x1e\xcc\xd2N\x0f\x8e\xd9\x1b;h\x8ec\"\xa8-ҔӘ\"\xfb\xb2\xc8K,\xe2\xcey\xfc\xe0\xd1\xf3\xfe5\x17\x05i\xf8\xbf\xc9E\xb2\xb0\xff\a\n!M\x94\x96\u007f\xe0\x9b\xad\f;\xb3C֭\xbd\x10\xad!-\x10Ǐ\"\xeb\xdf\x01\x8clQ\x93\x05\xc2̇\x02zw\x16\xf9\\\xc1\xd3A[\xef\x91w\x12\xb3\xe1\xc4W\xb7I\v\x97\x8fx\xba\xbc:\xb3K\x97\x1bu\xe9C\x84\xbe\xd6G\x80\xad#\x0e\xad\xb2\x13\\\xf2\xec˟\x17NEKg\xe4@\xbe\x06\x8d\x0f\xc4\xe9$[E\x134\xb5\xbe\x92\xa3\x10z\x1a\xfbH\xd9,\xb4u\v\x10\xfa\xa2\xad\xf3\x19\xd1N\xc0\xbb,\xdf\x06^\xaeB\x9e\r\xc4Ρ\x01봩.\xc0\xc8H\xf6\xd2\xc6\xc4E;w\xe0 \xc6\xd6\xd9;\x0f\x96\x0es\x97\x8d~{\xfb{\xe9o\xc6\xe8\xef9\x88\t\a0\f\xb90:Ak\xe7\xc4&\xca\xc2\xcf$6뤦\xf0\x87%\xbeq\x9a\x15ֹdkՖ\x84\xc2D\xce\xc5lj\xbb\xe7V^\x96\xcc\a\xfd?/\xb2˱\x03\xd6\xfa<\x17*ʁ\x9d!z\xeb\xe7V*\x16@\xf9#\x8aٗl.\x96D\xaeA\xf8\xbe\x9f` \x97jË\xc0\xfbo\x12>\xd4F\x17_v|\xb8\xadf7,\xa8;\x86\xaf\x0e\xc7Z\xa1\xf9\xbe\xc2`\x87\x93\xe7Y\xfdEa\xb3Ү\x9d\xfa ȅN\xdfY\xd8Ic]\x83l4Li\xa1\x9c\xb5 M[~\x96SwƼ\xf0(\xf7\xa3\x9f\xdbJ\xc6\x1d\xf4S}\xcd̈́\\\x10u\x1f\xc4\x11A\xee@:@\x95\xe8Rq҈\x8c\x01/\xe2\xd9\x11/\xc8\x10\xeb\xf7\x9a\x86\xaa\xccc\t\xb1bI\x94j&\xbfԞ\xf0'!\xb3o\xc5F's\xd4\xe5\x8ccnZ\xf7\x96\xde\xcf\xed\xd4/\xe4\xe2Y\xe6e\x0e\"'FD\x93\x9c\xa2\r\x99cW\x06\xe0IH\xc7\x1e\x89 \xb3{r:\x1ad\xa2\xf3\"C\x87\xb0ŝ6\xac\xefV\xa6X\xbb\xfe \x17z\xfcޱ\xdf\x04\xec\x84\xccJ\x13mu\x17rc\xd9\t)\x18\x9e\xd7=\xf8Ģ\xb0b\xf2E\xa5\xa3\xa3C\xda9OP\x98%\x01\xed\x17\x83\xaf\x1d>\x16F\x92,\xea\xb9\br\x06\"Ǘ\xdd\b2\x88\xa8P\xa7\xb1\x10r\x06&c\xf1\x16B\xbe\x85\x90qp\xdfBȷ\x10r\xbe\xbd\x85\x90o!d̄\xb7\x10\xf2-\x84\\\x8e\xc2\xff>\x84\x9c\xc7lŹ\xcfџ#\xb0\x89*!\x98Fvr\x95P\rs\x9b\x95֡\x89\xaf\xb9\xdd\f\xcfk\xd9ϧ\x03\xba\x03\x1aH\xfc\x90\x15?W\x19\x96\x8d\xa6ܢqYu\x11.)[\xa5(\xbe\x8a}6:\x8e,\xae\xddj\x9d\xa18W\xb6\xb8R\xae\xb9\x02\xaen\rr]\xe8u\xeb\x88_+Ɦ\xf6\x89\xa8\xf1\x89\x88\xcb\xe70\x8d\xa8\xe2YV\xbb\x13A\xc3\x17\xd6\xe9\xd4U8\xa3k/\xad\xce\xe9\xd6ތ\x82\x8d\xa9\xc9\x19\xa9\xb8\x19\x859Y\x89\x13[g3\n}\xd6}\xcfH\xce\xe4\xcfV\x89\xc2\x1e\xb4{\xd0Y\x99G\xc4\xcc_\xbb\xe3\a\x8e^\x14\xb1\x89G\x84$\xd3eZ\xc3\x1f\xde\x1e\xbf\x87?\xc1\x97\av.\xfc\xd0/i\x9e@\x06\xf7Q\x85r\xfd\x17\x92\xc3ov\xe1\xe7\x1fŬ\xd3F\xec\xf1\x93NZ\x1f5\x98\xa2Iw|\xe7\xcdz`~\x95l\tUI\xc3ak\xd8Q\x1f\\\x93c\xf5Qp\xeb\xbcJ\x98\x0e\xcbŤ\xe6:\x97\xcdn\xea\xfe\xfe\x93߈\x939\xae?\x96\xfe\x18\xbc*\x84\xb1H\xb4\xad6\xe8'm\xc7\f\xc4A?A\xa6\xc3\xee\u007f\xe8\xe3o\x90\xb3\xb9|\xde^\xbc\x8b#\x8b`%\x90\x15\xb9\xe6E\xf8ax^+\xf2n1\x8d\x8f~c\xb2;\x06IX\xab\x13)\xf8M\xa8t\xe1\x19Ƿx\xd0;\xee\x10F\x94~ȏ\xad\x86\x9e\x95\xaf\xea7\xee\x173@\xad\x13\xae\xb4\xf3\x1f\t\xe0a\x90\x88\u0095&$I\x93\xd2\xf0\xd3^\x02\x81\xfe\x05\xec\xf2\xcf\x04d\xc2:/X\x93\xaf\xf1?\xd5Ú(\xdd:\x9fm\xad4\x0f\x9e\x84\xe5w\xf8>\xd7*m\xf7\v*\xed6\xf2\x12\u007f\xa7M.\xdc\r\xa4\xc2\xe1\x8a`\xf7~\x9f\xb4L\xa3\xcc\xe6\xa7ϓ\xbb\xfbB#\xea\x1b\x9d@V\x9eV=\x98\x1e\xd9\xc9P\xca~\x05\x9f\xf1\xe9\xac\xefN\x11\xe2\xfd\\\x9a\xcf\xcac\xfaP\u007f\x03&vS\xcdWc\xf8\x12\xe4L\x01\xba:\xdb\x1b\xdc\xcb\xd4Љ\xbf\x81\xe7/<,\xfcZ\x9e\x1bZ>~%\xb4\x93\xdf\xf4~\x1b\xd1\xc2\t\r\x1c־\x01%\xe9u5\x1f\x02z\xdf\xfc\xc7K\xaf\u0087\u007f\x8e\xfe\xeeТ9bڒ\x95\xe0\x99BO\xa3y\"I\xb0p!\x13\xd8\xfe\x02\xd0\xe5%\xffS}\xe0\x87\xffM\xb4\xf21\xa0\xbd\x81\xbf\xfd\xfd\x02\x82\x17y\xa8\xf0\xa0\xce\xff\x06\x00\x00\xff\xff\x80wp\xca4I\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xe44\x10\xbd\xe7W\x94\x96\xc3^H\x9a\x01!\xa1\xdcP\xc3a\x04\x8cFӫ\xb9 \x0e\x8eS\xe9.Ʊ\x83\xab\xdcC\xf3\xeb\x91\xed\xa4?\xd2靝\xc3\xe6\x96J}<\xbfzUqQ\x96e\xa1\x06zF\xcf\xe4l\rj \xfcW\xd0\xc67\xae^~\xe2\x8a\xdcj\u007fנ\xa8\xbb\xe2\x85l[\xc3:\xb0\xb8\xfe\t\xd9\x05\xaf\xf1\x17\xecȒ\x90\xb3E\x8f\xa2Z%\xaa.\x00\x94\xb5NT4s|\x05\xd0ΊwƠ/\xb7h\xab\x97\xd0`\x13ȴ\xe8S\x85\xa9\xfe\xfe\xbb\xea\xfb\xea\xc7\x02@{L៨G\x16\xd5\x0f5\xd8`L\x01`U\x8f50\xfa\x18$J\x02{\xfc' \vW{4\xe8]E\xae\xe0\x01u,\xbc\xf5.\f5\x9c>\xe4\xf8\x11T>\xd0&\xa5ڤTO9U\xfaj\x88\xe5\xb7[\x1e\xbf\xd3\xe85\x98\xe0\x95Y\x06\x94\x1c\x98\xec6\x18\xe5\x17]\n\x00\xd6n\xc0\x1a\x1e\"\xacAil\v\x80\xbd2Ԧ\xf3g\xa0n@\xfb\xf3\xe3\xfd\xf3\x0f\x1b\xbd\xc3^e#@\x8b\xac=\r\xc9o\t$\x10\x83\x82\xb1\x14\x88\x03\xa552\x83\x0eޣ\x15\xc8P\x80l\xe7|\x9fʍ\x89\x01Tむ\xec\x10\x9e\x13w#\xf8jt\x18\xbc\x1b\xd0\vML\xa6\x90\x93\x8e\x8e\xb6\x19Ə\xf1\x10\xd9\aڨ\x1c\xe4Tc\xec?\xb6\xc0\xe9\x80\xe0:\x90\x1d1x\x1c<2Z\xb9D\x978\xe9@Yp\xcdߨ\xa5\x1aO\xcf\xc0;\x17L\x1b\xe5\xb6G/\xe0Q\xbb\xad\xa5\xff\x8e\x999\xd2\x10K\x1a%S\xa3\xa7\x87\xac\xa0\xb7\xcaD\xfa\x03~\vʶЫ\x03x\x8c5 سlɅ+\xf8\xc3yL\x04ְ\x13\x19\xb8^\xad\xb6$\xd3\xe4h\xd7\xf7\xc1\x92\x1cVI\xff\xd4\x04q\x9eW-\xeeѬ\x98\xb6\xa5\xf2zG\x82Z\x82Ǖ\x1a\xa8L\xc0m\x1a\x9c\xaao\xbf\xf1\xe3\x98\xf1\xc73\xa4r\x88\x82a\xf1d\xb7Gs\xd2\xf2Mޣ\x8e\xb3\x1arX\xc6\u007f\xa27\x9a\"+O\xbfn>\xc1T4\xb5\xe0\x92\xf3\xc4\xf6)\x8cO\xc4G\xa2\xc8v\xe8s\xe3:\xef\xfa\x94\x11m;8\xb2YK\xda\x10\xdaK\xd294=\tO*\x8d\xfd\xa9`\x9d\xf6\a4\bah\x95`[\xc1\xbd\x85\xb5\xeaѬ\x15\xe3W\xa7=2\xcce\xa4\xf4m\xe2\xcf\xd7ޥcf\xebh\x9ev\xd2b\x87\x16\xa6w3\xa0\x8e=\x8b\xc4\xc5X\xeaH\xa71\x80\xceyPK!՛\x18\x92\xf7\xbbP\x8c;\"\xe3\x98m\x8e8\x83o\xe1XZ\x15ɾS\x8c\x97\xa6\x19\x9a\xc7\xe81\xafl\xa8C}\xd0\x06s\x82\xbc)\xf0-\x10\xf1A\x1b\xfay\xbd\x12\x1e\xf0\xf5\xca\xf6\xe8]ܓi\x15\x9f?\x8b\xfd\x87\xfc\x13ؒ\xe5ϟ&\xfb\xa4\xdf\xca\xf9\xca=[\xb5c\x1a\xf0\xc1\xda8\x91\xceF\xf3,)\\n\xe4\xd9W\x12\xec\xafp,\"\xb9\xb7\x9dK\xbfe\x15K*\xc9s\x82cS\xc7\x1a\x19\xd1U\xba[=\xcd\xcf|\x15}\x01\x81\xf9I\xbf\xf6\xf7\a\xc6\xd5A\x1e\x17j\x96\t˂9V\xba2/N̈,\x18\xa3\x1a\x835\x88\x0f\xf3\xc8\x1c\xa7\xbcW\x87KUL2:]b>+\x90+\xf7\xa8\xfd\xd7\x1d\xda[\n\x87W\xc5K\xbd\xc9i\xa09\xdc\n\\\x1foc\xf3!ɲ\xac!n\xddR芥/ b\xa1KY\xaa\v\xb7\x83+\x126\xe7\x9e\xd3\xec_\b~\xba,̑\xdf(\xbe\xd0ԙ\xe9t\xf9\xbc;\xbd%a\x97\xe3e3}\x18Oў\x9d\x9c\xc5y\xb5\x9d\xb88\xed\xd6x\xcd\x1a\x04ۇ\xf9U\xf3Ç\x8b;cz\xd5ζ\x94o\xca\xf0\xe7_EΊ\xed\xf3\x84#\x1a\xff\x0f\x00\x00\xff\xff\a2\x1f\n\xa8\v\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f$5\f\xbdׯ\xb0\x96\xc3^\xa8j\x06\x84\x84\xea\xb6\x1a8\xacXV\xa3\x9de.\x88C:qu\x9bI%!v\x1a\x86_\x8f\xf2Q\xd3]=ݻ D\xdd\xe2r\xec\xe7\xe7g+]\xdf\xf7\x9d\n\xf4\x80\x91ɻ\x11T \xfcS\xd0\xe5\x13\x0f\x8f\xdf\xf1@~s\xb8٢\xa8\x9b\ue45c\x19\xe16\xb1\xf8\xf9\x03\xb2OQ\xe3\xf78\x91#!\xef\xba\x19E\x19%j\xec\x00\x94s^T6s>\x02h\xef$zk1\xf6;t\xc3c\xda\xe26\x915\x18K\x86%\xff\xe1\xab\xe1\xeb\xe1\xdb\x0e@G,\xd7?Ҍ,j\x0e#\xb8dm\a\xe0Ԍ#\x1c\xbcM3\xb2S\x81\xf7^\xac\xd75\xd9p@\x8b\xd1\x0f\xe4;\x0e\xa8s\xee]\xf4)\x8cp\xfcQC4\\\xb5\xa6\x87\x12\xed\xbeE{ע\x15\aK,?~\xc2\xe9\x1d\xb1\x14\xc7`ST\xf6*\xb2\xe2\xc3\xe4vɪxͫ\x03`\xed\x03\x8e\xf0>C\fJ\xa3\xe9\x00\x0eʒ)\x0e\x15\xb4\x0f\xe8\xdeܽ}\xf8\xe6^\xefqV\xd5\b`\x90u\xa4P\xfc\xae\xa0\x05bP\xb0\xa4\x83?\xf6\x18\x11\x1e\n5\xc0\xe2#rC\xd6B\x02,\x10yh\xa6\x10}\xc0(\xb40\x98\xbf\x13\t=\xdb\xce\xf0\xbc\u0380\xab\x0f\x98,\x1ad\x90=Bk=\x1a\xe0R\f\xf8\tdO\f\x11CDF'\xc7^,\x9f\x9f@9\xf0\xdb\xdfP\xcb\x00\xf7\x18s\x10\xe0\xbdO\xd6d\xa5\x1d0\nD\xd4~\xe7\xe8\xaf\xe7\xc8\f\xe2KJ\xab\x04[Ӗ\x8f\x9c`t\xcaf\xaa\x13~\t\xca\x19\x98\xd5\x13D\xcc9 \xb9\x93hŅ\a\xf8\xc9G\x04r\x93\x1fa/\x12x\xdclv$\xcb\xd0h?\xcfɑ\x01\xf2\xd24#HL5ySZ\xb3\x1c\xb5\xa0\xb4\xc6 hޟ?V^\xbdZ\xbd7\xcaQ{WǔG\xf8\xe5\u05eeFE\xf3\xb0\xe0\xc8ƿ\x03\x00\x00\xff\xff\xcf^\xca\x05\xed\t\x00\x00"), +} + +var CRDs = crds() + +func crds() []*apiextv1beta1.CustomResourceDefinition { + apiextinstall.Install(scheme.Scheme) + decode := scheme.Codecs.UniversalDeserializer().Decode + var objs []*apiextv1beta1.CustomResourceDefinition + for _, crd := range rawCRDs { + gzr, err := gzip.NewReader(bytes.NewReader(crd)) + if err != nil { + panic(err) + } + bytes, err := ioutil.ReadAll(gzr) + if err != nil { + panic(err) + } + gzr.Close() + + obj, _, err := decode(bytes, nil, nil) + if err != nil { + panic(err) + } + objs = append(objs, obj.(*apiextv1beta1.CustomResourceDefinition)) + } + return objs +} diff --git a/config/crd/crds/doc.go b/config/crd/crds/doc.go new file mode 100644 index 0000000000..1103893f74 --- /dev/null +++ b/config/crd/crds/doc.go @@ -0,0 +1,4 @@ +// Package crds embeds the controller-tools generated CRD manifests +package crds + +//go:generate go run ../../../hack/crd-gen/main.go diff --git a/hack/crd-gen/main.go b/hack/crd-gen/main.go new file mode 100644 index 0000000000..4b44990aac --- /dev/null +++ b/hack/crd-gen/main.go @@ -0,0 +1,136 @@ +/* +Copyright 2019 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. +*/ + +// This code embeds the CRD manifests in config/crd/bases in +// config/crd/crds/crds.go. + +package main + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "text/template" +) + +// This is relative to config/crd/crds +const goHeaderFile = "../../../hack/boilerplate.go.txt" + +const tpl = `{{.GoHeader}} +// Code generated by crds_generate.go; DO NOT EDIT. + +package crds + +import ( + "bytes" + "compress/gzip" + "io/ioutil" + + apiextinstall "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install" + apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + "k8s.io/client-go/kubernetes/scheme" +) + +var rawCRDs = [][]byte{ +{{- range .RawCRDs }} + []byte({{ . }}), +{{- end }} +} + +var CRDs = crds() + +func crds() []*apiextv1beta1.CustomResourceDefinition { + apiextinstall.Install(scheme.Scheme) + decode := scheme.Codecs.UniversalDeserializer().Decode + var objs []*apiextv1beta1.CustomResourceDefinition + for _, crd := range rawCRDs { + gzr, err := gzip.NewReader(bytes.NewReader(crd)) + if err != nil { + panic(err) + } + bytes, err := ioutil.ReadAll(gzr) + if err != nil { + panic(err) + } + gzr.Close() + + obj, _, err := decode(bytes, nil, nil) + if err != nil { + panic(err) + } + objs = append(objs, obj.(*apiextv1beta1.CustomResourceDefinition)) + } + return objs +} +` + +type templateData struct { + GoHeader string + RawCRDs []string +} + +func main() { + headerBytes, err := ioutil.ReadFile(goHeaderFile) + if err != nil { + log.Fatalln(err) + } + + data := templateData{ + GoHeader: string(headerBytes), + } + + // This is relative to config/crd/crds + manifests, err := ioutil.ReadDir("../bases") + if err != nil { + log.Fatalln(err) + } + + for _, crd := range manifests { + file, err := os.Open("../bases/" + crd.Name()) + if err != nil { + log.Fatalln(err) + } + + // gzip compress manifest + var buf bytes.Buffer + gzw := gzip.NewWriter(&buf) + if _, err := io.Copy(gzw, file); err != nil { + log.Fatalln(err) + } + file.Close() + gzw.Close() + + data.RawCRDs = append(data.RawCRDs, fmt.Sprintf("%q", buf.Bytes())) + } + + t, err := template.New("crd").Parse(tpl) + if err != nil { + log.Fatalln(err) + } + + out, err := os.Create("crds.go") + if err != nil { + log.Fatalln(err) + } + + if err := t.Execute(out, data); err != nil { + log.Fatalln(err) + } +} diff --git a/hack/update-generated-crd-code.sh b/hack/update-generated-crd-code.sh index fb065acbd4..a3fda859a2 100755 --- a/hack/update-generated-crd-code.sh +++ b/hack/update-generated-crd-code.sh @@ -21,20 +21,4 @@ set -o xtrace # this script expects to be run from the root of the Velero repo. -if [[ -z "${GOPATH}" ]]; then - GOPATH=~/go -fi - -if [[ ! -d "${GOPATH}/src/k8s.io/code-generator" ]]; then - echo "k8s.io/code-generator missing from GOPATH" - exit 1 -fi - -${GOPATH}/src/k8s.io/code-generator/generate-groups.sh \ - all \ - github.com/vmware-tanzu/velero/pkg/generated \ - github.com/vmware-tanzu/velero/pkg/apis \ - "velero:v1" \ - --go-header-file ./hack/boilerplate.go.txt \ - --output-base ../../.. \ - $@ \ No newline at end of file +go generate ./config/crd/crds \ No newline at end of file diff --git a/pkg/install/resources.go b/pkg/install/resources.go index 2dbe0a4ffe..d65975628d 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -17,23 +17,19 @@ limitations under the License. package install import ( - "io/ioutil" - "log" "time" corev1 "k8s.io/api/core/v1" rbacv1beta1 "k8s.io/api/rbac/v1beta1" - apiextinstall "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install" - apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" 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/runtime/schema" v1api "github.com/vmware-tanzu/velero/api/v1" + "github.com/vmware-tanzu/velero/config/crd/crds" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/buildinfo" - "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" ) // Use "latest" if the build process didn't supply a version @@ -233,25 +229,7 @@ func AllCRDs() *unstructured.UnstructuredList { // Set the GVK so that the serialization framework outputs the list properly resources.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "List"}) - apiextinstall.Install(scheme.Scheme) - decode := scheme.Codecs.UniversalDeserializer().Decode - - files, err := ioutil.ReadDir("config/crd/bases") - if err != nil { - log.Fatal(err) - } - for _, f := range files { - data, err := ioutil.ReadFile("config/crd/bases/" + f.Name()) - if err != nil { - log.Fatal(err) - } - - obj, _, err := decode([]byte(data), nil, nil) - if err != nil { - log.Fatal(err) - } - - crd := obj.(*apiextv1beta1.CustomResourceDefinition) + for _, crd := range crds.CRDs { crd.SetLabels(labels()) appendUnstructured(resources, crd) } From d9874897da618c3d0ffea669cda9959ec6e4a1fc Mon Sep 17 00:00:00 2001 From: Carlisia Date: Wed, 20 May 2020 15:17:05 -0700 Subject: [PATCH 08/34] Tie in CRD/code generation w/ existing scripts Signed-off-by: Carlisia --- Makefile | 12 +++--------- hack/verify-generated-crd-code.sh | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) create mode 100755 hack/verify-generated-crd-code.sh diff --git a/Makefile b/Makefile index a7d2a2d0f0..a384e14dd5 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ MANIFEST_PLATFORMS ?= amd64 ppc64le arm arm64 GIT_SHA = $(shell git rev-parse HEAD) GIT_DIRTY = $(shell git status --porcelain 2> /dev/null) -.PHONY: generate +.PHONY: generate-all generate-all: ## Generate code and yaml manifests $(MAKE) generate $(MAKE) manifests @@ -219,18 +219,12 @@ ifneq ($(SKIP_TESTS), 1) hack/test.sh $(WHAT) endif -verify: verify-gen +verify: ifneq ($(SKIP_TESTS), 1) @$(MAKE) shell CMD="-c 'hack/verify-all.sh'" endif -verify-gen: generate - @if !(git diff --quiet HEAD); then \ - git diff; \ - echo "files were out of date, `make generate` was run"; exit 1; \ - fi - -update: +update: generate-all @$(MAKE) shell CMD="-c 'hack/update-all.sh'" build-dirs: diff --git a/hack/verify-generated-crd-code.sh b/hack/verify-generated-crd-code.sh new file mode 100755 index 0000000000..3d77a23044 --- /dev/null +++ b/hack/verify-generated-crd-code.sh @@ -0,0 +1,29 @@ +#!/bin/bash -e +# +# Copyright 2017 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. + +HACK_DIR=$(dirname "${BASH_SOURCE}") + +${HACK_DIR}/update-generated-crd-code.sh --verify-only + +# ensure no changes to generated CRDs +if ! git diff --exit-code config/crd/crds/crds.go >/dev/null; then + # revert changes to state before running CRD generation to stay consistent + # with code-generator `--verify-only` option which discards generated changes + git checkout config/crd/bases + + echo "CRD verification - failed! Generated CRDs are out-of-date, please run 'make update'." + exit 1 +fi \ No newline at end of file From 17c12fd2c726271f0986ad2b83e67029bbee21f7 Mon Sep 17 00:00:00 2001 From: Carlisia Date: Wed, 20 May 2020 15:18:42 -0700 Subject: [PATCH 09/34] Mostly result of running update-fmt, updated file formatting Signed-off-by: Carlisia --- pkg/cmd/cli/backuplocation/create.go | 5 +++-- pkg/cmd/cli/restic/server.go | 2 +- pkg/cmd/server/server.go | 3 ++- pkg/controller/gc_controller.go | 3 ++- pkg/restic/common.go | 8 ++++---- pkg/restic/repository_manager.go | 9 +++++---- 6 files changed, 17 insertions(+), 13 deletions(-) diff --git a/pkg/cmd/cli/backuplocation/create.go b/pkg/cmd/cli/backuplocation/create.go index 17dacbec6f..97e090a6bf 100644 --- a/pkg/cmd/cli/backuplocation/create.go +++ b/pkg/cmd/cli/backuplocation/create.go @@ -28,13 +28,14 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" + velerov1api "github.com/vmware-tanzu/velero/api/v1" "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" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - k8sclient "sigs.k8s.io/controller-runtime/pkg/client" ) var ( diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/restic/server.go index 47fd86741a..c191c28d39 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/restic/server.go @@ -64,7 +64,7 @@ var ( func init() { _ = clientgoscheme.AddToScheme(scheme) - _ = velerov1.AddToScheme(scheme) + _ = veleroapiv1.AddToScheme(scheme) // +kubebuilder:scaffold:scheme } diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 92b57ab404..1651d98c51 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -74,9 +74,10 @@ import ( _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" ctrl "sigs.k8s.io/controller-runtime" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" k8sclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/manager" + + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" ) const ( diff --git a/pkg/controller/gc_controller.go b/pkg/controller/gc_controller.go index 03a0b811b9..c785e7957c 100644 --- a/pkg/controller/gc_controller.go +++ b/pkg/controller/gc_controller.go @@ -27,6 +27,8 @@ import ( "k8s.io/apimachinery/pkg/util/clock" "k8s.io/client-go/tools/cache" + "sigs.k8s.io/controller-runtime/pkg/client" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" @@ -34,7 +36,6 @@ import ( 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" - "sigs.k8s.io/controller-runtime/pkg/client" ) const ( diff --git a/pkg/restic/common.go b/pkg/restic/common.go index 220112e8bd..b70447e48c 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -28,7 +28,7 @@ import ( "k8s.io/apimachinery/pkg/labels" corev1listers "k8s.io/client-go/listers/core/v1" - velerov1 "github.com/vmware-tanzu/velero/api/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" 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" @@ -285,7 +285,7 @@ func TempCACertFile(caCert []byte, bsl string, fs filesystem.Interface) (string, return name, nil } -func GetCACert(loc *velerov1.BackupStorageLocation) ([]byte, error) { +func GetCACert(loc *veleroapiv1.BackupStorageLocation) ([]byte, error) { if loc.Spec.ObjectStorage != nil { return loc.Spec.ObjectStorage.CACert, nil } @@ -305,7 +305,7 @@ func NewPodVolumeRestoreListOptions(name string) metav1.ListOptions { // should be used when running a restic command for an Azure backend. This list is // the current environment, plus the Azure-specific variables restic needs, namely // a storage account name and key. -func AzureCmdEnv(loc *velerov1.BackupStorageLocation) ([]string, error) { +func AzureCmdEnv(loc *veleroapiv1.BackupStorageLocation) ([]string, error) { azureVars, err := getAzureResticEnvVars(loc.Spec.Config) if err != nil { return nil, errors.Wrap(err, "error getting azure restic env vars") @@ -323,7 +323,7 @@ func AzureCmdEnv(loc *velerov1.BackupStorageLocation) ([]string, error) { // should be used when running a restic command for an S3 backend. This list is // the current environment, plus the AWS-specific variables restic needs, namely // a credential profile. -func S3CmdEnv(loc *velerov1.BackupStorageLocation) ([]string, error) { +func S3CmdEnv(loc *veleroapiv1.BackupStorageLocation) ([]string, error) { awsVars, err := getS3ResticEnvVars(loc.Spec.Config) if err != nil { return nil, errors.Wrap(err, "error getting aws restic env vars") diff --git a/pkg/restic/repository_manager.go b/pkg/restic/repository_manager.go index 2869ffb652..94bc93665f 100644 --- a/pkg/restic/repository_manager.go +++ b/pkg/restic/repository_manager.go @@ -29,7 +29,10 @@ import ( corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" - velerov1 "github.com/vmware-tanzu/velero/api/v1" + kbcache "sigs.k8s.io/controller-runtime/pkg/cache" + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" + + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" 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" @@ -37,8 +40,6 @@ import ( velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" "github.com/vmware-tanzu/velero/pkg/util/filesystem" - kbcache "sigs.k8s.io/controller-runtime/pkg/cache" - k8sclient "sigs.k8s.io/controller-runtime/pkg/client" ) // RepositoryManager executes commands against restic repositories. @@ -245,7 +246,7 @@ func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error { cmd.PasswordFile = file - location := &velerov1.BackupStorageLocation{} + location := &veleroapiv1.BackupStorageLocation{} if err := rm.kbCache.Get(context.Background(), k8sclient.ObjectKey{ Namespace: rm.namespace, Name: backupLocation, From 79d2d20a5fa1e218f3b5798b4fabfdab39064f43 Mon Sep 17 00:00:00 2001 From: Carlisia Date: Wed, 20 May 2020 15:27:05 -0700 Subject: [PATCH 10/34] Just a copyright fix Signed-off-by: Carlisia --- api/v1/backupstoragelocation_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/v1/backupstoragelocation_types.go b/api/v1/backupstoragelocation_types.go index 4ffd60bf6c..e83d8c930f 100644 --- a/api/v1/backupstoragelocation_types.go +++ b/api/v1/backupstoragelocation_types.go @@ -1,5 +1,5 @@ /* -Copyright the Velero contributors. +Copyright 2018, 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. From 88328a8df6ff55acf6d3e8453f8688bb8da73f3c Mon Sep 17 00:00:00 2001 From: Carlisia Date: Thu, 21 May 2020 11:45:56 -0700 Subject: [PATCH 11/34] All the test fixes Signed-off-by: Carlisia --- go.mod | 3 +- pkg/cmd/cli/restic/server.go | 2 - pkg/controller/backup_controller.go | 1 - pkg/controller/backup_controller_test.go | 66 ++++++----- .../backup_deletion_controller_test.go | 79 +++++++----- pkg/controller/backup_sync_controller_test.go | 27 +++-- .../download_request_controller_test.go | 40 ++++--- pkg/controller/gc_controller_test.go | 30 +++-- .../pod_volume_backup_controller.go | 6 + pkg/controller/restore_controller_test.go | 34 ++++-- pkg/controller/suite_test.go | 112 ++++++++++++++++++ pkg/persistence/object_store_test.go | 5 +- pkg/restic/common.go | 6 +- pkg/restic/common_test.go | 46 ++++--- pkg/restic/config_test.go | 74 ++++++------ 15 files changed, 367 insertions(+), 164 deletions(-) create mode 100644 pkg/controller/suite_test.go diff --git a/go.mod b/go.mod index fe9716fcc5..4a5eb2ba64 100644 --- a/go.mod +++ b/go.mod @@ -21,9 +21,10 @@ require ( github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 // indirect github.com/joho/godotenv v1.3.0 github.com/kubernetes-csi/external-snapshotter/v2 v2.1.0 + github.com/onsi/ginkgo v1.11.0 + github.com/onsi/gomega v1.8.1 github.com/pkg/errors v0.8.1 github.com/prometheus/client_golang v1.0.0 - github.com/prometheus/common v0.4.1 github.com/robfig/cron v0.0.0-20170309132418-df38d32658d8 github.com/sirupsen/logrus v1.4.2 github.com/smartystreets/goconvey v1.6.4 // indirect diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/restic/server.go index c191c28d39..0b107208ea 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/restic/server.go @@ -52,8 +52,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" clientgoscheme "k8s.io/client-go/kubernetes/scheme" - - velerov1 "github.com/vmware-tanzu/velero/api/v1" ) var ( diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index b29e6c220e..77976b5805 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -393,7 +393,6 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg fmt.Sprintf("backup can't be created because backup storage location %s is currently in read-only mode", request.StorageLocation.Name)) } } - // validate and get the backup's VolumeSnapshotLocations, and store the // VolumeSnapshotLocation API objs on the request if locs, errs := c.validateAndGetSnapshotLocations(request.Backup); len(errs) > 0 { diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index a08d905a44..21d0c59c7f 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -18,8 +18,13 @@ package controller import ( "bytes" + "context" "fmt" "io" + + "sigs.k8s.io/controller-runtime/pkg/client" + + //"path/filepath" "sort" "strings" "testing" @@ -35,6 +40,8 @@ import ( "k8s.io/apimachinery/pkg/version" + . "github.com/onsi/gomega" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" @@ -132,6 +139,8 @@ func TestProcessBackupNonProcessedItems(t *testing.T) { } func TestProcessBackupValidationFailures(t *testing.T) { + g := NewWithT(t) + defaultBackupLocation := builder.ForBackupStorageLocation("velero", "loc-1").Result() tests := []struct { @@ -155,7 +164,7 @@ func TestProcessBackupValidationFailures(t *testing.T) { { name: "non-existent backup location fails validation", backup: defaultBackup().StorageLocation("nonexistent").Result(), - expectedErrs: []string{"a BackupStorageLocation CRD with the name specified in the backup spec needs to be created before this backup can be executed. Error: backupstoragelocation.velero.io \"nonexistent\" not found"}, + expectedErrs: []string{"a BackupStorageLocation CRD with the name specified in the backup spec needs to be created before this backup can be executed. Error: backupstoragelocations.velero.io \"nonexistent\" not found"}, }, { name: "backup for read-only backup location fails validation", @@ -178,12 +187,19 @@ func TestProcessBackupValidationFailures(t *testing.T) { discoveryHelper, err := discovery.NewHelper(apiServer.DiscoveryClient, logger) require.NoError(t, err) + var fakeClient client.Client + if test.backupLocation != nil { + fakeClient = newFakeClient(g, test.backupLocation) + } else { + fakeClient = newFakeClient(g) + } + c := &backupController{ genericController: newGenericController("backup-test", logger), discoveryHelper: discoveryHelper, client: clientset.VeleroV1(), lister: sharedInformers.Velero().V1().Backups().Lister(), - backupLocationLister: sharedInformers.Velero().V1().BackupStorageLocations().Lister(), + k8sClient: fakeClient, snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), defaultBackupLocation: defaultBackupLocation.Name, clock: &clock.RealClock{}, @@ -193,13 +209,6 @@ func TestProcessBackupValidationFailures(t *testing.T) { require.NotNil(t, test.backup) require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(test.backup)) - if test.backupLocation != nil { - _, err := clientset.VeleroV1().BackupStorageLocations(test.backupLocation.Namespace).Create(test.backupLocation) - require.NoError(t, err) - - require.NoError(t, sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(test.backupLocation)) - } - require.NoError(t, c.processBackup(fmt.Sprintf("%s/%s", test.backup.Namespace, test.backup.Name))) res, err := clientset.VeleroV1().Backups(test.backup.Namespace).Get(test.backup.Name, metav1.GetOptions{}) @@ -217,9 +226,11 @@ func TestProcessBackupValidationFailures(t *testing.T) { } func TestBackupLocationLabel(t *testing.T) { + g := NewWithT(t) + tests := []struct { name string - backup *veleroapiv1.Backup + backup *velerov1api.Backup backupLocation *veleroapiv1.BackupStorageLocation expectedBackupLocation string }{ @@ -245,6 +256,7 @@ func TestBackupLocationLabel(t *testing.T) { clientset = fake.NewSimpleClientset(test.backup) sharedInformers = informers.NewSharedInformerFactory(clientset, 0) logger = logging.DefaultLogger(logrus.DebugLevel, formatFlag) + fakeClient = newFakeClient(g) ) apiServer := velerotest.NewAPIServer(t) @@ -256,7 +268,7 @@ func TestBackupLocationLabel(t *testing.T) { discoveryHelper: discoveryHelper, client: clientset.VeleroV1(), lister: sharedInformers.Velero().V1().Backups().Lister(), - backupLocationLister: sharedInformers.Velero().V1().BackupStorageLocations().Lister(), + k8sClient: fakeClient, snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), defaultBackupLocation: test.backupLocation.Name, clock: &clock.RealClock{}, @@ -271,6 +283,8 @@ func TestBackupLocationLabel(t *testing.T) { } func TestDefaultBackupTTL(t *testing.T) { + g := NewWithT(t) + var ( defaultBackupTTL = metav1.Duration{Duration: 24 * 30 * time.Hour} ) @@ -304,6 +318,7 @@ func TestDefaultBackupTTL(t *testing.T) { formatFlag := logging.FormatText var ( clientset = fake.NewSimpleClientset(test.backup) + fakeClient = newFakeClient(g) logger = logging.DefaultLogger(logrus.DebugLevel, formatFlag) sharedInformers = informers.NewSharedInformerFactory(clientset, 0) ) @@ -317,7 +332,7 @@ func TestDefaultBackupTTL(t *testing.T) { c := &backupController{ genericController: newGenericController("backup-test", logger), discoveryHelper: discoveryHelper, - backupLocationLister: sharedInformers.Velero().V1().BackupStorageLocations().Lister(), + k8sClient: fakeClient, snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), defaultBackupTTL: defaultBackupTTL.Duration, clock: clock.NewFakeClock(now), @@ -333,6 +348,7 @@ func TestDefaultBackupTTL(t *testing.T) { } func TestProcessBackupCompletions(t *testing.T) { + g := NewWithT(t) defaultBackupLocation := builder.ForBackupStorageLocation("velero", "loc-1").Bucket("store-1").Result() now, err := time.Parse(time.RFC1123Z, time.RFC1123Z) @@ -343,7 +359,7 @@ func TestProcessBackupCompletions(t *testing.T) { tests := []struct { name string backup *velerov1api.Backup - backupLocation *velerov1api.BackupStorageLocation + backupLocation *veleroapiv1.BackupStorageLocation defaultVolumesToRestic bool expectedResult *velerov1api.Backup backupExists bool @@ -777,6 +793,14 @@ func TestProcessBackupCompletions(t *testing.T) { backupper = new(fakeBackupper) ) + var fakeClient client.Client + // add the test's backup storage location if it's different than the default + if test.backupLocation != nil && test.backupLocation != defaultBackupLocation { + fakeClient = newFakeClient(g, test.backupLocation) + } else { + fakeClient = newFakeClient(g) + } + apiServer := velerotest.NewAPIServer(t) apiServer.DiscoveryClient.FakedServerVersion = &version.Info{ @@ -799,7 +823,7 @@ func TestProcessBackupCompletions(t *testing.T) { discoveryHelper: discoveryHelper, client: clientset.VeleroV1(), lister: sharedInformers.Velero().V1().Backups().Lister(), - backupLocationLister: sharedInformers.Velero().V1().BackupStorageLocations().Lister(), + k8sClient: fakeClient, snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), defaultBackupLocation: defaultBackupLocation.Name, defaultVolumesToRestic: test.defaultVolumesToRestic, @@ -834,19 +858,7 @@ func TestProcessBackupCompletions(t *testing.T) { require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(test.backup)) // add the default backup storage location to the clientset and the informer/lister store - _, err = clientset.VeleroV1().BackupStorageLocations(defaultBackupLocation.Namespace).Create(defaultBackupLocation) - require.NoError(t, err) - - require.NoError(t, sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(defaultBackupLocation)) - - // add the test's backup storage location to the clientset and the informer/lister store - // if it's different than the default - if test.backupLocation != nil && test.backupLocation != defaultBackupLocation { - _, err := clientset.VeleroV1().BackupStorageLocations(test.backupLocation.Namespace).Create(test.backupLocation) - require.NoError(t, err) - - require.NoError(t, sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(test.backupLocation)) - } + require.NoError(t, fakeClient.Create(context.Background(), defaultBackupLocation)) require.NoError(t, c.processBackup(fmt.Sprintf("%s/%s", test.backup.Namespace, test.backup.Name))) diff --git a/pkg/controller/backup_deletion_controller_test.go b/pkg/controller/backup_deletion_controller_test.go index c7a7dd0188..e57febc504 100644 --- a/pkg/controller/backup_deletion_controller_test.go +++ b/pkg/controller/backup_deletion_controller_test.go @@ -17,10 +17,13 @@ limitations under the License. package controller import ( + "context" "fmt" "testing" "time" + "sigs.k8s.io/controller-runtime/pkg/client" + snapshotv1beta1api "github.com/kubernetes-csi/external-snapshotter/v2/pkg/apis/volumesnapshot/v1beta1" snapshotFake "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/clientset/versioned/fake" snapshotv1beta1informers "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/informers/externalversions" @@ -35,6 +38,9 @@ import ( "k8s.io/apimachinery/pkg/util/sets" core "k8s.io/client-go/testing" + . "github.com/onsi/gomega" + + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/builder" @@ -63,7 +69,7 @@ func TestBackupDeletionControllerProcessQueueItem(t *testing.T) { NewBackupTracker(), nil, // restic repository manager sharedInformers.Velero().V1().PodVolumeBackups().Lister(), - sharedInformers.Velero().V1().BackupStorageLocations().Lister(), + k8sClient, sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), nil, // csiSnapshotLister nil, // csiSnapshotContentLister @@ -128,7 +134,7 @@ type backupDeletionControllerTestData struct { req *velerov1.DeleteBackupRequest } -func setupBackupDeletionControllerTest(objects ...runtime.Object) *backupDeletionControllerTestData { +func setupBackupDeletionControllerTest(fakeClient client.Client, objects ...runtime.Object) *backupDeletionControllerTestData { req := pkgbackup.NewDeleteBackupRequest("foo", "uid") req.Namespace = "velero" req.Name = "foo-abcde" @@ -156,7 +162,7 @@ func setupBackupDeletionControllerTest(objects ...runtime.Object) *backupDeletio NewBackupTracker(), nil, // restic repository manager sharedInformers.Velero().V1().PodVolumeBackups().Lister(), - sharedInformers.Velero().V1().BackupStorageLocations().Lister(), + fakeClient, sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), nil, // csiSnapshotLister nil, // csiSnapshotContentLister @@ -168,7 +174,7 @@ func setupBackupDeletionControllerTest(objects ...runtime.Object) *backupDeletio req: req, } - data.controller.newBackupStore = func(*velerov1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { + data.controller.newBackupStore = func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { return backupStore, nil } @@ -178,8 +184,11 @@ func setupBackupDeletionControllerTest(objects ...runtime.Object) *backupDeletio } func TestBackupDeletionControllerProcessRequest(t *testing.T) { + g := NewWithT(t) + t.Run("missing spec.backupName", func(t *testing.T) { - td := setupBackupDeletionControllerTest() + fakeClient := newFakeClient(g) + td := setupBackupDeletionControllerTest(fakeClient) td.req.Spec.BackupName = "" @@ -200,7 +209,8 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("existing deletion requests for the backup are deleted", func(t *testing.T) { - td := setupBackupDeletionControllerTest() + fakeClient := newFakeClient(g) + td := setupBackupDeletionControllerTest(fakeClient) // add the backup to the tracker so the execution of processRequest doesn't progress // past checking for an in-progress backup. this makes validation easier. @@ -255,7 +265,8 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("deleting an in progress backup isn't allowed", func(t *testing.T) { - td := setupBackupDeletionControllerTest() + fakeClient := newFakeClient(g) + td := setupBackupDeletionControllerTest(fakeClient) td.controller.backupTracker.Add(td.req.Namespace, td.req.Spec.BackupName) @@ -279,9 +290,9 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result() location := builder.ForBackupStorageLocation("velero", "default").Result() - td := setupBackupDeletionControllerTest(backup) + fakeClient := newFakeClient(g, location) - td.sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(location) + td := setupBackupDeletionControllerTest(fakeClient, backup) td.client.PrependReactor("patch", "deletebackuprequests", func(action core.Action) (bool, runtime.Object, error) { return true, nil, errors.New("bad") @@ -311,9 +322,9 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result() location := builder.ForBackupStorageLocation("velero", "default").Result() - td := setupBackupDeletionControllerTest(backup) + fakeClient := newFakeClient(g, location) - td.sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(location) + td := setupBackupDeletionControllerTest(fakeClient, backup) td.client.PrependReactor("patch", "deletebackuprequests", func(action core.Action) (bool, runtime.Object, error) { return true, td.req, nil @@ -350,7 +361,9 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("unable to find backup", func(t *testing.T) { - td := setupBackupDeletionControllerTest() + fakeClient := newFakeClient(g) + + td := setupBackupDeletionControllerTest(fakeClient) err := td.controller.processRequest(td.req) require.NoError(t, err) @@ -374,9 +387,10 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("unable to find backup storage location", func(t *testing.T) { + fakeClient := newFakeClient(g) backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result() - td := setupBackupDeletionControllerTest(backup) + td := setupBackupDeletionControllerTest(fakeClient, backup) err := td.controller.processRequest(td.req) require.NoError(t, err) @@ -401,11 +415,10 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { t.Run("backup storage location is in read-only mode", func(t *testing.T) { backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result() - location := builder.ForBackupStorageLocation("velero", "default").AccessMode(velerov1.BackupStorageLocationAccessModeReadOnly).Result() + location := builder.ForBackupStorageLocation("velero", "default").AccessMode(veleroapiv1.BackupStorageLocationAccessModeReadOnly).Result() - td := setupBackupDeletionControllerTest(backup) - - td.sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(location) + fakeClient := newFakeClient(g, location) + td := setupBackupDeletionControllerTest(fakeClient, backup) err := td.controller.processRequest(td.req) require.NoError(t, err) @@ -437,27 +450,29 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { restore2 := builder.ForRestore("velero", "restore-2").Phase(velerov1.RestorePhaseCompleted).Backup("foo").Result() restore3 := builder.ForRestore("velero", "restore-3").Phase(velerov1.RestorePhaseCompleted).Backup("some-other-backup").Result() - td := setupBackupDeletionControllerTest(backup, restore1, restore2, restore3) + fakeClient := newFakeClient(g) + td := setupBackupDeletionControllerTest(fakeClient, 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) - location := &velerov1.BackupStorageLocation{ + location := &veleroapiv1.BackupStorageLocation{ ObjectMeta: metav1.ObjectMeta{ Namespace: backup.Namespace, Name: backup.Spec.StorageLocation, }, - Spec: velerov1.BackupStorageLocationSpec{ + Spec: veleroapiv1.BackupStorageLocationSpec{ Provider: "objStoreProvider", - StorageType: velerov1.StorageType{ - ObjectStorage: &velerov1.ObjectStorageLocation{ + StorageType: veleroapiv1.StorageType{ + ObjectStorage: &veleroapiv1.ObjectStorageLocation{ Bucket: "bucket", }, }, }, } - require.NoError(t, td.sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(location)) + + require.NoError(t, fakeClient.Create(context.Background(), location)) snapshotLocation := &velerov1.VolumeSnapshotLocation{ ObjectMeta: metav1.ObjectMeta{ @@ -596,7 +611,8 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { Backup("some-other-backup"). Result() - td := setupBackupDeletionControllerTest(backup, restore1, restore2, restore3) + fakeClient := newFakeClient(g) + td := setupBackupDeletionControllerTest(fakeClient, backup, restore1, restore2, restore3) td.req = pkgbackup.NewDeleteBackupRequest(backup.Name, string(backup.UID)) td.req.Namespace = "velero" td.req.Name = "foo-abcde" @@ -604,21 +620,21 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore2) td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore3) - location := &velerov1.BackupStorageLocation{ + location := &veleroapiv1.BackupStorageLocation{ ObjectMeta: metav1.ObjectMeta{ Namespace: backup.Namespace, Name: backup.Spec.StorageLocation, }, - Spec: velerov1.BackupStorageLocationSpec{ + Spec: veleroapiv1.BackupStorageLocationSpec{ Provider: "objStoreProvider", - StorageType: velerov1.StorageType{ - ObjectStorage: &velerov1.ObjectStorageLocation{ + StorageType: veleroapiv1.StorageType{ + ObjectStorage: &veleroapiv1.ObjectStorageLocation{ Bucket: "bucket", }, }, }, } - require.NoError(t, td.sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(location)) + require.NoError(t, fakeClient.Create(context.Background(), location)) snapshotLocation := &velerov1.VolumeSnapshotLocation{ ObjectMeta: metav1.ObjectMeta{ @@ -735,6 +751,8 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { } func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) { + g := NewWithT(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) @@ -845,6 +863,7 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { client := fake.NewSimpleClientset() + fakeClient := newFakeClient(g) sharedInformers := informers.NewSharedInformerFactory(client, 0) controller := NewBackupDeletionController( @@ -857,7 +876,7 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) { NewBackupTracker(), nil, sharedInformers.Velero().V1().PodVolumeBackups().Lister(), - sharedInformers.Velero().V1().BackupStorageLocations().Lister(), + fakeClient, sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), nil, // csiSnapshotLister nil, // csiSnapshotContentLister diff --git a/pkg/controller/backup_sync_controller_test.go b/pkg/controller/backup_sync_controller_test.go index 7327e79a52..76fef8980c 100644 --- a/pkg/controller/backup_sync_controller_test.go +++ b/pkg/controller/backup_sync_controller_test.go @@ -17,6 +17,7 @@ limitations under the License. package controller import ( + "context" "testing" "time" @@ -28,6 +29,8 @@ import ( "k8s.io/apimachinery/pkg/util/validation" core "k8s.io/client-go/testing" + . "github.com/onsi/gomega" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" @@ -108,6 +111,8 @@ func defaultLocationsListWithLongerLocationName(namespace string) []*veleroapiv1 } func TestBackupSyncControllerRun(t *testing.T) { + g := NewWithT(t) + type cloudBackupData struct { backup *velerov1api.Backup podVolumeBackups []*velerov1api.PodVolumeBackup @@ -333,6 +338,7 @@ func TestBackupSyncControllerRun(t *testing.T) { t.Run(test.name, func(t *testing.T) { var ( client = fake.NewSimpleClientset() + fakeClient = newFakeClient(g) sharedInformers = informers.NewSharedInformerFactory(client, 0) pluginManager = &pluginmocks.Manager{} backupStores = make(map[string]*persistencemocks.BackupStore) @@ -340,10 +346,9 @@ func TestBackupSyncControllerRun(t *testing.T) { c := NewBackupSyncController( client.VeleroV1(), - client.VeleroV1(), + fakeClient, client.VeleroV1(), sharedInformers.Velero().V1().Backups().Lister(), - sharedInformers.Velero().V1().BackupStorageLocations().Lister(), time.Duration(0), test.namespace, nil, // csiSnapshotClient @@ -353,7 +358,7 @@ func TestBackupSyncControllerRun(t *testing.T) { velerotest.NewLogger(), ).(*backupSyncController) - c.newBackupStore = func(loc *velerov1api.BackupStorageLocation, _ persistence.ObjectStoreGetter, _ logrus.FieldLogger) (persistence.BackupStore, error) { + c.newBackupStore = func(loc *veleroapiv1.BackupStorageLocation, _ persistence.ObjectStoreGetter, _ logrus.FieldLogger) (persistence.BackupStore, error) { // this gets populated just below, prior to exercising the method under test return backupStores[loc.Name], nil } @@ -361,7 +366,7 @@ func TestBackupSyncControllerRun(t *testing.T) { pluginManager.On("CleanupClients").Return(nil) for _, location := range test.locations { - require.NoError(t, sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(location)) + require.NoError(t, fakeClient.Create(context.Background(), location)) backupStores[location.Name] = &persistencemocks.BackupStore{} } @@ -398,7 +403,7 @@ func TestBackupSyncControllerRun(t *testing.T) { 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 + var location *veleroapiv1.BackupStorageLocation for _, loc := range test.locations { if loc.Spec.ObjectStorage.Bucket == bucket { location = loc @@ -470,6 +475,8 @@ func TestBackupSyncControllerRun(t *testing.T) { } func TestDeleteOrphanedBackups(t *testing.T) { + g := NewWithT(t) + baseBuilder := func(name string) *builder.BackupBuilder { return builder.ForBackup("ns-1", name).ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "default")) } @@ -560,15 +567,15 @@ func TestDeleteOrphanedBackups(t *testing.T) { t.Run(test.name, func(t *testing.T) { var ( client = fake.NewSimpleClientset() + fakeClient = newFakeClient(g) sharedInformers = informers.NewSharedInformerFactory(client, 0) ) c := NewBackupSyncController( client.VeleroV1(), - client.VeleroV1(), + fakeClient, client.VeleroV1(), sharedInformers.Velero().V1().Backups().Lister(), - sharedInformers.Velero().V1().BackupStorageLocations().Lister(), time.Duration(0), test.namespace, nil, // csiSnapshotClient @@ -613,6 +620,8 @@ func TestDeleteOrphanedBackups(t *testing.T) { } func TestStorageLabelsInDeleteOrphanedBackups(t *testing.T) { + g := NewWithT(t) + longLabelName := "the-really-long-location-name-that-is-much-more-than-63-characters" tests := []struct { name string @@ -653,15 +662,15 @@ func TestStorageLabelsInDeleteOrphanedBackups(t *testing.T) { t.Run(test.name, func(t *testing.T) { var ( client = fake.NewSimpleClientset() + fakeClient = newFakeClient(g) sharedInformers = informers.NewSharedInformerFactory(client, 0) ) c := NewBackupSyncController( client.VeleroV1(), - client.VeleroV1(), + fakeClient, client.VeleroV1(), sharedInformers.Velero().V1().Backups().Lister(), - sharedInformers.Velero().V1().BackupStorageLocations().Lister(), time.Duration(0), test.namespace, nil, // csiSnapshotClient diff --git a/pkg/controller/download_request_controller_test.go b/pkg/controller/download_request_controller_test.go index 89d9877765..8cc6e5e221 100644 --- a/pkg/controller/download_request_controller_test.go +++ b/pkg/controller/download_request_controller_test.go @@ -27,6 +27,11 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/clock" + . "github.com/onsi/gomega" + + "sigs.k8s.io/controller-runtime/pkg/client" + + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" v1 "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" @@ -48,7 +53,7 @@ type downloadRequestTestHarness struct { controller *downloadRequestController } -func newDownloadRequestTestHarness(t *testing.T) *downloadRequestTestHarness { +func newDownloadRequestTestHarness(t *testing.T, fakeClient client.Client) *downloadRequestTestHarness { var ( client = fake.NewSimpleClientset() informerFactory = informers.NewSharedInformerFactory(client, 0) @@ -58,7 +63,7 @@ func newDownloadRequestTestHarness(t *testing.T) *downloadRequestTestHarness { client.VeleroV1(), informerFactory.Velero().V1().DownloadRequests(), informerFactory.Velero().V1().Restores().Lister(), - informerFactory.Velero().V1().BackupStorageLocations().Lister(), + fakeClient, informerFactory.Velero().V1().Backups().Lister(), func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, velerotest.NewLogger(), @@ -69,7 +74,7 @@ func newDownloadRequestTestHarness(t *testing.T) *downloadRequestTestHarness { require.NoError(t, err) controller.clock = clock.NewFakeClock(clockTime) - controller.newBackupStore = func(*v1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { + controller.newBackupStore = func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { return backupStore, nil } @@ -102,16 +107,16 @@ func newDownloadRequest(phase v1.DownloadRequestPhase, targetKind v1.DownloadTar } } -func newBackupLocation(name, provider, bucket string) *v1.BackupStorageLocation { - return &v1.BackupStorageLocation{ +func newBackupLocation(name, provider, bucket string) *veleroapiv1.BackupStorageLocation { + return &veleroapiv1.BackupStorageLocation{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: v1.DefaultNamespace, }, - Spec: v1.BackupStorageLocationSpec{ + Spec: veleroapiv1.BackupStorageLocationSpec{ Provider: provider, - StorageType: v1.StorageType{ - ObjectStorage: &v1.ObjectStorageLocation{ + StorageType: veleroapiv1.StorageType{ + ObjectStorage: &veleroapiv1.ObjectStorageLocation{ Bucket: bucket, }, }, @@ -120,6 +125,8 @@ func newBackupLocation(name, provider, bucket string) *v1.BackupStorageLocation } func TestProcessDownloadRequest(t *testing.T) { + g := NewWithT(t) + defaultBackup := func() *v1.Backup { return builder.ForBackup(v1.DefaultNamespace, "a-backup").StorageLocation("a-location").Result() } @@ -130,7 +137,7 @@ func TestProcessDownloadRequest(t *testing.T) { downloadRequest *v1.DownloadRequest backup *v1.Backup restore *v1.Restore - backupLocation *v1.BackupStorageLocation + backupLocation *veleroapiv1.BackupStorageLocation expired bool expectedErr string expectGetsURL bool @@ -167,7 +174,7 @@ func TestProcessDownloadRequest(t *testing.T) { downloadRequest: newDownloadRequest("", v1.DownloadTargetKindBackupContents, "a-backup"), backup: defaultBackup(), backupLocation: newBackupLocation("non-matching-location", "a-provider", "a-bucket"), - expectedErr: "backupstoragelocation.velero.io \"a-location\" not found", + expectedErr: "backupstoragelocations.velero.io \"a-location\" not found", }, { name: "backup contents request with phase '' gets a url", @@ -244,7 +251,14 @@ func TestProcessDownloadRequest(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - harness := newDownloadRequestTestHarness(t) + var fakeClient client.Client + if tc.backupLocation != nil { + fakeClient = newFakeClient(g, tc.backupLocation) + } else { + fakeClient = newFakeClient(g) + } + + harness := newDownloadRequestTestHarness(t, fakeClient) // set up test case data @@ -273,10 +287,6 @@ func TestProcessDownloadRequest(t *testing.T) { require.NoError(t, harness.informerFactory.Velero().V1().Backups().Informer().GetStore().Add(tc.backup)) } - if tc.backupLocation != nil { - require.NoError(t, harness.informerFactory.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(tc.backupLocation)) - } - if tc.expectGetsURL { harness.backupStore.On("GetDownloadURL", tc.downloadRequest.Spec.Target).Return("a-url", nil) } diff --git a/pkg/controller/gc_controller_test.go b/pkg/controller/gc_controller_test.go index c8e053c62f..d79c2ac795 100644 --- a/pkg/controller/gc_controller_test.go +++ b/pkg/controller/gc_controller_test.go @@ -23,6 +23,9 @@ import ( "testing" "time" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" + + . "github.com/onsi/gomega" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -32,6 +35,8 @@ import ( "k8s.io/apimachinery/pkg/watch" core "k8s.io/client-go/testing" + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" + api "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" @@ -50,7 +55,7 @@ func TestGCControllerEnqueueAllBackups(t *testing.T) { sharedInformers.Velero().V1().Backups(), sharedInformers.Velero().V1().DeleteBackupRequests().Lister(), client.VeleroV1(), - sharedInformers.Velero().V1().BackupStorageLocations().Lister(), + k8sClient, ).(*gcController) ) @@ -111,7 +116,7 @@ func TestGCControllerHasUpdateFunc(t *testing.T) { sharedInformers.Velero().V1().Backups(), sharedInformers.Velero().V1().DeleteBackupRequests().Lister(), client.VeleroV1(), - sharedInformers.Velero().V1().BackupStorageLocations().Lister(), + k8sClient, ).(*gcController) keys := make(chan string) @@ -148,6 +153,8 @@ func TestGCControllerHasUpdateFunc(t *testing.T) { } func TestGCControllerProcessQueueItem(t *testing.T) { + g := NewWithT(t) + fakeClock := clock.NewFakeClock(time.Now()) defaultBackupLocation := builder.ForBackupStorageLocation("velero", "default").Result() @@ -155,7 +162,7 @@ func TestGCControllerProcessQueueItem(t *testing.T) { name string backup *api.Backup deleteBackupRequests []*api.DeleteBackupRequest - backupLocation *api.BackupStorageLocation + backupLocation *veleroapiv1.BackupStorageLocation expectDeletion bool createDeleteBackupRequestError bool expectError bool @@ -172,13 +179,13 @@ func TestGCControllerProcessQueueItem(t *testing.T) { { 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(api.BackupStorageLocationAccessModeReadOnly).Result(), + backupLocation: builder.ForBackupStorageLocation("velero", "read-only").AccessMode(veleroapiv1.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(api.BackupStorageLocationAccessModeReadWrite).Result(), + backupLocation: builder.ForBackupStorageLocation("velero", "read-write").AccessMode(veleroapiv1.BackupStorageLocationAccessModeReadWrite).Result(), expectDeletion: true, }, { @@ -246,12 +253,19 @@ func TestGCControllerProcessQueueItem(t *testing.T) { sharedInformers = informers.NewSharedInformerFactory(client, 0) ) + var fakeClient k8sclient.Client + if test.backupLocation != nil { + fakeClient = newFakeClient(g, test.backupLocation) + } else { + fakeClient = newFakeClient(g) + } + controller := NewGCController( velerotest.NewLogger(), sharedInformers.Velero().V1().Backups(), sharedInformers.Velero().V1().DeleteBackupRequests().Lister(), client.VeleroV1(), - sharedInformers.Velero().V1().BackupStorageLocations().Lister(), + fakeClient, ).(*gcController) controller.clock = fakeClock @@ -261,10 +275,6 @@ func TestGCControllerProcessQueueItem(t *testing.T) { sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(test.backup) } - if test.backupLocation != nil { - sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(test.backupLocation) - } - for _, dbr := range test.deleteBackupRequests { sharedInformers.Velero().V1().DeleteBackupRequests().Informer().GetStore().Add(dbr) } diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 9f5073ee2d..9de729d886 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -236,6 +236,8 @@ func (c *podVolumeBackupController) processBackup(req *velerov1api.PodVolumeBack ) // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic + //TODO(carlisia): before the client was being passed to the restic package and the + // "getting" of the bsl was being done there; is that better? location := &veleroapiv1.BackupStorageLocation{} if err := c.client.Get(context.Background(), client.ObjectKey{ Namespace: req.Namespace, @@ -244,6 +246,10 @@ func (c *podVolumeBackupController) processBackup(req *velerov1api.PodVolumeBack return err } + //TODO(carlisia): would it be better to fetch the cert here? + //if location.Spec.ObjectStorage != nil { + // caCert = location.Spec.ObjectStorage.CACert + //} caCert, err := restic.GetCACert(location) if err != nil { log.WithError(err).Error("Error getting caCert") diff --git a/pkg/controller/restore_controller_test.go b/pkg/controller/restore_controller_test.go index ae414872a8..63f1955b06 100644 --- a/pkg/controller/restore_controller_test.go +++ b/pkg/controller/restore_controller_test.go @@ -18,11 +18,15 @@ package controller import ( "bytes" + "context" "encoding/json" "io/ioutil" "testing" "time" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" + + . "github.com/onsi/gomega" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" @@ -52,10 +56,12 @@ import ( ) func TestFetchBackupInfo(t *testing.T) { + g := NewWithT(t) + tests := []struct { name string backupName string - informerLocations []*api.BackupStorageLocation + informerLocations []*veleroapiv1.BackupStorageLocation informerBackups []*api.Backup backupStoreBackup *api.Backup backupStoreError error @@ -65,7 +71,7 @@ func TestFetchBackupInfo(t *testing.T) { { name: "lister has backup", backupName: "backup-1", - informerLocations: []*api.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result()}, + informerLocations: []*veleroapiv1.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result()}, informerBackups: []*api.Backup{defaultBackup().StorageLocation("default").Result()}, expectedRes: defaultBackup().StorageLocation("default").Result(), }, @@ -73,7 +79,7 @@ func TestFetchBackupInfo(t *testing.T) { name: "lister does not have a backup, but backupSvc does", backupName: "backup-1", backupStoreBackup: defaultBackup().StorageLocation("default").Result(), - informerLocations: []*api.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result()}, + informerLocations: []*veleroapiv1.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result()}, informerBackups: []*api.Backup{defaultBackup().StorageLocation("default").Result()}, expectedRes: defaultBackup().StorageLocation("default").Result(), }, @@ -91,6 +97,7 @@ func TestFetchBackupInfo(t *testing.T) { t.Run(test.name, func(t *testing.T) { var ( client = fake.NewSimpleClientset() + fakeClient = newFakeClient(g) restorer = &fakeRestorer{} sharedInformers = informers.NewSharedInformerFactory(client, 0) logger = velerotest.NewLogger() @@ -108,7 +115,7 @@ func TestFetchBackupInfo(t *testing.T) { client.VeleroV1(), restorer, sharedInformers.Velero().V1().Backups().Lister(), - sharedInformers.Velero().V1().BackupStorageLocations().Lister(), + fakeClient, sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), logger, logrus.InfoLevel, @@ -118,13 +125,13 @@ func TestFetchBackupInfo(t *testing.T) { formatFlag, ).(*restoreController) - c.newBackupStore = func(*api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { + c.newBackupStore = func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { return backupStore, nil } if test.backupStoreError == nil { for _, itm := range test.informerLocations { - sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(itm) + require.NoError(t, fakeClient.Create(context.Background(), itm)) } for _, itm := range test.informerBackups { @@ -204,7 +211,7 @@ func TestProcessQueueItemSkips(t *testing.T) { client.VeleroV1(), restorer, sharedInformers.Velero().V1().Backups().Lister(), - sharedInformers.Velero().V1().BackupStorageLocations().Lister(), + k8sClient, sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), logger, logrus.InfoLevel, @@ -226,12 +233,14 @@ func TestProcessQueueItemSkips(t *testing.T) { } func TestProcessQueueItem(t *testing.T) { + g := NewWithT(t) + defaultStorageLocation := builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result() tests := []struct { name string restoreKey string - location *api.BackupStorageLocation + location *veleroapiv1.BackupStorageLocation restore *api.Restore backup *api.Backup restorerError error @@ -392,6 +401,7 @@ func TestProcessQueueItem(t *testing.T) { t.Run(test.name, func(t *testing.T) { var ( client = fake.NewSimpleClientset() + fakeClient = newFakeClient(g) restorer = &fakeRestorer{} sharedInformers = informers.NewSharedInformerFactory(client, 0) logger = velerotest.NewLogger() @@ -409,7 +419,7 @@ func TestProcessQueueItem(t *testing.T) { client.VeleroV1(), restorer, sharedInformers.Velero().V1().Backups().Lister(), - sharedInformers.Velero().V1().BackupStorageLocations().Lister(), + fakeClient, sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), logger, logrus.InfoLevel, @@ -419,12 +429,12 @@ func TestProcessQueueItem(t *testing.T) { formatFlag, ).(*restoreController) - c.newBackupStore = func(*api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { + c.newBackupStore = func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { return backupStore, nil } if test.location != nil { - sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(test.location) + require.NoError(t, fakeClient.Create(context.Background(), test.location)) } if test.backup != nil { sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(test.backup) @@ -634,7 +644,7 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) { client.VeleroV1(), nil, sharedInformers.Velero().V1().Backups().Lister(), - sharedInformers.Velero().V1().BackupStorageLocations().Lister(), + k8sClient, sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), logger, logrus.DebugLevel, diff --git a/pkg/controller/suite_test.go b/pkg/controller/suite_test.go new file mode 100644 index 0000000000..c0b0b5523c --- /dev/null +++ b/pkg/controller/suite_test.go @@ -0,0 +1,112 @@ +/* +Copyright 2019 microsoft. + +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 ( + "os" + "path/filepath" + "time" + + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gexec" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var k8sManager ctrl.Manager +var testEnv *envtest.Environment + +var _ = BeforeSuite(func(done Done) { + logf.SetLogger(zap.LoggerTo(GinkgoWriter, true)) + + By("bootstrapping test environment") + t := true + if os.Getenv("TEST_USE_EXISTING_CLUSTER") == "true" { + testEnv = &envtest.Environment{ + UseExistingCluster: &t, + } + } else { + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, + } + } + + cfg, err := testEnv.Start() + Expect(err).ToNot(HaveOccurred()) + Expect(cfg).ToNot(BeNil()) + + err = scheme.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + + k8sManager, err = ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + }) + Expect(err).ToNot(HaveOccurred()) + + //err = (&SecretScopeReconciler{ + // Client: k8sManager.GetClient(), + // Log: ctrl.Log.WithName("controllers").WithName("SecretScope"), + // Recorder: k8sManager.GetEventRecorderFor("secretscope-controller"), + // APIClient: apiClient, + //}).SetupWithManager(k8sManager) + //Expect(err).ToNot(HaveOccurred()) + + go func() { + err = k8sManager.Start(ctrl.SetupSignalHandler()) + Expect(err).ToNot(HaveOccurred()) + }() + + k8sClient = k8sManager.GetClient() + Expect(k8sClient).ToNot(BeNil()) + + close(done) +}, 60) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + gexec.KillAndWait(5 * time.Second) + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) +}) + +func newFakeClient(g *WithT, initObjs ...runtime.Object) client.Client { + g.Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) + g.Expect(veleroapiv1.AddToScheme(scheme.Scheme)).To(Succeed()) + return fake.NewFakeClientWithScheme(scheme.Scheme, initObjs...) +} diff --git a/pkg/persistence/object_store_test.go b/pkg/persistence/object_store_test.go index 61d628c09c..a2ebf0dd65 100644 --- a/pkg/persistence/object_store_test.go +++ b/pkg/persistence/object_store_test.go @@ -32,6 +32,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/plugin/velero" @@ -610,7 +611,7 @@ func (osg objectStoreGetter) GetObjectStore(provider string) (velero.ObjectStore func TestNewObjectBackupStore(t *testing.T) { tests := []struct { name string - location *velerov1api.BackupStorageLocation + location *veleroapiv1.BackupStorageLocation objectStoreGetter objectStoreGetter wantBucket string wantPrefix string @@ -618,7 +619,7 @@ func TestNewObjectBackupStore(t *testing.T) { }{ { name: "location with no ObjectStorage field results in an error", - location: new(velerov1api.BackupStorageLocation), + location: new(veleroapiv1.BackupStorageLocation), wantErr: "backup storage location does not use object storage", }, { diff --git a/pkg/restic/common.go b/pkg/restic/common.go index b70447e48c..6f0a02ec26 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -286,11 +286,11 @@ func TempCACertFile(caCert []byte, bsl string, fs filesystem.Interface) (string, } func GetCACert(loc *veleroapiv1.BackupStorageLocation) ([]byte, error) { - if loc.Spec.ObjectStorage != nil { - return loc.Spec.ObjectStorage.CACert, nil + if loc.Spec.ObjectStorage == nil { + return nil, nil } - return nil, nil + return loc.Spec.ObjectStorage.CACert, nil } // NewPodVolumeRestoreListOptions creates a ListOptions with a label selector configured to diff --git a/pkg/restic/common_test.go b/pkg/restic/common_test.go index c69a1a44ec..756f44bc5c 100644 --- a/pkg/restic/common_test.go +++ b/pkg/restic/common_test.go @@ -17,19 +17,26 @@ limitations under the License. package restic import ( + "context" "os" "sort" "testing" - velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" + "k8s.io/apimachinery/pkg/runtime" "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/client-go/kubernetes/scheme" corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" + "sigs.k8s.io/controller-runtime/pkg/client" + k8sfake "sigs.k8s.io/controller-runtime/pkg/client/fake" + . "github.com/onsi/gomega" + + veleroapiv1 "github.com/vmware-tanzu/velero/api/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" @@ -380,33 +387,36 @@ func TestTempCredentialsFile(t *testing.T) { } func TestTempCACertFile(t *testing.T) { + g := NewWithT(t) var ( - bslInformer = cache.NewSharedIndexInformer(nil, new(velerov1api.BackupStorageLocation), 0, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) - bslLister = velerov1listers.NewBackupStorageLocationLister(bslInformer.GetIndexer()) - fs = velerotest.NewFakeFileSystem() - bsl = &velerov1api.BackupStorageLocation{ + fs = velerotest.NewFakeFileSystem() + bsl = &veleroapiv1.BackupStorageLocation{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Namespace: "velero", Name: "default", }, - Spec: velerov1api.BackupStorageLocationSpec{ - StorageType: velerov1api.StorageType{ - ObjectStorage: &velerov1api.ObjectStorageLocation{CACert: []byte("cacert")}, + Spec: veleroapiv1.BackupStorageLocationSpec{ + StorageType: veleroapiv1.StorageType{ + ObjectStorage: &veleroapiv1.ObjectStorageLocation{CACert: []byte("cacert")}, }, }, } ) - // bsl not in lister: expect an error - caCert, err := GetCACert(bslLister, "velero", "default") - assert.Error(t, err) + //TODO(carlisia): not sure this test makes sense anymore since + fakeClient := newFakeClient(g) + fakeClient.Create(context.Background(), bsl) - // now add bsl to lister - require.NoError(t, bslInformer.GetStore().Add(bsl)) + locationCreated := &veleroapiv1.BackupStorageLocation{} + err := fakeClient.Get(context.Background(), client.ObjectKey{ + Namespace: bsl.Namespace, + Name: bsl.Name, + }, locationCreated) + require.NoError(t, err) - // bsl in lister: expect temp file to be created with cacert value - caCert, err = GetCACert(bslLister, "velero", "default") + // expect temp file to be created with cacert value + caCert, err := GetCACert(locationCreated) require.NoError(t, err) fileName, err := TempCACertFile(caCert, "default", fs) @@ -521,3 +531,9 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { }) } } + +func newFakeClient(g *WithT, initObjs ...runtime.Object) client.Client { + g.Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) + g.Expect(veleroapiv1.AddToScheme(scheme.Scheme)).To(Succeed()) + return k8sfake.NewFakeClientWithScheme(scheme.Scheme, initObjs...) +} diff --git a/pkg/restic/config_test.go b/pkg/restic/config_test.go index 67133aab6c..f51a9e0799 100644 --- a/pkg/restic/config_test.go +++ b/pkg/restic/config_test.go @@ -22,7 +22,7 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/assert" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" ) func TestGetRepoIdentifier(t *testing.T) { @@ -31,11 +31,11 @@ func TestGetRepoIdentifier(t *testing.T) { return "", errors.New("no region found") } - backupLocation := &velerov1api.BackupStorageLocation{ - Spec: velerov1api.BackupStorageLocationSpec{ + backupLocation := &veleroapiv1.BackupStorageLocation{ + Spec: veleroapiv1.BackupStorageLocationSpec{ Provider: "aws", - StorageType: velerov1api.StorageType{ - ObjectStorage: &velerov1api.ObjectStorageLocation{ + StorageType: veleroapiv1.StorageType{ + ObjectStorage: &veleroapiv1.ObjectStorageLocation{ Bucket: "bucket", Prefix: "prefix", }, @@ -51,11 +51,11 @@ func TestGetRepoIdentifier(t *testing.T) { return "us-west-2", nil } - backupLocation = &velerov1api.BackupStorageLocation{ - Spec: velerov1api.BackupStorageLocationSpec{ + backupLocation = &veleroapiv1.BackupStorageLocation{ + Spec: veleroapiv1.BackupStorageLocationSpec{ Provider: "aws", - StorageType: velerov1api.StorageType{ - ObjectStorage: &velerov1api.ObjectStorageLocation{ + StorageType: veleroapiv1.StorageType{ + ObjectStorage: &veleroapiv1.ObjectStorageLocation{ Bucket: "bucket", }, }, @@ -65,11 +65,11 @@ func TestGetRepoIdentifier(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "s3:s3-us-west-2.amazonaws.com/bucket/restic/repo-1", id) - backupLocation = &velerov1api.BackupStorageLocation{ - Spec: velerov1api.BackupStorageLocationSpec{ + backupLocation = &veleroapiv1.BackupStorageLocation{ + Spec: veleroapiv1.BackupStorageLocationSpec{ Provider: "aws", - StorageType: velerov1api.StorageType{ - ObjectStorage: &velerov1api.ObjectStorageLocation{ + StorageType: veleroapiv1.StorageType{ + ObjectStorage: &veleroapiv1.ObjectStorageLocation{ Bucket: "bucket", Prefix: "prefix", }, @@ -80,14 +80,14 @@ func TestGetRepoIdentifier(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "s3:s3-us-west-2.amazonaws.com/bucket/prefix/restic/repo-1", id) - backupLocation = &velerov1api.BackupStorageLocation{ - Spec: velerov1api.BackupStorageLocationSpec{ + backupLocation = &veleroapiv1.BackupStorageLocation{ + Spec: veleroapiv1.BackupStorageLocationSpec{ Provider: "aws", Config: map[string]string{ "s3Url": "alternate-url", }, - StorageType: velerov1api.StorageType{ - ObjectStorage: &velerov1api.ObjectStorageLocation{ + StorageType: veleroapiv1.StorageType{ + ObjectStorage: &veleroapiv1.ObjectStorageLocation{ Bucket: "bucket", Prefix: "prefix", }, @@ -98,14 +98,14 @@ func TestGetRepoIdentifier(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "s3:alternate-url/bucket/prefix/restic/repo-1", id) - backupLocation = &velerov1api.BackupStorageLocation{ - Spec: velerov1api.BackupStorageLocationSpec{ + backupLocation = &veleroapiv1.BackupStorageLocation{ + Spec: veleroapiv1.BackupStorageLocationSpec{ Provider: "aws", Config: map[string]string{ "s3Url": "alternate-url-with-trailing-slash/", }, - StorageType: velerov1api.StorageType{ - ObjectStorage: &velerov1api.ObjectStorageLocation{ + StorageType: veleroapiv1.StorageType{ + ObjectStorage: &veleroapiv1.ObjectStorageLocation{ Bucket: "bucket", Prefix: "prefix", }, @@ -116,11 +116,11 @@ func TestGetRepoIdentifier(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "s3:alternate-url-with-trailing-slash/bucket/prefix/restic/repo-1", id) - backupLocation = &velerov1api.BackupStorageLocation{ - Spec: velerov1api.BackupStorageLocationSpec{ + backupLocation = &veleroapiv1.BackupStorageLocation{ + Spec: veleroapiv1.BackupStorageLocationSpec{ Provider: "azure", - StorageType: velerov1api.StorageType{ - ObjectStorage: &velerov1api.ObjectStorageLocation{ + StorageType: veleroapiv1.StorageType{ + ObjectStorage: &veleroapiv1.ObjectStorageLocation{ Bucket: "bucket", Prefix: "prefix", }, @@ -131,11 +131,11 @@ func TestGetRepoIdentifier(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "azure:bucket:/prefix/restic/repo-1", id) - backupLocation = &velerov1api.BackupStorageLocation{ - Spec: velerov1api.BackupStorageLocationSpec{ + backupLocation = &veleroapiv1.BackupStorageLocation{ + Spec: veleroapiv1.BackupStorageLocationSpec{ Provider: "gcp", - StorageType: velerov1api.StorageType{ - ObjectStorage: &velerov1api.ObjectStorageLocation{ + StorageType: veleroapiv1.StorageType{ + ObjectStorage: &veleroapiv1.ObjectStorageLocation{ Bucket: "bucket-2", Prefix: "prefix-2", }, @@ -146,11 +146,11 @@ func TestGetRepoIdentifier(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "gs:bucket-2:/prefix-2/restic/repo-2", id) - backupLocation = &velerov1api.BackupStorageLocation{ - Spec: velerov1api.BackupStorageLocationSpec{ + backupLocation = &veleroapiv1.BackupStorageLocation{ + Spec: veleroapiv1.BackupStorageLocationSpec{ Provider: "unsupported-provider", - StorageType: velerov1api.StorageType{ - ObjectStorage: &velerov1api.ObjectStorageLocation{ + StorageType: veleroapiv1.StorageType{ + ObjectStorage: &veleroapiv1.ObjectStorageLocation{ Bucket: "bucket-2", Prefix: "prefix-2", }, @@ -161,14 +161,14 @@ func TestGetRepoIdentifier(t *testing.T) { assert.EqualError(t, err, "restic repository prefix (resticRepoPrefix) not specified in backup storage location's config") assert.Empty(t, id) - backupLocation = &velerov1api.BackupStorageLocation{ - Spec: velerov1api.BackupStorageLocationSpec{ + backupLocation = &veleroapiv1.BackupStorageLocation{ + Spec: veleroapiv1.BackupStorageLocationSpec{ Provider: "custom-repo-identifier", Config: map[string]string{ "resticRepoPrefix": "custom:prefix:/restic", }, - StorageType: velerov1api.StorageType{ - ObjectStorage: &velerov1api.ObjectStorageLocation{ + StorageType: veleroapiv1.StorageType{ + ObjectStorage: &veleroapiv1.ObjectStorageLocation{ Bucket: "bucket", Prefix: "prefix", }, From e6e0f5900569a8f1e22de31fcc604403d332c824 Mon Sep 17 00:00:00 2001 From: Carlisia Date: Fri, 22 May 2020 05:58:59 -0700 Subject: [PATCH 12/34] Add changelog + some cleanup Signed-off-by: Carlisia --- changelogs/unreleased/2561-carlisia | 1 + go.mod | 3 +- main.go | 5 +- pkg/cmd/cli/restic/server.go | 2 +- pkg/cmd/server/server.go | 14 +--- .../backup_deletion_controller_test.go | 2 +- pkg/controller/gc_controller_test.go | 4 +- pkg/controller/restore_controller_test.go | 4 +- pkg/controller/suite_test.go | 78 +------------------ site/docs/master/code-standards.md | 3 +- site/docs/v1.3.1/code-standards.md | 2 +- site/docs/v1.3.2/code-standards.md | 2 +- site/docs/v1.4/code-standards.md | 2 +- 13 files changed, 16 insertions(+), 106 deletions(-) create mode 100644 changelogs/unreleased/2561-carlisia diff --git a/changelogs/unreleased/2561-carlisia b/changelogs/unreleased/2561-carlisia new file mode 100644 index 0000000000..bedf5568c9 --- /dev/null +++ b/changelogs/unreleased/2561-carlisia @@ -0,0 +1 @@ +Convert manifests + BSL api client to kubebuilder \ No newline at end of file diff --git a/go.mod b/go.mod index 4a5eb2ba64..3f6e7cb11e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/vmware-tanzu/velero -go 1.13 +go 1.14 require ( cloud.google.com/go v0.46.2 // indirect @@ -21,7 +21,6 @@ require ( github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 // indirect github.com/joho/godotenv v1.3.0 github.com/kubernetes-csi/external-snapshotter/v2 v2.1.0 - github.com/onsi/ginkgo v1.11.0 github.com/onsi/gomega v1.8.1 github.com/pkg/errors v0.8.1 github.com/prometheus/client_golang v1.0.0 diff --git a/main.go b/main.go index 4b4f124e10..3723ff221e 100644 --- a/main.go +++ b/main.go @@ -1,5 +1,5 @@ /* - +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. @@ -13,9 +13,6 @@ 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. */ - -// +kubebuilder:scaffold:imports - package main func main() {} diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/restic/server.go index 0b107208ea..2c2e4d4cdd 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/restic/server.go @@ -248,7 +248,7 @@ func (s *resticServer) run() { go func() { defer wg.Done() // +kubebuilder:scaffold:builder - if err := s.mgr.Start(ctrl.SetupSignalHandler()); err != nil { // ***this blocks + if err := s.mgr.Start(ctrl.SetupSignalHandler()); err != nil { s.logger.Fatal("Problem starting manager", err) } }() diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 1651d98c51..748c49cde3 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -324,18 +324,6 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s return nil, err } - // logger.Debug("STARTING CACHE") - // var wgc sync.WaitGroup - // runstart := func() { - // defer wgc.Done() - // kbCache.Start(ctrl.SetupSignalHandler()) // ***this blocks - // logger.Debug("PAST CACHING insideyy") - // } - // wgc.Add(1) - // go runstart() - // wgc.Wait() - // logger.Debug("PAST CACHING OUTSIDE") - s := &server{ namespace: f.Namespace(), metricsAddress: config.metricsAddress, @@ -936,7 +924,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string go func() { defer wg.Done() // +kubebuilder:scaffold:builder - if err := s.mgr.Start(ctrl.SetupSignalHandler()); err != nil { // ***this blocks + if err := s.mgr.Start(ctrl.SetupSignalHandler()); err != nil { s.logger.Fatal("Problem starting manager", err) } }() diff --git a/pkg/controller/backup_deletion_controller_test.go b/pkg/controller/backup_deletion_controller_test.go index e57febc504..98b4abd0c1 100644 --- a/pkg/controller/backup_deletion_controller_test.go +++ b/pkg/controller/backup_deletion_controller_test.go @@ -69,7 +69,7 @@ func TestBackupDeletionControllerProcessQueueItem(t *testing.T) { NewBackupTracker(), nil, // restic repository manager sharedInformers.Velero().V1().PodVolumeBackups().Lister(), - k8sClient, + nil, sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), nil, // csiSnapshotLister nil, // csiSnapshotContentLister diff --git a/pkg/controller/gc_controller_test.go b/pkg/controller/gc_controller_test.go index d79c2ac795..d2e2736d76 100644 --- a/pkg/controller/gc_controller_test.go +++ b/pkg/controller/gc_controller_test.go @@ -55,7 +55,7 @@ func TestGCControllerEnqueueAllBackups(t *testing.T) { sharedInformers.Velero().V1().Backups(), sharedInformers.Velero().V1().DeleteBackupRequests().Lister(), client.VeleroV1(), - k8sClient, + nil, ).(*gcController) ) @@ -116,7 +116,7 @@ func TestGCControllerHasUpdateFunc(t *testing.T) { sharedInformers.Velero().V1().Backups(), sharedInformers.Velero().V1().DeleteBackupRequests().Lister(), client.VeleroV1(), - k8sClient, + nil, ).(*gcController) keys := make(chan string) diff --git a/pkg/controller/restore_controller_test.go b/pkg/controller/restore_controller_test.go index 63f1955b06..357cb4c3f5 100644 --- a/pkg/controller/restore_controller_test.go +++ b/pkg/controller/restore_controller_test.go @@ -211,7 +211,7 @@ func TestProcessQueueItemSkips(t *testing.T) { client.VeleroV1(), restorer, sharedInformers.Velero().V1().Backups().Lister(), - k8sClient, + nil, sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), logger, logrus.InfoLevel, @@ -644,7 +644,7 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) { client.VeleroV1(), nil, sharedInformers.Velero().V1().Backups().Lister(), - k8sClient, + nil, sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), logger, logrus.DebugLevel, diff --git a/pkg/controller/suite_test.go b/pkg/controller/suite_test.go index c0b0b5523c..1dd5242f71 100644 --- a/pkg/controller/suite_test.go +++ b/pkg/controller/suite_test.go @@ -1,5 +1,5 @@ /* -Copyright 2019 microsoft. +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. @@ -17,94 +17,18 @@ limitations under the License. package controller import ( - "os" - "path/filepath" - "time" - "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client/fake" veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/onsi/gomega/gexec" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" // +kubebuilder:scaffold:imports ) -// These tests use Ginkgo (BDD-style Go testing framework). Refer to -// These tests use Ginkgo (BDD-style Go testing framework). Refer to -// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. - -var cfg *rest.Config -var k8sClient client.Client -var k8sManager ctrl.Manager -var testEnv *envtest.Environment - -var _ = BeforeSuite(func(done Done) { - logf.SetLogger(zap.LoggerTo(GinkgoWriter, true)) - - By("bootstrapping test environment") - t := true - if os.Getenv("TEST_USE_EXISTING_CLUSTER") == "true" { - testEnv = &envtest.Environment{ - UseExistingCluster: &t, - } - } else { - testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, - } - } - - cfg, err := testEnv.Start() - Expect(err).ToNot(HaveOccurred()) - Expect(cfg).ToNot(BeNil()) - - err = scheme.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - - // +kubebuilder:scaffold:scheme - - k8sManager, err = ctrl.NewManager(cfg, ctrl.Options{ - Scheme: scheme.Scheme, - }) - Expect(err).ToNot(HaveOccurred()) - - //err = (&SecretScopeReconciler{ - // Client: k8sManager.GetClient(), - // Log: ctrl.Log.WithName("controllers").WithName("SecretScope"), - // Recorder: k8sManager.GetEventRecorderFor("secretscope-controller"), - // APIClient: apiClient, - //}).SetupWithManager(k8sManager) - //Expect(err).ToNot(HaveOccurred()) - - go func() { - err = k8sManager.Start(ctrl.SetupSignalHandler()) - Expect(err).ToNot(HaveOccurred()) - }() - - k8sClient = k8sManager.GetClient() - Expect(k8sClient).ToNot(BeNil()) - - close(done) -}, 60) - -var _ = AfterSuite(func() { - By("tearing down the test environment") - gexec.KillAndWait(5 * time.Second) - err := testEnv.Stop() - Expect(err).ToNot(HaveOccurred()) -}) - func newFakeClient(g *WithT, initObjs ...runtime.Object) client.Client { g.Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) g.Expect(veleroapiv1.AddToScheme(scheme.Scheme)).To(Succeed()) diff --git a/site/docs/master/code-standards.md b/site/docs/master/code-standards.md index cc28a0f0da..261c5945ff 100644 --- a/site/docs/master/code-standards.md +++ b/site/docs/master/code-standards.md @@ -53,7 +53,8 @@ Example: 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" - + + veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" // for the new kubebuilder apis velerov1api ""github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" ) diff --git a/site/docs/v1.3.1/code-standards.md b/site/docs/v1.3.1/code-standards.md index bc2f09de76..d1b83a29ec 100644 --- a/site/docs/v1.3.1/code-standards.md +++ b/site/docs/v1.3.1/code-standards.md @@ -43,7 +43,7 @@ Example: 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" ) diff --git a/site/docs/v1.3.2/code-standards.md b/site/docs/v1.3.2/code-standards.md index 4dbe03dbc2..e2a409c440 100644 --- a/site/docs/v1.3.2/code-standards.md +++ b/site/docs/v1.3.2/code-standards.md @@ -43,7 +43,7 @@ Example: 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" ) diff --git a/site/docs/v1.4/code-standards.md b/site/docs/v1.4/code-standards.md index e212ad98c3..2dc87f671e 100644 --- a/site/docs/v1.4/code-standards.md +++ b/site/docs/v1.4/code-standards.md @@ -51,7 +51,7 @@ Example: 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" ) From b3c92eac35efbf3c89c2003a299de71190e75f26 Mon Sep 17 00:00:00 2001 From: Carlisia Date: Wed, 27 May 2020 16:51:16 -0700 Subject: [PATCH 13/34] Update backup manifest Signed-off-by: Carlisia --- config/crd/bases/velero.io_backups.yaml | 1 + config/crd/crds/crds.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/config/crd/bases/velero.io_backups.yaml b/config/crd/bases/velero.io_backups.yaml index 876d46f452..a5036ac924 100644 --- a/config/crd/bases/velero.io_backups.yaml +++ b/config/crd/bases/velero.io_backups.yaml @@ -358,6 +358,7 @@ spec: description: Progress contains information about the backup's execution progress. Note that this information is best-effort only -- if Velero fails to update it during a backup for any reason, it may be inaccurate/stale. + nullable: true properties: itemsBackedUp: description: ItemsBackedUp is the number of items that have actually diff --git a/config/crd/crds/crds.go b/config/crd/crds/crds.go index d54d670e5f..6e6f1152cd 100644 --- a/config/crd/crds/crds.go +++ b/config/crd/crds/crds.go @@ -29,7 +29,7 @@ import ( ) var rawCRDs = [][]byte{ - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xa2u\xd2\xe8k\x10\xa5\xc4W\x8f\x9a\xfes\xeb\xa7\u007fuki\xae\x9e?lы\x0fgOR\xe7\xd7pS9o\x8a\x9fљ\xcaf\xf8\twRK/\x8d>+Ћ\\xq}\x06 \xb46^P\xb7\xa3\u007f\x012\xa3\xbd5J\xa1]\xedQ\xaf\x9f\xaa-n+\xa9r\xb4\xbcBZ\xff\xf9\x1f\xd7\xff\xb4\xfe\x973\x80\xcc\"O\u007f\x90\x05:/\x8a\xf2\x1at\xa5\xd4\x19\x80\x16\x05^\xc3VdOU\xe9\xd6ϨК\xb54g\xaeČ\xd6\xda[S\x95\xd7\xd0\xfc\x10\xa6D<\xc2\x1e~\xe0\xd9ܡ\xa4\xf3?\xb6:?K\xe7\xf9\x87RUV\xa8z%\xeesR\xef+%l\xea=\x03p\x99)\xf1\x1a\xbe\xd0\x12\xa5\xc80?\x03x\x16J\xe6\x8c~XԔ\xa8?\xdem\x1e\xff\xf9>;`!B'@\x8e.\xb3\xb2\xe4qqu\x90\x0e\x04<2\xee`#\x8d\xc1\x1f\x84\xa7\xffJ\x8b\x0e\xb5w\xe0\x0f\b\x99(}e\x11\xcc\x0e~\xac\xb6h5zt\x112@\xa6*\xe7т\xf3\xc2#\b\x0f\x02J#\xb5\a\xa9\xc1\xcb\x02\xe1/\x1f\xef6`\xb6\xbf`\xe6\x1d\b\x9d\x83p\xcedRx\xcc\xe1٨\xaa\xc00\xf7\xaf\xeb\b\xb3\xb4\xa6D\xebe\xa2$\xb5\x96\xe8\xd4}G\xfb\xba\xa0\x8d\x871\x90\x93\xb0`@?\xb2\x1cspL\x14ڇ?H\a\x16\xe36\x99\x80-\xb0@C\x84\x8eH\xaf\xe1\x1e-\x01\x01w0\x95\xcaI\u009e\xd1\x12\x9d2\xb3\xd7\xf2\u007fk\xc8\x0e\xbc\xe1%\x95\xf0\x18\x99\x9b\x9a\xd4\x1e\xad\x16\x8aXV\xe1%\x13\xa2\x10o`\x91րJ\xb7\xa0\xf1\x10\xb7\x86\u007f7\x16AꝹ\x86\x83\xf7\xa5\xbb\xbe\xba\xdaK\x9f\x94%3EQi\xe9߮X\xe4\xe5\xb6\xf2ƺ\xab\x1c\x9fQ]9\xb9_\t\x9b\x1d\xa4nj\x98w%J\xb9b\xc45\xebʺ\xc8\xff!q\xdd]\xb40\xf5o$d\xce[\xa9\xf7u7\xcb\xf2(\xddI\xa8\x838\x85i\x01\xff\x86\xbc\xd4ET\xf9\xf9\xf6\xfe\xa1-j\xd2ui\xce\xd4n\xa6\xb9\x86\xf0D(\xa9wh\x03\xe3v\xd6\x14\f\x11u\x1ed\x8d\xc5TI\xd4]\xa2\xbbj[HO\x9c\xfe\x9f\n\x1d\x89\xb3Y\xc3\r\x9b\f\xd8\"TeNR\xb8\x86\x8d\x86\x1bQ\xa0\xba\x11\x0e\u007fs\xb2\x13\x85݊H:O\xf8\xb6\xa5\xeb\x0e\fԪ\xbb\x93M\x1a\xe4P\xd0\xf8\xfb\x12\xb3\x8eb\xd0\x1c\xb9\x93\x19\x8b?\xec\x8cm\fB0:\xeb\x16\xc0!\xa5\xa4\x86\xaf\x99\xaar\xcck\xb3t\xf4\xfb\x11*\xb7\xbd\xe1l\xb0\x85\xd4$?d\"I\xf7t\xf3+[$a\xf1\b(\x00\xf1P\xea\x00\x8dm\xcd\x01\a\xd0f\xd5\xf3X\xf4\xb0\x1a!x\x84])%\xb6\n\xaf\xc1\xdb\xeax\xe90OX+\xde\x06)\x91\x0e\xace\x84\xa8GG\rR2cK[\xeb\t\xd3\xe2\x0fD\x86\x831O\xd3[\xff7\x1a\xd1\xe89d|\xce\xc3\x16\x0f\xe2Y\x1a\x1b7\x1b\x8d\xed\x16\x01_1\xab<\x9fw\xdd&<\xe4r\xb7CKPʃp\xe8\x82q\x1f#\xc1\x98\x10S\xb3cl\xeb\xe1߰LX\f\xfb\x1dC\x19^\x0e\xa8\x19\x99>uC\xa3\x93X\xe7\xf2Y\xe6\x95P \xb5\xf3Bga\x1f\xa2\xc6\xe9x\x1f0\xce\xce\x1e\xb6A\xf9\x13\xceD\xfb\x8e!0\x1a\xc1X(\xc8\xe6\xf5\x87\xbaA\xf80\xbaݭp\x98\x83\tbh+\x85..\x94\xb3}i\xf4\xfar\x04pͅpB*\xb1E\x05\x0e\x15f\xde\xd8!2L35\xb4y\x1b5B\xbb\x01k\x15\x8df4\xa1mCeFa\x02\xbc\x1cdv\b\x87\x17\xc9\vC\x81ܠc\xfd\x15e\xa9ކ7\aӜ\x0emB\x85\x9b6\xa9\xccǰ\xfajݴY;״\x19\x8bץe\xcd\xfa?\x0f)\x93\xe1>Y07\xbd\x89\xef)\x98DDI\x0e\xe8f\aX\x94\xfe\xed\x12\xa4O\xbd\xe4\xe3\n\x8e\x8fF\xc9S\xaf\xfd\x87cĩ2\xbd9\x9e\xf7\x8e2\xfd+\xb9P/\xfd\x87a\x02\x1b\xfb\xfbh\xeb\x172\xe0s{\xce%\xc8]̀\xfc\x12vRy\xb4G\x9c\x98ڮ\x99\xe6į%\xc1\xfcIE\xad\x10>;ܾ\x92w䚴\xc6\"j\x1cO\r>e\U000aaec7\xe9$T\xe0\x90IZ,B \xf6\xc0\x14lz\xd8\xf3\xf9\xf8\xe5\x13\xe6\xe3D\x81%\x12\xd6\xdb\xc2\xc7#4\xdb\xcbF\x17y\xd9\x06\xa2\x93RG\x17!\xa8\xbe\x04\x01O\xf8\x16\xbc\v\n\xf1K\xb4\x82\x96\xa1\xc1\xb3\x10-rd\xcf\x02\xf5\x84o\f$\x06\xeb3s\x97\xb1>\xb4'|\x9b\x1ftD6\xc2F\xba\x98| \xfaQ\a\x13\x80#\xbd\xa5$\x03N\xb5$\v3\xb7)Xj\"RK\xd4>y{5\x9b\x9a\xec@`\xe4\x85\vL!i?\xc8r\xd1\x06\xc9t\x82C։\x94jy\x14J\xe6\xf52A\xbe7\xfa\x12\xbe\x18\xbf\xd1c\xcej\xb7ݾJ\x173\\\x9f\f\xba/\xc6sϻ\x131\xa0|2\t\xc34V!\x1d\xcc0\xed\xbf\x9d\xb1\x99\x15\xe2\xd06!ªY\"\x1dl4\xc5\x10\x81V!\xe7\x16\x16\x9b\xb2\xf6\xddVT\x8eS2\xda\xe8\x15\x1fv\xeb\xa1u\"\x89\x17\nr\x9b\v}\xb4\xea%\xc3r\x8b >й\x10f\x87\xfc\xa1\x12\x19\xe6\x90WLD\xce\u007f\t\x8f{\x99A\x81v?~\x10\xb4[I6{\xc9\xf2\x8blih'\xc9Ӓ\xa39\xb5h\x8c\xf394V\xa4\x9b\xb3c\x12kg\x06\x0e&\xbc\xc6\a\xce\xed\x83\x0fI\xf6\x1bf\xa8)\xf2\x9c\xef\x1b\x84\xba[l\xbd\x17S\xbe\u007fn\a\x94\xc2\x19W\x88\x92\xb4\xf3\xff\xe8\xa8b\xa1\xfd\u007f(\x85\xb4\xb3\x1a\xfa\x91/\x0e\x14vfƬP{\x11\x82/\x1d\x107\x9f\x85:N\x9b\x0elː\xd5@\x15\x8ea\xb3\xeby\x1a\x97\xf0r0.\x9c\x8a;\x89*\a9\xe5iQ;\u007f·\xf3˞\x8e\x9fo\xf4y8\x9e{\x1a\x9b\xce\xf2\x19\xc0F\xab78\xe7\x99\xe7_\xef\xba,\x92\xba\x05\x83\xf8\x16i\x993K\xd1\\:\xc5iZ}SA\xae\xe88\xb6\vd\xae4\xce/D\xe2\xce8\x1f2t\x1d\xe7q 74\x1d\xd3Ĝ\x10\x88]\xb8\x1d26\xdd\x03\x90!;JU\x12\x97\x1c\x0e&8{\x10\xf3\bR(\x05獎\x06\xfbx\x1e.\ax\t\x91\xb1[0\x01\x91D\xa1\xb4&C\xe7\xa6\xc4a\xd6\xf2\xce$\xdc\xead\x9b\bAEH\xb5O%\xf7R[\xea6\x12iNr\xb3o_[9@Rm\xfa\u007fZ\xccN\xc3\b\xf8*\xb6(\x84\x9e=,z\xc8݄yI\x15\"\x98\xe0\xb2\xdb}\xc5j\xbc\xd4ӋB\xf3m\x0f\xd8B\xea\r\x03\x87\x0f\xefz\x1cC2\x89x\xbaK}\x93f6d\xae;\x82n\x96\xa6\x9fr\x1fj/\a\xb4\xd8\xe1T?3\xcc\xee\x9c6\xbe\x15\x9e/#t\xc0\xe3\xc2\xc1NZ\xe7\xdbH:\xa8&\xb5\xb6\x05\xe4\xa4\x18E\xdfZ\xfb\x15!\xcaOa^+\x01t0/\xe9>-\x10dю\xf9\x1a\x04A\xee@z@\x9d\x99Js\x12\x83\x94\x94\x17\b$\r\xc6t\xf6\x90\rm\x89bSC]\x15K6\xbeb\xe9\x91z\"\xd7\xd1\x1e\xfc7!\xa72U\xa9\x9d\xc4&/\v4\xd5ġִ\x0e\x9b\x1e¼\xceEh!^eQ\x15 \n\"\xf6\"\x8a\xd2\xc9,\v\xec\xf2\x17^\x84\xf4l\xdd\t*\x9bzoH)J\x85~Y4\xb0ŝ\xb1\xac\x8bN\xe6X\x1f\x99\x91\xe7F\x83\x80\x9d\x90\xaa\xb2\x8b,\xda\t\x14]\xee\xd9G%\u007f\x1f\xa7}ɲ+\xde\xfel\x9ar\x91\xab6eUK\xbb\xd4Q\xbb\xb3\xf8\x9e.Ri%Ɍy_/)\x8a\x92\xd0o\xdfݤ\x16m\xbe\xbbI\xbd\xf6\xddM\xea\xb4\xefn\xd2w7i\xb2}w\x93\xbe\xbbI\u007fV7i\x1a\x93\x15\xe7\xad\x06\u007f\x9aY}\xf6\nu\x1c\xb1Q\xc8\xf1V\xff&T(/\xab\xcb\xdb\f\xcfi٫\x97\x03\xfa\x03\xdaT\xf8\xbc\xe2\xba\xec>\x9f\x9b\xab\xff\xc6\xccׅz$\xfcIxC\xf9\xe5d\xe9ނB\xbc\xad1\n\x85\x1e\xda\xff\xd2\xdá\xa2\x92nMb]ؑ\x8a\x12MZ\xa2\xb7\xfbT\xefMnf\xbb\x82A(ծM\x11\xb6!\xca7\xaaW\x9c-\xfd\x98)\xf8\x98.\xdb\x1c\xa7Бk\xdf%\x91\xed\x94\x18~c\nM\xd6e\x8cWcě\f\xf4\xe2\xf9ú\xfb\x8b7\xb16\x03^\xa4?\xf46\xc0E\x93\x14\xb2\xe8}\xbb82\xc9T,\xb2?\xa6\x1c\x18\vZ\xaa\xcb\xc1\xba\x98\xfa\xfdA\x9b\x9c\xf0S\x19\x82\xa2\x93\xf4mʵ_R\xbb\xf1\xd5\x15\x1bݚ\x8cA#{\xdae\xc7\xd2\x12\xd2\xe55\x19ݚ\x8b\x91CfA%\xc6ɕ\x16\xf3\xf1\xd6dU\xc5W\xd4R\xa4:\x89\xa9\x03w\xa2\x82b\x81\xcf1_-\xf1U5\x12|\x997\x81\xf5I\x95\x11\xad\xaa\x87\t\x90\xcb\xea!\x16\x90d\xae\xf6\xe1䊇\xe3*\x83\x89M\xcc\xd59\x8c\xd70L\x00\x1d\xacnXR\xb90\x01\xb3\xaeix\xc7z\x85\x99*\x85\xf7\xa9$\xfc\xb5\xbe\xe7X\xcd\xc1L\xa5\xc1\x8cg:\x85\xd5L-\xc1\xf2\n\x82\x19\xfa|e\xb5@]\x0f0\xb8\xe6\xa95\x02\xdd*\x80A\x90\v+\x03F\xee\xfe\aA.\xa8\a\x98\xb9\xf1\x1f\x04;y0NH\xc4\xe8ON\x8b\xd2\x1d\x8c\u007f\xe4'\x8d\xd3^\xe4}w\xec@pA>\x8exBȔ\xa9\xf2\x1av\u007f+\xfcP\xf1\r\xee\x1e\xd9\xc8\xf3S\x98\xacy\b\x14Myr~\x8e\xdf\t\xfd\xf0\x9e\xc1\x86\xf3Ɗ=~6Y\xeb=\xea\xd8\xfe\xbbc;\x8f\a#SSH\x9f\xea Dz\xc5֝:\xe4;\xc6,[\xf0\x0f[\xd1\x17a\xd8\xe7\xf7\xa8\xe6y\xaf&7\xf1\xf0\xf09 \xeee\x81\xebOU\b\xe4V\xa5\xb0\x0e\x89~iCaҖ\xfe<\x98\x97\x1e\xc2\xcaĝ\xfep\x8c\xafE\xce\xe1q\xb4\xb8\x18\xeb\xf0\xa26\tX\"Ӵ8>\x0e\xcfi\xf9\xa2-\xa6\x84\xc0\xc6\xec\xc6f\xf56\xd8z\xeeK\xde~\xa8hy\xaf'j\xc3\xc6y\xf8\x89\xa4\x17\xbers\x8f$yPz\xf2\x1c3\xbe\x95\xe5\x17f\x01@\x10Ɠ\xdfI\xc6\xf4V\xe7\xa1\xf9\x14On\xfa\xe3\xf9\xc1\xb1\xcd\x03R\x9cV\x13I\xc8_\x84\xab\x13h\x03\x16\xad\x01\x16\xe6\xb13@\xb00\a|F\rFs\xbe\x8c_p\x85\xe7\xee\xc7s\xfa\xf1k\vFL\xc7U\xa52\"O\x9a\x1bQK\x8f\xa8\x1f\xd8\x1e\xd9g\xb4\x17n\x14b\xe5brd`\xfbǒ\xb53\xb6\x10\xfe\x1ar\xe1q5\x00p\x81\x1d\x1b\x10)N\x1e\xcf<\xdd\xe4!A;8\xef\xcc\"\xa1TL<\x17\xe8\x9cا7\x9b/d\x8e\xf6\xa8\xe9\x90\x1b\xc8\x12EW\xacI\\v\xdf/\x86\x88Nd\x9e\xe2߀Z\fa[\xa3.\xfa:\xa7̞\"l\x1e\x18\xdfUG\xfbS\xc7G\xea\xa2\xfe\xfb\xb8G\xcd\a\x87n\xe7\x1d\xa5ǣ\xc1Gw%\xa4\xc1\r\xbc\xe4\xde\xfcE\xee\xfa!DY*\x99\x11\xb6\u007f\xfdFw \xcf\v\x1c\x87\x8bI\x9f\x81\x1d\x84\xfa\xf8\x87OXZ\xccH+\xfb\xc8\xdf)\xa4#\xdd!v\x9d\x91\x8bž[7\nt\x1f\xbdǢ\x1cXk\"\fl&\x8d\x19>\x91\x06\xf46\x90>\xeb\x94@\xc5\xcb\xfeѸo\xf1Fjo┍ԓ\xc66⪌\fЮ\x1a:\x8a\xea\xb0\xea\x1dw\xf5\",\xc5\xd2\xd3\xda\xf3\x9fq\xd0@\xa0\x11\xe7\xbfo\xa8ъ4\x12~\xbfS\xac1`Ǐ\xba\x9a϶}h\xfec\xf2\xad\xe2gڞC\xc9\x18[˼\xa5\xda\x11\x95\xd8\xd3\xe4\x00D\x96!\xc9\xee\x97\xe3/\xb6\x9d\x9f\xf3?\xe9\xa3l\xfcoft8K\xdd5\xfc\xd7\u007f\x9fAL%=&<\xa8\xf3\xef\x01\x00\x00\xff\xff\xf2Hm[\xe2N\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xa2u\xd2\xe8k\x10\xa5\xc4W\x8f\x9a\xfes\xeb\xa7\u007fuki\xae\x9e?lы\x0fgOR\xe7\xd7pS9o\x8a\x9fљ\xcaf\xf8\twRK/\x8d>+Ћ\\xq}\x06 \xb46^P\xb7\xa3\u007f\x012\xa3\xbd5J\xa1]\xedQ\xaf\x9f\xaa-n+\xa9r\xb4\xbcBZ\xff\xf9\x1f\xd7\xff\xb4\xfe\x973\x80\xcc\"O\u007f\x90\x05:/\x8a\xf2\x1at\xa5\xd4\x19\x80\x16\x05^\xc3VdOU\xe9\xd6ϨК\xb54g\xaeČ\xd6\xda[S\x95\xd7\xd0\xfc\x10\xa6D<\xc2\x1e~\xe0\xd9ܡ\xa4\xf3?\xb6:?K\xe7\xf9\x87RUV\xa8z%\xeesR\xef+%l\xea=\x03p\x99)\xf1\x1a\xbe\xd0\x12\xa5\xc80?\x03x\x16J\xe6\x8c~XԔ\xa8?\xdem\x1e\xff\xf9>;`!B'@\x8e.\xb3\xb2\xe4qqu\x90\x0e\x04<2\xee`#\x8d\xc1\x1f\x84\xa7\xffJ\x8b\x0e\xb5w\xe0\x0f\b\x99(}e\x11\xcc\x0e~\xac\xb6h5zt\x112@\xa6*\xe7т\xf3\xc2#\b\x0f\x02J#\xb5\a\xa9\xc1\xcb\x02\xe1/\x1f\xef6`\xb6\xbf`\xe6\x1d\b\x9d\x83p\xcedRx\xcc\xe1٨\xaa\xc00\xf7\xaf\xeb\b\xb3\xb4\xa6D\xebe\xa2$\xb5\x96\xe8\xd4}G\xfb\xba\xa0\x8d\x871\x90\x93\xb0`@?\xb2\x1cspL\x14ڇ?H\a\x16\xe36\x99\x80-\xb0@C\x84\x8eH\xaf\xe1\x1e-\x01\x01w0\x95\xcaI\u009e\xd1\x12\x9d2\xb3\xd7\xf2\u007fk\xc8\x0e\xbc\xe1%\x95\xf0\x18\x99\x9b\x9a\xd4\x1e\xad\x16\x8aXV\xe1%\x13\xa2\x10o`\x91րJ\xb7\xa0\xf1\x10\xb7\x86\u007f7\x16AꝹ\x86\x83\xf7\xa5\xbb\xbe\xba\xdaK\x9f\x94%3EQi\xe9߮X\xe4\xe5\xb6\xf2ƺ\xab\x1c\x9fQ]9\xb9_\t\x9b\x1d\xa4nj\x98w%J\xb9b\xc45\xebʺ\xc8\xff!q\xdd]\xb40\xf5o$d\xce[\xa9\xf7u7\xcb\xf2(\xddI\xa8\x838\x85i\x01\xff\x86\xbc\xd4ET\xf9\xf9\xf6\xfe\xa1-j\xd2ui\xce\xd4n\xa6\xb9\x86\xf0D(\xa9wh\x03\xe3v\xd6\x14\f\x11u\x1ed\x8d\xc5TI\xd4]\xa2\xbbj[HO\x9c\xfe\x9f\n\x1d\x89\xb3Y\xc3\r\x9b\f\xd8\"TeNR\xb8\x86\x8d\x86\x1bQ\xa0\xba\x11\x0e\u007fs\xb2\x13\x85݊H:O\xf8\xb6\xa5\xeb\x0e\fԪ\xbb\x93M\x1a\xe4P\xd0\xf8\xfb\x12\xb3\x8eb\xd0\x1c\xb9\x93\x19\x8b?\xec\x8cm\fB0:\xeb\x16\xc0!\xa5\xa4\x86\xaf\x99\xaar\xcck\xb3t\xf4\xfb\x11*\xb7\xbd\xe1l\xb0\x85\xd4$?d\"I\xf7t\xf3+[$a\xf1\b(\x00\xf1P\xea\x00\x8dm\xcd\x01\a\xd0f\xd5\xf3X\xf4\xb0\x1a!x\x84])%\xb6\n\xaf\xc1\xdb\xeax\xe90OX+\xde\x06)\x91\x0e\xace\x84\xa8GG\rR2cK[\xeb\t\xd3\xe2\x0fD\x86\x831O\xd3[\xff7\x1a\xd1\xe89d|\xce\xc3\x16\x0f\xe2Y\x1a\x1b7\x1b\x8d\xed\x16\x01_1\xab<\x9fw\xdd&<\xe4r\xb7CKPʃp\xe8\x82q\x1f#\xc1\x98\x10S\xb3cl\xeb\xe1߰LX\f\xfb\x1dC\x19^\x0e\xa8\x19\x99>uC\xa3\x93X\xe7\xf2Y\xe6\x95P \xb5\xf3Bga\x1f\xa2\xc6\xe9x\x1f0\xce\xce\x1e\xb6A\xf9\x13\xceD\xfb\x8e!0\x1a\xc1X(\xc8\xe6\xf5\x87\xbaA\xf80\xbaݭp\x98\x83\tbh+\x85..\x94\xb3}i\xf4\xfar\x04pͅpB*\xb1E\x05\x0e\x15f\xde\xd8!2L35\xb4y\x1b5B\xbb\x01k\x15\x8df4\xa1mCeFa\x02\xbc\x1cdv\b\x87\x17\xc9\vC\x81ܠc\xfd\x15e\xa9ކ7\aӜ\x0emB\x85\x9b6\xa9\xccǰ\xfajݴY;״\x19\x8bץe\xcd\xfa?\x0f)\x93\xe1>Y07\xbd\x89\xef)\x98DDI\x0e\xe8f\aX\x94\xfe\xed\x12\xa4O\xbd\xe4\xe3\n\x8e\x8fF\xc9S\xaf\xfd\x87cĩ2\xbd9\x9e\xf7\x8e2\xfd+\xb9P/\xfd\x87a\x02\x1b\xfb\xfbh\xeb\x172\xe0s{\xce%\xc8]̀\xfc\x12vRy\xb4G\x9c\x98ڮ\x99\xe6į%\xc1\xfcIE\xad\x10>;ܾ\x92w䚴\xc6\"j\x1cO\r>e\U000aaec7\xe9$T\xe0\x90IZ,B \xf6\xc0\x14lz\xd8\xf3\xf9\xf8\xe5\x13\xe6\xe3D\x81%\x12\xd6\xdb\xc2\xc7#4\xdb\xcbF\x17y\xd9\x06\xa2\x93RG\x17!\xa8\xbe\x04\x01O\xf8\x16\xbc\v\n\xf1K\xb4\x82\x96\xa1\xc1\xb3\x10-rd\xcf\x02\xf5\x84o\f$\x06\xeb3s\x97\xb1>\xb4'|\x9b\x1ftD6\xc2F\xba\x98| \xfaQ\a\x13\x80#\xbd\xa5$\x03N\xb5$\v3\xb7)Xj\"RK\xd4>y{5\x9b\x9a\xec@`\xe4\x85\vL!i?\xc8r\xd1\x06\xc9t\x82C։\x94jy\x14J\xe6\xf52A\xbe7\xfa\x12\xbe\x18\xbf\xd1c\xcej\xb7ݾJ\x173\\\x9f\f\xba/\xc6sϻ\x131\xa0|2\t\xc34V!\x1d\xcc0\xed\xbf\x9d\xb1\x99\x15\xe2\xd06!ªY\"\x1dl4\xc5\x10\x81V!\xe7\x16\x16\x9b\xb2\xf6\xddVT\x8eS2\xda\xe8\x15\x1fv\xeb\xa1u\"\x89\x17\nr\x9b\v}\xb4\xea%\xc3r\x8b >й\x10f\x87\xfc\xa1\x12\x19\xe6\x90WLD\xce\u007f\t\x8f{\x99A\x81v?~\x10\xb4[I6{\xc9\xf2\x8blih'\xc9Ӓ\xa39\xb5h\x8c\xf394V\xa4\x9b\xb3c\x12kg\x06\x0e&\xbc\xc6\a\xce\xed\x83\x0fI\xf6\x1bf\xa8)\xf2\x9c\xef\x1b\x84\xba[l\xbd\x17S\xbe\u007fn\a\x94\xc2\x19W\x88\x92\xb4\xf3\xff\xe8\xa8b\xa1\xfd\u007f(\x85\xb4\xb3\x1a\xfa\x91/\x0e\x14vfƬP{\x11\x82/\x1d\x107\x9f\x85:N\x9b\x0elː\xd5@\x15\x8ea\xb3\xeby\x1a\x97\xf0r0.\x9c\x8a;\x89*\a9\xe5iQ;\u007f·\xf3˞\x8e\x9fo\xf4y8\x9e{\x1a\x9b\xce\xf2\x19\xc0F\xab78\xe7\x99\xe7_\xef\xba,\x92\xba\x05\x83\xf8\x16i\x993K\xd1\\:\xc5iZ}SA\xae\xe88\xb6\vd\xae4\xce/D\xe2\xce8\x1f2t\x1d\xe7q 74\x1d\xd3Ĝ\x10\x88]\xb8\x1d26\xdd\x03\x90!;JU\x12\x97\x1c\x0e&8{\x10\xf3\bR(\x05獎\x06\xfbx\x1e.\ax\t\x91\xb1[0\x01\x91D\xa1\xb4&C\xe7\xa6\xc4a\xd6\xf2\xce$\xdc\xead\x9b\bAEH\xb5O%\xf7R[\xea6\x12iNr\xb3o_[9@Rm\xfa\u007fZ\xccN\xc3\b\xf8*\xb6(\x84\x9e=,z\xc8݄yI\x15\"\x98\xe0\xb2\xdb}\xc5j\xbc\xd4ӋB\xf3m\x0f\xd8B\xea\r\x03\x87\x0f\xefz\x1cC2\x89x\xbaK}\x93f6d\xae;\x82n\x96\xa6\x9fr\x1fj/\a\xb4\xd8\xe1T?3\xcc\xee\x9c6\xbe\x15\x9e/#t\xc0\xe3\xc2\xc1NZ\xe7\xdbH:\xa8&\xb5\xb6\x05\xe4\xa4\x18E\xdfZ\xfb\x15!\xcaOa^+\x01t0/\xe9>-\x10dю\xf9\x1a\x04A\xee@z@\x9d\x99Js\x12\x83\x94\x94\x17\b$\r\xc6t\xf6\x90\rm\x89bSC]\x15K6\xbeb\xe9\x91z\"\xd7\xd1\x1e\xfc7!\xa72U\xa9\x9d\xc4&/\v4\xd5ġִ\x0e\x9b\x1e¼\xceEh!^eQ\x15 \n\"\xf6\"\x8a\xd2\xc9,\v\xec\xf2\x17^\x84\xf4l\xdd\t*\x9bzoH)J\x85~Y4\xb0ŝ\xb1\xac\x8bN\xe6X\x1f\x99\x91\xe7F\x83\x80\x9d\x90\xaa\xb2\x8b,\xda\t\x14]\xee\xd9G%\u007f\x1f\xa7}ɲ+\xde\xfel\x9ar\x91\xab6eUK\xbb\xd4Q\xbb\xb3\xf8\x9e.Ri%Ɍy_/)\x8a\x92\xd0o\xdfݤ\x16m\xbe\xbbI\xbd\xf6\xddM\xea\xb4\xefn\xd2w7i\xb2}w\x93\xbe\xbbI\u007fV7i\x1a\x93\x15\xe7\xad\x06\u007f\x9aY}\xf6\nu\x1c\xb1Q\xc8\xf1V\xff&T(/\xab\xcb\xdb\f\xcfi٫\x97\x03\xfa\x03\xdaT\xf8\xbc\xe2\xba\xec>\x9f\x9b\xab\xff\xc6\xccׅz$\xfcIxC\xf9\xe5d\xe9ނB\xbc\xad1\n\x85\x1e\xda\xff\xd2\xdá\xa2\x92nMb]ؑ\x8a\x12MZ\xa2\xb7\xfbT\xefMnf\xbb\x82A(ծM\x11\xb6!\xca7\xaaW\x9c-\xfd\x98)\xf8\x98.\xdb\x1c\xa7Бk\xdf%\x91\xed\x94\x18~c\nM\xd6e\x8cWcě\f\xf4\xe2\xf9ú\xfb\x8b7\xb16\x03^\xa4?\xf46\xc0E\x93\x14\xb2\xe8}\xbb82\xc9T,\xb2?\xa6\x1c\x18\vZ\xaa\xcb\xc1\xba\x98\xfa\xfdA\x9b\x9c\xf0S\x19\x82\xa2\x93\xf4mʵ_R\xbb\xf1\xd5\x15\x1bݚ\x8cA#{\xdae\xc7\xd2\x12\xd2\xe55\x19ݚ\x8b\x91CfA%\xc6ɕ\x16\xf3\xf1\xd6dU\xc5W\xd4R\xa4:\x89\xa9\x03w\xa2\x82b\x81\xcf1_-\xf1U5\x12|\x997\x81\xf5I\x95\x11\xad\xaa\x87\t\x90\xcb\xea!\x16\x90d\xae\xf6\xe1䊇\xe3*\x83\x89M\xcc\xd59\x8c\xd70L\x00\x1d\xacnXR\xb90\x01\xb3\xaeix\xc7z\x85\x99*\x85\xf7\xa9$\xfc\xb5\xbe\xe7X\xcd\xc1L\xa5\xc1\x8cg:\x85\xd5L-\xc1\xf2\n\x82\x19\xfa|e\xb5@]\x0f0\xb8\xe6\xa95\x02\xdd*\x80A\x90\v+\x03F\xee\xfe\aA.\xa8\a\x98\xb9\xf1\x1f\x04;y0NH\xc4\xe8ON\x8b\xd2\x1d\x8c\u007f\xe4'\x8d\xd3^\xe4}w\xec@pA>\x8exBȔ\xa9\xf2\x1av\u007f+\xfcP\xf1\r\xee\x1e\xd9\xc8\xf3S\x98\xacy\b\x14Myr~\x8e\xdf\t\xfd\xf0\x9e\xc1\x86\xf3Ɗ=~6Y\xeb=\xea\xd8\xfe\xbbc;\x8f\a#SSH\x9f\xea Dz\xc5֝:\xe4;\xc6,[\xf0\x0f[\xd1\x17a\xd8\xe7\xf7\xa8\xe6y\xaf&7\xf1\xf0\xf09 \xeee\x81\xebOU\b\xe4V\xa5\xb0\x0e\x89~iCaҖ\xfe<\x98\x97\x1e\xc2\xcaĝ\xfep\x8c\xafE\xce\xe1q\xb4\xb8\x18\xeb\xf0\xa26\tX\"Ӵ8>\x0e\xcfi\xf9\xa2-\xa6\x84\xc0\xc6\xec\xc6f\xf56\xd8z\xeeK\xde~\xa8hy\xaf'j\xc3\xc6y\xf8\x89\xa4\x17\xbers\x8f$yPz\xf2\x1c3\xbe\x95\xe5\x17f\x01@\x10Ɠ\xdfI\xc6\xf4V\xe7\xa1\xf9\x14On\xfa\xe3\xf9\xc1\xb1\xcd\x03R\x9cV\x13I\xc8_\x84\xab\x13h\x03\x16\xad\x01\x16\xe6\xb13@\xb00\a|F\rFs\xbe\x8c_p\x85\xe7\xee\xc7s\xfa\xf1k\vFL\xc7U\xa52\"O\x9a\x1bQK\x8f\xa8\x1f\xd8\x1e\xd9g\xb4\x17n\x14b\xe5brd`\xfbǒ\xb53\xb6\x10\xfe\x1ar\xe1q5\x00p\x81\x1d\x1b\x10)N\x1e\xcf<\xdd\xe4!A;8\xef\xcc\"\xa1TL<\x17\xe8\x9cا7\x9b/d\x8e\xf6\xa8\xe9\x90\x1b\xc8\x12EW\xacI\\v\xdf/\x86\x88Nd\x9e\xe2߀Z\fa[\xa3.\xfa:\xa7̞\"l\x1e\x18\xdfUG\xfb\xa8\x8c\x8d\xafg\xf4\xa11_\xde\xe4\xe7ƚ\x06\xad\x97-J\x1aG\xfe\xdf\xcd\r\xb0\\\x13\xd8$\x03\x82<\x1e\x13\x8c\xec\xb7(\xc0%@\xa6\x02\xbf\x95\x0e,6\x16\x1dj\u007f\xb0Q;L\x05L\x83Y\xff\x8a\xdc\x17\xb0BK\x87\x80ۚ\xa0\x04\x85\xc9\x0e\xad\a\x8b\xdcl\xb4\xfc\xbd;ف7\xf1J\xc5ls\a\xe2\x89(\xa9+\xb4\xc9p\x955u<\x11\xb5h\x8c\xd4>\xbep%Q\xf7Iwa]KO\x96\xfe-\xa0\xf3d\x9f\x02\x161\xef\xc1\x1a!4\x82y\x14\x05\xdchX\xb0\x1aՂ9\xfc\xe6\xb4\x13\xc3nJ\x94^&\xfe8]\xf7\x05\x13[\xddt\x9bIG-4\x1a\xa5\xab\x06y/N\x04:iɗ=\xf3\x18#\x00\x1ec\xa6\xebQ\xfar~{9xc\x00s\x8e\xce}4\x02\xfb\xf3\x03\xa8\xf3N\xac\x87\xadA[K\x17\v\xde \x9f@N(\x83C\x01\xda\fS\fVP\x87z\ba\n\xf7\xc8ĝV\xfbх\x1f\xad\xf4\xc3\vF\xcdE#\xc1Z\xed5_\xa2\x95F\x9cU\xf7\xfd@\xb8Szk\x9e\xa1\x8an\xab\xbd\xdaS^q{\xcd\xf3\xe1'\xbaΗ7\xd9!rp\xe4X\xca\xdc\x140\xcf1i*x\x03B:\xaax.\x1e9\xa4\x87\n8\xad\x96\xe0mx\xb5\xd2\xdc\xe8Jn\x86\xaa\x1e\x97\xf5q\xaf8{耫E\xbc\x83\x12\ry@c\xcdN\n\xb4S\xf2|YI\x9e1\x04\x9bjP%Q\t7\xd4n4v\xa0K>٭Ϛ\xec\xeeX\xf2\xd0b$\x14\xd9]\x1dz\xcaz\x0e4\x92;3;\xf4+ \x8br\xa35Y\xc9\x1b`\x9d>\xd7nh\xbc\xc1֗\x02\x8c\xc6:\xf0'\xf4\xa7\xf3C\xaf\x8bbm\x99N\x9b\bEp\x18\xb9=\x0f\xe0\x82\xcd\x008[\xa0\xbd\x8cb1'\xb1\xce\xe3\x19,\xe6\xb0\x0eZ(l\xb1P\aũ\xb3)\t#53\xee\xc4m\xb4y\xa6\xcdG\xa7\xc5\x03\xc0\xe8ԻSw@=jj\xb8\xe2ҳT\x8a\xda&\x8b\xb5\xd9\xc5/\x8a\xfe\xa0\x1eǢ\xda\xd37\x96\xa9`\xf7\xaexS\\\xfd\xc3\xd5W1穜\xa2\xb8ǝ\x1c~/\x9c\xb2y{\"\xdfzoW0\xe9\xe5sۈ\xcdl\x16\xfb|\xa2~%\x15\xb5\x8d#\xae\xde\xf5\"\xf9\xe3\xc0y\xf0\xb2\xc6\xf8\xf6~u{\xed\xe2\x977\xb5\xbc'\x87>\x93\xf9\\\x04H\x9f\x10&w\xba\xc1y\xb4#\xc6\xeel%\x1dh\x03\xca\xe8M/\x14\xd2\xc8}/\x18\v\xc9u\x8c\x05\x81ԲR\x9a\xe5[\xa67x\xf8\x96\xc9؏P\x92c\x9c\"\xed{\xc7\xc1\x1b\xa4\x1ew\x85W\xd8\xf0A\xd6\xe7\xa3\xe1\xb6'ښ\xae\xcfp\x87:\xdbR\x9d\xc6\xe4+\xb8\x1eH\xb7E\x84\x88\x9c\xd2U\u007fK\x1b\x15\xffk\x9cϱ$\xd1\xeaɃ\xb5\xa8\xfd!\xf7D\x87\x1a\xcb?\xafk\x82罟!\xc7+\xc3\x1f%\x17u\x19ɹ\x83\xa9\xc3路\x87\xb7\xfc\xc7'\xfd\x87\x89\v\x00)\xc1\x1e\x11\x99\xa3*\xcf\x1c\x129e\xd0ƣ\xf84\xfc'su\xd5\xfb\xb1\x12_\xb9ѩ9u%\xfc\xf4\xcb$\x9d\x8a\xe2\xb1\xc5A\x93\u007f\x05\x00\x00\xff\xff\xf8SƷz\x13\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96\xcfn\xe46\x0f\xc0\xef~\nb\xbf\xc3^>{\x9a\x16\x05\n\xdf\xda\xec\x1e\x82\xb6A\x90,r)z\xd0H\x9c1\x1b[RIj\xd2\xf4\xe9\v\xc9\xf6\xccx\xe2 \xdbC}\x13E\x91ԏ\u007f\xac\xaa\xae\xeb\xcaDzD\x16\n\xbe\x05\x13\t\xffR\xf4y%\xcd\xd3\x0f\xd2P\xd8\x1c\xae\xb6\xa8\xe6\xaaz\"\xefZ\xb8N\xa2a\xb8G\t\x89-~\xc2\x1dyR\n\xbe\x1aP\x8d3j\xda\n\xc0x\x1f\xd4d\xb1\xe4%\x80\r^9\xf4=r\xbdG\xdf<\xa5-n\x13\xf5\x0e\xb9x\x98\xfd\x1f\xbei\xbem\xbe\xaf\x00,c9\xfe\x85\x06\x145Cl\xc1\xa7\xbe\xaf\x00\xbc\x19\xb0\x05\x87=*n\x8d}J\x91\xf1τ\xa2\xd2\x1c\xb0G\x0e\r\x85J\"\xda\xecx\xcf!\xc5\x16N\x1b\xe3\xf9)\xa8\xf1B\x9f\x8a\xa9\x9f\x8a\xa9\xfb\xd1T\xd9\xedI\xf4\xe7\xb74~\xa1I+\xf6\x89M\xbf\x1ePQ\x10\xf2\xfb\xd4\x1b^U\xa9\x00Ć\x88-\xdc氢\xb1\xe8*\x80\x83\xe9ɕ\xfb\x8f\x81\x86\x88\xfeǻ\x9b\xc7\xef\x1el\x87\x83\x19\x85\x00\x0e\xc52Ţ\xb7\x16$\x90\x80\x81\xc9\x15h\x98\"\x80\xe0\x11\x02\xc3\x10\x18a\fG\x9a\xc9d\xe4\x10\x91\x95fD\xf9;+\x90\xa3\xec\xc2\xf9\xc7\x1cݨ\x03.\x97\x04\nh\x870%\x16\x1dH\x89\x1c\xc2\x0e\xb4#\x01\xc6\xc8(\xe8\xc7\"93\vY\xc5x\b\xdb?\xd0j\x03\x0f\xc8\xd9\bH\x17R\xefr\x1d\x1d\x90\x15\x18m\xd8{\xfa\xfbhY\xf2\xfd\xb2\xcb\xde\xe8\x9c\xc1\xf9#\xaf\xc8\xde\xf4\x99k\xc2\xff\x83\xf1\x0e\x06\xf3\x02\x8c\xd9\a$\u007ff\xad\xa8H\x03\xbff8\xe4w\xa1\x85N5J\xbb\xd9\xecI疰a\x18\x92'}ٔ¦m\xd2\xc0\xb2qx\xc0~#\xb4\xaf\rێ\x14\xad&ƍ\x89T\x97\xc0}\xe9\x88fp\xff\xe3\xa9\u007f\xe4\xe3Y\xa4\xfa\x92+A\x94\xc9\xef\x8f\xe2R\xa4or\xcf\x05:\xa6y<6\xc6\u007f\u009bE\x99\xca\xfd\xe7\x87/0;-)X2/\xb4O\xc7\xe4\x04>\x83\"\xbfC\x1e\x13\xb7\xe30\x14\x8b\xe8]\f\xe4\xb5,lO\xe8\x97\xd0%m\aR\x99\xcb/私\xeb2\x18`\x8b\x90\xa23\x8a\xae\x81\x1b\x0f\xd7f\xc0\xfe\xda\b\xfe\xe7\xd83a\xa93\xd2\xf7\xc1\x9fϳ\xa5\xe2H\xeb(\x9e\x87\xcdj\x86V\xda\xf2!\xa2\xcd9\xcb\xe0\xf2Yڑ-m\x00\xbb\xc0\xf0ܑ\xed\xe6\xb6\\\x10=6ps&^k\xd8\xfc\x8d\x06\xf2TY\xca߸,\x94<\x11\xe3\xa2\xd6\xea33\xefRP\xa3I\xfe\x15\x87rb&a\x133z\x9d\xec\x94)\xb0v\xe8k\xee\x8é\xe5\xf2ދp>\x17\x95\xf2[2\xe4\x05\x8c\u007f\x99\x8e\x81vF\xe1\x199\x97\xb8\r)\xcf\x0et\xe0\xd2\x05\xaf\tE\x87cRr\xfa\"\a\x8b\"ͅ\x16)\x0e\xaf\xa2y3\x0f\xf9˿:\xb3\xed\xb1\x05儫\xf93\xcc\xe6e\xb1\x13;#\xaf\x92\xbd\xb8\xf4]\xd6X\xe3\x8d\xe3\\\xc6\xf7\x80\x17\xb8>\r\x97^j\xb8\xc5\xe7W\xb2\x1b\u007f\xc7a\xcf(\xf2j\xebn$U~v_\xc1d\xa5\xe0.D\xa7\x17\xc4\xd5iU\xa0\xd7Ӌ\xa1l\x00H\x1ek\xee\f\xach`\xb3\x9fQ\x9f\xaa\xd8X\x8bQ\xd1\xdd^\xbe\x17>|X\xfc\xf8\xcb\xd2\x06\xefh|\xee\xc0o\xbfW\xa3Ut\x8fs\x1cY\xf8O\x00\x00\x00\xff\xff\xbe\x16\xd7\"m\t\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WO\x8fܶ\x0f\xbd\xfbS\x10\xf9\x1d\xf2+\x10{\x9a\x16\x05\x8a\xb9\xb5\x9b\x1c\x82\xa6A0\xbb\xddKуF\xe6\xd8\xeaʒ*R\xb3\xd9~\xfa\x82\x92=\u007f<\x9e\xdd\xf4й\x99\xa2\xa8\xa7\xc7GRS\xd5u]\xa9`\xee1\x92\xf1n\r*\x18\xfc\xc2\xe8䋚\x87\x1f\xa91~\xb5\u007f\xbbEVo\xab\a\xe3\xda5\xdc$b?l\x90|\x8a\x1a\xdf\xe1\xce8\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95\x98I>\x01\xb4w\x1c\xbd\xb5\x18\xeb\x0e]\U000d0db8Mƶ\x18\xf3\t\xd3\xf9\xfbo\x9b\xef\x9a\x1f*\x00\x1d1o\xbf3\x03\x12\xab!\xac\xc1%k+\x00\xa7\x06\\C\xeb\x1f\x9d\xf5\xaa\x8d\xf8WBbj\xf6h1\xfa\xc6\xf8\x8a\x02j9\xb4\x8b>\x855\x1c\x17\xca\xde\x11P\xb9̻1̦\x84\xc9+\xd6\x10\xff\xb2\xb4\xfaь\x1e\xc1\xa6\xa8\xec%\x88\xbcH\xc6uɪx\xb1\\\x01\x90\xf6\x01\xd7\xf0I`\x04\xa5\xb1\xad\x00\xf6ʚ6ߵ\x00\xf3\x01\xddO\x9f?\xdc\u007f\u007f\xab{\x1cT1\x02\xb4H:\x9a\x90\xfd\xe6\xc0\xc0\x10(\x18\x8f\x01\xf6\x87\x93A9P\x91\xcdNi\x86]\xf4\x03l\x95~Ha\x8c\t\xe0\xb7\u007f\xa2f \xf6Qu\xf8\x06(\xe9\x1e\x94D+\x8e`}\a;c\xb1\x19\xb7\x84\xe8\x03F6\x13\x8d\xf2;\x11\xd0\xc16\x03\xfcZnT|\xa0\x15\xc9 \x01\xf7\bc\xe2\xb1\x05ʷ\x05\xbf\x03\xee\rA\xc4\x10\x91\xd0\x15\x11\x9d\x84\x05qQnD\xde\xc0-F\t\x02\xd4\xfbd[\xd1\xd9\x1e#CD\xed;g\xfe>D&\xe1E\x8e\xb4\x8a\xa7LO?\xe3\x18\xa3SVr\x91\xf0\r(\xd7\u00a0\x9e bf'\xb9\x93hم\x1a\xf8\xd5G\x04\xe3v~\r=s\xa0\xf5j\xd5\x19\x9eJF\xfbaH\xce\xf0\xd3*\v\xdfl\x13\xfbH\xab\x16\xf7hWd\xbaZE\xdd\x1bF\xcd)\xe2J\x05Sg\xe0.WL3\xb4\xff\x8bc}\xd1\xeb\x13\xa4\xfc$\xea!\x8e\xc6u\as\x16\xf2U\xdeE\xc8E\x1ee[\xc1\u007f\xa4WL\xc2\xca\xe6\xfd\xed\x1dL\x87\xe6\x14\x9cs^tr\xd8FG\xe2\x85(\xe3v\x18K\xe2\xb2\xca$\"\xba6x\xe38\u007fhkН\x93Ni;\x18\xa6I\xb6\x92\x9f\x06nr\xe3\x80-B\n\xadbl\x1b\xf8\xe0\xe0F\rho\x14\xe1\u007fN\xbb0L\xb5P\xfa2\xf1\xa7\xfd\xeeܱ\xb0u0O\ri1C\xb3R\xbe\r\xa8%_B\x9a\xec3;\xa3s\t\xc0\xceGP\xc7\xca\x1eikN\xe2.\xd5f\x06\xa5b\x87|n\x9b\xa1\xb8\xcb.r\xf0c\xaf\xce[\xc8\xff\xb1\xe9\x1a\xe9\x034B(\x9d\xe1\x9bf\x16\xef\xda\xe9K\x1a]\xc40IU\xae. Date: Wed, 3 Jun 2020 10:27:04 -0700 Subject: [PATCH 14/34] Remove unneeded auto-generated files Signed-off-by: Carlisia --- config/certmanager/certificate.yaml | 26 ------- config/certmanager/kustomization.yaml | 5 -- config/certmanager/kustomizeconfig.yaml | 16 ----- config/crd/kustomization.yaml | 21 ------ config/crd/kustomizeconfig.yaml | 17 ----- ...cainjection_in_backupstoragelocations.yaml | 8 --- .../webhook_in_backupstoragelocations.yaml | 17 ----- config/default/kustomization.yaml | 70 ------------------- config/default/manager_auth_proxy_patch.yaml | 25 ------- config/default/manager_webhook_patch.yaml | 23 ------ config/default/webhookcainjection_patch.yaml | 15 ---- config/manager/kustomization.yaml | 2 - config/manager/manager.yaml | 39 ----------- config/prometheus/kustomization.yaml | 2 - config/prometheus/monitor.yaml | 16 ----- .../rbac/auth_proxy_client_clusterrole.yaml | 7 -- config/rbac/auth_proxy_role.yaml | 13 ---- config/rbac/auth_proxy_role_binding.yaml | 12 ---- config/rbac/auth_proxy_service.yaml | 14 ---- .../backupstoragelocation_editor_role.yaml | 24 ------- .../backupstoragelocation_viewer_role.yaml | 20 ------ config/rbac/kustomization.yaml | 12 ---- config/rbac/leader_election_role.yaml | 32 --------- config/rbac/leader_election_role_binding.yaml | 12 ---- config/rbac/role_binding.yaml | 12 ---- config/webhook/kustomization.yaml | 6 -- config/webhook/kustomizeconfig.yaml | 25 ------- config/webhook/service.yaml | 12 ---- main.go | 18 ----- 29 files changed, 521 deletions(-) delete mode 100644 config/certmanager/certificate.yaml delete mode 100644 config/certmanager/kustomization.yaml delete mode 100644 config/certmanager/kustomizeconfig.yaml delete mode 100644 config/crd/kustomization.yaml delete mode 100644 config/crd/kustomizeconfig.yaml delete mode 100644 config/crd/patches/cainjection_in_backupstoragelocations.yaml delete mode 100644 config/crd/patches/webhook_in_backupstoragelocations.yaml delete mode 100644 config/default/kustomization.yaml delete mode 100644 config/default/manager_auth_proxy_patch.yaml delete mode 100644 config/default/manager_webhook_patch.yaml delete mode 100644 config/default/webhookcainjection_patch.yaml delete mode 100644 config/manager/kustomization.yaml delete mode 100644 config/manager/manager.yaml delete mode 100644 config/prometheus/kustomization.yaml delete mode 100644 config/prometheus/monitor.yaml delete mode 100644 config/rbac/auth_proxy_client_clusterrole.yaml delete mode 100644 config/rbac/auth_proxy_role.yaml delete mode 100644 config/rbac/auth_proxy_role_binding.yaml delete mode 100644 config/rbac/auth_proxy_service.yaml delete mode 100644 config/rbac/backupstoragelocation_editor_role.yaml delete mode 100644 config/rbac/backupstoragelocation_viewer_role.yaml delete mode 100644 config/rbac/kustomization.yaml delete mode 100644 config/rbac/leader_election_role.yaml delete mode 100644 config/rbac/leader_election_role_binding.yaml delete mode 100644 config/rbac/role_binding.yaml delete mode 100644 config/webhook/kustomization.yaml delete mode 100644 config/webhook/kustomizeconfig.yaml delete mode 100644 config/webhook/service.yaml delete mode 100644 main.go diff --git a/config/certmanager/certificate.yaml b/config/certmanager/certificate.yaml deleted file mode 100644 index 58db114fa0..0000000000 --- a/config/certmanager/certificate.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# The following manifests contain a self-signed issuer CR and a certificate CR. -# More document can be found at https://docs.cert-manager.io -# WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for -# breaking changes -apiVersion: cert-manager.io/v1alpha2 -kind: Issuer -metadata: - name: selfsigned-issuer - namespace: system -spec: - selfSigned: {} ---- -apiVersion: cert-manager.io/v1alpha2 -kind: Certificate -metadata: - name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml - namespace: system -spec: - # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize - dnsNames: - - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc - - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local - issuerRef: - kind: Issuer - name: selfsigned-issuer - secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize diff --git a/config/certmanager/kustomization.yaml b/config/certmanager/kustomization.yaml deleted file mode 100644 index bebea5a595..0000000000 --- a/config/certmanager/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -resources: -- certificate.yaml - -configurations: -- kustomizeconfig.yaml diff --git a/config/certmanager/kustomizeconfig.yaml b/config/certmanager/kustomizeconfig.yaml deleted file mode 100644 index 90d7c313ca..0000000000 --- a/config/certmanager/kustomizeconfig.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# This configuration is for teaching kustomize how to update name ref and var substitution -nameReference: -- kind: Issuer - group: cert-manager.io - fieldSpecs: - - kind: Certificate - group: cert-manager.io - path: spec/issuerRef/name - -varReference: -- kind: Certificate - group: cert-manager.io - path: spec/commonName -- kind: Certificate - group: cert-manager.io - path: spec/dnsNames diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml deleted file mode 100644 index 8f3656ec54..0000000000 --- a/config/crd/kustomization.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# This kustomization.yaml is not intended to be run by itself, -# since it depends on service name and namespace that are out of this kustomize package. -# It should be run by config/default -resources: -- bases/velero.io_backupstoragelocations.yaml -# +kubebuilder:scaffold:crdkustomizeresource - -patchesStrategicMerge: -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. -# patches here are for enabling the conversion webhook for each CRD -#- patches/webhook_in_backupstoragelocations.yaml -# +kubebuilder:scaffold:crdkustomizewebhookpatch - -# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. -# patches here are for enabling the CA injection for each CRD -#- patches/cainjection_in_backupstoragelocations.yaml -# +kubebuilder:scaffold:crdkustomizecainjectionpatch - -# the following config is for teaching kustomize how to do kustomization for CRDs. -configurations: -- kustomizeconfig.yaml diff --git a/config/crd/kustomizeconfig.yaml b/config/crd/kustomizeconfig.yaml deleted file mode 100644 index 6f83d9a94b..0000000000 --- a/config/crd/kustomizeconfig.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# This file is for teaching kustomize how to substitute name and namespace reference in CRD -nameReference: -- kind: Service - version: v1 - fieldSpecs: - - kind: CustomResourceDefinition - group: apiextensions.k8s.io - path: spec/conversion/webhookClientConfig/service/name - -namespace: -- kind: CustomResourceDefinition - group: apiextensions.k8s.io - path: spec/conversion/webhookClientConfig/service/namespace - create: false - -varReference: -- path: metadata/annotations diff --git a/config/crd/patches/cainjection_in_backupstoragelocations.yaml b/config/crd/patches/cainjection_in_backupstoragelocations.yaml deleted file mode 100644 index 2b989b19dd..0000000000 --- a/config/crd/patches/cainjection_in_backupstoragelocations.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: backupstoragelocations.velero.io diff --git a/config/crd/patches/webhook_in_backupstoragelocations.yaml b/config/crd/patches/webhook_in_backupstoragelocations.yaml deleted file mode 100644 index 8851a00174..0000000000 --- a/config/crd/patches/webhook_in_backupstoragelocations.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# The following patch enables conversion webhook for CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: backupstoragelocations.velero.io -spec: - conversion: - strategy: Webhook - webhookClientConfig: - # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, - # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) - caBundle: Cg== - service: - namespace: system - name: webhook-service - path: /convert diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml deleted file mode 100644 index b9e0a79206..0000000000 --- a/config/default/kustomization.yaml +++ /dev/null @@ -1,70 +0,0 @@ -# Adds namespace to all resources. -namespace: velero-system - -# Value of this field is prepended to the -# names of all resources, e.g. a deployment named -# "wordpress" becomes "alices-wordpress". -# Note that it should also match with the prefix (text before '-') of the namespace -# field above. -namePrefix: velero- - -# Labels to add to all resources and selectors. -#commonLabels: -# someName: someValue - -bases: -- ../crd -- ../rbac -- ../manager -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml -#- ../webhook -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. -#- ../certmanager -# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. -#- ../prometheus - -patchesStrategicMerge: - # Protect the /metrics endpoint by putting it behind auth. - # If you want your controller-manager to expose the /metrics - # endpoint w/o any authn/z, please comment the following line. -- manager_auth_proxy_patch.yaml - -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml -#- manager_webhook_patch.yaml - -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. -# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. -# 'CERTMANAGER' needs to be enabled to use ca injection -#- webhookcainjection_patch.yaml - -# the following config is for teaching kustomize how to do var substitution -vars: -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. -#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR -# objref: -# kind: Certificate -# group: cert-manager.io -# version: v1alpha2 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldref: -# fieldpath: metadata.namespace -#- name: CERTIFICATE_NAME -# objref: -# kind: Certificate -# group: cert-manager.io -# version: v1alpha2 -# name: serving-cert # this name should match the one in certificate.yaml -#- name: SERVICE_NAMESPACE # namespace of the service -# objref: -# kind: Service -# version: v1 -# name: webhook-service -# fieldref: -# fieldpath: metadata.namespace -#- name: SERVICE_NAME -# objref: -# kind: Service -# version: v1 -# name: webhook-service diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml deleted file mode 100644 index 77e743d1c1..0000000000 --- a/config/default/manager_auth_proxy_patch.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# This patch inject a sidecar container which is a HTTP proxy for the -# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - containers: - - name: kube-rbac-proxy - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0 - args: - - "--secure-listen-address=0.0.0.0:8443" - - "--upstream=http://127.0.0.1:8080/" - - "--logtostderr=true" - - "--v=10" - ports: - - containerPort: 8443 - name: https - - name: manager - args: - - "--metrics-addr=127.0.0.1:8080" - - "--enable-leader-election" diff --git a/config/default/manager_webhook_patch.yaml b/config/default/manager_webhook_patch.yaml deleted file mode 100644 index 738de350b7..0000000000 --- a/config/default/manager_webhook_patch.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - containers: - - name: manager - ports: - - containerPort: 9443 - name: webhook-server - protocol: TCP - volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - readOnly: true - volumes: - - name: cert - secret: - defaultMode: 420 - secretName: webhook-server-cert diff --git a/config/default/webhookcainjection_patch.yaml b/config/default/webhookcainjection_patch.yaml deleted file mode 100644 index 7e79bf9955..0000000000 --- a/config/default/webhookcainjection_patch.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# This patch add annotation to admission webhook config and -# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: MutatingWebhookConfiguration -metadata: - name: mutating-webhook-configuration - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) ---- -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: ValidatingWebhookConfiguration -metadata: - name: validating-webhook-configuration - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml deleted file mode 100644 index 5c5f0b84cb..0000000000 --- a/config/manager/kustomization.yaml +++ /dev/null @@ -1,2 +0,0 @@ -resources: -- manager.yaml diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml deleted file mode 100644 index b6c85a52d5..0000000000 --- a/config/manager/manager.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - name: system ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system - labels: - control-plane: controller-manager -spec: - selector: - matchLabels: - control-plane: controller-manager - replicas: 1 - template: - metadata: - labels: - control-plane: controller-manager - spec: - containers: - - command: - - /manager - args: - - --enable-leader-election - image: controller:latest - name: manager - resources: - limits: - cpu: 100m - memory: 30Mi - requests: - cpu: 100m - memory: 20Mi - terminationGracePeriodSeconds: 10 diff --git a/config/prometheus/kustomization.yaml b/config/prometheus/kustomization.yaml deleted file mode 100644 index ed137168a1..0000000000 --- a/config/prometheus/kustomization.yaml +++ /dev/null @@ -1,2 +0,0 @@ -resources: -- monitor.yaml diff --git a/config/prometheus/monitor.yaml b/config/prometheus/monitor.yaml deleted file mode 100644 index 9b8047b760..0000000000 --- a/config/prometheus/monitor.yaml +++ /dev/null @@ -1,16 +0,0 @@ - -# Prometheus Monitor Service (Metrics) -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - labels: - control-plane: controller-manager - name: controller-manager-metrics-monitor - namespace: system -spec: - endpoints: - - path: /metrics - port: https - selector: - matchLabels: - control-plane: controller-manager diff --git a/config/rbac/auth_proxy_client_clusterrole.yaml b/config/rbac/auth_proxy_client_clusterrole.yaml deleted file mode 100644 index 7d62534c5f..0000000000 --- a/config/rbac/auth_proxy_client_clusterrole.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRole -metadata: - name: metrics-reader -rules: -- nonResourceURLs: ["/metrics"] - verbs: ["get"] diff --git a/config/rbac/auth_proxy_role.yaml b/config/rbac/auth_proxy_role.yaml deleted file mode 100644 index 618f5e4177..0000000000 --- a/config/rbac/auth_proxy_role.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: proxy-role -rules: -- apiGroups: ["authentication.k8s.io"] - resources: - - tokenreviews - verbs: ["create"] -- apiGroups: ["authorization.k8s.io"] - resources: - - subjectaccessreviews - verbs: ["create"] diff --git a/config/rbac/auth_proxy_role_binding.yaml b/config/rbac/auth_proxy_role_binding.yaml deleted file mode 100644 index 48ed1e4b85..0000000000 --- a/config/rbac/auth_proxy_role_binding.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: proxy-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: proxy-role -subjects: -- kind: ServiceAccount - name: default - namespace: system diff --git a/config/rbac/auth_proxy_service.yaml b/config/rbac/auth_proxy_service.yaml deleted file mode 100644 index 6cf656be14..0000000000 --- a/config/rbac/auth_proxy_service.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - control-plane: controller-manager - name: controller-manager-metrics-service - namespace: system -spec: - ports: - - name: https - port: 8443 - targetPort: https - selector: - control-plane: controller-manager diff --git a/config/rbac/backupstoragelocation_editor_role.yaml b/config/rbac/backupstoragelocation_editor_role.yaml deleted file mode 100644 index 56ee2eab4e..0000000000 --- a/config/rbac/backupstoragelocation_editor_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# permissions for end users to edit backupstoragelocations. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: backupstoragelocation-editor-role -rules: -- apiGroups: - - velero.io - resources: - - backupstoragelocations - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - velero.io - resources: - - backupstoragelocations/status - verbs: - - get diff --git a/config/rbac/backupstoragelocation_viewer_role.yaml b/config/rbac/backupstoragelocation_viewer_role.yaml deleted file mode 100644 index c82adae7ba..0000000000 --- a/config/rbac/backupstoragelocation_viewer_role.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# permissions for end users to view backupstoragelocations. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: backupstoragelocation-viewer-role -rules: -- apiGroups: - - velero.io - resources: - - backupstoragelocations - verbs: - - get - - list - - watch -- apiGroups: - - velero.io - resources: - - backupstoragelocations/status - verbs: - - get diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml deleted file mode 100644 index 66c28338fe..0000000000 --- a/config/rbac/kustomization.yaml +++ /dev/null @@ -1,12 +0,0 @@ -resources: -- role.yaml -- role_binding.yaml -- leader_election_role.yaml -- leader_election_role_binding.yaml -# Comment the following 4 lines if you want to disable -# the auth proxy (https://github.com/brancz/kube-rbac-proxy) -# which protects your /metrics endpoint. -- auth_proxy_service.yaml -- auth_proxy_role.yaml -- auth_proxy_role_binding.yaml -- auth_proxy_client_clusterrole.yaml diff --git a/config/rbac/leader_election_role.yaml b/config/rbac/leader_election_role.yaml deleted file mode 100644 index eaa79158fb..0000000000 --- a/config/rbac/leader_election_role.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# permissions to do leader election. -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: leader-election-role -rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - configmaps/status - verbs: - - get - - update - - patch -- apiGroups: - - "" - resources: - - events - verbs: - - create diff --git a/config/rbac/leader_election_role_binding.yaml b/config/rbac/leader_election_role_binding.yaml deleted file mode 100644 index eed16906f4..0000000000 --- a/config/rbac/leader_election_role_binding.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: leader-election-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: leader-election-role -subjects: -- kind: ServiceAccount - name: default - namespace: system diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml deleted file mode 100644 index 8f2658702c..0000000000 --- a/config/rbac/role_binding.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: manager-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: system diff --git a/config/webhook/kustomization.yaml b/config/webhook/kustomization.yaml deleted file mode 100644 index 9cf26134e4..0000000000 --- a/config/webhook/kustomization.yaml +++ /dev/null @@ -1,6 +0,0 @@ -resources: -- manifests.yaml -- service.yaml - -configurations: -- kustomizeconfig.yaml diff --git a/config/webhook/kustomizeconfig.yaml b/config/webhook/kustomizeconfig.yaml deleted file mode 100644 index 25e21e3c96..0000000000 --- a/config/webhook/kustomizeconfig.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# the following config is for teaching kustomize where to look at when substituting vars. -# It requires kustomize v2.1.0 or newer to work properly. -nameReference: -- kind: Service - version: v1 - fieldSpecs: - - kind: MutatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/name - - kind: ValidatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/name - -namespace: -- kind: MutatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/namespace - create: true -- kind: ValidatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/namespace - create: true - -varReference: -- path: metadata/annotations diff --git a/config/webhook/service.yaml b/config/webhook/service.yaml deleted file mode 100644 index 31e0f82959..0000000000 --- a/config/webhook/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ - -apiVersion: v1 -kind: Service -metadata: - name: webhook-service - namespace: system -spec: - ports: - - port: 443 - targetPort: 9443 - selector: - control-plane: controller-manager diff --git a/main.go b/main.go deleted file mode 100644 index 3723ff221e..0000000000 --- a/main.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -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 main - -func main() {} From 18ddea288ec126fd178bd1a8bb28b60f03b28418 Mon Sep 17 00:00:00 2001 From: Carlisia Date: Wed, 3 Jun 2020 15:18:59 -0700 Subject: [PATCH 15/34] Keep everything in the same (existing) package Signed-off-by: Carlisia --- Makefile | 21 ++- api/v1/zz_generated.deepcopy.go | 172 ------------------ hack/update-generated-crd-code.sh | 7 +- .../velero}/v1/backupstoragelocation_types.go | 0 .../apis/velero}/v1/groupversion_info.go | 0 pkg/apis/velero/v1/register.go | 24 +-- pkg/apis/velero/v1/zz_generated.deepcopy.go | 146 +++++++++++++++ 7 files changed, 162 insertions(+), 208 deletions(-) delete mode 100644 api/v1/zz_generated.deepcopy.go rename {api => pkg/apis/velero}/v1/backupstoragelocation_types.go (100%) rename {api => pkg/apis/velero}/v1/groupversion_info.go (100%) diff --git a/Makefile b/Makefile index a384e14dd5..4ffae270ba 100644 --- a/Makefile +++ b/Makefile @@ -44,11 +44,6 @@ MANIFEST_PLATFORMS ?= amd64 ppc64le arm arm64 GIT_SHA = $(shell git rev-parse HEAD) GIT_DIRTY = $(shell git status --porcelain 2> /dev/null) -.PHONY: generate-all -generate-all: ## Generate code and yaml manifests - $(MAKE) generate - $(MAKE) manifests - ### ### These variables should not need tweaking. ### @@ -90,7 +85,7 @@ IMAGE ?= $(REGISTRY)/$(BIN)-$(GOARCH) # If you want to build all binaries, see the 'all-build' rule. # If you want to build all containers, see the 'all-containers' rule. # If you want to build AND push all containers, see the 'all-push' rule. -all-targets: all +all: @$(MAKE) build @$(MAKE) build BIN=velero-restic-restore-helper @@ -219,12 +214,18 @@ ifneq ($(SKIP_TESTS), 1) hack/test.sh $(WHAT) endif -verify: +verify: verify-gen ifneq ($(SKIP_TESTS), 1) @$(MAKE) shell CMD="-c 'hack/verify-all.sh'" endif -update: generate-all +verify-gen: generate + @if !(git diff --quiet HEAD); then \ + git diff; \ + echo "files were out of date, `make generate` was run"; exit 1; \ + fi + +update: manifests generate @$(MAKE) shell CMD="-c 'hack/update-all.sh'" build-dirs: @@ -323,11 +324,11 @@ endif # Generate manifests e.g. CRD, RBAC etc. manifests: controller-gen - $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases + $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./pkg/apis/velero/v1" output:crd:artifacts:config=config/crd/bases # Generate code generate: controller-gen - $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./pkg/apis/velero/v1" # find or download controller-gen # download controller-gen if necessary diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go deleted file mode 100644 index 14c8056b1c..0000000000 --- a/api/v1/zz_generated.deepcopy.go +++ /dev/null @@ -1,172 +0,0 @@ -// +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 controller-gen. DO NOT EDIT. - -package v1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackupStorageLocation) DeepCopyInto(out *BackupStorageLocation) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocation. -func (in *BackupStorageLocation) DeepCopy() *BackupStorageLocation { - if in == nil { - return nil - } - out := new(BackupStorageLocation) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *BackupStorageLocation) 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 *BackupStorageLocationList) DeepCopyInto(out *BackupStorageLocationList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]BackupStorageLocation, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocationList. -func (in *BackupStorageLocationList) DeepCopy() *BackupStorageLocationList { - if in == nil { - return nil - } - out := new(BackupStorageLocationList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *BackupStorageLocationList) 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 *BackupStorageLocationSpec) DeepCopyInto(out *BackupStorageLocationSpec) { - *out = *in - if in.Config != nil { - in, out := &in.Config, &out.Config - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - in.StorageType.DeepCopyInto(&out.StorageType) - if in.BackupSyncPeriod != nil { - in, out := &in.BackupSyncPeriod, &out.BackupSyncPeriod - *out = new(metav1.Duration) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocationSpec. -func (in *BackupStorageLocationSpec) DeepCopy() *BackupStorageLocationSpec { - if in == nil { - return nil - } - out := new(BackupStorageLocationSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackupStorageLocationStatus) DeepCopyInto(out *BackupStorageLocationStatus) { - *out = *in - if in.LastSyncedTime != nil { - in, out := &in.LastSyncedTime, &out.LastSyncedTime - *out = (*in).DeepCopy() - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocationStatus. -func (in *BackupStorageLocationStatus) DeepCopy() *BackupStorageLocationStatus { - if in == nil { - return nil - } - out := new(BackupStorageLocationStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ObjectStorageLocation) DeepCopyInto(out *ObjectStorageLocation) { - *out = *in - if in.CACert != nil { - in, out := &in.CACert, &out.CACert - *out = make([]byte, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectStorageLocation. -func (in *ObjectStorageLocation) DeepCopy() *ObjectStorageLocation { - if in == nil { - return nil - } - out := new(ObjectStorageLocation) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *StorageType) DeepCopyInto(out *StorageType) { - *out = *in - if in.ObjectStorage != nil { - in, out := &in.ObjectStorage, &out.ObjectStorage - *out = new(ObjectStorageLocation) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageType. -func (in *StorageType) DeepCopy() *StorageType { - if in == nil { - return nil - } - out := new(StorageType) - in.DeepCopyInto(out) - return out -} diff --git a/hack/update-generated-crd-code.sh b/hack/update-generated-crd-code.sh index a3fda859a2..237763739e 100755 --- a/hack/update-generated-crd-code.sh +++ b/hack/update-generated-crd-code.sh @@ -14,11 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -set -o errexit -set -o nounset -set -o pipefail -set -o xtrace - # this script expects to be run from the root of the Velero repo. -go generate ./config/crd/crds \ No newline at end of file +go generate ./config/crd/crds diff --git a/api/v1/backupstoragelocation_types.go b/pkg/apis/velero/v1/backupstoragelocation_types.go similarity index 100% rename from api/v1/backupstoragelocation_types.go rename to pkg/apis/velero/v1/backupstoragelocation_types.go diff --git a/api/v1/groupversion_info.go b/pkg/apis/velero/v1/groupversion_info.go similarity index 100% rename from api/v1/groupversion_info.go rename to pkg/apis/velero/v1/groupversion_info.go diff --git a/pkg/apis/velero/v1/register.go b/pkg/apis/velero/v1/register.go index c9e60fb9b5..e0cda0ac16 100644 --- a/pkg/apis/velero/v1/register.go +++ b/pkg/apis/velero/v1/register.go @@ -20,27 +20,11 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - - velerov1api "github.com/vmware-tanzu/velero/api/v1" -) - -var ( - // SchemeBuilder collects the scheme builder functions for the Velero API - SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) - - // AddToScheme applies the SchemeBuilder functions to a specified scheme - AddToScheme = SchemeBuilder.AddToScheme ) -// GroupName is the group name for the Velero API -const GroupName = "velero.io" - -// SchemeGroupVersion is the GroupVersion for the Velero API -var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"} - // Resource gets a Velero GroupResource for a specified resource func Resource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() + return GroupVersion.WithResource(resource).GroupResource() } type typeInfo struct { @@ -69,7 +53,7 @@ func CustomResources() map[string]typeInfo { "PodVolumeBackup": newTypeInfo("podvolumebackups", &PodVolumeBackup{}, &PodVolumeBackupList{}), "PodVolumeRestore": newTypeInfo("podvolumerestores", &PodVolumeRestore{}, &PodVolumeRestoreList{}), "ResticRepository": newTypeInfo("resticrepositories", &ResticRepository{}, &ResticRepositoryList{}), - "BackupStorageLocation": newTypeInfo("backupstoragelocations", &velerov1api.BackupStorageLocation{}, &velerov1api.BackupStorageLocationList{}), + "BackupStorageLocation": newTypeInfo("backupstoragelocations", &BackupStorageLocation{}, &BackupStorageLocationList{}), "VolumeSnapshotLocation": newTypeInfo("volumesnapshotlocations", &VolumeSnapshotLocation{}, &VolumeSnapshotLocationList{}), "ServerStatusRequest": newTypeInfo("serverstatusrequests", &ServerStatusRequest{}, &ServerStatusRequestList{}), } @@ -77,9 +61,9 @@ func CustomResources() map[string]typeInfo { func addKnownTypes(scheme *runtime.Scheme) error { for _, typeInfo := range CustomResources() { - scheme.AddKnownTypes(SchemeGroupVersion, typeInfo.ItemType, typeInfo.ItemListType) + scheme.AddKnownTypes(GroupVersion, typeInfo.ItemType, typeInfo.ItemListType) } - metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + metav1.AddToGroupVersion(scheme, GroupVersion) return nil } diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index b5ea91f3ab..ca0c433daf 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -302,6 +302,112 @@ func (in *BackupStatus) DeepCopy() *BackupStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupStorageLocation) DeepCopyInto(out *BackupStorageLocation) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocation. +func (in *BackupStorageLocation) DeepCopy() *BackupStorageLocation { + if in == nil { + return nil + } + out := new(BackupStorageLocation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BackupStorageLocation) 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 *BackupStorageLocationList) DeepCopyInto(out *BackupStorageLocationList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]BackupStorageLocation, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocationList. +func (in *BackupStorageLocationList) DeepCopy() *BackupStorageLocationList { + if in == nil { + return nil + } + out := new(BackupStorageLocationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BackupStorageLocationList) 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 *BackupStorageLocationSpec) DeepCopyInto(out *BackupStorageLocationSpec) { + *out = *in + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.StorageType.DeepCopyInto(&out.StorageType) + if in.BackupSyncPeriod != nil { + in, out := &in.BackupSyncPeriod, &out.BackupSyncPeriod + *out = new(metav1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocationSpec. +func (in *BackupStorageLocationSpec) DeepCopy() *BackupStorageLocationSpec { + if in == nil { + return nil + } + out := new(BackupStorageLocationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupStorageLocationStatus) DeepCopyInto(out *BackupStorageLocationStatus) { + *out = *in + if in.LastSyncedTime != nil { + in, out := &in.LastSyncedTime, &out.LastSyncedTime + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocationStatus. +func (in *BackupStorageLocationStatus) DeepCopy() *BackupStorageLocationStatus { + if in == nil { + return nil + } + out := new(BackupStorageLocationStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DeleteBackupRequest) DeepCopyInto(out *DeleteBackupRequest) { *out = *in @@ -536,6 +642,26 @@ func (in *ExecHook) DeepCopy() *ExecHook { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectStorageLocation) DeepCopyInto(out *ObjectStorageLocation) { + *out = *in + if in.CACert != nil { + in, out := &in.CACert, &out.CACert + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectStorageLocation. +func (in *ObjectStorageLocation) DeepCopy() *ObjectStorageLocation { + if in == nil { + return nil + } + out := new(ObjectStorageLocation) + in.DeepCopyInto(out) + return out +} + // 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 @@ -1224,6 +1350,26 @@ func (in *ServerStatusRequestStatus) DeepCopy() *ServerStatusRequestStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StorageType) DeepCopyInto(out *StorageType) { + *out = *in + if in.ObjectStorage != nil { + in, out := &in.ObjectStorage, &out.ObjectStorage + *out = new(ObjectStorageLocation) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageType. +func (in *StorageType) DeepCopy() *StorageType { + if in == nil { + return nil + } + out := new(StorageType) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VolumeSnapshotLocation) DeepCopyInto(out *VolumeSnapshotLocation) { *out = *in From de261b415c2b6f1984f031a891d26e7a7bf8a65d Mon Sep 17 00:00:00 2001 From: Carlisia Date: Thu, 4 Jun 2020 07:32:33 -0700 Subject: [PATCH 16/34] Fix/clean scripts, generated code, and calls Deleting the entire `generated` directory and running `make update` works. Modifying an api and running `make verify` works as expected. Signed-off-by: Carlisia --- Makefile | 14 +- config/crd/bases/velero.io_backups.yaml | 4 + .../velero.io_backupstoragelocations.yaml | 18 +- config/crd/bases/velero.io_schedules.yaml | 4 + config/crd/crds/crds.go | 6 +- design/backup-resource-list.md | 4 +- go.mod | 2 +- go.sum | 4 + hack/update-generated-crd-code.sh | 28 +++ .../velero/v1/backupstoragelocation_types.go | 129 ++++++------ pkg/apis/velero/v1/groupversion_info.go | 8 +- pkg/apis/velero/v1/register.go | 6 +- pkg/apis/velero/v1/zz_generated.deepcopy.go | 6 + pkg/backup/request.go | 3 +- .../backup_storage_location_builder.go | 4 +- pkg/cmd/cli/backup/create.go | 3 +- pkg/cmd/cli/backuplocation/create.go | 2 +- pkg/cmd/cli/backuplocation/get.go | 8 +- pkg/cmd/cli/restic/server.go | 6 +- pkg/cmd/server/server.go | 8 +- .../output/backup_storage_location_printer.go | 2 +- pkg/cmd/util/output/output.go | 9 +- pkg/cmd/velero/velero.go | 2 +- pkg/controller/backup_controller.go | 7 +- pkg/controller/backup_controller_test.go | 15 +- pkg/controller/backup_deletion_controller.go | 7 +- .../backup_deletion_controller_test.go | 22 +- pkg/controller/backup_sync_controller.go | 11 +- pkg/controller/backup_sync_controller_test.go | 39 ++-- pkg/controller/download_request_controller.go | 6 +- .../download_request_controller_test.go | 16 +- pkg/controller/gc_controller.go | 5 +- pkg/controller/gc_controller_test.go | 8 +- .../pod_volume_backup_controller.go | 3 +- .../pod_volume_restore_controller.go | 5 +- .../restic_repository_controller.go | 4 +- pkg/controller/restore_controller.go | 7 +- pkg/controller/restore_controller_test.go | 14 +- pkg/controller/suite_test.go | 3 +- .../typed/velero/v1/backupstoragelocation.go | 191 ++++++++++++++++++ .../v1/fake/fake_backupstoragelocation.go | 140 +++++++++++++ .../velero/v1/fake/fake_velero_client.go | 4 + .../typed/velero/v1/generated_expansion.go | 2 + .../typed/velero/v1/velero_client.go | 5 + .../informers/externalversions/generic.go | 2 + .../velero/v1/backupstoragelocation.go | 89 ++++++++ .../externalversions/velero/v1/interface.go | 7 + .../velero/v1/backupstoragelocation.go | 94 +++++++++ .../listers/velero/v1/expansion_generated.go | 8 + pkg/install/resources.go | 24 +-- pkg/persistence/object_store.go | 3 +- pkg/persistence/object_store_test.go | 5 +- pkg/restic/common.go | 7 +- pkg/restic/common_test.go | 13 +- pkg/restic/config.go | 6 +- pkg/restic/config_test.go | 74 +++---- pkg/restic/repository_manager.go | 3 +- 57 files changed, 840 insertions(+), 289 deletions(-) create mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/backupstoragelocation.go create mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backupstoragelocation.go create mode 100644 pkg/generated/informers/externalversions/velero/v1/backupstoragelocation.go create mode 100644 pkg/generated/listers/velero/v1/backupstoragelocation.go diff --git a/Makefile b/Makefile index 4ffae270ba..ff31c8be7d 100644 --- a/Makefile +++ b/Makefile @@ -214,18 +214,12 @@ ifneq ($(SKIP_TESTS), 1) hack/test.sh $(WHAT) endif -verify: verify-gen +verify: ifneq ($(SKIP_TESTS), 1) @$(MAKE) shell CMD="-c 'hack/verify-all.sh'" endif -verify-gen: generate - @if !(git diff --quiet HEAD); then \ - git diff; \ - echo "files were out of date, `make generate` was run"; exit 1; \ - fi - -update: manifests generate +update: manifests @$(MAKE) shell CMD="-c 'hack/update-all.sh'" build-dirs: @@ -326,10 +320,6 @@ endif manifests: controller-gen $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./pkg/apis/velero/v1" output:crd:artifacts:config=config/crd/bases -# Generate code -generate: controller-gen - $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./pkg/apis/velero/v1" - # find or download controller-gen # download controller-gen if necessary controller-gen: diff --git a/config/crd/bases/velero.io_backups.yaml b/config/crd/bases/velero.io_backups.yaml index a5036ac924..9e55ad6872 100644 --- a/config/crd/bases/velero.io_backups.yaml +++ b/config/crd/bases/velero.io_backups.yaml @@ -35,6 +35,10 @@ spec: spec: description: BackupSpec defines the specification for a Velero backup. properties: + defaultVolumesToRestic: + description: DefaultVolumesToRestic specifies whether restic should + be used to take a backup of all pod volumes by default. + type: boolean excludedNamespaces: description: ExcludedNamespaces contains a list of namespaces that are not included in the backup. diff --git a/config/crd/bases/velero.io_backupstoragelocations.yaml b/config/crd/bases/velero.io_backupstoragelocations.yaml index e10f0ae01c..4ccf9890b3 100644 --- a/config/crd/bases/velero.io_backupstoragelocations.yaml +++ b/config/crd/bases/velero.io_backupstoragelocations.yaml @@ -8,11 +8,6 @@ metadata: creationTimestamp: null name: backupstoragelocations.velero.io spec: - additionalPrinterColumns: - - JSONPath: .status.phase - description: Backup Storage Location status such as Available/Unavailable - name: Phase - type: string group: velero.io names: kind: BackupStorageLocation @@ -20,12 +15,10 @@ spec: plural: backupstoragelocations singular: backupstoragelocation scope: Namespaced - subresources: - status: {} validation: openAPIV3Schema: - description: BackupStorageLocation is the Schema for the backupstoragelocations - API + description: BackupStorageLocation is a location where Velero stores backup + objects. properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -40,8 +33,8 @@ spec: metadata: type: object spec: - description: BackupStorageLocationSpec defines the desired state of a Velero - BackupStorageLocation + description: BackupStorageLocationSpec defines the specification for a Velero + BackupStorageLocation. properties: accessMode: description: AccessMode defines the permissions for the backup storage @@ -87,7 +80,8 @@ spec: - provider type: object status: - description: BackupStorageLocationStatus defines the observed state of BackupStorageLocation + description: BackupStorageLocationStatus describes the current status of + a Velero BackupStorageLocation. properties: accessMode: description: "AccessMode is an unused field. \n Deprecated: there is diff --git a/config/crd/bases/velero.io_schedules.yaml b/config/crd/bases/velero.io_schedules.yaml index ab594cae42..ad03a1d072 100644 --- a/config/crd/bases/velero.io_schedules.yaml +++ b/config/crd/bases/velero.io_schedules.yaml @@ -43,6 +43,10 @@ spec: description: Template is the definition of the Backup to be run on the provided schedule properties: + defaultVolumesToRestic: + description: DefaultVolumesToRestic specifies whether restic should + be used to take a backup of all pod volumes by default. + type: boolean excludedNamespaces: description: ExcludedNamespaces contains a list of namespaces that are not included in the backup. diff --git a/config/crd/crds/crds.go b/config/crd/crds/crds.go index 6e6f1152cd..4a7cccb6d2 100644 --- a/config/crd/crds/crds.go +++ b/config/crd/crds/crds.go @@ -29,15 +29,15 @@ import ( ) var rawCRDs = [][]byte{ - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xa2u\xd2\xe8k\x10\xa5\xc4W\x8f\x9a\xfes\xeb\xa7\u007fuki\xae\x9e?lы\x0fgOR\xe7\xd7pS9o\x8a\x9fљ\xcaf\xf8\twRK/\x8d>+Ћ\\xq}\x06 \xb46^P\xb7\xa3\u007f\x012\xa3\xbd5J\xa1]\xedQ\xaf\x9f\xaa-n+\xa9r\xb4\xbcBZ\xff\xf9\x1f\xd7\xff\xb4\xfe\x973\x80\xcc\"O\u007f\x90\x05:/\x8a\xf2\x1at\xa5\xd4\x19\x80\x16\x05^\xc3VdOU\xe9\xd6ϨК\xb54g\xaeČ\xd6\xda[S\x95\xd7\xd0\xfc\x10\xa6D<\xc2\x1e~\xe0\xd9ܡ\xa4\xf3?\xb6:?K\xe7\xf9\x87RUV\xa8z%\xeesR\xef+%l\xea=\x03p\x99)\xf1\x1a\xbe\xd0\x12\xa5\xc80?\x03x\x16J\xe6\x8c~XԔ\xa8?\xdem\x1e\xff\xf9>;`!B'@\x8e.\xb3\xb2\xe4qqu\x90\x0e\x04<2\xee`#\x8d\xc1\x1f\x84\xa7\xffJ\x8b\x0e\xb5w\xe0\x0f\b\x99(}e\x11\xcc\x0e~\xac\xb6h5zt\x112@\xa6*\xe7т\xf3\xc2#\b\x0f\x02J#\xb5\a\xa9\xc1\xcb\x02\xe1/\x1f\xef6`\xb6\xbf`\xe6\x1d\b\x9d\x83p\xcedRx\xcc\xe1٨\xaa\xc00\xf7\xaf\xeb\b\xb3\xb4\xa6D\xebe\xa2$\xb5\x96\xe8\xd4}G\xfb\xba\xa0\x8d\x871\x90\x93\xb0`@?\xb2\x1cspL\x14ڇ?H\a\x16\xe36\x99\x80-\xb0@C\x84\x8eH\xaf\xe1\x1e-\x01\x01w0\x95\xcaI\u009e\xd1\x12\x9d2\xb3\xd7\xf2\u007fk\xc8\x0e\xbc\xe1%\x95\xf0\x18\x99\x9b\x9a\xd4\x1e\xad\x16\x8aXV\xe1%\x13\xa2\x10o`\x91րJ\xb7\xa0\xf1\x10\xb7\x86\u007f7\x16AꝹ\x86\x83\xf7\xa5\xbb\xbe\xba\xdaK\x9f\x94%3EQi\xe9߮X\xe4\xe5\xb6\xf2ƺ\xab\x1c\x9fQ]9\xb9_\t\x9b\x1d\xa4nj\x98w%J\xb9b\xc45\xebʺ\xc8\xff!q\xdd]\xb40\xf5o$d\xce[\xa9\xf7u7\xcb\xf2(\xddI\xa8\x838\x85i\x01\xff\x86\xbc\xd4ET\xf9\xf9\xf6\xfe\xa1-j\xd2ui\xce\xd4n\xa6\xb9\x86\xf0D(\xa9wh\x03\xe3v\xd6\x14\f\x11u\x1ed\x8d\xc5TI\xd4]\xa2\xbbj[HO\x9c\xfe\x9f\n\x1d\x89\xb3Y\xc3\r\x9b\f\xd8\"TeNR\xb8\x86\x8d\x86\x1bQ\xa0\xba\x11\x0e\u007fs\xb2\x13\x85݊H:O\xf8\xb6\xa5\xeb\x0e\fԪ\xbb\x93M\x1a\xe4P\xd0\xf8\xfb\x12\xb3\x8eb\xd0\x1c\xb9\x93\x19\x8b?\xec\x8cm\fB0:\xeb\x16\xc0!\xa5\xa4\x86\xaf\x99\xaar\xcck\xb3t\xf4\xfb\x11*\xb7\xbd\xe1l\xb0\x85\xd4$?d\"I\xf7t\xf3+[$a\xf1\b(\x00\xf1P\xea\x00\x8dm\xcd\x01\a\xd0f\xd5\xf3X\xf4\xb0\x1a!x\x84])%\xb6\n\xaf\xc1\xdb\xeax\xe90OX+\xde\x06)\x91\x0e\xace\x84\xa8GG\rR2cK[\xeb\t\xd3\xe2\x0fD\x86\x831O\xd3[\xff7\x1a\xd1\xe89d|\xce\xc3\x16\x0f\xe2Y\x1a\x1b7\x1b\x8d\xed\x16\x01_1\xab<\x9fw\xdd&<\xe4r\xb7CKPʃp\xe8\x82q\x1f#\xc1\x98\x10S\xb3cl\xeb\xe1߰LX\f\xfb\x1dC\x19^\x0e\xa8\x19\x99>uC\xa3\x93X\xe7\xf2Y\xe6\x95P \xb5\xf3Bga\x1f\xa2\xc6\xe9x\x1f0\xce\xce\x1e\xb6A\xf9\x13\xceD\xfb\x8e!0\x1a\xc1X(\xc8\xe6\xf5\x87\xbaA\xf80\xbaݭp\x98\x83\tbh+\x85..\x94\xb3}i\xf4\xfar\x04pͅpB*\xb1E\x05\x0e\x15f\xde\xd8!2L35\xb4y\x1b5B\xbb\x01k\x15\x8df4\xa1mCeFa\x02\xbc\x1cdv\b\x87\x17\xc9\vC\x81ܠc\xfd\x15e\xa9ކ7\aӜ\x0emB\x85\x9b6\xa9\xccǰ\xfajݴY;״\x19\x8bץe\xcd\xfa?\x0f)\x93\xe1>Y07\xbd\x89\xef)\x98DDI\x0e\xe8f\aX\x94\xfe\xed\x12\xa4O\xbd\xe4\xe3\n\x8e\x8fF\xc9S\xaf\xfd\x87cĩ2\xbd9\x9e\xf7\x8e2\xfd+\xb9P/\xfd\x87a\x02\x1b\xfb\xfbh\xeb\x172\xe0s{\xce%\xc8]̀\xfc\x12vRy\xb4G\x9c\x98ڮ\x99\xe6į%\xc1\xfcIE\xad\x10>;ܾ\x92w䚴\xc6\"j\x1cO\r>e\U000aaec7\xe9$T\xe0\x90IZ,B \xf6\xc0\x14lz\xd8\xf3\xf9\xf8\xe5\x13\xe6\xe3D\x81%\x12\xd6\xdb\xc2\xc7#4\xdb\xcbF\x17y\xd9\x06\xa2\x93RG\x17!\xa8\xbe\x04\x01O\xf8\x16\xbc\v\n\xf1K\xb4\x82\x96\xa1\xc1\xb3\x10-rd\xcf\x02\xf5\x84o\f$\x06\xeb3s\x97\xb1>\xb4'|\x9b\x1ftD6\xc2F\xba\x98| \xfaQ\a\x13\x80#\xbd\xa5$\x03N\xb5$\v3\xb7)Xj\"RK\xd4>y{5\x9b\x9a\xec@`\xe4\x85\vL!i?\xc8r\xd1\x06\xc9t\x82C։\x94jy\x14J\xe6\xf52A\xbe7\xfa\x12\xbe\x18\xbf\xd1c\xcej\xb7ݾJ\x173\\\x9f\f\xba/\xc6sϻ\x131\xa0|2\t\xc34V!\x1d\xcc0\xed\xbf\x9d\xb1\x99\x15\xe2\xd06!ªY\"\x1dl4\xc5\x10\x81V!\xe7\x16\x16\x9b\xb2\xf6\xddVT\x8eS2\xda\xe8\x15\x1fv\xeb\xa1u\"\x89\x17\nr\x9b\v}\xb4\xea%\xc3r\x8b >й\x10f\x87\xfc\xa1\x12\x19\xe6\x90WLD\xce\u007f\t\x8f{\x99A\x81v?~\x10\xb4[I6{\xc9\xf2\x8blih'\xc9Ӓ\xa39\xb5h\x8c\xf394V\xa4\x9b\xb3c\x12kg\x06\x0e&\xbc\xc6\a\xce\xed\x83\x0fI\xf6\x1bf\xa8)\xf2\x9c\xef\x1b\x84\xba[l\xbd\x17S\xbe\u007fn\a\x94\xc2\x19W\x88\x92\xb4\xf3\xff\xe8\xa8b\xa1\xfd\u007f(\x85\xb4\xb3\x1a\xfa\x91/\x0e\x14vfƬP{\x11\x82/\x1d\x107\x9f\x85:N\x9b\x0elː\xd5@\x15\x8ea\xb3\xeby\x1a\x97\xf0r0.\x9c\x8a;\x89*\a9\xe5iQ;\u007f·\xf3˞\x8e\x9fo\xf4y8\x9e{\x1a\x9b\xce\xf2\x19\xc0F\xab78\xe7\x99\xe7_\xef\xba,\x92\xba\x05\x83\xf8\x16i\x993K\xd1\\:\xc5iZ}SA\xae\xe88\xb6\vd\xae4\xce/D\xe2\xce8\x1f2t\x1d\xe7q 74\x1d\xd3Ĝ\x10\x88]\xb8\x1d26\xdd\x03\x90!;JU\x12\x97\x1c\x0e&8{\x10\xf3\bR(\x05獎\x06\xfbx\x1e.\ax\t\x91\xb1[0\x01\x91D\xa1\xb4&C\xe7\xa6\xc4a\xd6\xf2\xce$\xdc\xead\x9b\bAEH\xb5O%\xf7R[\xea6\x12iNr\xb3o_[9@Rm\xfa\u007fZ\xccN\xc3\b\xf8*\xb6(\x84\x9e=,z\xc8݄yI\x15\"\x98\xe0\xb2\xdb}\xc5j\xbc\xd4ӋB\xf3m\x0f\xd8B\xea\r\x03\x87\x0f\xefz\x1cC2\x89x\xbaK}\x93f6d\xae;\x82n\x96\xa6\x9fr\x1fj/\a\xb4\xd8\xe1T?3\xcc\xee\x9c6\xbe\x15\x9e/#t\xc0\xe3\xc2\xc1NZ\xe7\xdbH:\xa8&\xb5\xb6\x05\xe4\xa4\x18E\xdfZ\xfb\x15!\xcaOa^+\x01t0/\xe9>-\x10dю\xf9\x1a\x04A\xee@z@\x9d\x99Js\x12\x83\x94\x94\x17\b$\r\xc6t\xf6\x90\rm\x89bSC]\x15K6\xbeb\xe9\x91z\"\xd7\xd1\x1e\xfc7!\xa72U\xa9\x9d\xc4&/\v4\xd5ġִ\x0e\x9b\x1e¼\xceEh!^eQ\x15 \n\"\xf6\"\x8a\xd2\xc9,\v\xec\xf2\x17^\x84\xf4l\xdd\t*\x9bzoH)J\x85~Y4\xb0ŝ\xb1\xac\x8bN\xe6X\x1f\x99\x91\xe7F\x83\x80\x9d\x90\xaa\xb2\x8b,\xda\t\x14]\xee\xd9G%\u007f\x1f\xa7}ɲ+\xde\xfel\x9ar\x91\xab6eUK\xbb\xd4Q\xbb\xb3\xf8\x9e.Ri%Ɍy_/)\x8a\x92\xd0o\xdfݤ\x16m\xbe\xbbI\xbd\xf6\xddM\xea\xb4\xefn\xd2w7i\xb2}w\x93\xbe\xbbI\u007fV7i\x1a\x93\x15\xe7\xad\x06\u007f\x9aY}\xf6\nu\x1c\xb1Q\xc8\xf1V\xff&T(/\xab\xcb\xdb\f\xcfi٫\x97\x03\xfa\x03\xdaT\xf8\xbc\xe2\xba\xec>\x9f\x9b\xab\xff\xc6\xccׅz$\xfcIxC\xf9\xe5d\xe9ނB\xbc\xad1\n\x85\x1e\xda\xff\xd2\xdá\xa2\x92nMb]ؑ\x8a\x12MZ\xa2\xb7\xfbT\xefMnf\xbb\x82A(ծM\x11\xb6!\xca7\xaaW\x9c-\xfd\x98)\xf8\x98.\xdb\x1c\xa7Бk\xdf%\x91\xed\x94\x18~c\nM\xd6e\x8cWcě\f\xf4\xe2\xf9ú\xfb\x8b7\xb16\x03^\xa4?\xf46\xc0E\x93\x14\xb2\xe8}\xbb82\xc9T,\xb2?\xa6\x1c\x18\vZ\xaa\xcb\xc1\xba\x98\xfa\xfdA\x9b\x9c\xf0S\x19\x82\xa2\x93\xf4mʵ_R\xbb\xf1\xd5\x15\x1bݚ\x8cA#{\xdae\xc7\xd2\x12\xd2\xe55\x19ݚ\x8b\x91CfA%\xc6ɕ\x16\xf3\xf1\xd6dU\xc5W\xd4R\xa4:\x89\xa9\x03w\xa2\x82b\x81\xcf1_-\xf1U5\x12|\x997\x81\xf5I\x95\x11\xad\xaa\x87\t\x90\xcb\xea!\x16\x90d\xae\xf6\xe1䊇\xe3*\x83\x89M\xcc\xd59\x8c\xd70L\x00\x1d\xacnXR\xb90\x01\xb3\xaeix\xc7z\x85\x99*\x85\xf7\xa9$\xfc\xb5\xbe\xe7X\xcd\xc1L\xa5\xc1\x8cg:\x85\xd5L-\xc1\xf2\n\x82\x19\xfa|e\xb5@]\x0f0\xb8\xe6\xa95\x02\xdd*\x80A\x90\v+\x03F\xee\xfe\aA.\xa8\a\x98\xb9\xf1\x1f\x04;y0NH\xc4\xe8ON\x8b\xd2\x1d\x8c\u007f\xe4'\x8d\xd3^\xe4}w\xec@pA>\x8exBȔ\xa9\xf2\x1av\u007f+\xfcP\xf1\r\xee\x1e\xd9\xc8\xf3S\x98\xacy\b\x14Myr~\x8e\xdf\t\xfd\xf0\x9e\xc1\x86\xf3Ɗ=~6Y\xeb=\xea\xd8\xfe\xbbc;\x8f\a#SSH\x9f\xea Dz\xc5֝:\xe4;\xc6,[\xf0\x0f[\xd1\x17a\xd8\xe7\xf7\xa8\xe6y\xaf&7\xf1\xf0\xf09 \xeee\x81\xebOU\b\xe4V\xa5\xb0\x0e\x89~iCaҖ\xfe<\x98\x97\x1e\xc2\xcaĝ\xfep\x8c\xafE\xce\xe1q\xb4\xb8\x18\xeb\xf0\xa26\tX\"Ӵ8>\x0e\xcfi\xf9\xa2-\xa6\x84\xc0\xc6\xec\xc6f\xf56\xd8z\xeeK\xde~\xa8hy\xaf'j\xc3\xc6y\xf8\x89\xa4\x17\xbers\x8f$yPz\xf2\x1c3\xbe\x95\xe5\x17f\x01@\x10Ɠ\xdfI\xc6\xf4V\xe7\xa1\xf9\x14On\xfa\xe3\xf9\xc1\xb1\xcd\x03R\x9cV\x13I\xc8_\x84\xab\x13h\x03\x16\xad\x01\x16\xe6\xb13@\xb00\a|F\rFs\xbe\x8c_p\x85\xe7\xee\xc7s\xfa\xf1k\vFL\xc7U\xa52\"O\x9a\x1bQK\x8f\xa8\x1f\xd8\x1e\xd9g\xb4\x17n\x14b\xe5brd`\xfbǒ\xb53\xb6\x10\xfe\x1ar\xe1q5\x00p\x81\x1d\x1b\x10)N\x1e\xcf<\xdd\xe4!A;8\xef\xcc\"\xa1TL<\x17\xe8\x9cا7\x9b/d\x8e\xf6\xa8\xe9\x90\x1b\xc8\x12EW\xacI\\v\xdf/\x86\x88Nd\x9e\xe2߀Z\fa[\xa3.\xfa:\xa7̞\"l\x1e\x18\xdfUG\xfb\xa8\x8c\x8d\xafg\xf4\xa11_\xde\xe4\xe7ƚ\x06\xad\x97-J\x1aG\xfe\xdf\xcd\r\xb0\\\x13\xd8$\x03\x82<\x1e\x13\x8c\xec\xb7(\xc0%@\xa6\x02\xbf\x95\x0e,6\x16\x1dj\u007f\xb0Q;L\x05L\x83Y\xff\x8a\xdc\x17\xb0BK\x87\x80ۚ\xa0\x04\x85\xc9\x0e\xad\a\x8b\xdcl\xb4\xfc\xbd;ف7\xf1J\xc5ls\a\xe2\x89(\xa9+\xb4\xc9p\x955u<\x11\xb5h\x8c\xd4>\xbep%Q\xf7Iwa]KO\x96\xfe-\xa0\xf3d\x9f\x02\x161\xef\xc1\x1a!4\x82y\x14\x05\xdchX\xb0\x1aՂ9\xfc\xe6\xb4\x13\xc3nJ\x94^&\xfe8]\xf7\x05\x13[\xddt\x9bIG-4\x1a\xa5\xab\x06y/N\x04:iɗ=\xf3\x18#\x00\x1ec\xa6\xebQ\xfar~{9xc\x00s\x8e\xce}4\x02\xfb\xf3\x03\xa8\xf3N\xac\x87\xadA[K\x17\v\xde \x9f@N(\x83C\x01\xda\fS\fVP\x87z\ba\n\xf7\xc8ĝV\xfbх\x1f\xad\xf4\xc3\vF\xcdE#\xc1Z\xed5_\xa2\x95F\x9cU\xf7\xfd@\xb8Szk\x9e\xa1\x8an\xab\xbd\xdaS^q{\xcd\xf3\xe1'\xbaΗ7\xd9!rp\xe4X\xca\xdc\x140\xcf1i*x\x03B:\xaax.\x1e9\xa4\x87\n8\xad\x96\xe0mx\xb5\xd2\xdc\xe8Jn\x86\xaa\x1e\x97\xf5q\xaf8{耫E\xbc\x83\x12\ry@c\xcdN\n\xb4S\xf2|YI\x9e1\x04\x9bjP%Q\t7\xd4n4v\xa0K>٭Ϛ\xec\xeeX\xf2\xd0b$\x14\xd9]\x1dz\xcaz\x0e4\x92;3;\xf4+ \x8br\xa35Y\xc9\x1b`\x9d>\xd7nh\xbc\xc1֗\x02\x8c\xc6:\xf0'\xf4\xa7\xf3C\xaf\x8bbm\x99N\x9b\bEp\x18\xb9=\x0f\xe0\x82\xcd\x008[\xa0\xbd\x8cb1'\xb1\xce\xe3\x19,\xe6\xb0\x0eZ(l\xb1P\aũ\xb3)\t#53\xee\xc4m\xb4y\xa6\xcdG\xa7\xc5\x03\xc0\xe8ԻSw@=jj\xb8\xe2ҳT\x8a\xda&\x8b\xb5\xd9\xc5/\x8a\xfe\xa0\x1eǢ\xda\xd37\x96\xa9`\xf7\xaexS\\\xfd\xc3\xd5W1穜\xa2\xb8ǝ\x1c~/\x9c\xb2y{\"\xdfzoW0\xe9\xe5sۈ\xcdl\x16\xfb|\xa2~%\x15\xb5\x8d#\xae\xde\xf5\"\xf9\xe3\xc0y\xf0\xb2\xc6\xf8\xf6~u{\xed\xe2\x977\xb5\xbc'\x87>\x93\xf9\\\x04H\x9f\x10&w\xba\xc1y\xb4#\xc6\xeel%\x1dh\x03\xca\xe8M/\x14\xd2\xc8}/\x18\v\xc9u\x8c\x05\x81ԲR\x9a\xe5[\xa67x\xf8\x96\xc9؏P\x92c\x9c\"\xed{\xc7\xc1\x1b\xa4\x1ew\x85W\xd8\xf0A\xd6\xe7\xa3\xe1\xb6'ښ\xae\xcfp\x87:\xdbR\x9d\xc6\xe4+\xb8\x1eH\xb7E\x84\x88\x9c\xd2U\u007fK\x1b\x15\xffk\x9cϱ$\xd1\xeaɃ\xb5\xa8\xfd!\xf7D\x87\x1a\xcb?\xafk\x82罟!\xc7+\xc3\x1f%\x17u\x19ɹ\x83\xa9\xc3路\x87\xb7\xfc\xc7'\xfd\x87\x89\v\x00)\xc1\x1e\x11\x99\xa3*\xcf\x1c\x129e\xd0ƣ\xf84\xfc'su\xd5\xfb\xb1\x12_\xb9ѩ9u%\xfc\xf4\xcb$\x9d\x8a\xe2\xb1\xc5A\x93\u007f\x05\x00\x00\xff\xff\xf8SƷz\x13\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xa2u\xd2\xe8k\x10\xa5\xc4W\x8f\x9a\xfes\xeb\xa7\u007fuki\xae\x9e?lы\x0fgOR\xe7\xd7pS9o\x8a\x9fљ\xcaf\xf8\twRK/\x8d>+Ћ\\xq}\x06 \xb46^P\xb7\xa3\u007f\x012\xa3\xbd5J\xa1]\xedQ\xaf\x9f\xaa-n+\xa9r\xb4\xbcBZ\xff\xf9\x1f\xd7\xff\xb4\xfe\x973\x80\xcc\"O\u007f\x90\x05:/\x8a\xf2\x1at\xa5\xd4\x19\x80\x16\x05^\xc3VdOU\xe9\xd6ϨК\xb54g\xaeČ\xd6\xda[S\x95\xd7\xd0\xfc\x10\xa6D<\xc2\x1e~\xe0\xd9ܡ\xa4\xf3?\xb6:?K\xe7\xf9\x87RUV\xa8z%\xeesR\xef+%l\xea=\x03p\x99)\xf1\x1a\xbe\xd0\x12\xa5\xc80?\x03x\x16J\xe6\x8c~XԔ\xa8?\xdem\x1e\xff\xf9>;`!B'@\x8e.\xb3\xb2\xe4qqu\x90\x0e\x04<2\xee`#\x8d\xc1\x1f\x84\xa7\xffJ\x8b\x0e\xb5w\xe0\x0f\b\x99(}e\x11\xcc\x0e~\xac\xb6h5zt\x112@\xa6*\xe7т\xf3\xc2#\b\x0f\x02J#\xb5\a\xa9\xc1\xcb\x02\xe1/\x1f\xef6`\xb6\xbf`\xe6\x1d\b\x9d\x83p\xcedRx\xcc\xe1٨\xaa\xc00\xf7\xaf\xeb\b\xb3\xb4\xa6D\xebe\xa2$\xb5\x96\xe8\xd4}G\xfb\xba\xa0\x8d\x871\x90\x93\xb0`@?\xb2\x1cspL\x14ڇ?H\a\x16\xe36\x99\x80-\xb0@C\x84\x8eH\xaf\xe1\x1e-\x01\x01w0\x95\xcaI\u009e\xd1\x12\x9d2\xb3\xd7\xf2\u007fk\xc8\x0e\xbc\xe1%\x95\xf0\x18\x99\x9b\x9a\xd4\x1e\xad\x16\x8aXV\xe1%\x13\xa2\x10o`\x91րJ\xb7\xa0\xf1\x10\xb7\x86\u007f7\x16AꝹ\x86\x83\xf7\xa5\xbb\xbe\xba\xdaK\x9f\x94%3EQi\xe9߮X\xe4\xe5\xb6\xf2ƺ\xab\x1c\x9fQ]9\xb9_\t\x9b\x1d\xa4nj\x98w%J\xb9b\xc45\xebʺ\xc8\xff!q\xdd]\xb40\xf5o$d\xce[\xa9\xf7u7\xcb\xf2(\xddI\xa8\x838\x85i\x01\xff\x86\xbc\xd4ET\xf9\xf9\xf6\xfe\xa1-j\xd2ui\xce\xd4n\xa6\xb9\x86\xf0D(\xa9wh\x03\xe3v\xd6\x14\f\x11u\x1ed\x8d\xc5TI\xd4]\xa2\xbbj[HO\x9c\xfe\x9f\n\x1d\x89\xb3Y\xc3\r\x9b\f\xd8\"TeNR\xb8\x86\x8d\x86\x1bQ\xa0\xba\x11\x0e\u007fs\xb2\x13\x85݊H:O\xf8\xb6\xa5\xeb\x0e\fԪ\xbb\x93M\x1a\xe4P\xd0\xf8\xfb\x12\xb3\x8eb\xd0\x1c\xb9\x93\x19\x8b?\xec\x8cm\fB0:\xeb\x16\xc0!\xa5\f\v\xedD\xa5\xfc#+\xb2{0?\xa3\xf32\xeb\x8e9B\xe7\xd3\xe0\x94\x84\x0e:x9\xa0?\xa0%Y\xe1\x1fX\xed\x8e \x023\xd0a\xce:'\x9e\x10DĚ\x95W)(M\xb2/\x0e\xb6o\t\xd1\xf5\x11\x9c@ͭ1\nE\xd7\x06\xe0k\xa6\xaa\x1c\xf3\xda\xe0\xba\xc9]\xdd\xf6\x86\xf3Q$\xa4&\xcd \xe3O\x88\xe9\xe6W\xb6\xb5\xc2boc$\x9dR\ahlE\x0f8\xc0\x10j\xd2c\xd1\xc3jD\x94\"\xecJ)\xb1Ux\r\xdeV\xc7K\x87y\xc2Z\xf16H\x89t\x14/#D=:\xda\x06%3>Cj\v\xc0\xb4\xf8\x03\x91\xe1`\xcc\xd3\xf4\xd6\xff\x8dF4\x16\f2\xf6``\x8b\a\xf1,\x8d\x8d\x9b\x8d\xc7\xc8\x16\x01_1\xab<\xf6e[x\xc8\xe5n\x87\x96\xa0\x94\a\xe1Ѕck\x8c\x04c\xeaI͎\xb1\xad\x87\u007f\xc32a1\xecw\feRR\xcd\xc8\xf4\xa9\x1b\x1a\xf9\x18:\x97\xcf2\xaf\x84\x02\xa9\x9d\x17:\v\xfb\x105N\xc7\xfb\x80qv\xf6\xb0\rf-\xe1L\xb4\xef\x988\xa3\x11\x8c\x85\x82\xacy\u007f\xa8\x1b\x84\x0f\xa3\xdb\xdd\n\xb25&\x88\xa1\xad\x14\xba\xb8PΖ\xb3\xd1\xeb\xcb\x11\xc05\x17\xc2ٯ\xc4\x16\x158T\x98yc\x87\xc80\xcd\xd4\xd0\xe6m\xd4\b\xed\x06\xacUc\u007fi\x8bmCeFa\x02\xbc\x1cdv\b\xc72\xc9\vC\x81ܠc\xfd\x15e\xa9ކ7\aӜ\x0emB\x85\x9b6\xa9\xccǰ\xfajݴY;״\x19\x8bץe\xcd\xfa?\x0f)\x93\xe1>Y07\xbd\x89\xef)\x98DDI\xae\xf5f\aX\x94\xfe\xed\x12\xa4O\xbd\xe4I\b\x8e\xfcF\xc9S\xaf\xfd\x87cĩ2\xbd9\x9e\xf7\x8e2\xfd+\xb9P/\xfd\x87a\x02\x1b\xfb\xfbh\xeb\x172\xe0s{\xce%\xc8]̀\xfc\x12vRy\xb4G\x9c\x98ڮ\x99\xe6į%\xc1\xfcIE\xad\x10>;ܾ\x92w䚄\xcd\"j\x1cO\r>e\U000aaec7\xe9$T\xe0`PZ,B\x88\xf9\xc0\x14lz\xd8\xf3\xf9\xf8\xe5\x13\xe6\xe3D\x81%\x12\xd6\xdb\xc2\xc7#4\xdb\xcbF\x17y\xd9\x06\xa2\x93RG\x17!]p\t\x02\x9e\xf0-x\x17B\x031D\xd024x\x16\xa2E\xceY\xb0@=\xe1\x1b\x03\x89i\x88\x99\xb9\xcbX\x1f\xda\x13\xbe\xcd\x0f:\"\x1ba#]L\xab\x10\xfd\xa8\x83\t\xc01\xecR\x92\x01'\x91\x92\x85\x99\xdb\x14,5\x11\xa9%j\x9f\xbc\xbd\x9aMM\xde#0\xf2\xc2\x05\xa6\x90\xb4\x1fd\xb9h\x83d:\xc1!\xebDJ\"=\n%\xf3z\x99 \xdf\x1b}\t_\x8c\xdf\xe81g\xb5\xdbn_\xa5\x8b\xb9\xbbO\x06\xdd\x17\xe3\xb9\xe7݉\x18P>\x99\x84a\x1a\xab\x90\x0ef\x98\xf6\xdf\xceE\xcd\nqh\x9b\x10a\xd5,\x91\x0e6\x9ab\x88@\xab\x90M\f\x8bMY\xfbn+*\xc7\xc9&m\xf4\x8a\x0f\xbb\xf5\xd0:\x91\xc4\v\x05\xb9ͅ>Z\xf5\x92a\xb9E\x10\x1f\xe8\\\b\xb3CfT\x89\fs\xc8+&\"g\xf6\x84ǽ̠@\xbb\x1f?\bڭ$\x9b\xbdd\xf9E\xb64\xb4\x93\xe4i\xc9ќZ4\xc6\xf9\x1c\x1a+\xd2\xcd\xd91\x89\xb53\x03\aSy\xe3\x03\xe7\xf6\xc1\x87$\xfb\r3\xd4\x14y\xce7)B\xdd-\xb6ދ)\xdf?\xb7\x03J\xe1\x8c+\x04'\xe8\xfe\x8f\x8e*\x16\xda\xff\x87RH;\xab\xa1\x1f\xf9JDagf\xcc\n\xb5\x17!\xf8\xd2\x01q\xf3Y\xa8\xe3\x84\xf0\xc0\xb6\fY\rT\xe1\x186\xbb\x9e\xa7q\t/\a\xe3©\xb8\x93\xa8r\x90S\x9e\x16\xb5\xf3'|;\xbf\xec\xe9\xf8\xf9F\x9f\x87㹧\xb1\xe9,\x9f\x01l\xb4z\x83s\x9ey\xfe\xf5\xae\xcb\"\xa9[0\x88\xefǖ9\xb3\x14ͥS\x9c\xa6\xd5w0䊎c\xbb@\xe6J\xe3\xfcB$\xee\x8c\xf3!C\xd7q\x1e\arC\xd31M\xcc\t\x81\u0605{/c\xd3\r\a\x19\xb2\xa3T%q\xc9\xe1`\x82\xb3\a1\x8f \x85Rp\xde\xe8h\xb0\x8f\xe7\xe1ڃ\x97\x10\x19\xbb\x05\x13\x10I\x14Jk2tnJ\x1cf-\xefL\u00adN\xb6\x89\x10T\x84K\x84\xa9\xe4^jK\xddF\"\xcdIn\xf6\xedk+\aH\xaaM\xffO\x8b\xd9i\x18\x01_2\x17\x85г\x87E\x0f\xb9\x9b0/\xa9B\x04\x13\\v\xbb\xafX\x8d\x97zzQh\xbe\xed\x01[H\xbda\xe0\xf0\xe1]\x8fcH&\x11Ow\xa9o\xd2̆\xccuG\xd0\xcd\xd2\xf4S\xeeC\xed\xe5\x80\x16;\x9c\xeag\x86ٝ\xd3Ʒ\xc2\xf3e\x84\x0ex\\8\xd8I\xeb|\x1bI\xc7\x17[\xef\x1f\xa3\xe8[k\xbf\"D\xf9)\xcck%\x80\x0e\xe6%\xdd\x14\x8e\\\xce\r5\xbe\x06A\x90;\x90\x1ePg\xa6Ҝ\xc4 %\xe5\x05\x02I\x831\x9d=dC[\xa2\xd8\xd4PWŒ\x8d\xafXz\xa4\x9e\xc8u\xb4\a\xffMȩLUj'\xb1\xc9\xcb\x02M5q\xa85\xadæ\x870\xafs\xc5[\x88WYT\x05\x88\x82\x88\xbd\x88\xa2t2\xcb\x02\xbb\xfc\x85\x17!=[w\x82ʦ\xde\x1bR\x8aR\xa1_\x16\rlqg,뢓9\xd6Gf\xe4\xb9\xd1 `'\xa4\xaa\xec\"\x8bv\x02E\x97{\xf6Q\xc9\xdf\xc7i_\xb2슷?\x9b\xa6\\\xe4\xaaMY\xd5\xd2.u\xd4\xee,\xbe\xa7\x8bTZI2c\xde\xd7K\x8a\xa2$\xf4\xdbw7\xa9E\x9b\xefnR\xaf}w\x93:\xed\xbb\x9b\xf4\xddM\x9al\xdfݤ\xefnҟ\xd5M\x9a\xc6d\xc5y\xab\xc1\x9ffV\x9f\xbdB\x1dGl\x14r\xbcտ\t\xb5\xd7\xcb\xea\xf26\xc3s\x06\xea.cI\xf7\x8a+\xce\xfb|n\xae\xfe\x1b3_\x17\xea\x91\xf0'\xe1\r\x85\xa5\x93\xa5{\v\n\xf1\x86j3\xe7\xcbK\xe6\x8aJ\xba5\x89uaG*J4i\x89\xde\xeeS%;\xb9\x99\xed\n\x06\xa1T\xbb6E؆(ߨ^q\xb6\xf4c\xa6\xe0c\xbals\x9cBG\xae}\x97D\xb6Sb\xf8\x8d)4Y\x971^\x8d\x11o2Ћ\xe7\x0f\xeb\xee/\xde\xc4\xda\fx\x91\xfe\xd0\xdb\x00\x17MRȢ\xf7\xed\xe2\xc8$S\xf1\xf9\xc01\xe5\xc0X\xd0R]\x0e\xd6\xc5\xd4/+\xda䄟\xca\x10\x14\x9d\xa4oS\xae\xfd\x92ڍ\xaf\xae\xd8\xe8\xd6d\f\x1a\xd9\xd3.;\x96\x96\x90.\xaf\xc9\xe8\xd6\\\x8c\x1c2\v*1N\xae\xb4\x98\x8f\xb7&\xab*\xbe\xa2\x96\"\xd5IL\x1d\xb8\x13\x15\x14\v|\x8e\xf9j\x89\xaf\xaa\x91\xe0˼\t\xacO\xaa\x8chU=L\x80\\V\x0f\xb1\x80$s\xb5\x0f'W<\x1cW\x19Llb\xae\xcea\xbc\x86a\x02\xe8`uÒʅ\t\x98uM\xc3;\xd6+\xccT)\xbcO%\xe1\xaf\xf5=\xc7j\x0ef*\rf<\xd3)\xacfj\t\x96W\x10\xcc\xd0\xe7+\xab\x05\xeaz\x80\xc15O\xad\x11\xe8V\x01\f\x82\\X\x190r\xf7?\brA=\xc0̍\xff \xd8ɃqB\"F\u007frZ\x94\xee`҃\xadI?\xe9\xbe;v \xb8Hϵ2e\xaa\xbc\x86\xdd\xdf\n?\xc1|\x83\xbbG6\xf2\xfc\x14&k\x1e\x02ES\x9e\x9c\x9f\xe3wB?\xbcg\xb0ἱb\x8f\x9fM\xd6zi;\xb6\xff\xee\xd8γ\xc8\xc8\xd4\x14ҧ:\b\x91\xde\xe7u\xa7\x0e\xf9\x8e1\xcb\x16߹5\xd1\x17a\xd8\xe7\xf7\xa8\xe6y\xaf&7\xf1\xf0\xf09 \xeee\x81\xebOU\b\xe4V\xa5\xb0\x0e\x89~iCaҖ\xfe<\x98\x97\x1e\xc2\xcaĝ\xfep\x8c\xafE\xce\xe1q\xb4\xb8\x18\xeb\xf0\x96/\tX\"Ӵ8>\x0e\xcfi\xf9\xa2-\xa6\x84\xc0\xc6\xec\xc6f\xf56\xd8z\xc8L\xde~\xa8hy\xaf'j\xc3\xc6y\xf8\xf1\xa7\x17\xbers\xcf?yPz\xcc\x1d3\xbe\x95\xe5\x17f\x01@\x10Ɠ_\x80\xc6\xf4V\xe7\t\xfd\x14On\xfa\xe3\xf9)\xb5\xcd\x03R\x9cV\xab\x1fs\xbe\bW'\xd0\x06,Z\x03,\xcccg\x80`a\x0e\xf8\x8c\x1a\x8c\xe6|\x19\xbf\xe0\n\x0f\xf9\x8f\xe7\xf4\xe3\xd7\x16\x8c\x98\x8e\xabJeD\x9e47\xbd3\x8d\xcf\xc3\x1f\xd8\x1e\xd9g\xb4\x17n\x14\"?U\xdd\x19;\xb4\xfdc\xc9\xda\x19[\b\u007f\r\xb9\xf0\xb8\x1a\x00\xb8\xc0\x8e\r\x88\x14'\x8fg\x9en\xf2\x90\xa0\x1d\x9cwNoiC\xe2\xb9@\xe7\xc4>\xbd\xd9|!s\xb4GM\x87\xdc@\x96(\xbabM\xe2\xb2\xfb~1Dt\"\xf3\x14\xff\x06\xd4b\b\xdb\x1au\xd1\xd79e\xf6\x14a\xf3\xc0\xf8b<\xda\xe7aC\"\xb5\xc7=v\xdd#|-\xa5\x9d\xb7\xe5\xb7\xf50\xa2\b\x87\xee\xac\xe1\xcd\a\x14Pɽ$\x83H\x8c\xdd\v\xbb\x15{\\eFQ\x1c%\x8d>\xc6\xe8\xb7\xe1k\x80:\xf0u\x84ކ\xfe\xd6\x1e\x99\x02\xc1(\xcc\x01J\xfaX\xc2e\xff\xac+\xa4 \xaaz\x0e6\x1a3\x01\xb0\xa2\xc29\xac\x85|\x8a5\x05\xe7\xc5\x06\x8d\x93\xcdY\xc5\x0e\rzWh7\xa1\x1a%\x1f\xbd\xf1.\xd6s8l4\x1a2\xacƤ\xb7I٪Qv\x9f\x95\xa5}\xa3)\xfct^\xe6^SHr\xb5\x89^\x98s\xb0\x92\bi\xbb\x89F\xf83B\x13\x00\x92\xae\xc69|dx\xb5\x90\xa8&\x00;a\xb4J\x02\r`W\xa3\xbd[\xbe\u007f\xfc\xdfJn\xb1\x12\xcd\"\x80B\x92^\xd7In\x1c*h\x02\x01\xedi\xf0\xbcE\x8f\xf0\x98X\x01\x86\x82\x94qe\x8d\x00n\xfd\a\xca@E^\xa8\xbd\xab\xd1\a\xddR\xc7\xe3(t\xba\xb5\x01\x98[F\xdbȀ\xe2`A\x82\xb0E\xc8.G\x05\x94,\x01WB\xd8j\x02\x8f\xb5GB\x1b\x0e^\xe8\x10\x95 l\xc6U\xc0\n=+\x01ںh\x14G\xd8\x0e}\x00\x8f\xd2m\xac\xfe\xab\xd3L\x10\\:҈\x80\xd9_\xed\xd06\xa0\xb7\xc20\xcf\x11\xff\v\xc2*\xa8\xc4\x1e<\xf2\x19\x10푶$B\x05|p\x1eA\xdb\xd2\xcda\x1bBM\xf3\xd9l\xa3C\x9b,\xd2UU\xb4:\xecg)\xe4\xf5:\x06\xe7i\xa6p\x87fFz3\x15^nu@\x19\xa2Ǚ\xa8\xf54\x01\xb7M\xfcV\xea?>g\x16\xdd\x1e!\r{\x8e\f\n^\xdbM\xb7\x9c\x82\xf7,\xef\x1c\xb6\x8dӛ\xcf\x1a\xfc\azy\x89Yy\xf8q\xf5\x19\xdaC\x93\v\xfa\x9c'\xb6\x0f\x9fсx&J\xdb\x12}\xe3\xb8һ*iD\xabj\xa7mH\x13i4\xda>\xe9\x14ו\x0e\xec\xe9?#R`\xff\x14\xb0H%\x03\xd6\b\xb1V\"\xa0*ཅ\x85\xa8\xd0,\x04\xe1w\xa7\x9d\x19\xa6)Sz\x9d\xf8\xe3J\xd7\x17l\xd8\xea\x96\xdb\"4\xea\xa1\xd14]\xd5({y\xc2*t\xa9sږ\u0383\xc8i\xdb\xe3tTYq$2\x96\xbe)\x85\xa5D\xa2\x0fNa\u007f}\x00\xf6\xae\x13롫\xd1W\x9a\xd2m\x91\xb0\xf1ZSF \u05f7\x81R\xe8JP1\xd8A\x1b\xab!\x84)<\xa0P\x9f\xacُn\xfc\xe2u\x18\x1e0\xea0\x1e\r\xac\xd5\xde\xca%z\xed\xd4Es\xdf\x0e\x84;\xa3\xb7\xee\x19\xca\x14\xb86\x98=W\x16\xda[9,\x9d\xed\xb8[\xbeo\xcbh\x93\x1e9\x9b27\x05\xdc\xe5\xact%\xbc\x02\xa5I\xac\rRR9\xa4\x87o?ޝC\xf0\xf1\xc5FKgK\xbd\x19\x9a*\x94JW\xb60\xcb3QqQ逫E:\x83K\rG@\xed\xddN+\xf4\xd36p3\x86\xe8s\x04k4\x8a\x86֍f\x0ft\xe5'\xc7\xf5E\x97}:\x96\xecn\xbd\x8c\xa2M&\f\\\xf7\b,r8\v?\x8c+`\x8fJg-{)8\x10\x9d=\xb74t\xde\xe0\xd3s\t\xc6c\x1d\xe5\x13\x86\xd3\xf5a\xd4%1f2\xe5Q3\v\x0e\"a\xe2\xf62\x80+>\x03\x90b\x81\xfe:\x8a\xc5\x1d\x8bu\x11/`q\a\xebh\x95\xc1\x16\xcb\xf3\x16-_\xe0\xba\xdc\xf3-\xf2\xf9~5\xa2\x13Z\x1eSq\xc8Wp\xcb\xe6\x18\xf6\xd2\xf9J\x849\xac\xf7'I}մ\xdac\xa9\xbf^5m\x99\xc4Z\x82k\x11\xb6\xa0-i\x85 F\xe8\x1e)\xb3\xed\xe8\x12\xf8S\xdd$\xd27:\x83+\x88\xf6xR\x84\xa6\x19\xc6Kӣ\xe5\xf3bf,\xb3Pgw;O-װ`\x8f\xa7\xe6\x89\x15c\x16L\xfb\xe9\xda\xdbi\x0f\xbdze\x06\x11\"}㥙\xbeɂ\xeb\x9c\xeb2z\x8f6d\x85\xe0\xca\xfe\xb5\xd7\xf6\xbe\xdf\xfb\xe2\xbc9\xba9\xb9\x1b\xb3\x10m$TM!,\xe07\v︷\x92\xdc\xf3\xcc\x199\xb79t\x12N\xd6=\xf3\xc7Gڒ\x02p6Y\x9b\xfa\x06\xee^\x9bV,m=kc\xb8\xa1\xf2X\xb9]zM\xf4\aw?\x1e\xcd\x1e\x04\xd3\x03\xbb7ū\xe2\xe6_\xbe\x95\x8d\xa0\xc0\xd7,\xaa\a\xdc\xe9\xe1K\xe2\x94\xcd\xfb\x13\xf96\xaa\xbb\x8b\x94'_\xda\x16m\xe6\xb3ؗ\x13\xf3Km\xb8\xa1\x1cI\x81\xc33\xa9y6P\x80\xa0+L\xb3\xb7\xab\xfb[J\xcfYn\x86O\x94>\xb3\xfb(\x01\xe4Dž\xcb=p\xa4\x80~\xc4ٝ\xaf4\x81u`\x9c\xdd\xf4R\xa4\x19\xb9#\x06\xe7\xa1\t\x1d\xe7A!7\xb3\\~\xe5V\xd8\r\x1e^9\x19\xfb\x11J\x0e\x8cS\xa4\xfd\xe88D\x83\xb6\xe3\xa1\xf0\x02\x1f\xf2k\xfd\xa2\xff\xee{\xa2\xad\xeb\xfa\fw\xa8\xb3/\xcd\xf1\x83\xfc\xc5\\\x0f\xa4\xdb˅\x89\x9c\xf2Q\xffH{Uo\x05]6x\xc9\x12\xad\x9d\xc7%\xa9\vի\x05\bΦ\xe1\xddN\xe8\x84\xfad\xe7g+\xce읱e\xa4\x16\x0f\x96\x0e\xffh^\x1ff\xa9&N\xf3?\x99\xb4\x01@\xfc\x0e\xff\xc7\xdc\xdc\xf4~\xaa\xa4\xa9t\xb6iZi\x0e\xbf\xfe>i\xb4\xa2zlq\xf0\xe2\xdf\x01\x00\x00\xff\xffN\xa7\x18\xba\xcf\x12\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96\xcfn\xe46\x0f\xc0\xef~\nb\xbf\xc3^>{\x9a\x16\x05\n\xdf\xda\xec\x1e\x82\xb6A\x90,r)z\xd0H\x9c1\x1b[RIj\xd2\xf4\xe9\v\xc9\xf6\xccx\xe2 \xdbC}\x13E\x91ԏ\u007f\xac\xaa\xae\xeb\xcaDzD\x16\n\xbe\x05\x13\t\xffR\xf4y%\xcd\xd3\x0f\xd2P\xd8\x1c\xae\xb6\xa8\xe6\xaaz\"\xefZ\xb8N\xa2a\xb8G\t\x89-~\xc2\x1dyR\n\xbe\x1aP\x8d3j\xda\n\xc0x\x1f\xd4d\xb1\xe4%\x80\r^9\xf4=r\xbdG\xdf<\xa5-n\x13\xf5\x0e\xb9x\x98\xfd\x1f\xbei\xbem\xbe\xaf\x00,c9\xfe\x85\x06\x145Cl\xc1\xa7\xbe\xaf\x00\xbc\x19\xb0\x05\x87=*n\x8d}J\x91\xf1τ\xa2\xd2\x1c\xb0G\x0e\r\x85J\"\xda\xecx\xcf!\xc5\x16N\x1b\xe3\xf9)\xa8\xf1B\x9f\x8a\xa9\x9f\x8a\xa9\xfb\xd1T\xd9\xedI\xf4\xe7\xb74~\xa1I+\xf6\x89M\xbf\x1ePQ\x10\xf2\xfb\xd4\x1b^U\xa9\x00Ć\x88-\xdc氢\xb1\xe8*\x80\x83\xe9ɕ\xfb\x8f\x81\x86\x88\xfeǻ\x9b\xc7\xef\x1el\x87\x83\x19\x85\x00\x0e\xc52Ţ\xb7\x16$\x90\x80\x81\xc9\x15h\x98\"\x80\xe0\x11\x02\xc3\x10\x18a\fG\x9a\xc9d\xe4\x10\x91\x95fD\xf9;+\x90\xa3\xec\xc2\xf9\xc7\x1cݨ\x03.\x97\x04\nh\x870%\x16\x1dH\x89\x1c\xc2\x0e\xb4#\x01\xc6\xc8(\xe8\xc7\"93\vY\xc5x\b\xdb?\xd0j\x03\x0f\xc8\xd9\bH\x17R\xefr\x1d\x1d\x90\x15\x18m\xd8{\xfa\xfbhY\xf2\xfd\xb2\xcb\xde\xe8\x9c\xc1\xf9#\xaf\xc8\xde\xf4\x99k\xc2\xff\x83\xf1\x0e\x06\xf3\x02\x8c\xd9\a$\u007ff\xad\xa8H\x03\xbff8\xe4w\xa1\x85N5J\xbb\xd9\xecI疰a\x18\x92'}ٔ¦m\xd2\xc0\xb2qx\xc0~#\xb4\xaf\rێ\x14\xad&ƍ\x89T\x97\xc0}\xe9\x88fp\xff\xe3\xa9\u007f\xe4\xe3Y\xa4\xfa\x92+A\x94\xc9\xef\x8f\xe2R\xa4or\xcf\x05:\xa6y<6\xc6\u007f\u009bE\x99\xca\xfd\xe7\x87/0;-)X2/\xb4O\xc7\xe4\x04>\x83\"\xbfC\x1e\x13\xb7\xe30\x14\x8b\xe8]\f\xe4\xb5,lO\xe8\x97\xd0%m\aR\x99\xcb/私\xeb2\x18`\x8b\x90\xa23\x8a\xae\x81\x1b\x0f\xd7f\xc0\xfe\xda\b\xfe\xe7\xd83a\xa93\xd2\xf7\xc1\x9fϳ\xa5\xe2H\xeb(\x9e\x87\xcdj\x86V\xda\xf2!\xa2\xcd9\xcb\xe0\xf2Yڑ-m\x00\xbb\xc0\xf0ܑ\xed\xe6\xb6\\\x10=6ps&^k\xd8\xfc\x8d\x06\xf2TY\xca߸,\x94<\x11\xe3\xa2\xd6\xea33\xefRP\xa3I\xfe\x15\x87rb&a\x133z\x9d\xec\x94)\xb0v\xe8k\xee\x8é\xe5\xf2ދp>\x17\x95\xf2[2\xe4\x05\x8c\u007f\x99\x8e\x81vF\xe1\x199\x97\xb8\r)\xcf\x0et\xe0\xd2\x05\xaf\tE\x87cRr\xfa\"\a\x8b\"ͅ\x16)\x0e\xaf\xa2y3\x0f\xf9˿:\xb3\xed\xb1\x05儫\xf93\xcc\xe6e\xb1\x13;#\xaf\x92\xbd\xb8\xf4]\xd6X\xe3\x8d\xe3\\\xc6\xf7\x80\x17\xb8>\r\x97^j\xb8\xc5\xe7W\xb2\x1b\u007f\xc7a\xcf(\xf2j\xebn$U~v_\xc1d\xa5\xe0.D\xa7\x17\xc4\xd5iU\xa0\xd7Ӌ\xa1l\x00H\x1ek\xee\f\xach`\xb3\x9fQ\x9f\xaa\xd8X\x8bQ\xd1\xdd^\xbe\x17>|X\xfc\xf8\xcb\xd2\x06\xefh|\xee\xc0o\xbfW\xa3Ut\x8fs\x1cY\xf8O\x00\x00\x00\xff\xff\xbe\x16\xd7\"m\t\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WO\x8fܶ\x0f\xbd\xfbS\x10\xf9\x1d\xf2+\x10{\x9a\x16\x05\x8a\xb9\xb5\x9b\x1c\x82\xa6A0\xbb\xddKуF\xe6\xd8\xeaʒ*R\xb3\xd9~\xfa\x82\x92=\u007f<\x9e\xdd\xf4й\x99\xa2\xa8\xa7\xc7GRS\xd5u]\xa9`\xee1\x92\xf1n\r*\x18\xfc\xc2\xe8䋚\x87\x1f\xa91~\xb5\u007f\xbbEVo\xab\a\xe3\xda5\xdc$b?l\x90|\x8a\x1a\xdf\xe1\xce8\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95\x98I>\x01\xb4w\x1c\xbd\xb5\x18\xeb\x0e]\U000d0db8Mƶ\x18\xf3\t\xd3\xf9\xfbo\x9b\xef\x9a\x1f*\x00\x1d1o\xbf3\x03\x12\xab!\xac\xc1%k+\x00\xa7\x06\\C\xeb\x1f\x9d\xf5\xaa\x8d\xf8WBbj\xf6h1\xfa\xc6\xf8\x8a\x02j9\xb4\x8b>\x855\x1c\x17\xca\xde\x11P\xb9̻1̦\x84\xc9+\xd6\x10\xff\xb2\xb4\xfaь\x1e\xc1\xa6\xa8\xec%\x88\xbcH\xc6uɪx\xb1\\\x01\x90\xf6\x01\xd7\xf0I`\x04\xa5\xb1\xad\x00\xf6ʚ6ߵ\x00\xf3\x01\xddO\x9f?\xdc\u007f\u007f\xab{\x1cT1\x02\xb4H:\x9a\x90\xfd\xe6\xc0\xc0\x10(\x18\x8f\x01\xf6\x87\x93A9P\x91\xcdNi\x86]\xf4\x03l\x95~Ha\x8c\t\xe0\xb7\u007f\xa2f \xf6Qu\xf8\x06(\xe9\x1e\x94D+\x8e`}\a;c\xb1\x19\xb7\x84\xe8\x03F6\x13\x8d\xf2;\x11\xd0\xc16\x03\xfcZnT|\xa0\x15\xc9 \x01\xf7\bc\xe2\xb1\x05ʷ\x05\xbf\x03\xee\rA\xc4\x10\x91\xd0\x15\x11\x9d\x84\x05qQnD\xde\xc0-F\t\x02\xd4\xfbd[\xd1\xd9\x1e#CD\xed;g\xfe>D&\xe1E\x8e\xb4\x8a\xa7LO?\xe3\x18\xa3SVr\x91\xf0\r(\xd7\u00a0\x9e bf'\xb9\x93hم\x1a\xf8\xd5G\x04\xe3v~\r=s\xa0\xf5j\xd5\x19\x9eJF\xfbaH\xce\xf0\xd3*\v\xdfl\x13\xfbH\xab\x16\xf7hWd\xbaZE\xdd\x1bF\xcd)\xe2J\x05Sg\xe0.WL3\xb4\xff\x8bc}\xd1\xeb\x13\xa4\xfc$\xea!\x8e\xc6u\as\x16\xf2U\xdeE\xc8E\x1ee[\xc1\u007f\xa4WL\xc2\xca\xe6\xfd\xed\x1dL\x87\xe6\x14\x9cs^tr\xd8FG\xe2\x85(\xe3v\x18K\xe2\xb2\xca$\"\xba6x\xe38\u007fhkН\x93Ni;\x18\xa6I\xb6\x92\x9f\x06nr\xe3\x80-B\n\xadbl\x1b\xf8\xe0\xe0F\rho\x14\xe1\u007fN\xbb0L\xb5P\xfa2\xf1\xa7\xfd\xeeܱ\xb0u0O\ri1C\xb3R\xbe\r\xa8%_B\x9a\xec3;\xa3s\t\xc0\xceGP\xc7\xca\x1eikN\xe2.\xd5f\x06\xa5b\x87|n\x9b\xa1\xb8\xcb.r\xf0c\xaf\xce[\xc8\xff\xb1\xe9\x1a\xe9\x034B(\x9d\xe1\x9bf\x16\xef\xda\xe9K\x1a]\xc40IU\xae.w\vZtR\trq\x87~\xff\xd5o˷\xe5\x1f&\x00\x95\xa3\xb8\xfcI\xb6\xe4\x03\xb6v\x06\xbaSj\x02\xa0\xb1\xa5\x19X#VFu--\xb0z\xee\xac/W\xa4șR\x9a\x89\xb7T\xf1\xa6\xb53\x9d\x9d\xc1\xeeCZ\x9b\x15J\xc6<\x1a\xf11\x8ay\x17\xc5\xc4/J\xfa\U00037c6f?I\x1f\xe2\f\xab:\x87\xeaX\x89\xf8\xd1K]w\n\xdd\xd1\xe7\t\x80\xaf\x8c\xa5\x19<\xb0\x1a\x16+\x12\x13\x80\x15*)\xa2\xadI1cI\xff\xf0x\xff\xf1w\xf3\xaa\xa1\x16\xd3 \x80uƒ\v\xb2ן\x9f\xbd\x93ێ\x01\b\xf2\x95\x936J\x84k\x16\x95\xe6\x80\xe0\xb3\"\x0f\xa1!Ȉ\x93\x00\x1f\xb7\x01\xb3\x84\xd0H\x0f\x8e\xac#O:\x9dޞX\xe0)\xa8\xc1,\xfeAU(aN\x8e\x85\x80oL\xa7\x04\x1f\xf0\x8a\\\x00G\x95\xa9\xb5\xfc\xd7V\xb2\x87`\xe2\x96\n\x03e\x00\xfbG\xea@N\xa3b\x10:\xba\x01\xd4\x02Z܀#\xde\x03:\xbd'-N\xf1%\xfcl\x1c\x81\xd4K3\x83&\x04\xebg\xd3i-C𥉉i\xdbN˰\x99F\x8f\x93\x8b.\x18秂V\xa4\xa6^\xd6\x05\xba\xaa\x91\x81\xaa\xd09\x9a\xa2\x95ET\\GW-[\xf1\x9dˎ\xed\xaf\xf74\r\x1b>6\x1f\x9c\xd4\xf5v8z\xd0I\xdcك@z\xc0\xbc,鿃\x97\x87\x18\x95\x0f\u007f\x9e?A\xbfi<\x82!\xe6\x11\xed\xdd2\xbf\x03\x9e\x81\x92zI.\x1d\xdcҙ6J$-\xac\x91:ėJI\xd2C\xd0}\xb7he\xe0\x93\xfegG>\xf0\xf9\x94p\x1b#\x16\x16\x04\x9d\x15\x18H\x94p\xaf\xe1\x16[R\xb7\xe8\xe9\xab\xc3\xce\b\xfb\x82!}\x19\xf8\xfdD3\x9c\x98\xd0\xda\x0e\xf7\x99`\xf4\x84\x0e\x82{n\xa9\xe2\xf3b\xd0x\x9d\\\xca*\x86\x00,\x8d\x03<\x9c^\xee\x89\x1d\vM~R\xd8σqX\xd3O\xa6\xda\v\xf2\x13:\xbd\x1b[\xd1k\xc5\xc9+\x85)e\xd1\xe0\xd3\xcc\x03\x91\x00\xaa_\xban\xc8Q\\\xe1\xc8\aY\xb1#\x19/\x83q\x1b\x16\xcb\xebI\x94\a\xebGA\xe7G\x1bAg\xf5\u007f0\x82\xc6\xd4\xe5\x85\x10\x1aL>\xf9hbd\xb8Nk\x8e\x02\xa3/V\xc0\x1aqv\xff,\x19\xc1ђ\x1ci\x8e\xa8\x94|\xac\x89)*\xa0\xd4}\xe4\xa5\xdc\f\xc1\x1c\xc1\xb7H\x00\x93\x80\xe1A\x9f;l8\x99\x8fG5\xfd\xe1\xf1\xbe\xcf\xc1=HY\xe7p\xb8\xe3YD\xf8YJR\xe2\x11C\xf3\xe2\xae\xd7\xf7˴M\xccH\xc1\x00\x82\x95T\xd1 \xb5\x83\xd4>\x10\x8a48\"\x12\x80\x03\xd7Q\x9e\u007f\x93\xf2ONs\xbbr\xc0X\x03\xa6\xfa\x06\u007f\x9d\xbf\u007f\x98\xfe\xc5$]GebU\x91g1\x18\xa8%\x1dn\xc0wU\x03\xe8\xd9\x04\xe9H\xcc\xf9K٢\x96K\xf2\xa1\xcc;\x90\xf3\x9f\xde~\x1e\xc3\f\xe0G〾`k\x15݀L(o\x13j\xef \xd2' \xb6\xf2`-C#\xc7\rGv\xa4l\xf0:\x1a\x1a\xf0\x99\xc0dC;\x02%\x9fi\x06W\x9cB\xf6T\xfc7G\xc3\u007f\xaeFe\xfe&\x05\xe9\x15O\xb9J\x8amk\xe6~\x10\xed\x14L\x91\xe4d]\x93\xa3q4c!\xe0\x04\xfb=\x18Ƕk\xb3' \x8a\xe53K\x89\x8eđ\u009f\xde~>\xa1\xed\x10'\x90Z\xd0\x17x\vR'T\xac\x11ߗ\xf0\x14=b\xa3\x03~\xe1}\xaa\xc6x\xd2`\xb4ڌkk\xa0\xc1\x15\x817-\xc1\x9a\x94*\x12W\x11\xb0\xc6\r\xdb\xdf\x1f\x17{\x18\x82E\x17\x86ldT\xea\xd3\xfb\xbb\xf7\xb3\xa4\x15\xbbP\x1d3)W\xb9\xa5d\xce\xc1d#UN\xf6\xc9\bG\x97\x9c#\x18\xa8\x1a\xd4#\x89\x15\"i\x89\xe8.;\xaee\xe5\xf5k\xa3\xf5\x906\xf4\xcf\b}8L\f\xbfR\x11\xbeȬH\xcb_4\xebaϟϚ\xc5\r\x82\xd3\x14(Z&L\xe5٨\x8al\xf0S\xb3\"\xb7\x92\xb4\x9e\xae\x8d{\x96\xba.\xd8\x11\x8b\xe4\t~\x1a9\xfe\xf4\xbb\xf8\xe7\x17Y\x11\x99\xf9e\xa6ĩ\xdf\xc2\x1e\xde\xc7O_mN\xcf+/\xadJ\xd7\xf3\xcc|\x0eWrH\xac\x1bY5}\x93\xb0˞\xa31ҢH)\x17\xf5櫻-\x03\xd99\xd6gS\xe4>\xb3@-\xf8\u007f/}\xe0\xf1W#\xd7\xc9\v\x82\xf4\xef\xf7w\xdfƙ;\xf9\xea\x88\x1c%\xc4\xc9'\xac\xb9\x17\f\xdfR\x92;K\xa7>\f\xa6\xf6\xc4n\x84In\xe7\\\xcc\xe4\x02\xd6G\x04\n\x85\x887\t\xa8\x1eϐ\xac36\x0f\x94\u007f\xc2\xda\x03:\x02\x84\x16-\x9f\xd33m\x8aT\xa4-J\xae\xb1\\F3_Y\x10\xa0\xb5J\x8e\x94\xd3\\\x8a3]\xcc̛\xdbZ\xac\xfd\xb8\xbd#\xa8\xa7\xd5g\xd1N\xed\xc5\x18}\xce['^\xb2\xa5\xd0\xc1\xec\x88\xea\xb1\xff\x1e\x11\xd7\x13\xb8q\x17\xc8\xecj_\xb5b\xbcu\x19\xcc`J?\x18\xb0F\fއ~6\xf8\x94\xecy\xb1{\v\x18:\u007fq\xff\x16g\xf7\xe8\xa5|\x10\xb2\x8c\xc8\x15~I\aW\x19\xe6\x8e\xc3{\xa8sGx{\xb4F\xdf\xefp܄\xc1\x9e\xb0\xb4.\xe6]\x96E\"R;f\x9dK\x94\x8a\x04\xf4\x97`\x87k\x8ed\xee\xcbXВSUg\x95A\xd17EY\xb5\xfe\x92牻\xe1x\xdfp\xedOJ\xec<\x89\xd8%\x8f\x98\u007fX\x1e\x96Ƶ\x18f 0P1\"PwJ\xe1B\xd1\f\x82\xeb\x0e?\x9e\f\xfd\x96\xbc\xc7\xfa|x\xfd\x9c\xe6\xa4\xfe0/\x00\\\x98.l\x1b\xc4A\x88_\xfb\xec=\x97w\xa7#-\xd8\xd0e\x91\t\xb3\xcf\xf4Q\xa9\xb8b?\xacw\xb7\xa4Q\x9f\x05\xf1\xb1\xfc\xaf\x11\x0e`\x1b\xf4\xe7\xc1y\xe4\x19c\xc1\xb3\xcdAg\xa2\abGص\x87;\x14\xf0@룱{\xfd\xe8L\xed\xc8\x1f\xbaF\xd1\xfbϑ\xb1\x05\xfc\x18\xfd\xfcb{\xf3\x06\xe7MΓ\xa01\xaa\x0fO\x13P\x81\xee\xda\x059\xb6{\xb1\t\xe4\x87I\xf8\xb8\xe7\x8f]\xc4\x0e\xb4\xbd\xd5\xfd\x15B\x92\x93\x9b\xa2\nu\xbce\xe3\x98\t\x06\x84\xf4V\xe1qWԛ\x10\x99\x04\x87\f\x87\xf4\xce[\xfb0\xb5\xe4\xe2\xa7\xd7\xdcRDm\xee\x8c\x1e\xe5\xb8}|J\x1d\xfe\xf8\xfb\x93\x8cC\xea@\xf5 \xa9\xe7\xaf\f\xe0;\x96\xff\xff\x96}\xb2\xb0z\x8d\xd67&\xdcߝ=\xed\xf9vZ\xef\xe5;\xd2\x12sW\xbc\xf7˓\xfa#\x1f\x96\xb4\xf4$7\xb88\xf4|@\x17.+\x1e\xf3\xc1\xd4\x17\xeaF\x94K\xa2\x849Yt\x18\x8e\x1d3\xde\a\xdf\x1e\xfe\x8cr\x03^\xc6k1\xe6>\x89\f\xa5V\xd7s9ajg\\\xf2\xd5c\x89\x83B0H\xfcCտE\xce\x1f\U000470e1\xddoJovo\xd1/\x8b\xfc\x1bR\xfc\x90\xcd\x12{\x9b\xe7[\xd5<\xb2\xa3!X1e'\xf1p\xf8+\xd2U\xba*\xe9\u007f\x16\x8a\xaf\x95щ\xcd\xfa\x19|\xfa<\x81|\xd7\xfa\xb1׃\a\xff\x1b\x00\x00\xff\xff=\x1a\xe4\xa9\u007f\x1b\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y͒۸\x11\xbe\xeb)\xbaf\x0f\x93\xad2\xa9\xd8\xf9\xa9\x94n\xbb3ٔ\x92\xdd\xf1\x945\xf1\xc5\xe5\x03D4EdH4\x034$+\xa9\xbc{\xaa\x01R\x12%J\xa3qe\x1d]l\x02\x8d\xc6\xd7_\xff\xa0\x81\x99dY6Q\xad\xf9\x88\xce\x1b\xb23P\xad\xc1/\x8cV\xbe|\xfe\xfc'\x9f\x1b\x9a\xae\xdf.\x91\xd5\xdbɳ\xb1z\x06w\xc135\x1f\xd0Sp\x05\xdeci\xacaCv\xd2 +\xadX\xcd&\x00\xcaZb%\xc3^>\x01\n\xb2쨮\xd1e+\xb4\xf9sX\xe22\x98Z\xa3\x8b;\xf4\xfb\xaf\u007f\x9b\xbf\xcb\xff0\x01(\x1c\xc6\xe5O\xa6AϪig`C]O\x00\xacjp\x06-\xe95աA\x87\x9eɡ\xcf\xd7X\xa3\xa3\xdc\xd0ķXȮ+G\xa1\x9d\xc1~\"-\xee\x10%k\x1eI\u007f\x8cz>$=q\xaa6\x9e\xff6:\xfd\xb3\xf1\x1cE\xda:8U\x8f\xe0\x88\xb3\xde\xd8U\xa8\x95;\x9d\x9f\x00\xf8\x82Z\x9c\xc1\x83@iU\x81z\x02\xb0V\xb5\xd1\xd1\xe0\x04\x8eZ\xb4?<\xce?\xfenQTب4\b\xd0:jѱ\xe9m\x90߁\xfbvc\x00\x1a}\xe1L\x1b5\u00ad\xa8J2\xa0\xc5a\xe8\x81+\x84\x8ev\xd4\xe0\xe36@%pe<8l\x1dz\xb4Ʌ\ajAD\x94\x05Z\xfe\x03\v\xcea\x81N\x94\x80\xaf(\xd4Z\xbc\xbcF\xc7ఠ\x955\xff\xdai\xf6\xc0\x14\xb7\xac\x15cGa\xff3\x96\xd1YU\v\t\x01߀\xb2\x1a\x1a\xb5\x05\x87\xb2\a\x04{\xa0-\x8a\xf8\x1c~!\x87`lI3\xa8\x98[?\x9bNW\x86\xfb\x80-\xa8i\x825\xbc\x9dư3\xcb\xc0\xe4\xfcT\xe3\x1a\xeb\xa97\xabL\xb9\xa22\x8c\x05\a\x87S՚,\x02\xb71^\xf3F\u007f\xe7\xba\xe8\xf6\xb7\aHy+n\xf3\xec\x8c]\xed\x86c\x14\x9d\xe5]\x82\b\x8c\a\xd5-K\xf8\xf7\xf4ʐ\xb0\xf2\xe1ϋ'\xe87\x8d.\x18r\x1e\xd9\xde/\xf3{\xe2\x85(cKt\xc9q\xa5\xa3&jD\xab[2\x96\xe3GQ\x1b\xb4C\xd2}X6\x86\xc5\xd3\xff\f\xe8Y\xfc\x93\xc3]L[X\"\x84V+F\x9d\xc3\xdc\u009dj\xb0\xbeS\x1e\u007fuڅa\x9f\t\xa5/\x13\u007fXm\x86\x82\x89\xad\xddp_\rF=t\x9cߋ\x16\vq\x98\xb0&\vMi\x8a\x98\x03P\x92\x03u\"\x9f\x1f(\x1eKN\xf9-U\xf1\x1c\xda\x05\x93S+\xfc\x99\x8a\x834?\x83\xeaDZ\x15=,)a)Q\xb1S\r>I\x1e\xa9\x04\xa8\xfb\xa5\x9b\n\x1d\xc6\x15R\x86L!\xa1D\xde0\xb9\xad\xa8\x8d\xa6\xe8\xfch\xfd(\xed\xd1P\xd2\x17\xe1?R\x17\xf4\x0eKth%\xa4S\xf6\xb7\x14k\x04+c\xfb\xd0O\xd5\x11\x98N\xd0/\x13\xda1h稆\xb3\xf5p\x14\xe8\x0f\x8f\xf3\xbe\x06\xf6\x8cv\x90\xf9xNj\x84ȯ4X\xebG\xc5Ջ\xbb\xde\xce˴M\xac\bL\xa0\xa05X࠴\x82\xb1\x9eQ\xe948\xa2\x12@\x12\xc7a'\xff&\xe5\u007fWf\xf6\xe5X\xa8\x06\x95\xce\x17\xf8\xeb\xe2\xfd\xc3\xf4/\x94\xb0\x8e\xeaTE\x81^\xd4(\xc6\x06-\xbf\x01\x1f\x8a\n\x94\x17\x13\x8cC\xbd\x90\x99\xbcQ֔\xe89\xefv@\xe7?\xbd\xfb<\xc6\x19\xc0O\xe4\x00\xbf\xa8\xa6\xad\xf1\r\x98\xc4\xf2\xae\xa0\xf5\xf1a|\"b\xa7\x0f6\x86+3n\xb8\x928\xea\f\xdeDCY=#Pgh@\xa8\xcd3\xce\xe0F2\xf8\x00\xe2\xbf%u\xfes3\xaa\xf37)EnD\xe4&\x01\u06ddY\x87\x19\xb7\aȕb`gV+t8\xcef,\xc4R\xe0\xbe\arb\xbb\xa5\x03\x05Q\xad\xf8,\xd5\x19\xd4'\x80?\xbd\xfb|\x06\xed\x90'0V\xe3\x17x\a\xc6&VZ\xd2\xdf\xe7\xf0\x14#bkY}\x91}\x8a\x8a^U\x92k\x14\xcf@\xaeə(8\x9a\xb7\xa1\xaeղ\xc6\x19\xb0\vǓgӠA\xef\xd5\xear\x1e\xfc\x92d\xd2\r\xab[\x00jI\x81wW\xac.!:\xf3o}\xe7\xf1\xeb/x\x95\xf2\x97A<\x8a\xc4X\\\xed\x92\xf2R`A\xbc\xbd\x84\xe6x\x8b\f\x1eps26\xb7\x8f\x8eV\x0e\xfd\xb1\x0f\xb2\xdeQ'\xedw\x06?\xc5\b\xb8\xda\xe0n\x83\xcb6wBPQ\xddG.\xb1\xaa\xc1\x86f\x89N\f_n\x19}\xcf@\x9f\xe8\xa77\xd4\xd8\xf3\xeeyۯ\xef\xabUR\xd4u\xf0\x85\xb2\xf1IF\xa2\x93\t\xb4\xf1m\xadN[\xf8ކx\xecIpJ\x86\xec\xe3\xa2\xcf.I\xe98\xf7\x9a;u\x84sOv\xb4#\xebS\xc1X\xfe\xe3\xefϞ\x8f\xc62\xae\x06\xa5\xb0\x9b\x15\n\u007f\x14\xfd\xffk\xddg\x0f_\xcf\xca\xf1u\xa5k1\x10}\xa9jE\xc5c5\xeb\xb0\xfc\x9c\x96\x9b\xe1&ߢҌPs4\xb4\u007f\x91\u007f\xbb\xff\x8a.ʺ\x17\xf88\x01\xc9,}\xb0y\xf7\x18Ս\xec\x0f,UH\xaf\x85\xfa\xe1\xf8\t\xfe\xe6f\xf0\xa2\x1e?\v\xb2ڤ?\x1f\xc0\xa7\xcf\x13螨>\xf68d\xf0\xbf\x01\x00\x00\xff\xffV\xe6G\x99\xbd\x18\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VMo\xe36\x13\xbe\xfbW\f\xf6=\xec\xe5\x95\xdcmQ\xa0\xf0\xadM[`\xd1$X8\x8b\\\x8a\x1e(rdOC\x91,g\xe8\xd4\xfd\xf5\x05)ɖm\xd9\x1b,P\xdd4\x9c\x8fg\x9e\xf9 \x17UU-T\xa0g\x8cLޭ@\x05¿\x05]\xfe\xe3\xfa\xe5\a\xae\xc9/w\x1f\x1a\x14\xf5a\xf1Bά\xe0.\xb1\xf8n\x8d\xecS\xd4\xf83\xb6\xe4HȻE\x87\xa2\x8c\x12\xb5Z\x00(缨,\xe6\xfc\v\xa0\xbd\x93\xe8\xad\xc5Xm\xd0\xd5/\xa9\xc1&\x915\x18K\x841\xfe\xee\x9b\xfa\xdb\xfa\xfb\x05\x80\x8eX\xcc?S\x87,\xaa\v+p\xc9\xda\x05\x80S\x1d\xae \"\v\xe9\x88\xc13\x89\x8f\x84\\\xef\xd0b\xf45\xf9\x05\a\xd49\xec&\xfa\x14Vp<\xe8\xad\aH}:\xeb\xe2h=:ڗ#K,\xbf\xcd\x1e\xdf\x13KQ\t6Ee瀔c&\xb7IV\xc5\v\x85\x1c\x80\xb5\x0f\xb8\x82nj%(\x8df\x01\xb0S\x96LI\xb9G\xe7\x03\xba\x1f?}|\xfe\xeeIo\xb1S\xbd\x10 D\x1f0\n\x8dI\xe4oR\xc0\x83\f\xc0 \xebH\xa1x\x84\xf7\xd9U\xaf\x03&\x97\f\x19d\x8b0\x10\x8f\x06\xb8\x84\x01߂l\x89!b\x88\xc8\xe8\xfa\"N\xdcBVQ\x0e|\xf3'j\xa9\xe1\tcv\x02\xbc\xf5ɚ\\\xe7\x1dF\x81\x88\xdao\x1c\xfds\xf0\xcc \xbe\x84\xb4Jp\xe0p\xfc\xc8\tF\xa7l&!\xe1\xffA9\x03\x9d\xdaC\xc4\x1c\x03\x92\x9bx+*\\Ã\x8f\b\xe4Z\xbf\x82\xadH\xe0\xd5r\xb9!\x19[V\xfb\xaeK\x8ed\xbf,\x8dGM\x12\x1fyip\x87vɴ\xa9T\xd4[\x12Ԓ\".U\xa0\xaa\x00w\xa5c\xeb\xce\xfc/\x0e\xfd\xcd\xef'He\x9f\xcb\xc6\x12\xc9m\x0e\xe2\xd2FWy\xcf]\x04Ġ\x06\xb3\x1e\xff\x91\xde,ʬ\xac\u007fy\xfa\fc\xd0R\x82S\xce\v\xdbG3>\x12\x9f\x89\"\xd7b\xec\v\xd7F\xdf\x15\x8f\xe8L\xf0\xe4\xa4\xfchK\xe8NI\xe7\xd4t$\xb9\xd2\u007f%d\xc9\xf5\xa9\xe1\xae\f.4\b)\x18%hj\xf8\xe8\xe0Nuh\xef\x14\xe3\u007fN{f\x98\xabL闉\x9f\xee\x9bSŞ\xad\x83x\\\a\xb3\x15:\x1f\xf0\xa7\x80:\x17,\xb3\x96\r\xa9%]f\x00Z\x1fA]\xe8\xd7\x13\xc7sÙ\xbfF\xe9\x97\x14\x9e\xc4G\xb5\xc1{\xaf'c~\x05\xd5Os\x16#\xac\xbc\xc3\xfaA\xc5y\xc53\xcf\x00\xb2U2\x99PQ\xe4\x0ec>\x93\xc7U\xca\v\xed*\x8f\xabSN㯥w\x9c\xde\xdf\xcc\xe5a\xc6 \xa7\xb2\xf5\xaf\xe0[A7u9\xa2l\xf0\"\x89\x98ܛA\xf6K\xf7\xa3ɭ\xd5\x12ƛ\x00\xd7g\xca#\xcfm\xb2v\xf0Ti\xdf\x05%\xd4X\x1c\a\xb9\xf5\xf1\x02\"\xf5>\xf6\xfdT\u007f\x1d\xbf;oS\x87\x87\xbb\xe1&\xf2\xe7S\xddi\x83\xf4\x82\x01DN\x01\xe2\xe9\x1d7\xfd\x86\x9e`\b\xde\f\x00\x86\xa6\xe5\x9c\xe7\x1b\xb1\xe7\xe2RēmX\xcd7\xff\x89\xc6\\G\x9d(\x9cW\xf3\xe4\xf0\x8c\xaf/.\x03Q\x92\xf8\xed련\x8f\xc4\xea\x14#:\x19\x9c\x94\x9b\xf0\xab\x16\x82U,\x93\xb1ȏ\x9c\x9bu\xbe\xbf\xd4\x1f!eW Y0\x9d\xa2W\xc5s\xf3\xd2\xfa\xd8)YA^\xedU6:;\xcfO,\xd5X\\\x81\xc4t~x}# \xb3\xda\xdc\xce\xe0\xa1\xd7\xe9\xaf\xc2\xc1\x00T\xe3\x93\\!\xb6\\\x8a7\xa8\xbd\x89(l\x15\xdf\xc6\xf3)k̕\x15\xdf\x1a\x1c]\xea\xceCT\xf0\x88\xaf\x17\xb25*s>s\x15/ޤ\xaeVp\x17\x9c7\xf53:\x13l\x89_p#\xb5\xf4\xd2\xe8E\x8d^T\u008b\xd5\x02@hm\xbc\xa0eG\u007f\x01J\xa3\xbd5J\xa1]nQ\x17oa\x8d\xeb U\x85\x96O\xc8\xe7\xef\u007f[\xfc\xae\xf8\xc3\x02\xa0\xb4\xc8ۿ\xca\x1a\x9d\x17u\xb3\x02\x1d\x94Z\x00hQ\xe3\n,:o,\xbab\x8f\n\xad)\xa4Y\xb8\x06K:lkMhVн\x88{\x92 Q\x89縝W\x94t\xfe\xbb\xfe\xea\xf7\xd2y~Ө`\x85\xea\x0e\xe3E'\xf56(a\xdb\xe5\x05\x80+M\x83+x\xa4c\x1aQb\xb5\x00\xd8\v%+\xd6!\x1el\x1aԷO\x0f\xaf\xbf\u007f)wX\x8b\xb8\bP\xa1+\xadl\x98.\v\x00ҁ\x80WV\x80Na\xa4\xc1\xef\x84\a\x8b\x8dE\x87\xda;\xf0;\x04\xd14J\x96|\n\x98Mb\t\xed\x1e\a\x1bk\xea\x8e\xd7Z\x94o\xa1\x01o@\x80\x17v\x8b\x1e\xbe\vk\xb4\x1a=:(Up\x1em\x91\xd84\xd64h\xbd\xcc\xc8\xd1ӳ\x95v\xedH\x87O\xa4d\xa4\x81\x8a\xac\x03\xa3\xa8鎱\x02\xc7\x00\x80ـ\xdfIש\xc4j\xf4\xd8\x02\x91\b\rf\xfd\x0f,}\x01/h\x89\t\xb8\x9d\t\xaa\"\x93ڣ%HJ\xb3\xd5\xf2\x9f-gG\nґJxLW\x99\x1f\xa9=Z-\x14]O\xc0k\x10\xba\x82Z\x1c\xc0\"\x9d\x01A\xf7\xb81\x89+\xe0\a\xbe\x12\xbd1+\xd8y߸\xd5\xcd\xcdV\xfa\xec\x1d\xa5\xa9력?ܰ\x8d\xcbu\xf0ƺ\x9b\n\xf7\xa8n\x9c\xdc.\x85-w\xd2c\xe9\x83\xc5\x1b\xd1\xc8%\v\xae\xd99\x8a\xba\xfaM{Y\x9fz\x92\xfa\x03\x19\x94\xf3V\xeam\xbb̶{\x12w\xb2\xe1h9q[\x94\xbf\x83\x97\x96\b\x95\xe7\xfb\x97\xaf}\xab\x92n\x889\xa3\xdd3\xb4\x0ex\x02J\xea\r\xdaxql[\xc4\x11u\xd5\x18\xa9=\xff)\x95D=\x04݅u-=\xdd\xf4\x8f\x01\x1d\x99\xae)\xe0\x8ec\x04\xac\x11BS\t\x8fU\x01\x0f\x1a\xeeD\x8d\xeaN8\xfc\xd9a'\x84ݒ =\x0f|?\xb4\r\t#Z\xedr\x8eA\x937\x94\xbc\xfb\xa5\xc1r\xe0\x19\xb4In\xb2\x1bo\x8c\x1d8?m)z,\xa7ܒ\x9e\xe8\xdb\x14\x82\x86\xebGB\xfc\xa9%#[\xa1ブ?\x06\xe4\x18\x19}\x12\xc7\xe1\xc2\xf6\xe2e\xff!\x13(\x8eV'\x11\xa4\a?J\x15*\xac\xda0\xe9f%\xbd\x1f\x91s\x16\x11R\x93\x8dS\xd4&qu\xf7\x96\x03\xa4\x98\x90\x92\xecL\xea\xc8\r\xa4f\x15'\x90\xa5Gz\xacGb\xcd\xe8\x04\x9c\x96\xc4Z\xe1\n\xbc\r\xc7g\xc7}\xc2Zq\x98\x84\"\xa7\xd1ːh\xa9\x93\x9b+Y\xf2\x95\xb5\xce\xcc`\xfc\x9apH\xd2\xdc\xc5\xccs\x19\x1a\x0f\xd3{\xb2\x1b\xa1\x83\xf7\x1d\xfa\x1dڜЖ\x9c\x9e\xab\x912]\x96L\x19e\x8d\x1d<䆥\xd1NVh\xa3c\x1e\x01\x06\x0f\x9b1\xc0A\xa9k\xf2m\x11\x94\x8fiȆ\x11\xb6\x17 \xb56F\xa1\xd0SX]\xea>\x0f#\xf2#\xabi='\x9b\x8d\xc9G\x8c\xf4\x8aQ.F|R\x1c\xb0n\xfc\xe1\x1a\x84R}\a\x14\xb6\x03\xf0\x975\xa8\v\x1d\xeb\xe1\x98\xfa\x8cc\x9dFhl\x1c}\x8c:KKt)\xb2\xfe\x1f\x00\xa6\xc4\x1a\xd5\v*,\xbd\xb1\xb3`}ߧ\x8c@Q^\xdc\u007f.\x86o\xbc\x81\x8dT\x1e-\xbcK\xbf\x1b)\xf0\xbeC\x9dp\xa2\x8aD\xeaJ\xeee\x15\x84\x1aXY\x0f\xa5\x0eL0\x16\xb4T\xd7#\x9e\x84q\xde=\xc0\x14\xfe\xc2\xc2\v\xf5\x93|\xf0T\x8a\xa5\xa7\x16\xbe\xdc\xdd\u007fPq\xe4\xba\xf6f\x06\xb6\xe3\r\x11\xb9\x9c\xbe\x18~p\x19;\xaa\x90\xa4Ś\xeb\xae\t\xce\x00_\xd9\xca:*\xd6\xf7\xf6\xf1\xcb\u0600\xe0\xb4\x11\x8d\x84\xbc\x9d\x11$\xf9D{\xbd\x94]r\"\x9e\xe4\f\xa9r\xbe\x06\x01ox\x88E6\xd5\xf1\r\x85\xd2\xcc\xc2\"\x97\xe7|\xd1ox`\xa2TqOr\x9d\xbb\x94\xf8\xbc\xe1\xe1ԫ#u\xe9\xbcT\xfdD\xbdi\x81\xa5\xe2z,\xab\xca\xdd\x15\x9eR\x92\x1eo\xa6\x85\x85yO\xcdOF\xe4B\xb1[\x00{] C\xfc\xc9E8ɾv26x3R;d\xdb\xcb\xfd\xcd+u\xaa-\xf3hQ\x0f\xfa\x1a\x1e\x8d\xa7\x9f\xfb\x0fI5\xbb\xd0\xe3\f\xda=_\f\xbaG\xe3\x99\xf6\xbf\x82$\nu! \x91\x98\rT\xc7\xd8Fz\xf5\xdb\x1f\xc7уn5\xeb7\xa3\x84tԂ\x18\x9b5\xe7\xb65\x1e\x11\x99\xd7\xc1qǢ\x8d^rD\xca\xdcg\x98\xb6\x97&]\x86\xd2\xd8\x01^'\x0e\x9a\xe1\xb9FH\xc7\u007f\xa5F,\ue26d\xb4\x12%VP\x05\x86\x80[A\xe1q+K\xa8\xd1n\xe7\xe4l(N\x9d\xbe\xba\x99H\x12\x9f\v\xee\xf6t\x16\xcaO\n;\xd5\xf4AK\xb2\xf5\x13of\xafw\xb2W\xbbL*\x0eߜ\xe0&\xb5\x17U%c\x86y:\x13\x9f\xce\xe03\xce\x19\xf1ДhEC\x96\xfd/\n\xa7l(\xff\x86FH\xeb\n\xb8偔\x9a\xbe\xd9>}\xaa<\xfa\xac\x89\xabt@\x98\uf162PO\x81C\x03*\x0e\xfc\x93,\xcdf\x94Ѯ\xe1}g\\\x8c\xe2\x1b\x89\x8a\xa7\x10Wox\xb8\xba\x1ex\x1e\xc8\xe9Pz\xf5\xa0\xafb\x92\x18\xf9A\xdb\xf0\x19\xad\x0ep\xc5ﮊQ\x12\x9cd;\x9b\x18g,\xe2䫶\xd2\xfdA4\x8d\xd4\xdb\xe3{\xbe\xcc\x16f\xec``\x03\x8fG\xa7\r\f\xa1_\x96\x0eJ\xf8\xf1qq\xcc7Q\xec\xdb\xbd\xceW\xf2\xcf-\xd9D\x1f\xd8S\x96*\xc5V\x81\xa7ױ\xe5p\xf1\xe9\xb4h\xdc\xcex\xf8f/E\x9aj\x99P5\xd6\xec\xa9\x1f\xfc\xf6\u007f\xd4ѹr\x87UPxvh\xf3\xd2#b\x86F\xaf\xa5\xbe\xb0\x05&\xb4\xda\xde貸\x81\xe6\a?)`\xe2w\xf15\xcc\xe7\xaeLZ\xf7\x97N\xf7'i\x1d\xffTd\xa5\x11Yk=\xee\xb5R\xed\xcbL\x98\xa6\xff\x02\xc0&\xba\xc0\x1b\xf8LK\x15\"\xc1\xf4\x02\xe0(2\x99\xf2F\xfc\xe2\xba@\xf5\xe1\xcb\xe6\xe1\xf7\xb4P.|'@\x8a61\xb2\xe0q5\x0e -\bx\xe0]\x80\t\xf4\x06w\x10\x0e\f\x16\x06-*G#\n\x83\xab\n\x8d\x14\xb4\t0\x01\n4R\xa72\x81\x1fD\xf2X\x16~\xaa=\xe82Ka\x8b`J\xb5\x0ec\v\xa3\v4NV4\xa2\xd6\x12\x8b\xba\xaf\x87\xe9;ڊ\x1f\x03)\t\x02Zp\a\x84\xc0NL\x99<\xb9\x00\xbd\x03w\x90\xb6\xc1\x9bI\xd2\x02\v4D(\xd0\xdb\u007f`\xe2\xd6\xf0\x15\r\x01\xa9\xb0M\xb4:\xa2\xa1}'z\xaf\xe4\xbfj\xc8\x16\x9c\xe6%3\xe10\xb0\xacjR94JdĄ\x12\xaf@\xa8\x14rq\x02\x83\xb4\x06\x94\xaa\x05\x8d\x87\xd85\xfcU\x1b\x04\xa9v\xfa\x06\x0e\xce\x15\xf6\xe6\xfaz/]\xa5\b\x89\xce\xf3RIw\xbafq\x96\xdb\xd2ic\xafS\x94\xc9\xc4\x163\xb0\x98a\xe2\xb4\x19#\xcb<\xd3}\x8b\xb3\x85#\xf4\x1c\xb0\x8a\xc1\xcf\x05\xaf\xd7lp\x12(\x90\xbd\u007f:\xc8\xe4\xe0\xa3\x0e\x92)\x86\x04\xa9F˶@\x14Ev\x1a\xdf,\xccKBXh\xda\x1c4m\xd60\xf4a\x0e\x9b\x88\xa6E\xd9Ӧ\xcdX\xd6.\x9dk\x11y#s\xe54^$Л\xb3ɯ-\xd0D`I'\x8e\xcd\x0e0/\xdc\xe9\n\xa4\xabz\xe7a\x8a,k\xe1\xf0\x8b`\xd4K\xf4aӟ\xfb\xca\xfa\xf0\n\\\xaaQ\xf8\xbff\x12;\x9b\xaf\xc1\xd7,`Ч\xf6\xbc+\x90\xbb\x9aA\xe9\x15\xecd\xe6\x90\xe3\x9ei\x14[\xaeo\x96S\xafE\x968\xafI-\x17.9\xdc\xd5\a\xab\xd9\xf1=\n\xf5\xa7\xfbX\xb9:It\x9d\xfc,d\xe0s\xb74\x98\xfb\xd3\xfc=\xeb@\xd3Ñڇ\xcf\x1f1\x9d&\x14\xc4J\xe4\xd9v>\xf4Pn/\x1f\x8e\x01\xf1\x9b\t\x01U}\xc2\xf2Y\x9a+\x10\xf0\x88'\x1f\x05\t\x05\xc4(AK\x8d\x1e$Ή\xc4\xe9\"6\x11\x8fxb@!\x03\x141?^4|{\xc4S\xdc\xc0\x1e)\t\xb3p>\xf64\xa5\x0e&\b'\x12\x96\x90\x118\x9f\xc7\x1a\x02N\xc7l\x12\x96\x98\x9b\xaaU\x9cx\xd1vk6v\x92\x9b\x8fxzg=\xc3H;\x0e\xb2\x88\xde0\x19`\xb0\xc8zT\xe5\xf7\x1eD&\xd3z)\xaf\x0f\x1b5\x15uw\xdbg\xed6\xea\n\ue7a5%\xf4T\n\x1f5\xda\xcf\xdaq\xcf7#\xacG\xffEd\xf5SY\xf5\x947\xf3D\x8fv\xda0J\xe8}\xdb\xf8\x13f\xcd*ia\xa3\xe8\xac\x14\xe8\xc2\xc9_\x86\x19/\x96\x8cR^Z\xce\x0f*\xadV\xech\xd7\x03kE\xc3\f\xecѦÝ6z\xade\xa3\xa1ҁΣvO\xbe\xc7C\xf0I\xedL$\x98BZ2QE4D\xeb\x8cp\xb8\x97\t\xe4h\xf6\b\x05\xf9\x82XnD\xdbg\xdf\x16\xcb\\lhP\xb5`\xe8\xd3\x18\x94V\xa4\xd7Q\xe3*\xf6G\f\x1e\xcc\xd2N\x0f\x8e\xd9\x1b;h\x8ec\"\xa8-ҔӘ\"\xfb\xb2\xc8K,\xe2\xcey\xfc\xe0\xd1\xf3\xfe5\x17\x05i\xf8\xbf\xc9E\xb2\xb0\xff\a\n!M\x94\x96\u007f\xe0\x9b\xad\f;\xb3C֭\xbd\x10\xad!-\x10Ǐ\"\xeb\xdf\x01\x8clQ\x93\x05\xc2̇\x02zw\x16\xf9\\\xc1\xd3A[\xef\x91w\x12\xb3\xe1\xc4W\xb7I\v\x97\x8fx\xba\xbc:\xb3K\x97\x1bu\xe9C\x84\xbe\xd6G\x80\xad#\x0e\xad\xb2\x13\\\xf2\xec˟\x17NEKg\xe4@\xbe\x06\x8d\x0f\xc4\xe9$[E\x134\xb5\xbe\x92\xa3\x10z\x1a\xfbH\xd9,\xb4u\v\x10\xfa\xa2\xad\xf3\x19\xd1N\xc0\xbb,\xdf\x06^\xaeB\x9e\r\xc4Ρ\x01봩.\xc0\xc8H\xf6\xd2\xc6\xc4E;w\xe0 \xc6\xd6\xd9;\x0f\x96\x0es\x97\x8d~{\xfb{\xe9o\xc6\xe8\xef9\x88\t\a0\f\xb90:Ak\xe7\xc4&\xca\xc2\xcf$6뤦\xf0\x87%\xbeq\x9a\x15ֹdkՖ\x84\xc2D\xce\xc5lj\xbb\xe7V^\x96\xcc\a\xfd?/\xb2˱\x03\xd6\xfa<\x17*ʁ\x9d!z\xeb\xe7V*\x16@\xf9#\x8aٗl.\x96D\xaeA\xf8\xbe\x9f` \x97jË\xc0\xfbo\x12>\xd4F\x17_v|\xb8\xadf7,\xa8;\x86\xaf\x0e\xc7Z\xa1\xf9\xbe\xc2`\x87\x93\xe7Y\xfdEa\xb3Ү\x9d\xfa ȅN\xdfY\xd8Ic]\x83l4Li\xa1\x9c\xb5 M[~\x96SwƼ\xf0(\xf7\xa3\x9f\xdbJ\xc6\x1d\xf4S}\xcd̈́\\\x10u\x1f\xc4\x11A\xee@:@\x95\xe8Rq҈\x8c\x01/\xe2\xd9\x11/\xc8\x10\xeb\xf7\x9a\x86\xaa\xccc\t\xb1bI\x94j&\xbfԞ\xf0'!\xb3o\xc5F's\xd4\xe5\x8ccnZ\xf7\x96\xde\xcf\xed\xd4/\xe4\xe2Y\xe6e\x0e\"'FD\x93\x9c\xa2\r\x99cW\x06\xe0IH\xc7\x1e\x89 \xb3{r:\x1ad\xa2\xf3\"C\x87\xb0ŝ6\xac\xefV\xa6X\xbb\xfe \x17z\xfcޱ\xdf\x04\xec\x84\xccJ\x13mu\x17rc\xd9\t)\x18\x9e\xd7=\xf8Ģ\xb0b\xf2E\xa5\xa3\xa3C\xda9OP\x98%\x01\xed\x17\x83\xaf\x1d>\x16F\x92,\xea\xb9\br\x06\"Ǘ\xdd\b2\x88\xa8P\xa7\xb1\x10r\x06&c\xf1\x16B\xbe\x85\x90qp\xdfBȷ\x10r\xbe\xbd\x85\x90o!d̄\xb7\x10\xf2-\x84\\\x8e\xc2\xff>\x84\x9c\xc7lŹ\xcfџ#\xb0\x89*!\x98Fvr\x95P\rs\x9b\x95֡\x89\xaf\xb9\xdd\f\xcfk\xd9ϧ\x03\xba\x03\x1aH\xfc\x90\x15?W\x19\x96\x8d\xa6ܢqYu\x11.)[\xa5(\xbe\x8a}6:\x8e,\xae\xddj\x9d\xa18W\xb6\xb8R\xae\xb9\x02\xaen\rr]\xe8u\xeb\x88_+Ɦ\xf6\x89\xa8\xf1\x89\x88\xcb\xe70\x8d\xa8\xe2YV\xbb\x13A\xc3\x17\xd6\xe9\xd4U8\xa3k/\xad\xce\xe9\xd6ތ\x82\x8d\xa9\xc9\x19\xa9\xb8\x19\x859Y\x89\x13[g3\n}\xd6}\xcfH\xce\xe4\xcfV\x89\xc2\x1e\xb4{\xd0Y\x99G\xc4\xcc_\xbb\xe3\a\x8e^\x14\xb1\x89G\x84$\xd3eZ\xc3\x1f\xde\x1e\xbf\x87?\xc1\x97\av.\xfc\xd0/i\x9e@\x06\xf7Q\x85r\xfd\x17\x92\xc3ov\xe1\xe7\x1fŬ\xd3F\xec\xf1\x93NZ\x1f5\x98\xa2Iw|\xe7\xcdz`~\x95l\tUI\xc3ak\xd8Q\x1f\\\x93c\xf5Qp\xeb\xbcJ\x98\x0e\xcbŤ\xe6:\x97\xcdn\xea\xfe\xfe\x93߈\x939\xae?\x96\xfe\x18\xbc*\x84\xb1H\xb4\xad6\xe8'm\xc7\f\xc4A?A\xa6\xc3\xee\u007f\xe8\xe3o\x90\xb3\xb9|\xde^\xbc\x8b#\x8b`%\x90\x15\xb9\xe6E\xf8ax^+\xf2n1\x8d\x8f~c\xb2;\x06IX\xab\x13)\xf8M\xa8t\xe1\x19Ƿx\xd0;\xee\x10F\x94~ȏ\xad\x86\x9e\x95\xaf\xea7\xee\x173@\xad\x13\xae\xb4\xf3\x1f\t\xe0a\x90\x88\u0095&$I\x93\xd2\xf0\xd3^\x02\x81\xfe\x05\xec\xf2\xcf\x04d\xc2:/X\x93\xaf\xf1?\xd5Ú(\xdd:\x9fm\xad4\x0f\x9e\x84\xe5w\xf8>\xd7*m\xf7\v*\xed6\xf2\x12\u007f\xa7M.\xdc\r\xa4\xc2\xe1\x8a`\xf7~\x9f\xb4L\xa3\xcc\xe6\xa7ϓ\xbb\xfbB#\xea\x1b\x9d@V\x9eV=\x98\x1e\xd9\xc9P\xca~\x05\x9f\xf1\xe9\xac\xefN\x11\xe2\xfd\\\x9a\xcf\xcac\xfaP\u007f\x03&vS\xcdWc\xf8\x12\xe4L\x01\xba:\xdb\x1b\xdc\xcb\xd4Љ\xbf\x81\xe7/<,\xfcZ\x9e\x1bZ>~%\xb4\x93\xdf\xf4~\x1b\xd1\xc2\t\r\x1c־\x01%\xe9u5\x1f\x02z\xdf\xfc\xc7K\xaf\u0087\u007f\x8e\xfe\xeeТ9bڒ\x95\xe0\x99BO\xa3y\"I\xb0p!\x13\xd8\xfe\x02\xd0\xe5%\xffS}\xe0\x87\xffM\xb4\xf21\xa0\xbd\x81\xbf\xfd\xfd\x02\x82\x17y\xa8\xf0\xa0\xce\xff\x06\x00\x00\xff\xff\x80wp\xca4I\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\x1cMo$;\xf1\x9e_Q\n\x87\x05)3aAH(\xb7}\xd9 \"\x96}\xab\x97(\x17\xc4\xc1\xd3]3c\xd2m7\xb6{\x92\x01\xf1\xdfQ\x95ݟ\xd3\x1f\xee\xbc\x04-O\xf1)\xf1\xd8\xe5r}\xbb\\\xee\xb3\xd5ju&\n\xf9\x80\xc6J\xad\xae@\x14\x12\x9f\x1d*\xfaϮ\x1f\xffh\xd7R_\x1e>nЉ\x8fg\x8fR\xa5Wp]Z\xa7\xf3\x9f\xd0\xea\xd2$\xf8\x19\xb7RI'\xb5:\xcbщT8qu\x06 \x94\xd2NP\xb7\xa5\u007f\x01\x12\xad\x9c\xd1Y\x86f\xb5C\xb5~,7\xb8)e\x96\xa2\xe1\x15\xaa\xf5\x0f\xbf]\xffn\xfd\x873\x80\xc4 O\xbf\x979Z'\xf2\xe2\nT\x99eg\x00J\xe4x\x056\xd9cZfh\xd7\a\xcc\xd0\xe8\xb5\xd4g\xb6\xc0\x84V\xdb\x19]\x16W\xd0\xfc\xe0'\x05L\xfc.\xee\xc2|\xeeʤu\u007f\xe9t\u007f\x91\xd6\xf1OEV\x1a\x91\xb5\xd6\xe3^+ծ̄i\xfa\xcf\x00l\xa2\v\xbc\x82\xaf\xb4T!\x12L\xcf\x00\x0e\"\x93)o\xc4/\xae\vT\x9f\xbe\xdd>\xfc\x9e\x16ʅ\xef\x04H\xd1&F\x16<\xae\xc6\x01\xa4\x05\x01\x0f\xbc\v0\x81\xde\xe0\xf6\u0081\xc1\u00a0E\xe5hDapU\xa1\x91\x826\x01&@\x81F\xeaT&\xf0\x83H\x1e\xcb\xc2O\xb5{]f)l\x10L\xa9\xd6alat\x81\xc6ɊF\xd4ZbQ\xf7\xf50\xfd@[\xf1c %A@\vn\x8f\x10؉)\x93'\x17\xa0\xb7\xe0\xf6\xd26x3IZ`\x81\x86\b\x05z\xf3\x0fL\xdc\x1a\xee\xd0\x10\x90\n\xdbD\xab\x03\x1a\xdaw\xa2wJ\xfe\xab\x86l\xc1i^2\x13\x0e\x03˪&\x95C\xa3DFL(\xf1\x02\x84J!\x17G0Hk@\xa9Z\xd0x\x88]\xc3_\xb5A\x90j\xab\xaf`\xef\\a\xaf./w\xd2U\x8a\x90\xe8\x13\xa7\x1b}%9yڣ\"-2\xa5\"\f{\x10!\x18\x8fu\xaf\u007f\x90v\xfc\x03\xe6\x05)\xe3$j\xf7a\x10\xa1FTIk/\xe2M\x05\xd6&K\aK\x05z\x18\xbb\xc2\xe8\x83L\x83=\xe8Qo\x8a\x82\x1e\xa5\xad(3\xf7\xa0\xb32G{\xaf\u007fB\xebdr:\xae\x87\xfc\xe7\xc1i\x15g\xd1\x12E\xdd\x1e\r)\x1e\xff\xc06l\x00*\xb0FXLو\x89G\x04\x01\x1b\xbfo\xb2\x86Y\x06\x85N\xe1\xe0ׁͱB\xb8ϋ\x86\x1f\x1b\xad3\x14\xea\xe4w|N\xb22Ŵ\xf6M\x03\xd4\xe8\xed\xf2\xe6d\n\xfbp!\x15I\x13yLBR5\xbf\x92w\x19ܤ0\b\xa4\xfeRy\x88 \x99\x95a\xabC\x9b\x91\x0e\xf3A\f'\xe4\xce7\x8a\x11\xc4&\xc3+p\xa6<\x95\x96j\xbe0F\x1cG\xa9T\xc56\xf1D\xaag\x04\xa3\x9c\xc9\x04\x89<\xb5\xe9e:\xfd\x02H\xb4\xd7\xfaq\x9e,\u007f\xa6Q\x8d[\x81\x84CF\xd8\xe0^\x1c\xa46\xb6\x1f\x89\xe03&\xa5\xc3a\x1d\x11\x0eR\xb9ݢ!H\xc5^X\xb4\x95\x91\x18'ϔ\xdaS3S,>\xd9O\xc3^b\x14\xd3`l\vlNG`\x02#L6\xb7,@\xaaT\x1edZ\x8a\f\xa4\xb2N\xa8\xc4\xefKԸ\r\xed\v\xa6Y\u007f\x82\xb97\xa3\x15\xfeė\x8eG\xd2\nA\x1b\xc8\xc9\xfd\x9e\x0e\xb5\xa3k\xc0\xe8\xf67\x82\xec\x997\xd6`(`\x0e\x8b\xa5\xec\xec\x1a{q1\x01\xbc\xe6\x8e\x0f\xda2\xb1\xc1\f,f\x988m\xc6\xc82\xcft\xdf\xe2l\xe1\b=\a\xacbc\xf7i\xcb\xcd\x06'\x81\x02\x99\xfc\xa7\xbdL\xf6>\xbe\"\x99bH\x90j\xb4l\vDQd\xc7\xf1\xcd¼$\x84\x85\xa6\xcdA\xd3f\rC\x1f氉hZ\x94=mڌe\xedҹ\x16\x91w2WN\xe3E\x02}{2\xf9\xb5\x05\x9a\b,\xe9lu\xbb\x05\xcc\vw\xbc\x00\xe9\xaa\xdey\x98\x14\x0658\xfc\"\x18\xf5\x12}\xb8\xed\xcf}e}x\x05.\xd5(\xfc_3\x89\x9d\xcd]\xf05\v\x18\xf4\xa5=\xef\x02\xe4\xb6fPz\x01[\x999\xe4\xb8g\x1aŖ\xeb\x9b\xe5\xd4k\x91%\xcekR˅K\xf67\xf5\x11rv|\x8fB\xfd\xe9>V\xaeN\x12]'?\v\x198\xc3 \r\xe6>oq\xcf:\xd0\xf4p\xa4\xf6\xe9\xebgL\xa7\t\x05\xb1\x12y\xb2\x9dO=\x94\xdbˇc@\xfcfB@U\x9f\xb0|>\xea\x02\x04<\xe2\xd1GAB\x011J\xd0R\xa3\a\x89S\"qb\x8cM\xc4#\x1e\x19P\xc8uȄ\x17\r\xdf\x1e\xf1\x187\xb0GJ\xc2,d\x02\x17\xf0\xb4\xd7\xd6{\xe4\xadđ\xe4p\xb7I\v\xe7\x8fx<\xbf8\xb1K\xe7\xb7\xea܇\b}\xad\x8f\x00[G\x1cZeG8\xe7\xd9\xe7?/\x9c\x8a\x96\xceȁ|\xe1\x1b\x1f\x88\xd3I\xb6\x8a&hj}\xf9H!\xf44\xf6\x91\xb2Yh\xeb\x16 \xf4M[\xe73\xa2\x9d\x80wY\xbe\r\xbc\\\x85<\x1b\x88\xadC\x03\xd6iS]\xf5\x91\x91쥍\x89\x8bv\xee\xc0A\x8c\xad\xb3w\x1e,\x1d\xe6\xce\x1b\xfd\xf6\xf6\xf7\xdc\xdf\x01\xd2\xdfs\x10\x13\x0e`\x18rat\x82\xd6ΉM\x94\x85\x9fIl\xd6IM\xe1\x0fK|\xb76+\xacs\xc9֪-\t\x85\x89\x9c\x8b\x8f\x137ϭ\xbc,\x99\x0f\xfa\u007f^d\x97c\a\xac\xf5y.T\x94\x03;A\xf4\xdaϭT,\x80\xf2G\x14\xb3+\xd9\\,\x89\\\x83\xf0}?\xc1@.\xd5-/\x02\x1f\xdf$|\xa8\x8d.\xbe\xec\xf8p]\xcdnXPw\f_\x92\x8e\xb5B\xf3}\x85\xc1\x0e'O\xb3\xfa\x8b\xc2f\xa5];\xf5A\x90\v\x9d~\xb0\xb0\x95ƺ\x06\xd9h\x98\xd2\xf2%\xe9\u06dd\xe5ԍ1/<\xca\xfd\xe8綒q{\xfdT_\xe8\x8f_\xfc\x0e5\xbe\x1eC\x90[\x90\x0eP%\xbaT\x9c4\"c\xc0\x8bxv\xc4\v2\xc4\xfa\xbd\xa6\xa1*\xf3XB\xacX\x12\xa5\x9a\xc9/\xb5'\xfcI\xc8\xec\xad\xd8\xe8d\x8e\xba\x9cq\xccM\xeb\xd6#\xf8\xb9\x9dJ\x8d\\<˼\xccA\xe4Ĉh\x92S\xb4!s\xec\xca\x00<\t\xe9\xd8#\x11dvONG\x83Lt^d\xe8\x106\xb8Ն\xf5\xdd\xca\x14k\xd7\x1f\xe4B\x8f\xdf;\xf6\x9b\x80\xad\x90Yi\xa2\xad\xeeBn,;!\x05\xc3\xf3\xba\a\x9fX\x14VL\xbe\xa8tttH;\xe7\t\n\xb3$\xa0\xfdf\xf0\xb5\xc3\xc7\xc2H\x92E=\x17A\xce@\xe4\xf8\xb2\x1bA\x06\x11\x15\xea8\x16B\xce\xc0d,\xdeC\xc8\xf7\x102\x0e\xee{\b\xf9\x1eBη\xf7\x10\xf2=\x84\x8c\x99\xf0\x1eB\xbe\x87\x90\xcbQ\xf8߇\x90\xf3\x98\xad8\xf79\xfas\x046Q%\x04\xd3\xc8N\xae\x12\xaaa\xae\xb3\xd2:4\xf15\xb7\xb7\xc3\xf3\x06\xea\xaf\x13?d\xc5\x0fs\x86e\xa3)\xb7h\\V]\x84K\xcaV)\x8a\xafן\x8d\x8e#\x8bk\xc7\xea\xb4\xe3J\xb9\xe6\n\xb8\xba5\xc8u\xf1TU\x84i\x99F\x99\xcdO\x9f'w\xf7\x8dF\xd47:\x81\xac<\xadz0=\xb2\x93\xa1\x94\xfd\n\xbe\xe2\xd3Iߍ\"\xc4\xfb\xb94\x9f\x95\xc7\xf4\xa1\xfe\xdaM즚\xef\xe3\xf0%ȉ\x02tu\xb67\xb8\x97\xa9\xa1\x13\u007f\x03\xcf_xX\xf8\xb5<5\xb4|\xfcJh'\xbf\xe9\xfd6\xa2\x85\x13\x1a8\xac}\x03J\xd2\xebj>y\xf4\xb1\xf9\x8f\x97^\x85O\x1c\x1d\xfcݡEs\xc0\xb4%+\xc13\x85\x9eF\xf3D\x92`\xe1B&\xb0\xfd\xad\xa3\xf3s\xfe\xa7\xfa\x94\x11\xff\x9bh\xe5c@{\x05\u007f\xfb\xfb\x19\x04/\xf2P\xe1A\x9d\xff\r\x00\x00\xff\xff\xcd6\xa5\xa7\x1eJ\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xe44\x10\xbd\xe7W\x94\x96\xc3^H\x9a\x01!\xa1\xdcP\xc3a\x04\x8cFӫ\xb9 \x0e\x8eS\xe9.Ʊ\x83\xab\xdcC\xf3\xeb\x91\xed\xa4?\xd2靝\xc3\xe6\x96J}<\xbfzUqQ\x96e\xa1\x06zF\xcf\xe4l\rj \xfcW\xd0\xc67\xae^~\xe2\x8a\xdcj\u007fנ\xa8\xbb\xe2\x85l[\xc3:\xb0\xb8\xfe\t\xd9\x05\xaf\xf1\x17\xecȒ\x90\xb3E\x8f\xa2Z%\xaa.\x00\x94\xb5NT4s|\x05\xd0ΊwƠ/\xb7h\xab\x97\xd0`\x13ȴ\xe8S\x85\xa9\xfe\xfe\xbb\xea\xfb\xea\xc7\x02@{L៨G\x16\xd5\x0f5\xd8`L\x01`U\x8f50\xfa\x18$J\x02{\xfc' \vW{4\xe8]E\xae\xe0\x01u,\xbc\xf5.\f5\x9c>\xe4\xf8\x11T>\xd0&\xa5ڤTO9U\xfaj\x88\xe5\xb7[\x1e\xbf\xd3\xe85\x98\xe0\x95Y\x06\x94\x1c\x98\xec6\x18\xe5\x17]\n\x00\xd6n\xc0\x1a\x1e\"\xacAil\v\x80\xbd2Ԧ\xf3g\xa0n@\xfb\xf3\xe3\xfd\xf3\x0f\x1b\xbd\xc3^e#@\x8b\xac=\r\xc9o\t$\x10\x83\x82\xb1\x14\x88\x03\xa552\x83\x0eޣ\x15\xc8P\x80l\xe7|\x9fʍ\x89\x01Tむ\xec\x10\x9e\x13w#\xf8jt\x18\xbc\x1b\xd0\vML\xa6\x90\x93\x8e\x8e\xb6\x19Ə\xf1\x10\xd9\aڨ\x1c\xe4Tc\xec?\xb6\xc0\xe9\x80\xe0:\x90\x1d1x\x1c<2Z\xb9D\x978\xe9@Yp\xcdߨ\xa5\x1aO\xcf\xc0;\x17L\x1b\xe5\xb6G/\xe0Q\xbb\xad\xa5\xff\x8e\x999\xd2\x10K\x1a%S\xa3\xa7\x87\xac\xa0\xb7\xcaD\xfa\x03~\vʶЫ\x03x\x8c5 سlɅ+\xf8\xc3yL\x04ְ\x13\x19\xb8^\xad\xb6$\xd3\xe4h\xd7\xf7\xc1\x92\x1cVI\xff\xd4\x04q\x9eW-\xeeѬ\x98\xb6\xa5\xf2zG\x82Z\x82Ǖ\x1a\xa8L\xc0m\x1a\x9c\xaao\xbf\xf1\xe3\x98\xf1\xc73\xa4r\x88\x82a\xf1d\xb7Gs\xd2\xf2Mޣ\x8e\xb3\x1arX\xc6\u007f\xa27\x9a\"+O\xbfn>\xc1T4\xb5\xe0\x92\xf3\xc4\xf6)\x8cO\xc4G\xa2\xc8v\xe8s\xe3:\xef\xfa\x94\x11m;8\xb2YK\xda\x10\xdaK\xd294=\tO*\x8d\xfd\xa9`\x9d\xf6\a4\bah\x95`[\xc1\xbd\x85\xb5\xeaѬ\x15\xe3W\xa7=2\xcce\xa4\xf4m\xe2\xcf\xd7ޥcf\xebh\x9ev\xd2b\x87\x16\xa6w3\xa0\x8e=\x8b\xc4\xc5X\xeaH\xa71\x80\xceyPK!՛\x18\x92\xf7\xbbP\x8c;\"\xe3\x98m\x8e8\x83o\xe1XZ\x15ɾS\x8c\x97\xa6\x19\x9a\xc7\xe81\xafl\xa8C}\xd0\x06s\x82\xbc)\xf0-\x10\xf1A\x1b\xfay\xbd\x12\x1e\xf0\xf5\xca\xf6\xe8]ܓi\x15\x9f?\x8b\xfd\x87\xfc\x13ؒ\xe5ϟ&\xfb\xa4\xdf\xca\xf9\xca=[\xb5c\x1a\xf0\xc1\xda8\x91\xceF\xf3,)\\n\xe4\xd9W\x12\xec\xafp,\"\xb9\xb7\x9dK\xbfe\x15K*\xc9s\x82cS\xc7\x1a\x19\xd1U\xba[=\xcd\xcf|\x15}\x01\x81\xf9I\xbf\xf6\xf7\a\xc6\xd5A\x1e\x17j\x96\t˂9V\xba2/N̈,\x18\xa3\x1a\x835\x88\x0f\xf3\xc8\x1c\xa7\xbcW\x87KUL2:]b>+\x90+\xf7\xa8\xfd\xd7\x1d\xda[\n\x87W\xc5K\xbd\xc9i\xa09\xdc\n\\\x1foc\xf3!ɲ\xac!n\xddR芥/ b\xa1KY\xaa\v\xb7\x83+\x126\xe7\x9e\xd3\xec_\b~\xba,̑\xdf(\xbe\xd0ԙ\xe9t\xf9\xbc;\xbd%a\x97\xe3e3}\x18Oў\x9d\x9c\xc5y\xb5\x9d\xb88\xed\xd6x\xcd\x1a\x04ۇ\xf9U\xf3Ç\x8b;cz\xd5ζ\x94o\xca\xf0\xe7_EΊ\xed\xf3\x84#\x1a\xff\x0f\x00\x00\xff\xff\a2\x1f\n\xa8\v\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f$5\f\xbdׯ\xb0\x96\xc3^\xa8j\x06\x84\x84\xea\xb6\x1a8\xacXV\xa3\x9de.\x88C:qu\x9bI%!v\x1a\x86_\x8f\xf2Q\xd3]=ݻ D\xdd\xe2r\xec\xe7\xe7g+]\xdf\xf7\x9d\n\xf4\x80\x91ɻ\x11T \xfcS\xd0\xe5\x13\x0f\x8f\xdf\xf1@~s\xb8٢\xa8\x9b\ue45c\x19\xe16\xb1\xf8\xf9\x03\xb2OQ\xe3\xf78\x91#!\xef\xba\x19E\x19%j\xec\x00\x94s^T6s>\x02h\xef$zk1\xf6;t\xc3c\xda\xe26\x915\x18K\x86%\xff\xe1\xab\xe1\xeb\xe1\xdb\x0e@G,\xd7?Ҍ,j\x0e#\xb8dm\a\xe0Ԍ#\x1c\xbcM3\xb2S\x81\xf7^\xac\xd75\xd9p@\x8b\xd1\x0f\xe4;\x0e\xa8s\xee]\xf4)\x8cp\xfcQC4\\\xb5\xa6\x87\x12\xed\xbeE{ע\x15\aK,?~\xc2\xe9\x1d\xb1\x14\xc7`ST\xf6*\xb2\xe2\xc3\xe4vɪxͫ\x03`\xed\x03\x8e\xf0>C\fJ\xa3\xe9\x00\x0eʒ)\x0e\x15\xb4\x0f\xe8\xdeܽ}\xf8\xe6^\xefqV\xd5\b`\x90u\xa4P\xfc\xae\xa0\x05bP\xb0\xa4\x83?\xf6\x18\x11\x1e\n5\xc0\xe2#rC\xd6B\x02,\x10yh\xa6\x10}\xc0(\xb40\x98\xbf\x13\t=\xdb\xce\xf0\xbc\u0380\xab\x0f\x98,\x1ad\x90=Bk=\x1a\xe0R\f\xf8\tdO\f\x11CDF'\xc7^,\x9f\x9f@9\xf0\xdb\xdfP\xcb\x00\xf7\x18s\x10\xe0\xbdO\xd6d\xa5\x1d0\nD\xd4~\xe7\xe8\xaf\xe7\xc8\f\xe2KJ\xab\x04[Ӗ\x8f\x9c`t\xcaf\xaa\x13~\t\xca\x19\x98\xd5\x13D\xcc9 \xb9\x93hŅ\a\xf8\xc9G\x04r\x93\x1fa/\x12x\xdclv$\xcb\xd0h?\xcfɑ\x01\xf2\xd24#HL5ySZ\xb3\x1c\xb5\xa0\xb4\xc6 hޟ?V^\xbdZ\xbd7\xcaQ{WǔG\xf8\xe5\u05eeFE\xf3\xb0\xe0\xc8ƿ\x03\x00\x00\xff\xff\xcf^\xca\x05\xed\t\x00\x00"), } diff --git a/design/backup-resource-list.md b/design/backup-resource-list.md index 309679a425..aa56631962 100644 --- a/design/backup-resource-list.md +++ b/design/backup-resource-list.md @@ -57,10 +57,10 @@ The Backupper currently initialises a map to track the `backedUpItems` (https:// This property will be moved to the [Backup request struct](https://github.com/heptio/velero/blob/16910a6215cbd8f0bde385dba9879629ebcbcc28/pkg/backup/request.go#L11), allowing the BackupController to access it after a successful backup. `backedUpItems` currently uses the `schema.GroupResource` as a key for the resource. -In order to record the API group, version and kind for the resource, this key will be constructed from the object's `schema.GroupVersionKind` in the format `{group}/{version}/{kind}` (e.g. `apps/v1/Deployment`). +In order to record the API group, version and kind for the resource, this key will be constructed from the object's `schema.SchemeGroupVersionKind` in the format `{group}/{version}/{kind}` (e.g. `apps/v1/Deployment`). The `backedUpItems` map is kept as a flat structure internally for quick lookup. -When the backup is ready to upload, `backedUpItems` will be converted to a nested structure representing the metadata file above, grouped by `schema.GroupVersionKind`. +When the backup is ready to upload, `backedUpItems` will be converted to a nested structure representing the metadata file above, grouped by `schema.SchemeGroupVersionKind`. After converting to the right format, it can be passed to the `persistBackup` function to persist the file in object storage. ### Changes to DownloadRequest CRD and processing diff --git a/go.mod b/go.mod index 3f6e7cb11e..1a7f114334 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.4.0 - golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 + golang.org/x/net v0.0.0-20200602114024-627f9648deb9 google.golang.org/grpc v1.26.0 k8s.io/api v0.17.4 k8s.io/apiextensions-apiserver v0.17.4 diff --git a/go.sum b/go.sum index 73cf89b9b0..4794e33fcd 100644 --- a/go.sum +++ b/go.sum @@ -456,6 +456,8 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 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 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= @@ -488,6 +490,8 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220220014-0732a990476f h1:72l8qCJ1nGxMGH26QVBVIxKd/D34cfGt0OvrPtpemyY= golang.org/x/sys v0.0.0-20191220220014-0732a990476f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/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= diff --git a/hack/update-generated-crd-code.sh b/hack/update-generated-crd-code.sh index 237763739e..ee45f7f6f2 100755 --- a/hack/update-generated-crd-code.sh +++ b/hack/update-generated-crd-code.sh @@ -14,6 +14,34 @@ # See the License for the specific language governing permissions and # limitations under the License. +set -o errexit +set -o nounset +set -o pipefail +set -o xtrace + # this script expects to be run from the root of the Velero repo. +if [[ -z "${GOPATH}" ]]; then + GOPATH=~/go +fi + +if [[ ! -d "${GOPATH}/src/k8s.io/code-generator" ]]; then + echo "k8s.io/code-generator missing from GOPATH" + exit 1 +fi + +if ! command -v controller-gen > /dev/null; then + echo "controller-gen is missing" + exit 1 +fi + +${GOPATH}/src/k8s.io/code-generator/generate-groups.sh \ + all \ + github.com/vmware-tanzu/velero/pkg/generated \ + github.com/vmware-tanzu/velero/pkg/apis \ + "velero:v1" \ + --go-header-file ./hack/boilerplate.go.txt \ + --output-base ../../.. \ + $@ + go generate ./config/crd/crds diff --git a/pkg/apis/velero/v1/backupstoragelocation_types.go b/pkg/apis/velero/v1/backupstoragelocation_types.go index e83d8c930f..f030040c89 100644 --- a/pkg/apis/velero/v1/backupstoragelocation_types.go +++ b/pkg/apis/velero/v1/backupstoragelocation_types.go @@ -1,5 +1,5 @@ /* -Copyright 2018, 2020 the Velero contributors. +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. @@ -21,85 +21,33 @@ import ( "k8s.io/apimachinery/pkg/types" ) -func init() { - SchemeBuilder.Register(&BackupStorageLocation{}, &BackupStorageLocation{}) -} +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// BackupStorageLocationSpec defines the desired state of a Velero BackupStorageLocation -type BackupStorageLocationSpec struct { - // Provider is the provider of the backup storage. - Provider string `json:"provider"` - - // Config is for provider-specific configuration fields. - // +optional - Config map[string]string `json:"config,omitempty"` - - StorageType `json:",inline"` - - // AccessMode defines the permissions for the backup storage location. - // +optional - AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"` - - // BackupSyncPeriod defines how frequently to sync backup API objects from object storage. A value of 0 disables sync. - // +optional - // +nullable - BackupSyncPeriod *metav1.Duration `json:"backupSyncPeriod,omitempty"` -} - -// BackupStorageLocationStatus defines the observed state of BackupStorageLocation -type BackupStorageLocationStatus struct { - // Phase is the current state of the BackupStorageLocation. - // +optional - Phase BackupStorageLocationPhase `json:"phase,omitempty"` +// BackupStorageLocation is a location where Velero stores backup objects. +type BackupStorageLocation struct { + metav1.TypeMeta `json:",inline"rated/clientset/versioned/typed/velero/v1/fake/fake_backupstoragelocationspec.go:77` - // LastSyncedTime is the last time the contents of the location were synced into - // the cluster. // +optional - // +nullable - LastSyncedTime *metav1.Time `json:"lastSyncedTime,omitempty"` + metav1.ObjectMeta `json:"metadata,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. - // - // Deprecated: this field is no longer updated or used for detecting changes to - // the location's contents and will be removed entirely in v2.0. // +optional - LastSyncedRevision types.UID `json:"lastSyncedRevision,omitempty"` + Spec BackupStorageLocationSpec `json:"spec,omitempty"` - // AccessMode is an unused field. - // - // Deprecated: there is now an AccessMode field on the Spec and this field - // will be removed entirely as of v2.0. // +optional - AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"` -} - -// +kubebuilder:object:root=true -// +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" - -// BackupStorageLocation is the Schema for the backupstoragelocations API -type BackupStorageLocation struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec BackupStorageLocationSpec `json:"spec,omitempty"` Status BackupStorageLocationStatus `json:"status,omitempty"` } -// +kubebuilder:object:root=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// BackupStorageLocationList contains a list of BackupStorageLocation +// BackupStorageLocationList is a list of BackupStorageLocations. type BackupStorageLocationList struct { metav1.TypeMeta `json:",inline"` + + // +optional metav1.ListMeta `json:"metadata,omitempty"` - Items []BackupStorageLocation `json:"items"` -} -func init() { - SchemeBuilder.Register(&BackupStorageLocation{}, &BackupStorageLocationList{}) + Items []BackupStorageLocation `json:"items"` } // StorageType represents the type of storage that a backup location uses. @@ -122,7 +70,28 @@ type ObjectStorageLocation struct { CACert []byte `json:"caCert,omitempty"` } -// BackupStorageLocationPhase is the lifecycle phase of a Velero BackupStorageLocation. +// BackupStorageLocationSpec defines the specification for a Velero BackupStorageLocation. +type BackupStorageLocationSpec struct { + // Provider is the provider of the backup storage. + Provider string `json:"provider"` + + // Config is for provider-specific configuration fields. + // +optional + Config map[string]string `json:"config,omitempty"` + + StorageType `json:",inline"` + + // AccessMode defines the permissions for the backup storage location. + // +optional + AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"` + + // BackupSyncPeriod defines how frequently to sync backup API objects from object storage. A value of 0 disables sync. + // +optional + // +nullable + BackupSyncPeriod *metav1.Duration `json:"backupSyncPeriod,omitempty"` +} + +// BackupStorageLocationPhase is the lifecyle phase of a Velero BackupStorageLocation. // +kubebuilder:validation:Enum=Available;Unavailable type BackupStorageLocationPhase string @@ -148,3 +117,31 @@ const ( // TODO(2.0): remove the AccessMode field from BackupStorageLocationStatus. // TODO(2.0): remove the LastSyncedRevision field from BackupStorageLocationStatus. + +// BackupStorageLocationStatus describes the current status of a Velero BackupStorageLocation. +type BackupStorageLocationStatus struct { + // Phase is the current state of the BackupStorageLocation. + // +optional + Phase BackupStorageLocationPhase `json:"phase,omitempty"` + + // LastSyncedTime is the last time the contents of the location were synced into + // the cluster. + // +optional + // +nullable + LastSyncedTime *metav1.Time `json:"lastSyncedTime,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. + // + // Deprecated: this field is no longer updated or used for detecting changes to + // the location's contents and will be removed entirely in v2.0. + // +optional + LastSyncedRevision types.UID `json:"lastSyncedRevision,omitempty"` + + // AccessMode is an unused field. + // + // Deprecated: there is now an AccessMode field on the Spec and this field + // will be removed entirely as of v2.0. + // +optional + AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"` +} diff --git a/pkg/apis/velero/v1/groupversion_info.go b/pkg/apis/velero/v1/groupversion_info.go index 9bb6d76c27..84737a0236 100644 --- a/pkg/apis/velero/v1/groupversion_info.go +++ b/pkg/apis/velero/v1/groupversion_info.go @@ -20,16 +20,16 @@ limitations under the License. package v1 import ( + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/scheme" ) var ( - // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "velero.io", Version: "v1"} + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: "velero.io", Version: "v1"} // SchemeBuilder is used to add go types to the GroupVersionKind scheme - SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) // AddToScheme adds the types in this group-version to the given scheme. AddToScheme = SchemeBuilder.AddToScheme diff --git a/pkg/apis/velero/v1/register.go b/pkg/apis/velero/v1/register.go index e0cda0ac16..ea7df3b5de 100644 --- a/pkg/apis/velero/v1/register.go +++ b/pkg/apis/velero/v1/register.go @@ -24,7 +24,7 @@ import ( // Resource gets a Velero GroupResource for a specified resource func Resource(resource string) schema.GroupResource { - return GroupVersion.WithResource(resource).GroupResource() + return SchemeGroupVersion.WithResource(resource).GroupResource() } type typeInfo struct { @@ -61,9 +61,9 @@ func CustomResources() map[string]typeInfo { func addKnownTypes(scheme *runtime.Scheme) error { for _, typeInfo := range CustomResources() { - scheme.AddKnownTypes(GroupVersion, typeInfo.ItemType, typeInfo.ItemListType) + scheme.AddKnownTypes(SchemeGroupVersion, typeInfo.ItemType, typeInfo.ItemListType) } - metav1.AddToGroupVersion(scheme, GroupVersion) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil } diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index ca0c433daf..7e5e8ad391 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -309,6 +309,7 @@ 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. @@ -341,6 +342,7 @@ 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. @@ -377,6 +379,7 @@ 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. @@ -396,6 +399,7 @@ func (in *BackupStorageLocationStatus) DeepCopyInto(out *BackupStorageLocationSt in, out := &in.LastSyncedTime, &out.LastSyncedTime *out = (*in).DeepCopy() } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageLocationStatus. @@ -650,6 +654,7 @@ 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. @@ -1358,6 +1363,7 @@ 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. diff --git a/pkg/backup/request.go b/pkg/backup/request.go index f0709ac51f..bfb91452df 100644 --- a/pkg/backup/request.go +++ b/pkg/backup/request.go @@ -20,7 +20,6 @@ import ( "fmt" "sort" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/util/collections" "github.com/vmware-tanzu/velero/pkg/volume" @@ -37,7 +36,7 @@ type itemKey struct { type Request struct { *velerov1api.Backup - StorageLocation *veleroapiv1.BackupStorageLocation + StorageLocation *velerov1api.BackupStorageLocation SnapshotLocations []*velerov1api.VolumeSnapshotLocation NamespaceIncludesExcludes *collections.IncludesExcludes ResourceIncludesExcludes *collections.IncludesExcludes diff --git a/pkg/builder/backup_storage_location_builder.go b/pkg/builder/backup_storage_location_builder.go index 6099cf9521..9b0fa66385 100644 --- a/pkg/builder/backup_storage_location_builder.go +++ b/pkg/builder/backup_storage_location_builder.go @@ -19,7 +19,7 @@ package builder import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - velerov1api "github.com/vmware-tanzu/velero/api/v1" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" ) // BackupStorageLocationBuilder builds BackupStorageLocation objects. @@ -32,7 +32,7 @@ func ForBackupStorageLocation(ns, name string) *BackupStorageLocationBuilder { return &BackupStorageLocationBuilder{ object: &velerov1api.BackupStorageLocation{ TypeMeta: metav1.TypeMeta{ - APIVersion: velerov1api.GroupVersion.String(), + APIVersion: velerov1api.SchemeGroupVersion.String(), Kind: "BackupStorageLocation", }, ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index 48cd6d9feb..c59df270fe 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -29,7 +29,6 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/client" @@ -182,7 +181,7 @@ func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Facto } if o.StorageLocation != "" { - location := &veleroapiv1.BackupStorageLocation{} + location := &velerov1api.BackupStorageLocation{} if err := clientKB.Get(context.Background(), k8sclient.ObjectKey{ Namespace: f.Namespace(), Name: o.StorageLocation, diff --git a/pkg/cmd/cli/backuplocation/create.go b/pkg/cmd/cli/backuplocation/create.go index 97e090a6bf..78b2d11458 100644 --- a/pkg/cmd/cli/backuplocation/create.go +++ b/pkg/cmd/cli/backuplocation/create.go @@ -31,7 +31,7 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" k8sclient "sigs.k8s.io/controller-runtime/pkg/client" - velerov1api "github.com/vmware-tanzu/velero/api/v1" + 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/util/flag" diff --git a/pkg/cmd/cli/backuplocation/get.go b/pkg/cmd/cli/backuplocation/get.go index e57a51d471..0c1c15e095 100644 --- a/pkg/cmd/cli/backuplocation/get.go +++ b/pkg/cmd/cli/backuplocation/get.go @@ -23,7 +23,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8sclient "sigs.k8s.io/controller-runtime/pkg/client" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" + 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/util/output" @@ -47,9 +47,9 @@ func NewGetCommand(f client.Factory, use string) *cobra.Command { }) cmd.CheckError(err) - location := &veleroapiv1.BackupStorageLocation{} - var locations *veleroapiv1.BackupStorageLocationList - locations = new(veleroapiv1.BackupStorageLocationList) + location := &velerov1api.BackupStorageLocation{} + var locations *velerov1api.BackupStorageLocationList + locations = new(velerov1api.BackupStorageLocationList) if len(args) > 0 { for _, name := range args { err = clientKB.Get(context.Background(), k8sclient.ObjectKey{ diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/restic/server.go index 2c2e4d4cdd..bf3c167b5d 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/restic/server.go @@ -36,7 +36,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/buildinfo" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/cmd" @@ -62,7 +62,7 @@ var ( func init() { _ = clientgoscheme.AddToScheme(scheme) - _ = veleroapiv1.AddToScheme(scheme) + _ = velerov1api.AddToScheme(scheme) // +kubebuilder:scaffold:scheme } @@ -198,7 +198,7 @@ func (s *resticServer) run() { // TODO(carlisia): how to handle the fetching of the bsl informer: // - options are: 1) get informer for specific obj (below, but w/o namespace or obj name info because we don't know it here) or 2) for a specific kind, which would be CRD? // - should it go here, or inside the controller? Note that neither resolves issue above - location := &veleroapiv1.BackupStorageLocation{} + location := &velerov1api.BackupStorageLocation{} bslInformer, _ := s.mgr.GetCache().GetInformer(location) backupController := controller.NewPodVolumeBackupController( diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 748c49cde3..8291f4c161 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -77,7 +77,7 @@ import ( k8sclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/manager" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" ) const ( @@ -130,7 +130,7 @@ var disableControllerList = []string{ func init() { _ = clientgoscheme.AddToScheme(scheme) - _ = veleroapiv1.AddToScheme(scheme) + _ = velerov1api.AddToScheme(scheme) // +kubebuilder:scaffold:scheme } @@ -373,7 +373,7 @@ func (s *server) run() error { return err } - bsl := &veleroapiv1.BackupStorageLocation{} + bsl := &velerov1api.BackupStorageLocation{} if err := s.mgr.GetAPIReader().Get(context.Background(), k8sclient.ObjectKey{ Namespace: s.namespace, Name: s.config.defaultBackupLocation, @@ -477,7 +477,7 @@ func (s *server) validateBackupStorageLocations() error { pluginManager := clientmgmt.NewManager(s.logger, s.logLevel, s.pluginRegistry) defer pluginManager.CleanupClients() - locations := &veleroapiv1.BackupStorageLocationList{} + locations := &velerov1api.BackupStorageLocationList{} if err := s.mgr.GetAPIReader().List(context.Background(), locations, &k8sclient.ListOptions{ Namespace: s.namespace, }); err != nil { diff --git a/pkg/cmd/util/output/backup_storage_location_printer.go b/pkg/cmd/util/output/backup_storage_location_printer.go index e4aa4089a9..851bc7b737 100644 --- a/pkg/cmd/util/output/backup_storage_location_printer.go +++ b/pkg/cmd/util/output/backup_storage_location_printer.go @@ -20,7 +20,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - velerov1api "github.com/vmware-tanzu/velero/api/v1" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" ) var ( diff --git a/pkg/cmd/util/output/output.go b/pkg/cmd/util/output/output.go index 1b28f0c33e..00fe5c9098 100644 --- a/pkg/cmd/util/output/output.go +++ b/pkg/cmd/util/output/output.go @@ -29,7 +29,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/cli-runtime/pkg/printers" - kbvelerov1api "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" "github.com/vmware-tanzu/velero/pkg/util/encode" @@ -188,15 +187,15 @@ func printTable(cmd *cobra.Command, obj runtime.Object) (bool, error) { ColumnDefinitions: resticRepoColumns, Rows: printResticRepoList(obj.(*velerov1api.ResticRepositoryList)), } - case *kbvelerov1api.BackupStorageLocation: + case *velerov1api.BackupStorageLocation: table = &metav1.Table{ ColumnDefinitions: backupStorageLocationColumns, - Rows: printBackupStorageLocation(obj.(*kbvelerov1api.BackupStorageLocation)), + Rows: printBackupStorageLocation(obj.(*velerov1api.BackupStorageLocation)), } - case *kbvelerov1api.BackupStorageLocationList: + case *velerov1api.BackupStorageLocationList: table = &metav1.Table{ ColumnDefinitions: backupStorageLocationColumns, - Rows: printBackupStorageLocationList(obj.(*kbvelerov1api.BackupStorageLocationList)), + Rows: printBackupStorageLocationList(obj.(*velerov1api.BackupStorageLocationList)), } case *velerov1api.VolumeSnapshotLocation: table = &metav1.Table{ diff --git a/pkg/cmd/velero/velero.go b/pkg/cmd/velero/velero.go index 1ff26d2c48..bf45001f8d 100644 --- a/pkg/cmd/velero/velero.go +++ b/pkg/cmd/velero/velero.go @@ -27,7 +27,7 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" - velerov1api "github.com/vmware-tanzu/velero/api/v1" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/cmd/cli/backup" "github.com/vmware-tanzu/velero/pkg/cmd/cli/backuplocation" diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 77976b5805..b8ee7fe891 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -41,7 +41,6 @@ import ( snapshotv1beta1api "github.com/kubernetes-csi/external-snapshotter/v2/pkg/apis/volumesnapshot/v1beta1" snapshotv1beta1listers "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/listers/volumesnapshot/v1beta1" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" 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" @@ -80,7 +79,7 @@ type backupController struct { snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister defaultSnapshotLocations map[string]string metrics *metrics.ServerMetrics - newBackupStore func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) + newBackupStore func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) formatFlag logging.Format volumeSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister volumeSnapshotContentLister snapshotv1beta1listers.VolumeSnapshotContentLister @@ -375,7 +374,7 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg } // validate the storage location, and store the BackupStorageLocation API obj on the request - storageLocation := &veleroapiv1.BackupStorageLocation{} + storageLocation := &velerov1api.BackupStorageLocation{} if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ Namespace: request.Namespace, Name: request.Spec.StorageLocation, @@ -388,7 +387,7 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg } else { request.StorageLocation = storageLocation - if request.StorageLocation.Spec.AccessMode == veleroapiv1.BackupStorageLocationAccessModeReadOnly { + if request.StorageLocation.Spec.AccessMode == velerov1api.BackupStorageLocationAccessModeReadOnly { request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("backup can't be created because backup storage location %s is currently in read-only mode", request.StorageLocation.Name)) } diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 21d0c59c7f..714aadb855 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -42,7 +42,6 @@ import ( . "github.com/onsi/gomega" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/builder" @@ -146,7 +145,7 @@ func TestProcessBackupValidationFailures(t *testing.T) { tests := []struct { name string backup *velerov1api.Backup - backupLocation *veleroapiv1.BackupStorageLocation + backupLocation *velerov1api.BackupStorageLocation expectedErrs []string }{ { @@ -169,7 +168,7 @@ func TestProcessBackupValidationFailures(t *testing.T) { { name: "backup for read-only backup location fails validation", backup: defaultBackup().StorageLocation("read-only").Result(), - backupLocation: builder.ForBackupStorageLocation("velero", "read-only").AccessMode(veleroapiv1.BackupStorageLocationAccessModeReadOnly).Result(), + 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"}, }, } @@ -231,7 +230,7 @@ func TestBackupLocationLabel(t *testing.T) { tests := []struct { name string backup *velerov1api.Backup - backupLocation *veleroapiv1.BackupStorageLocation + backupLocation *velerov1api.BackupStorageLocation expectedBackupLocation string }{ { @@ -296,7 +295,7 @@ func TestDefaultBackupTTL(t *testing.T) { tests := []struct { name string backup *velerov1api.Backup - backupLocation *veleroapiv1.BackupStorageLocation + backupLocation *velerov1api.BackupStorageLocation expectedTTL metav1.Duration expectedExpiration metav1.Time }{ @@ -359,7 +358,7 @@ func TestProcessBackupCompletions(t *testing.T) { tests := []struct { name string backup *velerov1api.Backup - backupLocation *veleroapiv1.BackupStorageLocation + backupLocation *velerov1api.BackupStorageLocation defaultVolumesToRestic bool expectedResult *velerov1api.Backup backupExists bool @@ -443,7 +442,7 @@ func TestProcessBackupCompletions(t *testing.T) { backup: defaultBackup().StorageLocation("read-write").Result(), backupLocation: builder.ForBackupStorageLocation("velero", "read-write"). Bucket("store-1"). - AccessMode(veleroapiv1.BackupStorageLocationAccessModeReadWrite). + AccessMode(velerov1api.BackupStorageLocationAccessModeReadWrite). Result(), defaultVolumesToRestic: true, expectedResult: &velerov1api.Backup{ @@ -831,7 +830,7 @@ func TestProcessBackupCompletions(t *testing.T) { metrics: metrics.NewServerMetrics(), clock: clock.NewFakeClock(now), newPluginManager: func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, - newBackupStore: func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { + newBackupStore: func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { return backupStore, nil }, backupper: backupper, diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 2c6ffc0b9b..60ab1be740 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -36,7 +36,6 @@ import ( kubeerrs "k8s.io/apimachinery/pkg/util/errors" "k8s.io/client-go/tools/cache" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/features" @@ -75,7 +74,7 @@ type backupDeletionController struct { processRequestFunc func(*velerov1api.DeleteBackupRequest) error clock clock.Clock newPluginManager func(logrus.FieldLogger) clientmgmt.Manager - newBackupStore func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) + newBackupStore func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) metrics *metrics.ServerMetrics } @@ -217,7 +216,7 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR } // Don't allow deleting backups in read-only storage locations - location := &veleroapiv1.BackupStorageLocation{} + location := &velerov1api.BackupStorageLocation{} if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ Namespace: backup.Namespace, Name: backup.Spec.StorageLocation, @@ -232,7 +231,7 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR return errors.Wrap(err, "error getting backup storage location") } - if location.Spec.AccessMode == veleroapiv1.BackupStorageLocationAccessModeReadOnly { + if location.Spec.AccessMode == velerov1api.BackupStorageLocationAccessModeReadOnly { _, err := c.patchDeleteBackupRequest(req, 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)) diff --git a/pkg/controller/backup_deletion_controller_test.go b/pkg/controller/backup_deletion_controller_test.go index 98b4abd0c1..15e3709f81 100644 --- a/pkg/controller/backup_deletion_controller_test.go +++ b/pkg/controller/backup_deletion_controller_test.go @@ -40,8 +40,8 @@ import ( . "github.com/onsi/gomega" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake" @@ -174,7 +174,7 @@ func setupBackupDeletionControllerTest(fakeClient client.Client, objects ...runt req: req, } - data.controller.newBackupStore = func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { + data.controller.newBackupStore = func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { return backupStore, nil } @@ -415,7 +415,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { t.Run("backup storage location is in read-only mode", func(t *testing.T) { backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result() - location := builder.ForBackupStorageLocation("velero", "default").AccessMode(veleroapiv1.BackupStorageLocationAccessModeReadOnly).Result() + location := builder.ForBackupStorageLocation("velero", "default").AccessMode(velerov1api.BackupStorageLocationAccessModeReadOnly).Result() fakeClient := newFakeClient(g, location) td := setupBackupDeletionControllerTest(fakeClient, backup) @@ -457,15 +457,15 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore2) td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore3) - location := &veleroapiv1.BackupStorageLocation{ + location := &velerov1api.BackupStorageLocation{ ObjectMeta: metav1.ObjectMeta{ Namespace: backup.Namespace, Name: backup.Spec.StorageLocation, }, - Spec: veleroapiv1.BackupStorageLocationSpec{ + Spec: velerov1api.BackupStorageLocationSpec{ Provider: "objStoreProvider", - StorageType: veleroapiv1.StorageType{ - ObjectStorage: &veleroapiv1.ObjectStorageLocation{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ Bucket: "bucket", }, }, @@ -620,15 +620,15 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore2) td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore3) - location := &veleroapiv1.BackupStorageLocation{ + location := &velerov1api.BackupStorageLocation{ ObjectMeta: metav1.ObjectMeta{ Namespace: backup.Namespace, Name: backup.Spec.StorageLocation, }, - Spec: veleroapiv1.BackupStorageLocationSpec{ + Spec: velerov1api.BackupStorageLocationSpec{ Provider: "objStoreProvider", - StorageType: veleroapiv1.StorageType{ - ObjectStorage: &veleroapiv1.ObjectStorageLocation{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ Bucket: "bucket", }, }, diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index f4ab2a323c..c6a47df5cb 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -29,7 +29,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/kubernetes" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" 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" @@ -55,7 +54,7 @@ type backupSyncController struct { defaultBackupLocation string defaultBackupSyncPeriod time.Duration newPluginManager func(logrus.FieldLogger) clientmgmt.Manager - newBackupStore func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) + newBackupStore func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) } func NewBackupSyncController( @@ -102,8 +101,8 @@ func NewBackupSyncController( // 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 *veleroapiv1.BackupStorageLocationList, defaultLocationName string) []veleroapiv1.BackupStorageLocation { - var result []veleroapiv1.BackupStorageLocation +func orderedBackupLocations(locationList *velerov1api.BackupStorageLocationList, defaultLocationName string) []velerov1api.BackupStorageLocation { + var result []velerov1api.BackupStorageLocation for i := range locationList.Items { if locationList.Items[i].Name == defaultLocationName { @@ -124,7 +123,7 @@ func orderedBackupLocations(locationList *veleroapiv1.BackupStorageLocationList, func (c *backupSyncController) run() { c.logger.Debug("Checking for existing backup storage locations to sync into cluster") - locationList := &veleroapiv1.BackupStorageLocationList{} + locationList := &velerov1api.BackupStorageLocationList{} if err := c.k8sClient.List(context.Background(), locationList, &client.ListOptions{ Namespace: c.namespace, }); err != nil { @@ -307,7 +306,7 @@ func (c *backupSyncController) run() { c.deleteOrphanedBackups(location.Name, backupStoreBackups, log) - locationUpdate := &veleroapiv1.BackupStorageLocation{} + locationUpdate := &velerov1api.BackupStorageLocation{} if err = c.k8sClient.Get(context.Background(), k8sclient.ObjectKey{ Namespace: c.namespace, Name: location.Name, diff --git a/pkg/controller/backup_sync_controller_test.go b/pkg/controller/backup_sync_controller_test.go index 76fef8980c..feb6549d58 100644 --- a/pkg/controller/backup_sync_controller_test.go +++ b/pkg/controller/backup_sync_controller_test.go @@ -31,7 +31,6 @@ import ( . "github.com/onsi/gomega" - veleroapiv1 "github.com/vmware-tanzu/velero/api/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" @@ -44,17 +43,17 @@ import ( velerotest "github.com/vmware-tanzu/velero/pkg/test" ) -func defaultLocationsList(namespace string) []*veleroapiv1.BackupStorageLocation { - return []*veleroapiv1.BackupStorageLocation{ +func defaultLocationsList(namespace string) []*velerov1api.BackupStorageLocation { + return []*velerov1api.BackupStorageLocation{ { ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, Name: "location-1", }, - Spec: veleroapiv1.BackupStorageLocationSpec{ + Spec: velerov1api.BackupStorageLocationSpec{ Provider: "objStoreProvider", - StorageType: veleroapiv1.StorageType{ - ObjectStorage: &veleroapiv1.ObjectStorageLocation{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ Bucket: "bucket-1", }, }, @@ -65,10 +64,10 @@ func defaultLocationsList(namespace string) []*veleroapiv1.BackupStorageLocation Namespace: namespace, Name: "location-2", }, - Spec: veleroapiv1.BackupStorageLocationSpec{ + Spec: velerov1api.BackupStorageLocationSpec{ Provider: "objStoreProvider", - StorageType: veleroapiv1.StorageType{ - ObjectStorage: &veleroapiv1.ObjectStorageLocation{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ Bucket: "bucket-2", }, }, @@ -77,17 +76,17 @@ func defaultLocationsList(namespace string) []*veleroapiv1.BackupStorageLocation } } -func defaultLocationsListWithLongerLocationName(namespace string) []*veleroapiv1.BackupStorageLocation { - return []*veleroapiv1.BackupStorageLocation{ +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", }, - Spec: veleroapiv1.BackupStorageLocationSpec{ + Spec: velerov1api.BackupStorageLocationSpec{ Provider: "objStoreProvider", - StorageType: veleroapiv1.StorageType{ - ObjectStorage: &veleroapiv1.ObjectStorageLocation{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ Bucket: "bucket-1", }, }, @@ -98,10 +97,10 @@ func defaultLocationsListWithLongerLocationName(namespace string) []*veleroapiv1 Namespace: namespace, Name: "the-really-long-location-name-that-is-much-more-than-63-characters-2", }, - Spec: veleroapiv1.BackupStorageLocationSpec{ + Spec: velerov1api.BackupStorageLocationSpec{ Provider: "objStoreProvider", - StorageType: veleroapiv1.StorageType{ - ObjectStorage: &veleroapiv1.ObjectStorageLocation{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ Bucket: "bucket-2", }, }, @@ -121,7 +120,7 @@ func TestBackupSyncControllerRun(t *testing.T) { tests := []struct { name string namespace string - locations []*veleroapiv1.BackupStorageLocation + locations []*velerov1api.BackupStorageLocation cloudBuckets map[string][]*cloudBackupData existingBackups []*velerov1api.Backup existingPodVolumeBackups []*velerov1api.PodVolumeBackup @@ -358,7 +357,7 @@ func TestBackupSyncControllerRun(t *testing.T) { velerotest.NewLogger(), ).(*backupSyncController) - c.newBackupStore = func(loc *veleroapiv1.BackupStorageLocation, _ persistence.ObjectStoreGetter, _ logrus.FieldLogger) (persistence.BackupStore, error) { + c.newBackupStore = func(loc *velerov1api.BackupStorageLocation, _ persistence.ObjectStoreGetter, _ logrus.FieldLogger) (persistence.BackupStore, error) { // this gets populated just below, prior to exercising the method under test return backupStores[loc.Name], nil } @@ -403,7 +402,7 @@ func TestBackupSyncControllerRun(t *testing.T) { for bucket, backupDataSet := range test.cloudBuckets { // figure out which location this bucket is for; we need this for verification // purposes later - var location *veleroapiv1.BackupStorageLocation + var location *velerov1api.BackupStorageLocation for _, loc := range test.locations { if loc.Spec.ObjectStorage.Bucket == bucket { location = loc diff --git a/pkg/controller/download_request_controller.go b/pkg/controller/download_request_controller.go index 8c123f600c..ff262e4322 100644 --- a/pkg/controller/download_request_controller.go +++ b/pkg/controller/download_request_controller.go @@ -32,8 +32,8 @@ import ( "k8s.io/client-go/tools/cache" "sigs.k8s.io/controller-runtime/pkg/client" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" v1 "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" velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" @@ -52,7 +52,7 @@ type downloadRequestController struct { k8sClient client.Client backupLister velerov1listers.BackupLister newPluginManager func(logrus.FieldLogger) clientmgmt.Manager - newBackupStore func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) + newBackupStore func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) } // NewDownloadRequestController creates a new DownloadRequestController. @@ -162,7 +162,7 @@ func (c *downloadRequestController) generatePreSignedURL(downloadRequest *v1.Dow return errors.WithStack(err) } - backupLocation := &veleroapiv1.BackupStorageLocation{} + backupLocation := &velerov1api.BackupStorageLocation{} if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ Namespace: backup.Namespace, Name: backup.Spec.StorageLocation, diff --git a/pkg/controller/download_request_controller_test.go b/pkg/controller/download_request_controller_test.go index 8cc6e5e221..f73337af7c 100644 --- a/pkg/controller/download_request_controller_test.go +++ b/pkg/controller/download_request_controller_test.go @@ -31,8 +31,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" v1 "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" @@ -74,7 +74,7 @@ func newDownloadRequestTestHarness(t *testing.T, fakeClient client.Client) *down require.NoError(t, err) controller.clock = clock.NewFakeClock(clockTime) - controller.newBackupStore = func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { + controller.newBackupStore = func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { return backupStore, nil } @@ -107,16 +107,16 @@ func newDownloadRequest(phase v1.DownloadRequestPhase, targetKind v1.DownloadTar } } -func newBackupLocation(name, provider, bucket string) *veleroapiv1.BackupStorageLocation { - return &veleroapiv1.BackupStorageLocation{ +func newBackupLocation(name, provider, bucket string) *velerov1api.BackupStorageLocation { + return &velerov1api.BackupStorageLocation{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: v1.DefaultNamespace, }, - Spec: veleroapiv1.BackupStorageLocationSpec{ + Spec: velerov1api.BackupStorageLocationSpec{ Provider: provider, - StorageType: veleroapiv1.StorageType{ - ObjectStorage: &veleroapiv1.ObjectStorageLocation{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ Bucket: bucket, }, }, @@ -137,7 +137,7 @@ func TestProcessDownloadRequest(t *testing.T) { downloadRequest *v1.DownloadRequest backup *v1.Backup restore *v1.Restore - backupLocation *veleroapiv1.BackupStorageLocation + backupLocation *velerov1api.BackupStorageLocation expired bool expectedErr string expectGetsURL bool diff --git a/pkg/controller/gc_controller.go b/pkg/controller/gc_controller.go index c785e7957c..24ff715c45 100644 --- a/pkg/controller/gc_controller.go +++ b/pkg/controller/gc_controller.go @@ -29,7 +29,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" 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" @@ -134,7 +133,7 @@ func (c *gcController) processQueueItem(key string) error { log.Info("Backup has expired") - loc := &veleroapiv1.BackupStorageLocation{} + loc := &velerov1api.BackupStorageLocation{} if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ Namespace: ns, Name: backup.Spec.StorageLocation, @@ -145,7 +144,7 @@ func (c *gcController) processQueueItem(key string) error { return errors.Wrap(err, "error getting backup storage location") } - if loc.Spec.AccessMode == veleroapiv1.BackupStorageLocationAccessModeReadOnly { + 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) return nil } diff --git a/pkg/controller/gc_controller_test.go b/pkg/controller/gc_controller_test.go index d2e2736d76..552c4de3a9 100644 --- a/pkg/controller/gc_controller_test.go +++ b/pkg/controller/gc_controller_test.go @@ -23,7 +23,7 @@ import ( "testing" "time" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" . "github.com/onsi/gomega" "github.com/pkg/errors" @@ -162,7 +162,7 @@ func TestGCControllerProcessQueueItem(t *testing.T) { name string backup *api.Backup deleteBackupRequests []*api.DeleteBackupRequest - backupLocation *veleroapiv1.BackupStorageLocation + backupLocation *velerov1api.BackupStorageLocation expectDeletion bool createDeleteBackupRequestError bool expectError bool @@ -179,13 +179,13 @@ func TestGCControllerProcessQueueItem(t *testing.T) { { 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(veleroapiv1.BackupStorageLocationAccessModeReadOnly).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(veleroapiv1.BackupStorageLocationAccessModeReadWrite).Result(), + backupLocation: builder.ForBackupStorageLocation("velero", "read-write").AccessMode(velerov1api.BackupStorageLocationAccessModeReadWrite).Result(), expectDeletion: true, }, { diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 9de729d886..c40ece0ef6 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -36,7 +36,6 @@ import ( corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" @@ -238,7 +237,7 @@ func (c *podVolumeBackupController) processBackup(req *velerov1api.PodVolumeBack // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic //TODO(carlisia): before the client was being passed to the restic package and the // "getting" of the bsl was being done there; is that better? - location := &veleroapiv1.BackupStorageLocation{} + location := &velerov1api.BackupStorageLocation{} if err := c.client.Get(context.Background(), client.ObjectKey{ Namespace: req.Namespace, Name: req.Spec.BackupStorageLocation, diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 3df689d2d3..2e69093e94 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -40,7 +40,6 @@ import ( k8scache "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" @@ -301,7 +300,7 @@ func (c *podVolumeRestoreController) processRestore(req *velerov1api.PodVolumeRe defer os.Remove(credsFile) // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic - location := &veleroapiv1.BackupStorageLocation{} + location := &velerov1api.BackupStorageLocation{} if err := c.client.Get(context.Background(), client.ObjectKey{ Namespace: req.Namespace, Name: req.Spec.BackupStorageLocation, @@ -361,7 +360,7 @@ func (c *podVolumeRestoreController) restorePodVolume(req *velerov1api.PodVolume // Running restic command might need additional provider specific environment variables. Based on the provider, we // set resticCmd.Env appropriately (currently for Azure and S3 based backuplocations) - location := &veleroapiv1.BackupStorageLocation{} + location := &velerov1api.BackupStorageLocation{} if err := c.client.Get(context.Background(), client.ObjectKey{ Namespace: req.Namespace, Name: req.Spec.BackupStorageLocation, diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index 40fc3f491e..5fa3516925 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -32,8 +32,8 @@ import ( "k8s.io/apimachinery/pkg/util/clock" "k8s.io/client-go/tools/cache" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" v1 "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" velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" @@ -159,7 +159,7 @@ func (c *resticRepositoryController) initializeRepo(req *v1.ResticRepository, lo log.Info("Initializing restic repository") // confirm the repo's BackupStorageLocation is valid - loc := &veleroapiv1.BackupStorageLocation{} + loc := &velerov1api.BackupStorageLocation{} if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ Namespace: req.Namespace, Name: req.Spec.BackupStorageLocation, diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index ce445716b1..9837eb818c 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -36,7 +36,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/tools/cache" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" 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" @@ -92,7 +91,7 @@ type restoreController struct { logFormat logging.Format newPluginManager func(logger logrus.FieldLogger) clientmgmt.Manager - newBackupStore func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) + newBackupStore func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) } func NewRestoreController( @@ -278,7 +277,7 @@ func (c *restoreController) processRestore(restore *api.Restore) error { type backupInfo struct { backup *api.Backup - location *veleroapiv1.BackupStorageLocation + location *velerov1api.BackupStorageLocation backupStore persistence.BackupStore } @@ -400,7 +399,7 @@ func (c *restoreController) fetchBackupInfo(backupName string, pluginManager cli return backupInfo{}, err } - location := &veleroapiv1.BackupStorageLocation{} + location := &velerov1api.BackupStorageLocation{} if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ Namespace: c.namespace, Name: backup.Spec.StorageLocation, diff --git a/pkg/controller/restore_controller_test.go b/pkg/controller/restore_controller_test.go index 357cb4c3f5..8b5b8eb800 100644 --- a/pkg/controller/restore_controller_test.go +++ b/pkg/controller/restore_controller_test.go @@ -24,7 +24,7 @@ import ( "testing" "time" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" . "github.com/onsi/gomega" "github.com/pkg/errors" @@ -61,7 +61,7 @@ func TestFetchBackupInfo(t *testing.T) { tests := []struct { name string backupName string - informerLocations []*veleroapiv1.BackupStorageLocation + informerLocations []*velerov1api.BackupStorageLocation informerBackups []*api.Backup backupStoreBackup *api.Backup backupStoreError error @@ -71,7 +71,7 @@ func TestFetchBackupInfo(t *testing.T) { { name: "lister has backup", backupName: "backup-1", - informerLocations: []*veleroapiv1.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result()}, + informerLocations: []*velerov1api.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result()}, informerBackups: []*api.Backup{defaultBackup().StorageLocation("default").Result()}, expectedRes: defaultBackup().StorageLocation("default").Result(), }, @@ -79,7 +79,7 @@ func TestFetchBackupInfo(t *testing.T) { name: "lister does not have a backup, but backupSvc does", backupName: "backup-1", backupStoreBackup: defaultBackup().StorageLocation("default").Result(), - informerLocations: []*veleroapiv1.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result()}, + informerLocations: []*velerov1api.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result()}, informerBackups: []*api.Backup{defaultBackup().StorageLocation("default").Result()}, expectedRes: defaultBackup().StorageLocation("default").Result(), }, @@ -125,7 +125,7 @@ func TestFetchBackupInfo(t *testing.T) { formatFlag, ).(*restoreController) - c.newBackupStore = func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { + c.newBackupStore = func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { return backupStore, nil } @@ -240,7 +240,7 @@ func TestProcessQueueItem(t *testing.T) { tests := []struct { name string restoreKey string - location *veleroapiv1.BackupStorageLocation + location *velerov1api.BackupStorageLocation restore *api.Restore backup *api.Backup restorerError error @@ -429,7 +429,7 @@ func TestProcessQueueItem(t *testing.T) { formatFlag, ).(*restoreController) - c.newBackupStore = func(*veleroapiv1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { + c.newBackupStore = func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) { return backupStore, nil } diff --git a/pkg/controller/suite_test.go b/pkg/controller/suite_test.go index 1dd5242f71..882e1fe35b 100644 --- a/pkg/controller/suite_test.go +++ b/pkg/controller/suite_test.go @@ -20,7 +20,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client/fake" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" . "github.com/onsi/gomega" @@ -31,6 +30,6 @@ import ( func newFakeClient(g *WithT, initObjs ...runtime.Object) client.Client { g.Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) - g.Expect(veleroapiv1.AddToScheme(scheme.Scheme)).To(Succeed()) + g.Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) return fake.NewFakeClientWithScheme(scheme.Scheme, initObjs...) } diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/backupstoragelocation.go b/pkg/generated/clientset/versioned/typed/velero/v1/backupstoragelocation.go new file mode 100644 index 0000000000..1862fe7292 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/velero/v1/backupstoragelocation.go @@ -0,0 +1,191 @@ +/* +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 v1 + +import ( + "time" + + v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + scheme "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// BackupStorageLocationsGetter has a method to return a BackupStorageLocationInterface. +// A group's client should implement this interface. +type BackupStorageLocationsGetter interface { + BackupStorageLocations(namespace string) BackupStorageLocationInterface +} + +// BackupStorageLocationInterface has methods to work with BackupStorageLocation resources. +type BackupStorageLocationInterface interface { + Create(*v1.BackupStorageLocation) (*v1.BackupStorageLocation, error) + Update(*v1.BackupStorageLocation) (*v1.BackupStorageLocation, error) + UpdateStatus(*v1.BackupStorageLocation) (*v1.BackupStorageLocation, error) + Delete(name string, options *metav1.DeleteOptions) error + DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error + Get(name string, options metav1.GetOptions) (*v1.BackupStorageLocation, error) + List(opts metav1.ListOptions) (*v1.BackupStorageLocationList, error) + Watch(opts metav1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.BackupStorageLocation, err error) + BackupStorageLocationExpansion +} + +// backupStorageLocations implements BackupStorageLocationInterface +type backupStorageLocations struct { + client rest.Interface + ns string +} + +// newBackupStorageLocations returns a BackupStorageLocations +func newBackupStorageLocations(c *VeleroV1Client, namespace string) *backupStorageLocations { + return &backupStorageLocations{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the backupStorageLocation, and returns the corresponding backupStorageLocation object, and an error if there is any. +func (c *backupStorageLocations) Get(name string, options metav1.GetOptions) (result *v1.BackupStorageLocation, err error) { + result = &v1.BackupStorageLocation{} + err = c.client.Get(). + Namespace(c.ns). + Resource("backupstoragelocations"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of BackupStorageLocations that match those selectors. +func (c *backupStorageLocations) List(opts metav1.ListOptions) (result *v1.BackupStorageLocationList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1.BackupStorageLocationList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("backupstoragelocations"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested backupStorageLocations. +func (c *backupStorageLocations) Watch(opts metav1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("backupstoragelocations"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a backupStorageLocation and creates it. Returns the server's representation of the backupStorageLocation, and an error, if there is any. +func (c *backupStorageLocations) Create(backupStorageLocation *v1.BackupStorageLocation) (result *v1.BackupStorageLocation, err error) { + result = &v1.BackupStorageLocation{} + err = c.client.Post(). + Namespace(c.ns). + Resource("backupstoragelocations"). + Body(backupStorageLocation). + Do(). + Into(result) + return +} + +// Update takes the representation of a backupStorageLocation and updates it. Returns the server's representation of the backupStorageLocation, and an error, if there is any. +func (c *backupStorageLocations) Update(backupStorageLocation *v1.BackupStorageLocation) (result *v1.BackupStorageLocation, err error) { + result = &v1.BackupStorageLocation{} + err = c.client.Put(). + Namespace(c.ns). + Resource("backupstoragelocations"). + Name(backupStorageLocation.Name). + Body(backupStorageLocation). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *backupStorageLocations) UpdateStatus(backupStorageLocation *v1.BackupStorageLocation) (result *v1.BackupStorageLocation, err error) { + result = &v1.BackupStorageLocation{} + err = c.client.Put(). + Namespace(c.ns). + Resource("backupstoragelocations"). + Name(backupStorageLocation.Name). + SubResource("status"). + Body(backupStorageLocation). + Do(). + Into(result) + return +} + +// Delete takes name of the backupStorageLocation and deletes it. Returns an error if one occurs. +func (c *backupStorageLocations) Delete(name string, options *metav1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("backupstoragelocations"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *backupStorageLocations) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("backupstoragelocations"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched backupStorageLocation. +func (c *backupStorageLocations) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.BackupStorageLocation, err error) { + result = &v1.BackupStorageLocation{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("backupstoragelocations"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backupstoragelocation.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backupstoragelocation.go new file mode 100644 index 0000000000..650ddc7d3d --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backupstoragelocation.go @@ -0,0 +1,140 @@ +/* +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 ( + 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" +) + +// FakeBackupStorageLocations implements BackupStorageLocationInterface +type FakeBackupStorageLocations struct { + Fake *FakeVeleroV1 + ns string +} + +var backupstoragelocationsResource = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "backupstoragelocations"} + +var backupstoragelocationsKind = schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "BackupStorageLocation"} + +// Get takes name of the backupStorageLocation, and returns the corresponding backupStorageLocation object, and an error if there is any. +func (c *FakeBackupStorageLocations) Get(name string, options v1.GetOptions) (result *velerov1.BackupStorageLocation, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(backupstoragelocationsResource, c.ns, name), &velerov1.BackupStorageLocation{}) + + if obj == nil { + return nil, err + } + return obj.(*velerov1.BackupStorageLocation), err +} + +// List takes label and field selectors, and returns the list of BackupStorageLocations that match those selectors. +func (c *FakeBackupStorageLocations) List(opts v1.ListOptions) (result *velerov1.BackupStorageLocationList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(backupstoragelocationsResource, backupstoragelocationsKind, c.ns, opts), &velerov1.BackupStorageLocationList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &velerov1.BackupStorageLocationList{ListMeta: obj.(*velerov1.BackupStorageLocationList).ListMeta} + for _, item := range obj.(*velerov1.BackupStorageLocationList).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 backupStorageLocations. +func (c *FakeBackupStorageLocations) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(backupstoragelocationsResource, c.ns, opts)) + +} + +// Create takes the representation of a backupStorageLocation and creates it. Returns the server's representation of the backupStorageLocation, and an error, if there is any. +func (c *FakeBackupStorageLocations) Create(backupStorageLocation *velerov1.BackupStorageLocation) (result *velerov1.BackupStorageLocation, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(backupstoragelocationsResource, c.ns, backupStorageLocation), &velerov1.BackupStorageLocation{}) + + if obj == nil { + return nil, err + } + return obj.(*velerov1.BackupStorageLocation), err +} + +// Update takes the representation of a backupStorageLocation and updates it. Returns the server's representation of the backupStorageLocation, and an error, if there is any. +func (c *FakeBackupStorageLocations) Update(backupStorageLocation *velerov1.BackupStorageLocation) (result *velerov1.BackupStorageLocation, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(backupstoragelocationsResource, c.ns, backupStorageLocation), &velerov1.BackupStorageLocation{}) + + if obj == nil { + return nil, err + } + return obj.(*velerov1.BackupStorageLocation), 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 *FakeBackupStorageLocations) UpdateStatus(backupStorageLocation *velerov1.BackupStorageLocation) (*velerov1.BackupStorageLocation, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(backupstoragelocationsResource, "status", c.ns, backupStorageLocation), &velerov1.BackupStorageLocation{}) + + if obj == nil { + return nil, err + } + return obj.(*velerov1.BackupStorageLocation), err +} + +// Delete takes name of the backupStorageLocation and deletes it. Returns an error if one occurs. +func (c *FakeBackupStorageLocations) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(backupstoragelocationsResource, c.ns, name), &velerov1.BackupStorageLocation{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeBackupStorageLocations) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(backupstoragelocationsResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &velerov1.BackupStorageLocationList{}) + return err +} + +// Patch applies the patch and returns the patched backupStorageLocation. +func (c *FakeBackupStorageLocations) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *velerov1.BackupStorageLocation, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(backupstoragelocationsResource, c.ns, name, pt, data, subresources...), &velerov1.BackupStorageLocation{}) + + if obj == nil { + return nil, err + } + return obj.(*velerov1.BackupStorageLocation), 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 da65a7771d..e901158134 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) BackupStorageLocations(namespace string) v1.BackupStorageLocationInterface { + return &FakeBackupStorageLocations{c, namespace} +} + func (c *FakeVeleroV1) DeleteBackupRequests(namespace string) v1.DeleteBackupRequestInterface { return &FakeDeleteBackupRequests{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 caf5a4f82f..5deaaa51af 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 BackupStorageLocationExpansion interface{} + type DeleteBackupRequestExpansion interface{} type DownloadRequestExpansion 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 78893332c5..5758967efa 100644 --- a/pkg/generated/clientset/versioned/typed/velero/v1/velero_client.go +++ b/pkg/generated/clientset/versioned/typed/velero/v1/velero_client.go @@ -27,6 +27,7 @@ import ( type VeleroV1Interface interface { RESTClient() rest.Interface BackupsGetter + BackupStorageLocationsGetter DeleteBackupRequestsGetter DownloadRequestsGetter PodVolumeBackupsGetter @@ -47,6 +48,10 @@ func (c *VeleroV1Client) Backups(namespace string) BackupInterface { return newBackups(c, namespace) } +func (c *VeleroV1Client) BackupStorageLocations(namespace string) BackupStorageLocationInterface { + return newBackupStorageLocations(c, namespace) +} + func (c *VeleroV1Client) DeleteBackupRequests(namespace string) DeleteBackupRequestInterface { return newDeleteBackupRequests(c, namespace) } diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go index b8409616d7..15770dcebb 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("backupstoragelocations"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().BackupStorageLocations().Informer()}, nil case v1.SchemeGroupVersion.WithResource("deletebackuprequests"): return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().DeleteBackupRequests().Informer()}, nil case v1.SchemeGroupVersion.WithResource("downloadrequests"): diff --git a/pkg/generated/informers/externalversions/velero/v1/backupstoragelocation.go b/pkg/generated/informers/externalversions/velero/v1/backupstoragelocation.go new file mode 100644 index 0000000000..7e356339b4 --- /dev/null +++ b/pkg/generated/informers/externalversions/velero/v1/backupstoragelocation.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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1 + +import ( + time "time" + + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" + internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" + v1 "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// BackupStorageLocationInformer provides access to a shared informer and lister for +// BackupStorageLocations. +type BackupStorageLocationInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1.BackupStorageLocationLister +} + +type backupStorageLocationInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewBackupStorageLocationInformer constructs a new informer for BackupStorageLocation 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 NewBackupStorageLocationInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredBackupStorageLocationInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredBackupStorageLocationInformer constructs a new informer for BackupStorageLocation 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 NewFilteredBackupStorageLocationInformer(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().BackupStorageLocations(namespace).List(options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.VeleroV1().BackupStorageLocations(namespace).Watch(options) + }, + }, + &velerov1.BackupStorageLocation{}, + resyncPeriod, + indexers, + ) +} + +func (f *backupStorageLocationInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredBackupStorageLocationInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *backupStorageLocationInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&velerov1.BackupStorageLocation{}, f.defaultInformer) +} + +func (f *backupStorageLocationInformer) Lister() v1.BackupStorageLocationLister { + return v1.NewBackupStorageLocationLister(f.Informer().GetIndexer()) +} diff --git a/pkg/generated/informers/externalversions/velero/v1/interface.go b/pkg/generated/informers/externalversions/velero/v1/interface.go index 51615cfa8d..981470c409 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 + // BackupStorageLocations returns a BackupStorageLocationInformer. + BackupStorageLocations() BackupStorageLocationInformer // DeleteBackupRequests returns a DeleteBackupRequestInformer. DeleteBackupRequests() DeleteBackupRequestInformer // DownloadRequests returns a DownloadRequestInformer. @@ -62,6 +64,11 @@ func (v *version) Backups() BackupInformer { return &backupInformer{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} +} + // DeleteBackupRequests returns a DeleteBackupRequestInformer. func (v *version) DeleteBackupRequests() DeleteBackupRequestInformer { return &deleteBackupRequestInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/generated/listers/velero/v1/backupstoragelocation.go b/pkg/generated/listers/velero/v1/backupstoragelocation.go new file mode 100644 index 0000000000..8c6c140bd4 --- /dev/null +++ b/pkg/generated/listers/velero/v1/backupstoragelocation.go @@ -0,0 +1,94 @@ +/* +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" +) + +// BackupStorageLocationLister helps list BackupStorageLocations. +type BackupStorageLocationLister interface { + // List lists all BackupStorageLocations in the indexer. + List(selector labels.Selector) (ret []*v1.BackupStorageLocation, err error) + // BackupStorageLocations returns an object that can list and get BackupStorageLocations. + BackupStorageLocations(namespace string) BackupStorageLocationNamespaceLister + BackupStorageLocationListerExpansion +} + +// backupStorageLocationLister implements the BackupStorageLocationLister interface. +type backupStorageLocationLister struct { + indexer cache.Indexer +} + +// NewBackupStorageLocationLister returns a new BackupStorageLocationLister. +func NewBackupStorageLocationLister(indexer cache.Indexer) BackupStorageLocationLister { + return &backupStorageLocationLister{indexer: indexer} +} + +// List lists all BackupStorageLocations in the indexer. +func (s *backupStorageLocationLister) List(selector labels.Selector) (ret []*v1.BackupStorageLocation, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1.BackupStorageLocation)) + }) + return ret, err +} + +// BackupStorageLocations returns an object that can list and get BackupStorageLocations. +func (s *backupStorageLocationLister) BackupStorageLocations(namespace string) BackupStorageLocationNamespaceLister { + return backupStorageLocationNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// BackupStorageLocationNamespaceLister helps list and get BackupStorageLocations. +type BackupStorageLocationNamespaceLister interface { + // List lists all BackupStorageLocations in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1.BackupStorageLocation, err error) + // Get retrieves the BackupStorageLocation from the indexer for a given namespace and name. + Get(name string) (*v1.BackupStorageLocation, error) + BackupStorageLocationNamespaceListerExpansion +} + +// backupStorageLocationNamespaceLister implements the BackupStorageLocationNamespaceLister +// interface. +type backupStorageLocationNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all BackupStorageLocations in the indexer for a given namespace. +func (s backupStorageLocationNamespaceLister) List(selector labels.Selector) (ret []*v1.BackupStorageLocation, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1.BackupStorageLocation)) + }) + return ret, err +} + +// Get retrieves the BackupStorageLocation from the indexer for a given namespace and name. +func (s backupStorageLocationNamespaceLister) Get(name string) (*v1.BackupStorageLocation, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1.Resource("backupstoragelocation"), name) + } + return obj.(*v1.BackupStorageLocation), nil +} diff --git a/pkg/generated/listers/velero/v1/expansion_generated.go b/pkg/generated/listers/velero/v1/expansion_generated.go index cb5c5fb8c5..b57656650d 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{} +// BackupStorageLocationListerExpansion allows custom methods to be added to +// BackupStorageLocationLister. +type BackupStorageLocationListerExpansion interface{} + +// BackupStorageLocationNamespaceListerExpansion allows custom methods to be added to +// BackupStorageLocationNamespaceLister. +type BackupStorageLocationNamespaceListerExpansion interface{} + // DeleteBackupRequestListerExpansion allows custom methods to be added to // DeleteBackupRequestLister. type DeleteBackupRequestListerExpansion interface{} diff --git a/pkg/install/resources.go b/pkg/install/resources.go index d65975628d..2c5a0d9a14 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -19,6 +19,8 @@ package install import ( "time" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + corev1 "k8s.io/api/core/v1" rbacv1beta1 "k8s.io/api/rbac/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -26,9 +28,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - v1api "github.com/vmware-tanzu/velero/api/v1" "github.com/vmware-tanzu/velero/config/crd/crds" - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/buildinfo" ) @@ -138,17 +138,17 @@ func Namespace(namespace string) *corev1.Namespace { } } -func BackupStorageLocation(namespace, provider, bucket, prefix string, config map[string]string, caCert []byte) *v1api.BackupStorageLocation { - return &v1api.BackupStorageLocation{ +func BackupStorageLocation(namespace, provider, bucket, prefix string, config map[string]string, caCert []byte) *velerov1api.BackupStorageLocation { + return &velerov1api.BackupStorageLocation{ ObjectMeta: objectMeta(namespace, "default"), TypeMeta: metav1.TypeMeta{ Kind: "BackupStorageLocation", - APIVersion: v1api.GroupVersion.String(), + APIVersion: velerov1api.SchemeGroupVersion.String(), }, - Spec: v1api.BackupStorageLocationSpec{ + Spec: velerov1api.BackupStorageLocationSpec{ Provider: provider, - StorageType: v1api.StorageType{ - ObjectStorage: &v1api.ObjectStorageLocation{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ Bucket: bucket, Prefix: prefix, CACert: caCert, @@ -159,14 +159,14 @@ func BackupStorageLocation(namespace, provider, bucket, prefix string, config ma } } -func VolumeSnapshotLocation(namespace, provider string, config map[string]string) *v1.VolumeSnapshotLocation { - return &v1.VolumeSnapshotLocation{ +func VolumeSnapshotLocation(namespace, provider string, config map[string]string) *velerov1api.VolumeSnapshotLocation { + return &velerov1api.VolumeSnapshotLocation{ ObjectMeta: objectMeta(namespace, "default"), TypeMeta: metav1.TypeMeta{ Kind: "VolumeSnapshotLocation", - APIVersion: v1.SchemeGroupVersion.String(), + APIVersion: velerov1api.SchemeGroupVersion.String(), }, - Spec: v1.VolumeSnapshotLocationSpec{ + Spec: velerov1api.VolumeSnapshotLocationSpec{ Provider: provider, Config: config, }, diff --git a/pkg/persistence/object_store.go b/pkg/persistence/object_store.go index 311f313f7e..545f3f0423 100644 --- a/pkg/persistence/object_store.go +++ b/pkg/persistence/object_store.go @@ -29,7 +29,6 @@ import ( "github.com/sirupsen/logrus" kerrors "k8s.io/apimachinery/pkg/util/errors" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" 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/plugin/velero" @@ -91,7 +90,7 @@ type ObjectStoreGetter interface { GetObjectStore(provider string) (velero.ObjectStore, error) } -func NewObjectBackupStore(location *veleroapiv1.BackupStorageLocation, objectStoreGetter ObjectStoreGetter, logger logrus.FieldLogger) (BackupStore, error) { +func NewObjectBackupStore(location *velerov1api.BackupStorageLocation, objectStoreGetter ObjectStoreGetter, logger logrus.FieldLogger) (BackupStore, error) { if location.Spec.ObjectStorage == nil { return nil, errors.New("backup storage location does not use object storage") } diff --git a/pkg/persistence/object_store_test.go b/pkg/persistence/object_store_test.go index a2ebf0dd65..61d628c09c 100644 --- a/pkg/persistence/object_store_test.go +++ b/pkg/persistence/object_store_test.go @@ -32,7 +32,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/plugin/velero" @@ -611,7 +610,7 @@ func (osg objectStoreGetter) GetObjectStore(provider string) (velero.ObjectStore func TestNewObjectBackupStore(t *testing.T) { tests := []struct { name string - location *veleroapiv1.BackupStorageLocation + location *velerov1api.BackupStorageLocation objectStoreGetter objectStoreGetter wantBucket string wantPrefix string @@ -619,7 +618,7 @@ func TestNewObjectBackupStore(t *testing.T) { }{ { name: "location with no ObjectStorage field results in an error", - location: new(veleroapiv1.BackupStorageLocation), + location: new(velerov1api.BackupStorageLocation), wantErr: "backup storage location does not use object storage", }, { diff --git a/pkg/restic/common.go b/pkg/restic/common.go index 6f0a02ec26..7d98ba1213 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -28,7 +28,6 @@ import ( "k8s.io/apimachinery/pkg/labels" corev1listers "k8s.io/client-go/listers/core/v1" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" 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" @@ -285,7 +284,7 @@ func TempCACertFile(caCert []byte, bsl string, fs filesystem.Interface) (string, return name, nil } -func GetCACert(loc *veleroapiv1.BackupStorageLocation) ([]byte, error) { +func GetCACert(loc *velerov1api.BackupStorageLocation) ([]byte, error) { if loc.Spec.ObjectStorage == nil { return nil, nil } @@ -305,7 +304,7 @@ func NewPodVolumeRestoreListOptions(name string) metav1.ListOptions { // should be used when running a restic command for an Azure backend. This list is // the current environment, plus the Azure-specific variables restic needs, namely // a storage account name and key. -func AzureCmdEnv(loc *veleroapiv1.BackupStorageLocation) ([]string, error) { +func AzureCmdEnv(loc *velerov1api.BackupStorageLocation) ([]string, error) { azureVars, err := getAzureResticEnvVars(loc.Spec.Config) if err != nil { return nil, errors.Wrap(err, "error getting azure restic env vars") @@ -323,7 +322,7 @@ func AzureCmdEnv(loc *veleroapiv1.BackupStorageLocation) ([]string, error) { // should be used when running a restic command for an S3 backend. This list is // the current environment, plus the AWS-specific variables restic needs, namely // a credential profile. -func S3CmdEnv(loc *veleroapiv1.BackupStorageLocation) ([]string, error) { +func S3CmdEnv(loc *velerov1api.BackupStorageLocation) ([]string, error) { awsVars, err := getS3ResticEnvVars(loc.Spec.Config) if err != nil { return nil, errors.Wrap(err, "error getting aws restic env vars") diff --git a/pkg/restic/common_test.go b/pkg/restic/common_test.go index 756f44bc5c..2fcddabd79 100644 --- a/pkg/restic/common_test.go +++ b/pkg/restic/common_test.go @@ -36,7 +36,6 @@ import ( . "github.com/onsi/gomega" - veleroapiv1 "github.com/vmware-tanzu/velero/api/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" @@ -390,15 +389,15 @@ func TestTempCACertFile(t *testing.T) { g := NewWithT(t) var ( fs = velerotest.NewFakeFileSystem() - bsl = &veleroapiv1.BackupStorageLocation{ + bsl = &velerov1api.BackupStorageLocation{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Namespace: "velero", Name: "default", }, - Spec: veleroapiv1.BackupStorageLocationSpec{ - StorageType: veleroapiv1.StorageType{ - ObjectStorage: &veleroapiv1.ObjectStorageLocation{CACert: []byte("cacert")}, + Spec: velerov1api.BackupStorageLocationSpec{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{CACert: []byte("cacert")}, }, }, } @@ -408,7 +407,7 @@ func TestTempCACertFile(t *testing.T) { fakeClient := newFakeClient(g) fakeClient.Create(context.Background(), bsl) - locationCreated := &veleroapiv1.BackupStorageLocation{} + locationCreated := &velerov1api.BackupStorageLocation{} err := fakeClient.Get(context.Background(), client.ObjectKey{ Namespace: bsl.Namespace, Name: bsl.Name, @@ -534,6 +533,6 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { func newFakeClient(g *WithT, initObjs ...runtime.Object) client.Client { g.Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) - g.Expect(veleroapiv1.AddToScheme(scheme.Scheme)).To(Succeed()) + g.Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) return k8sfake.NewFakeClientWithScheme(scheme.Scheme, initObjs...) } diff --git a/pkg/restic/config.go b/pkg/restic/config.go index 8a78ba769a..771c5448d5 100644 --- a/pkg/restic/config.go +++ b/pkg/restic/config.go @@ -27,7 +27,7 @@ import ( "github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/pkg/errors" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/persistence" ) @@ -45,7 +45,7 @@ var getAWSBucketRegion = getBucketRegion // getRepoPrefix returns the prefix of the value of the --repo flag for // restic commands, i.e. everything except the "/". -func getRepoPrefix(location *veleroapiv1.BackupStorageLocation) (string, error) { +func getRepoPrefix(location *velerov1api.BackupStorageLocation) (string, error) { var bucket, prefix string if location.Spec.ObjectStorage != nil { @@ -93,7 +93,7 @@ func getRepoPrefix(location *veleroapiv1.BackupStorageLocation) (string, error) // GetRepoIdentifier returns the string to be used as the value of the --repo flag in // restic commands for the given repository. -func GetRepoIdentifier(location *veleroapiv1.BackupStorageLocation, name string) (string, error) { +func GetRepoIdentifier(location *velerov1api.BackupStorageLocation, name string) (string, error) { prefix, err := getRepoPrefix(location) if err != nil { return "", err diff --git a/pkg/restic/config_test.go b/pkg/restic/config_test.go index f51a9e0799..67133aab6c 100644 --- a/pkg/restic/config_test.go +++ b/pkg/restic/config_test.go @@ -22,7 +22,7 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/assert" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" ) func TestGetRepoIdentifier(t *testing.T) { @@ -31,11 +31,11 @@ func TestGetRepoIdentifier(t *testing.T) { return "", errors.New("no region found") } - backupLocation := &veleroapiv1.BackupStorageLocation{ - Spec: veleroapiv1.BackupStorageLocationSpec{ + backupLocation := &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ Provider: "aws", - StorageType: veleroapiv1.StorageType{ - ObjectStorage: &veleroapiv1.ObjectStorageLocation{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ Bucket: "bucket", Prefix: "prefix", }, @@ -51,11 +51,11 @@ func TestGetRepoIdentifier(t *testing.T) { return "us-west-2", nil } - backupLocation = &veleroapiv1.BackupStorageLocation{ - Spec: veleroapiv1.BackupStorageLocationSpec{ + backupLocation = &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ Provider: "aws", - StorageType: veleroapiv1.StorageType{ - ObjectStorage: &veleroapiv1.ObjectStorageLocation{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ Bucket: "bucket", }, }, @@ -65,11 +65,11 @@ func TestGetRepoIdentifier(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "s3:s3-us-west-2.amazonaws.com/bucket/restic/repo-1", id) - backupLocation = &veleroapiv1.BackupStorageLocation{ - Spec: veleroapiv1.BackupStorageLocationSpec{ + backupLocation = &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ Provider: "aws", - StorageType: veleroapiv1.StorageType{ - ObjectStorage: &veleroapiv1.ObjectStorageLocation{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ Bucket: "bucket", Prefix: "prefix", }, @@ -80,14 +80,14 @@ func TestGetRepoIdentifier(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "s3:s3-us-west-2.amazonaws.com/bucket/prefix/restic/repo-1", id) - backupLocation = &veleroapiv1.BackupStorageLocation{ - Spec: veleroapiv1.BackupStorageLocationSpec{ + backupLocation = &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ Provider: "aws", Config: map[string]string{ "s3Url": "alternate-url", }, - StorageType: veleroapiv1.StorageType{ - ObjectStorage: &veleroapiv1.ObjectStorageLocation{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ Bucket: "bucket", Prefix: "prefix", }, @@ -98,14 +98,14 @@ func TestGetRepoIdentifier(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "s3:alternate-url/bucket/prefix/restic/repo-1", id) - backupLocation = &veleroapiv1.BackupStorageLocation{ - Spec: veleroapiv1.BackupStorageLocationSpec{ + backupLocation = &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ Provider: "aws", Config: map[string]string{ "s3Url": "alternate-url-with-trailing-slash/", }, - StorageType: veleroapiv1.StorageType{ - ObjectStorage: &veleroapiv1.ObjectStorageLocation{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ Bucket: "bucket", Prefix: "prefix", }, @@ -116,11 +116,11 @@ func TestGetRepoIdentifier(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "s3:alternate-url-with-trailing-slash/bucket/prefix/restic/repo-1", id) - backupLocation = &veleroapiv1.BackupStorageLocation{ - Spec: veleroapiv1.BackupStorageLocationSpec{ + backupLocation = &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ Provider: "azure", - StorageType: veleroapiv1.StorageType{ - ObjectStorage: &veleroapiv1.ObjectStorageLocation{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ Bucket: "bucket", Prefix: "prefix", }, @@ -131,11 +131,11 @@ func TestGetRepoIdentifier(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "azure:bucket:/prefix/restic/repo-1", id) - backupLocation = &veleroapiv1.BackupStorageLocation{ - Spec: veleroapiv1.BackupStorageLocationSpec{ + backupLocation = &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ Provider: "gcp", - StorageType: veleroapiv1.StorageType{ - ObjectStorage: &veleroapiv1.ObjectStorageLocation{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ Bucket: "bucket-2", Prefix: "prefix-2", }, @@ -146,11 +146,11 @@ func TestGetRepoIdentifier(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "gs:bucket-2:/prefix-2/restic/repo-2", id) - backupLocation = &veleroapiv1.BackupStorageLocation{ - Spec: veleroapiv1.BackupStorageLocationSpec{ + backupLocation = &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ Provider: "unsupported-provider", - StorageType: veleroapiv1.StorageType{ - ObjectStorage: &veleroapiv1.ObjectStorageLocation{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ Bucket: "bucket-2", Prefix: "prefix-2", }, @@ -161,14 +161,14 @@ func TestGetRepoIdentifier(t *testing.T) { assert.EqualError(t, err, "restic repository prefix (resticRepoPrefix) not specified in backup storage location's config") assert.Empty(t, id) - backupLocation = &veleroapiv1.BackupStorageLocation{ - Spec: veleroapiv1.BackupStorageLocationSpec{ + backupLocation = &velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ Provider: "custom-repo-identifier", Config: map[string]string{ "resticRepoPrefix": "custom:prefix:/restic", }, - StorageType: veleroapiv1.StorageType{ - ObjectStorage: &veleroapiv1.ObjectStorageLocation{ + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ Bucket: "bucket", Prefix: "prefix", }, diff --git a/pkg/restic/repository_manager.go b/pkg/restic/repository_manager.go index 94bc93665f..76980bbd42 100644 --- a/pkg/restic/repository_manager.go +++ b/pkg/restic/repository_manager.go @@ -32,7 +32,6 @@ import ( kbcache "sigs.k8s.io/controller-runtime/pkg/cache" k8sclient "sigs.k8s.io/controller-runtime/pkg/client" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" 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" @@ -246,7 +245,7 @@ func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error { cmd.PasswordFile = file - location := &veleroapiv1.BackupStorageLocation{} + location := &velerov1api.BackupStorageLocation{} if err := rm.kbCache.Get(context.Background(), k8sclient.ObjectKey{ Namespace: rm.namespace, Name: backupLocation, From d9a4256a7133b483ee024acfbe5ef655dc1c61eb Mon Sep 17 00:00:00 2001 From: Carlisia Date: Sun, 7 Jun 2020 14:22:35 -0700 Subject: [PATCH 17/34] Clean up schema and client calls + code reviews Signed-off-by: Carlisia --- .../velero.io_backupstoragelocations.yaml | 9 +- config/crd/crds/crds.go | 2 +- .../velero_v1_backupstoragelocation.yaml | 15 ++- go.mod | 1 - hack/update-generated-crd-code.sh | 5 - .../velero/v1/backupstoragelocation_types.go | 126 +++++++++--------- pkg/client/factory.go | 25 +++- pkg/cmd/cli/backup/create.go | 23 +--- pkg/cmd/cli/backuplocation/create.go | 24 +--- pkg/cmd/cli/backuplocation/get.go | 7 +- pkg/cmd/cli/restic/server.go | 25 ++-- pkg/cmd/server/server.go | 14 +- pkg/cmd/velero/velero.go | 15 --- pkg/controller/backup_controller_test.go | 21 +-- .../backup_deletion_controller_test.go | 26 ++-- pkg/controller/backup_sync_controller_test.go | 11 +- .../download_request_controller_test.go | 7 +- pkg/controller/gc_controller_test.go | 6 +- pkg/controller/restore_controller_test.go | 7 +- pkg/controller/suite_test.go | 11 +- pkg/restic/common_test.go | 11 +- 21 files changed, 153 insertions(+), 238 deletions(-) diff --git a/config/crd/bases/velero.io_backupstoragelocations.yaml b/config/crd/bases/velero.io_backupstoragelocations.yaml index 4ccf9890b3..8d193a5234 100644 --- a/config/crd/bases/velero.io_backupstoragelocations.yaml +++ b/config/crd/bases/velero.io_backupstoragelocations.yaml @@ -18,7 +18,7 @@ spec: validation: openAPIV3Schema: description: BackupStorageLocation is a location where Velero stores backup - objects. + objects properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -33,8 +33,8 @@ spec: metadata: type: object spec: - description: BackupStorageLocationSpec defines the specification for a Velero - BackupStorageLocation. + description: BackupStorageLocationSpec defines the desired state of a Velero + BackupStorageLocation properties: accessMode: description: AccessMode defines the permissions for the backup storage @@ -80,8 +80,7 @@ spec: - provider type: object status: - description: BackupStorageLocationStatus describes the current status of - a Velero BackupStorageLocation. + description: BackupStorageLocationStatus defines the observed state of BackupStorageLocation properties: accessMode: description: "AccessMode is an unused field. \n Deprecated: there is diff --git a/config/crd/crds/crds.go b/config/crd/crds/crds.go index 4a7cccb6d2..d2c5453046 100644 --- a/config/crd/crds/crds.go +++ b/config/crd/crds/crds.go @@ -30,7 +30,7 @@ import ( var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xa2u\xd2\xe8k\x10\xa5\xc4W\x8f\x9a\xfes\xeb\xa7\u007fuki\xae\x9e?lы\x0fgOR\xe7\xd7pS9o\x8a\x9fљ\xcaf\xf8\twRK/\x8d>+Ћ\\xq}\x06 \xb46^P\xb7\xa3\u007f\x012\xa3\xbd5J\xa1]\xedQ\xaf\x9f\xaa-n+\xa9r\xb4\xbcBZ\xff\xf9\x1f\xd7\xff\xb4\xfe\x973\x80\xcc\"O\u007f\x90\x05:/\x8a\xf2\x1at\xa5\xd4\x19\x80\x16\x05^\xc3VdOU\xe9\xd6ϨК\xb54g\xaeČ\xd6\xda[S\x95\xd7\xd0\xfc\x10\xa6D<\xc2\x1e~\xe0\xd9ܡ\xa4\xf3?\xb6:?K\xe7\xf9\x87RUV\xa8z%\xeesR\xef+%l\xea=\x03p\x99)\xf1\x1a\xbe\xd0\x12\xa5\xc80?\x03x\x16J\xe6\x8c~XԔ\xa8?\xdem\x1e\xff\xf9>;`!B'@\x8e.\xb3\xb2\xe4qqu\x90\x0e\x04<2\xee`#\x8d\xc1\x1f\x84\xa7\xffJ\x8b\x0e\xb5w\xe0\x0f\b\x99(}e\x11\xcc\x0e~\xac\xb6h5zt\x112@\xa6*\xe7т\xf3\xc2#\b\x0f\x02J#\xb5\a\xa9\xc1\xcb\x02\xe1/\x1f\xef6`\xb6\xbf`\xe6\x1d\b\x9d\x83p\xcedRx\xcc\xe1٨\xaa\xc00\xf7\xaf\xeb\b\xb3\xb4\xa6D\xebe\xa2$\xb5\x96\xe8\xd4}G\xfb\xba\xa0\x8d\x871\x90\x93\xb0`@?\xb2\x1cspL\x14ڇ?H\a\x16\xe36\x99\x80-\xb0@C\x84\x8eH\xaf\xe1\x1e-\x01\x01w0\x95\xcaI\u009e\xd1\x12\x9d2\xb3\xd7\xf2\u007fk\xc8\x0e\xbc\xe1%\x95\xf0\x18\x99\x9b\x9a\xd4\x1e\xad\x16\x8aXV\xe1%\x13\xa2\x10o`\x91րJ\xb7\xa0\xf1\x10\xb7\x86\u007f7\x16AꝹ\x86\x83\xf7\xa5\xbb\xbe\xba\xdaK\x9f\x94%3EQi\xe9߮X\xe4\xe5\xb6\xf2ƺ\xab\x1c\x9fQ]9\xb9_\t\x9b\x1d\xa4nj\x98w%J\xb9b\xc45\xebʺ\xc8\xff!q\xdd]\xb40\xf5o$d\xce[\xa9\xf7u7\xcb\xf2(\xddI\xa8\x838\x85i\x01\xff\x86\xbc\xd4ET\xf9\xf9\xf6\xfe\xa1-j\xd2ui\xce\xd4n\xa6\xb9\x86\xf0D(\xa9wh\x03\xe3v\xd6\x14\f\x11u\x1ed\x8d\xc5TI\xd4]\xa2\xbbj[HO\x9c\xfe\x9f\n\x1d\x89\xb3Y\xc3\r\x9b\f\xd8\"TeNR\xb8\x86\x8d\x86\x1bQ\xa0\xba\x11\x0e\u007fs\xb2\x13\x85݊H:O\xf8\xb6\xa5\xeb\x0e\fԪ\xbb\x93M\x1a\xe4P\xd0\xf8\xfb\x12\xb3\x8eb\xd0\x1c\xb9\x93\x19\x8b?\xec\x8cm\fB0:\xeb\x16\xc0!\xa5\f\v\xedD\xa5\xfc#+\xb2{0?\xa3\xf32\xeb\x8e9B\xe7\xd3\xe0\x94\x84\x0e:x9\xa0?\xa0%Y\xe1\x1fX\xed\x8e \x023\xd0a\xce:'\x9e\x10DĚ\x95W)(M\xb2/\x0e\xb6o\t\xd1\xf5\x11\x9c@ͭ1\nE\xd7\x06\xe0k\xa6\xaa\x1c\xf3\xda\xe0\xba\xc9]\xdd\xf6\x86\xf3Q$\xa4&\xcd \xe3O\x88\xe9\xe6W\xb6\xb5\xc2boc$\x9dR\ahlE\x0f8\xc0\x10j\xd2c\xd1\xc3jD\x94\"\xecJ)\xb1Ux\r\xdeV\xc7K\x87y\xc2Z\xf16H\x89t\x14/#D=:\xda\x06%3>Cj\v\xc0\xb4\xf8\x03\x91\xe1`\xcc\xd3\xf4\xd6\xff\x8dF4\x16\f2\xf6``\x8b\a\xf1,\x8d\x8d\x9b\x8d\xc7\xc8\x16\x01_1\xab<\xf6e[x\xc8\xe5n\x87\x96\xa0\x94\a\xe1Ѕck\x8c\x04c\xeaI͎\xb1\xad\x87\u007f\xc32a1\xecw\feRR\xcd\xc8\xf4\xa9\x1b\x1a\xf9\x18:\x97\xcf2\xaf\x84\x02\xa9\x9d\x17:\v\xfb\x105N\xc7\xfb\x80qv\xf6\xb0\rf-\xe1L\xb4\xef\x988\xa3\x11\x8c\x85\x82\xacy\u007f\xa8\x1b\x84\x0f\xa3\xdb\xdd\n\xb25&\x88\xa1\xad\x14\xba\xb8PΖ\xb3\xd1\xeb\xcb\x11\xc05\x17\xc2ٯ\xc4\x16\x158T\x98yc\x87\xc80\xcd\xd4\xd0\xe6m\xd4\b\xed\x06\xacUc\u007fi\x8bmCeFa\x02\xbc\x1cdv\b\xc72\xc9\vC\x81ܠc\xfd\x15e\xa9ކ7\aӜ\x0emB\x85\x9b6\xa9\xccǰ\xfajݴY;״\x19\x8bץe\xcd\xfa?\x0f)\x93\xe1>Y07\xbd\x89\xef)\x98DDI\xae\xf5f\aX\x94\xfe\xed\x12\xa4O\xbd\xe4I\b\x8e\xfcF\xc9S\xaf\xfd\x87cĩ2\xbd9\x9e\xf7\x8e2\xfd+\xb9P/\xfd\x87a\x02\x1b\xfb\xfbh\xeb\x172\xe0s{\xce%\xc8]̀\xfc\x12vRy\xb4G\x9c\x98ڮ\x99\xe6į%\xc1\xfcIE\xad\x10>;ܾ\x92w䚄\xcd\"j\x1cO\r>e\U000aaec7\xe9$T\xe0`PZ,B\x88\xf9\xc0\x14lz\xd8\xf3\xf9\xf8\xe5\x13\xe6\xe3D\x81%\x12\xd6\xdb\xc2\xc7#4\xdb\xcbF\x17y\xd9\x06\xa2\x93RG\x17!]p\t\x02\x9e\xf0-x\x17B\x031D\xd024x\x16\xa2E\xceY\xb0@=\xe1\x1b\x03\x89i\x88\x99\xb9\xcbX\x1f\xda\x13\xbe\xcd\x0f:\"\x1ba#]L\xab\x10\xfd\xa8\x83\t\xc01\xecR\x92\x01'\x91\x92\x85\x99\xdb\x14,5\x11\xa9%j\x9f\xbc\xbd\x9aMM\xde#0\xf2\xc2\x05\xa6\x90\xb4\x1fd\xb9h\x83d:\xc1!\xebDJ\"=\n%\xf3z\x99 \xdf\x1b}\t_\x8c\xdf\xe81g\xb5\xdbn_\xa5\x8b\xb9\xbbO\x06\xdd\x17\xe3\xb9\xe7݉\x18P>\x99\x84a\x1a\xab\x90\x0ef\x98\xf6\xdf\xceE\xcd\nqh\x9b\x10a\xd5,\x91\x0e6\x9ab\x88@\xab\x90M\f\x8bMY\xfbn+*\xc7\xc9&m\xf4\x8a\x0f\xbb\xf5\xd0:\x91\xc4\v\x05\xb9ͅ>Z\xf5\x92a\xb9E\x10\x1f\xe8\\\b\xb3CfT\x89\fs\xc8+&\"g\xf6\x84ǽ̠@\xbb\x1f?\bڭ$\x9b\xbdd\xf9E\xb64\xb4\x93\xe4i\xc9ќZ4\xc6\xf9\x1c\x1a+\xd2\xcd\xd91\x89\xb53\x03\aSy\xe3\x03\xe7\xf6\xc1\x87$\xfb\r3\xd4\x14y\xce7)B\xdd-\xb6ދ)\xdf?\xb7\x03J\xe1\x8c+\x04'\xe8\xfe\x8f\x8e*\x16\xda\xff\x87RH;\xab\xa1\x1f\xf9JDagf\xcc\n\xb5\x17!\xf8\xd2\x01q\xf3Y\xa8\xe3\x84\xf0\xc0\xb6\fY\rT\xe1\x186\xbb\x9e\xa7q\t/\a\xe3©\xb8\x93\xa8r\x90S\x9e\x16\xb5\xf3'|;\xbf\xec\xe9\xf8\xf9F\x9f\x87㹧\xb1\xe9,\x9f\x01l\xb4z\x83s\x9ey\xfe\xf5\xae\xcb\"\xa9[0\x88\xefǖ9\xb3\x14ͥS\x9c\xa6\xd5w0䊎c\xbb@\xe6J\xe3\xfcB$\xee\x8c\xf3!C\xd7q\x1e\arC\xd31M\xcc\t\x81\u0605{/c\xd3\r\a\x19\xb2\xa3T%q\xc9\xe1`\x82\xb3\a1\x8f \x85Rp\xde\xe8h\xb0\x8f\xe7\xe1ڃ\x97\x10\x19\xbb\x05\x13\x10I\x14Jk2tnJ\x1cf-\xefL\u00adN\xb6\x89\x10T\x84K\x84\xa9\xe4^jK\xddF\"\xcdIn\xf6\xedk+\aH\xaaM\xffO\x8b\xd9i\x18\x01_2\x17\x85г\x87E\x0f\xb9\x9b0/\xa9B\x04\x13\\v\xbb\xafX\x8d\x97zzQh\xbe\xed\x01[H\xbda\xe0\xf0\xe1]\x8fcH&\x11Ow\xa9o\xd2̆\xccuG\xd0\xcd\xd2\xf4S\xeeC\xed\xe5\x80\x16;\x9c\xeag\x86ٝ\xd3Ʒ\xc2\xf3e\x84\x0ex\\8\xd8I\xeb|\x1bI\xc7\x17[\xef\x1f\xa3\xe8[k\xbf\"D\xf9)\xcck%\x80\x0e\xe6%\xdd\x14\x8e\\\xce\r5\xbe\x06A\x90;\x90\x1ePg\xa6Ҝ\xc4 %\xe5\x05\x02I\x831\x9d=dC[\xa2\xd8\xd4PWŒ\x8d\xafXz\xa4\x9e\xc8u\xb4\a\xffMȩLUj'\xb1\xc9\xcb\x02M5q\xa85\xadæ\x870\xafs\xc5[\x88WYT\x05\x88\x82\x88\xbd\x88\xa2t2\xcb\x02\xbb\xfc\x85\x17!=[w\x82ʦ\xde\x1bR\x8aR\xa1_\x16\rlqg,뢓9\xd6Gf\xe4\xb9\xd1 `'\xa4\xaa\xec\"\x8bv\x02E\x97{\xf6Q\xc9\xdf\xc7i_\xb2슷?\x9b\xa6\\\xe4\xaaMY\xd5\xd2.u\xd4\xee,\xbe\xa7\x8bTZI2c\xde\xd7K\x8a\xa2$\xf4\xdbw7\xa9E\x9b\xefnR\xaf}w\x93:\xed\xbb\x9b\xf4\xddM\x9al\xdfݤ\xefnҟ\xd5M\x9a\xc6d\xc5y\xab\xc1\x9ffV\x9f\xbdB\x1dGl\x14r\xbcտ\t\xb5\xd7\xcb\xea\xf26\xc3s\x06\xea.cI\xf7\x8a+\xce\xfb|n\xae\xfe\x1b3_\x17\xea\x91\xf0'\xe1\r\x85\xa5\x93\xa5{\v\n\xf1\x86j3\xe7\xcbK\xe6\x8aJ\xba5\x89uaG*J4i\x89\xde\xeeS%;\xb9\x99\xed\n\x06\xa1T\xbb6E؆(ߨ^q\xb6\xf4c\xa6\xe0c\xbals\x9cBG\xae}\x97D\xb6Sb\xf8\x8d)4Y\x971^\x8d\x11o2Ћ\xe7\x0f\xeb\xee/\xde\xc4\xda\fx\x91\xfe\xd0\xdb\x00\x17MRȢ\xf7\xed\xe2\xc8$S\xf1\xf9\xc01\xe5\xc0X\xd0R]\x0e\xd6\xc5\xd4/+\xda䄟\xca\x10\x14\x9d\xa4oS\xae\xfd\x92ڍ\xaf\xae\xd8\xe8\xd6d\f\x1a\xd9\xd3.;\x96\x96\x90.\xaf\xc9\xe8\xd6\\\x8c\x1c2\v*1N\xae\xb4\x98\x8f\xb7&\xab*\xbe\xa2\x96\"\xd5IL\x1d\xb8\x13\x15\x14\v|\x8e\xf9j\x89\xaf\xaa\x91\xe0˼\t\xacO\xaa\x8chU=L\x80\\V\x0f\xb1\x80$s\xb5\x0f'W<\x1cW\x19Llb\xae\xcea\xbc\x86a\x02\xe8`uÒʅ\t\x98uM\xc3;\xd6+\xccT)\xbcO%\xe1\xaf\xf5=\xc7j\x0ef*\rf<\xd3)\xacfj\t\x96W\x10\xcc\xd0\xe7+\xab\x05\xeaz\x80\xc15O\xad\x11\xe8V\x01\f\x82\\X\x190r\xf7?\brA=\xc0̍\xff \xd8ɃqB\"F\u007frZ\x94\xee`҃\xadI?\xe9\xbe;v \xb8Hϵ2e\xaa\xbc\x86\xdd\xdf\n?\xc1|\x83\xbbG6\xf2\xfc\x14&k\x1e\x02ES\x9e\x9c\x9f\xe3wB?\xbcg\xb0ἱb\x8f\x9fM\xd6zi;\xb6\xff\xee\xd8γ\xc8\xc8\xd4\x14ҧ:\b\x91\xde\xe7u\xa7\x0e\xf9\x8e1\xcb\x16߹5\xd1\x17a\xd8\xe7\xf7\xa8\xe6y\xaf&7\xf1\xf0\xf09 \xeee\x81\xebOU\b\xe4V\xa5\xb0\x0e\x89~iCaҖ\xfe<\x98\x97\x1e\xc2\xcaĝ\xfep\x8c\xafE\xce\xe1q\xb4\xb8\x18\xeb\xf0\x96/\tX\"Ӵ8>\x0e\xcfi\xf9\xa2-\xa6\x84\xc0\xc6\xec\xc6f\xf56\xd8z\xc8L\xde~\xa8hy\xaf'j\xc3\xc6y\xf8\xf1\xa7\x17\xbers\xcf?yPz\xcc\x1d3\xbe\x95\xe5\x17f\x01@\x10Ɠ_\x80\xc6\xf4V\xe7\t\xfd\x14On\xfa\xe3\xf9)\xb5\xcd\x03R\x9cV\xab\x1fs\xbe\bW'\xd0\x06,Z\x03,\xcccg\x80`a\x0e\xf8\x8c\x1a\x8c\xe6|\x19\xbf\xe0\n\x0f\xf9\x8f\xe7\xf4\xe3\xd7\x16\x8c\x98\x8e\xabJeD\x9e47\xbd3\x8d\xcf\xc3\x1f\xd8\x1e\xd9g\xb4\x17n\x14\"?U\xdd\x19;\xb4\xfdc\xc9\xda\x19[\b\u007f\r\xb9\xf0\xb8\x1a\x00\xb8\xc0\x8e\r\x88\x14'\x8fg\x9en\xf2\x90\xa0\x1d\x9cwNoiC\xe2\xb9@\xe7\xc4>\xbd\xd9|!s\xb4GM\x87\xdc@\x96(\xbabM\xe2\xb2\xfb~1Dt\"\xf3\x14\xff\x06\xd4b\b\xdb\x1au\xd1\xd79e\xf6\x14a\xf3\xc0\xf8b<\xda\xe7aC\"\xb5\xc7=v\xdd#|-\xa5\x9d\xb7\xe5\xb7\xf50\xa2\b\x87\xee\xac\xe1\xcd\a\x14Pɽ$\x83H\x8c\xdd\v\xbb\x15{\\eFQ\x1c%\x8d>\xc6\xe8\xb7\xe1k\x80:\xf0u\x84ކ\xfe\xd6\x1e\x99\x02\xc1(\xcc\x01J\xfaX\xc2e\xff\xac+\xa4 \xaaz\x0e6\x1a3\x01\xb0\xa2\xc29\xac\x85|\x8a5\x05\xe7\xc5\x06\x8d\x93\xcdY\xc5\x0e\rzWh7\xa1\x1a%\x1f\xbd\xf1.\xd6s8l4\x1a2\xacƤ\xb7I٪Qv\x9f\x95\xa5}\xa3)\xfct^\xe6^SHr\xb5\x89^\x98s\xb0\x92\bi\xbb\x89F\xf83B\x13\x00\x92\xae\xc69|dx\xb5\x90\xa8&\x00;a\xb4J\x02\r`W\xa3\xbd[\xbe\u007f\xfc\xdfJn\xb1\x12\xcd\"\x80B\x92^\xd7In\x1c*h\x02\x01\xedi\xf0\xbcE\x8f\xf0\x98X\x01\x86\x82\x94qe\x8d\x00n\xfd\a\xca@E^\xa8\xbd\xab\xd1\a\xddR\xc7\xe3(t\xba\xb5\x01\x98[F\xdbȀ\xe2`A\x82\xb0E\xc8.G\x05\x94,\x01WB\xd8j\x02\x8f\xb5GB\x1b\x0e^\xe8\x10\x95 l\xc6U\xc0\n=+\x01ںh\x14G\xd8\x0e}\x00\x8f\xd2m\xac\xfe\xab\xd3L\x10\\:҈\x80\xd9_\xed\xd06\xa0\xb7\xc20\xcf\x11\xff\v\xc2*\xa8\xc4\x1e<\xf2\x19\x10푶$B\x05|p\x1eA\xdb\xd2\xcda\x1bBM\xf3\xd9l\xa3C\x9b,\xd2UU\xb4:\xecg)\xe4\xf5:\x06\xe7i\xa6p\x87fFz3\x15^nu@\x19\xa2Ǚ\xa8\xf54\x01\xb7M\xfcV\xea?>g\x16\xdd\x1e!\r{\x8e\f\n^\xdbM\xb7\x9c\x82\xf7,\xef\x1c\xb6\x8dӛ\xcf\x1a\xfc\azy\x89Yy\xf8q\xf5\x19\xdaC\x93\v\xfa\x9c'\xb6\x0f\x9fсx&J\xdb\x12}\xe3\xb8һ*iD\xabj\xa7mH\x13i4\xda>\xe9\x14ו\x0e\xec\xe9?#R`\xff\x14\xb0H%\x03\xd6\b\xb1V\"\xa0*ཅ\x85\xa8\xd0,\x04\xe1w\xa7\x9d\x19\xa6)Sz\x9d\xf8\xe3J\xd7\x17l\xd8\xea\x96\xdb\"4\xea\xa1\xd14]\xd5({y\xc2*t\xa9sږ\u0383\xc8i\xdb\xe3tTYq$2\x96\xbe)\x85\xa5D\xa2\x0fNa\u007f}\x00\xf6\xae\x13롫\xd1W\x9a\xd2m\x91\xb0\xf1ZSF \u05f7\x81R\xe8JP1\xd8A\x1b\xab!\x84)<\xa0P\x9f\xacُn\xfc\xe2u\x18\x1e0\xea0\x1e\r\xac\xd5\xde\xca%z\xed\xd4Es\xdf\x0e\x84;\xa3\xb7\xee\x19\xca\x14\xb86\x98=W\x16\xda[9,\x9d\xed\xb8[\xbeo\xcbh\x93\x1e9\x9b27\x05\xdc\xe5\xact%\xbc\x02\xa5I\xac\rRR9\xa4\x87o?ޝC\xf0\xf1\xc5FKgK\xbd\x19\x9a*\x94JW\xb60\xcb3QqQ逫E:\x83K\rG@\xed\xddN+\xf4\xd36p3\x86\xe8s\x04k4\x8a\x86֍f\x0ft\xe5'\xc7\xf5E\x97}:\x96\xecn\xbd\x8c\xa2M&\f\\\xf7\b,r8\v?\x8c+`\x8fJg-{)8\x10\x9d=\xb74t\xde\xe0\xd3s\t\xc6c\x1d\xe5\x13\x86\xd3\xf5a\xd4%1f2\xe5Q3\v\x0e\"a\xe2\xf62\x80+>\x03\x90b\x81\xfe:\x8a\xc5\x1d\x8bu\x11/`q\a\xebh\x95\xc1\x16\xcb\xf3\x16-_\xe0\xba\xdc\xf3-\xf2\xf9~5\xa2\x13Z\x1eSq\xc8Wp\xcb\xe6\x18\xf6\xd2\xf9J\x849\xac\xf7'I}մ\xdac\xa9\xbf^5m\x99\xc4Z\x82k\x11\xb6\xa0-i\x85 F\xe8\x1e)\xb3\xed\xe8\x12\xf8S\xdd$\xd27:\x83+\x88\xf6xR\x84\xa6\x19\xc6Kӣ\xe5\xf3bf,\xb3Pgw;O-װ`\x8f\xa7\xe6\x89\x15c\x16L\xfb\xe9\xda\xdbi\x0f\xbdze\x06\x11\"}㥙\xbeɂ\xeb\x9c\xeb2z\x8f6d\x85\xe0\xca\xfe\xb5\xd7\xf6\xbe\xdf\xfb\xe2\xbc9\xba9\xb9\x1b\xb3\x10m$TM!,\xe07\v︷\x92\xdc\xf3\xcc\x199\xb79t\x12N\xd6=\xf3\xc7Gڒ\x02p6Y\x9b\xfa\x06\xee^\x9bV,m=kc\xb8\xa1\xf2X\xb9]zM\xf4\aw?\x1e\xcd\x1e\x04\xd3\x03\xbb7ū\xe2\xe6_\xbe\x95\x8d\xa0\xc0\xd7,\xaa\a\xdc\xe9\xe1K\xe2\x94\xcd\xfb\x13\xf96\xaa\xbb\x8b\x94'_\xda\x16m\xe6\xb3ؗ\x13\xf3Km\xb8\xa1\x1cI\x81\xc33\xa9y6P\x80\xa0+L\xb3\xb7\xab\xfb[J\xcfYn\x86O\x94>\xb3\xfb(\x01\xe4Dž\xcb=p\xa4\x80~\xc4ٝ\xaf4\x81u`\x9c\xdd\xf4R\xa4\x19\xb9#\x06\xe7\xa1\t\x1d\xe7A!7\xb3\\~\xe5V\xd8\r\x1e^9\x19\xfb\x11J\x0e\x8cS\xa4\xfd\xe88D\x83\xb6\xe3\xa1\xf0\x02\x1f\xf2k\xfd\xa2\xff\xee{\xa2\xad\xeb\xfa\fw\xa8\xb3/\xcd\xf1\x83\xfc\xc5\\\x0f\xa4\xdb˅\x89\x9c\xf2Q\xffH{Uo\x05]6x\xc9\x12\xad\x9d\xc7%\xa9\vի\x05\bΦ\xe1\xddN\xe8\x84\xfad\xe7g+\xce읱e\xa4\x16\x0f\x96\x0e\xffh^\x1ff\xa9&N\xf3?\x99\xb4\x01@\xfc\x0e\xff\xc7\xdc\xdc\xf4~\xaa\xa4\xa9t\xb6iZi\x0e\xbf\xfe>i\xb4\xa2zlq\xf0\xe2\xdf\x01\x00\x00\xff\xffN\xa7\x18\xba\xcf\x12\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcXO\x8f۶\x12\xbf\xfbS\f\xf6\x1d\xf6\xf2,'yx@\xe1\x9b\xe3\xf4\x10t\x93\x18\xebt{h\v\x84&G6\xbb\x14\xa9rHo\xdcO_\fEɖ,{7@S\xddH\x8e\x86\xbf\xf9\xcd_i2\x9dN'\xa2\xd6\x0f\xe8I;;\aQk\xfc\x1a\xd0\xf2\x8a\x8a\xc7\x1f\xa8\xd0n\xb6\u007f\xbd\xc1 ^O\x1e\xb5UsXF\n\xae\xbaGr\xd1K|\x87\xa5\xb6:hg'\x15\x06\xa1D\x10\xf3\t\x80\xb0\xd6\x05\xc1\xdb\xc4K\x00\xe9l\xf0\xce\x18\xf4\xd3-\xda\xe21np\x13\xb5Q\xe8\xd3\r\xed\xfd\xfbWś\xe2\xff\x13\x00\xe91\xbd\xfeYWHAT\xf5\x1cl4f\x02`E\x85s\xd8\b\xf9\x18k\n\u038b-\x1a'\x9b\xbb\x8a=\x1a\xf4\xae\xd0nB5J\xbez\xeb]\xac\xe7p{\x9a\x16\x05\n\xdf\xda\xec\x1e\x82\xb6A\x90,r)z\xd0H\x9c1\x1b[RIj\xd2\xf4\xe9\v\xc9\xf6\xccx\xe2 \xdbC}\x13E\x91ԏ\u007f\xac\xaa\xae\xeb\xcaDzD\x16\n\xbe\x05\x13\t\xffR\xf4y%\xcd\xd3\x0f\xd2P\xd8\x1c\xae\xb6\xa8\xe6\xaaz\"\xefZ\xb8N\xa2a\xb8G\t\x89-~\xc2\x1dyR\n\xbe\x1aP\x8d3j\xda\n\xc0x\x1f\xd4d\xb1\xe4%\x80\r^9\xf4=r\xbdG\xdf<\xa5-n\x13\xf5\x0e\xb9x\x98\xfd\x1f\xbei\xbem\xbe\xaf\x00,c9\xfe\x85\x06\x145Cl\xc1\xa7\xbe\xaf\x00\xbc\x19\xb0\x05\x87=*n\x8d}J\x91\xf1τ\xa2\xd2\x1c\xb0G\x0e\r\x85J\"\xda\xecx\xcf!\xc5\x16N\x1b\xe3\xf9)\xa8\xf1B\x9f\x8a\xa9\x9f\x8a\xa9\xfb\xd1T\xd9\xedI\xf4\xe7\xb74~\xa1I+\xf6\x89M\xbf\x1ePQ\x10\xf2\xfb\xd4\x1b^U\xa9\x00Ć\x88-\xdc氢\xb1\xe8*\x80\x83\xe9ɕ\xfb\x8f\x81\x86\x88\xfeǻ\x9b\xc7\xef\x1el\x87\x83\x19\x85\x00\x0e\xc52Ţ\xb7\x16$\x90\x80\x81\xc9\x15h\x98\"\x80\xe0\x11\x02\xc3\x10\x18a\fG\x9a\xc9d\xe4\x10\x91\x95fD\xf9;+\x90\xa3\xec\xc2\xf9\xc7\x1cݨ\x03.\x97\x04\nh\x870%\x16\x1dH\x89\x1c\xc2\x0e\xb4#\x01\xc6\xc8(\xe8\xc7\"93\vY\xc5x\b\xdb?\xd0j\x03\x0f\xc8\xd9\bH\x17R\xefr\x1d\x1d\x90\x15\x18m\xd8{\xfa\xfbhY\xf2\xfd\xb2\xcb\xde\xe8\x9c\xc1\xf9#\xaf\xc8\xde\xf4\x99k\xc2\xff\x83\xf1\x0e\x06\xf3\x02\x8c\xd9\a$\u007ff\xad\xa8H\x03\xbff8\xe4w\xa1\x85N5J\xbb\xd9\xecI疰a\x18\x92'}ٔ¦m\xd2\xc0\xb2qx\xc0~#\xb4\xaf\rێ\x14\xad&ƍ\x89T\x97\xc0}\xe9\x88fp\xff\xe3\xa9\u007f\xe4\xe3Y\xa4\xfa\x92+A\x94\xc9\xef\x8f\xe2R\xa4or\xcf\x05:\xa6y<6\xc6\u007f\u009bE\x99\xca\xfd\xe7\x87/0;-)X2/\xb4O\xc7\xe4\x04>\x83\"\xbfC\x1e\x13\xb7\xe30\x14\x8b\xe8]\f\xe4\xb5,lO\xe8\x97\xd0%m\aR\x99\xcb/私\xeb2\x18`\x8b\x90\xa23\x8a\xae\x81\x1b\x0f\xd7f\xc0\xfe\xda\b\xfe\xe7\xd83a\xa93\xd2\xf7\xc1\x9fϳ\xa5\xe2H\xeb(\x9e\x87\xcdj\x86V\xda\xf2!\xa2\xcd9\xcb\xe0\xf2Yڑ-m\x00\xbb\xc0\xf0ܑ\xed\xe6\xb6\\\x10=6ps&^k\xd8\xfc\x8d\x06\xf2TY\xca߸,\x94<\x11\xe3\xa2\xd6\xea33\xefRP\xa3I\xfe\x15\x87rb&a\x133z\x9d\xec\x94)\xb0v\xe8k\xee\x8é\xe5\xf2ދp>\x17\x95\xf2[2\xe4\x05\x8c\u007f\x99\x8e\x81vF\xe1\x199\x97\xb8\r)\xcf\x0et\xe0\xd2\x05\xaf\tE\x87cRr\xfa\"\a\x8b\"ͅ\x16)\x0e\xaf\xa2y3\x0f\xf9˿:\xb3\xed\xb1\x05儫\xf93\xcc\xe6e\xb1\x13;#\xaf\x92\xbd\xb8\xf4]\xd6X\xe3\x8d\xe3\\\xc6\xf7\x80\x17\xb8>\r\x97^j\xb8\xc5\xe7W\xb2\x1b\u007f\xc7a\xcf(\xf2j\xebn$U~v_\xc1d\xa5\xe0.D\xa7\x17\xc4\xd5iU\xa0\xd7Ӌ\xa1l\x00H\x1ek\xee\f\xach`\xb3\x9fQ\x9f\xaa\xd8X\x8bQ\xd1\xdd^\xbe\x17>|X\xfc\xf8\xcb\xd2\x06\xefh|\xee\xc0o\xbfW\xa3Ut\x8fs\x1cY\xf8O\x00\x00\x00\xff\xff\xbe\x16\xd7\"m\t\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WO\x8fܶ\x0f\xbd\xfbS\x10\xf9\x1d\xf2+\x10{\x9a\x16\x05\x8a\xb9\xb5\x9b\x1c\x82\xa6A0\xbb\xddKуF\xe6\xd8\xeaʒ*R\xb3\xd9~\xfa\x82\x92=\u007f<\x9e\xdd\xf4й\x99\xa2\xa8\xa7\xc7GRS\xd5u]\xa9`\xee1\x92\xf1n\r*\x18\xfc\xc2\xe8䋚\x87\x1f\xa91~\xb5\u007f\xbbEVo\xab\a\xe3\xda5\xdc$b?l\x90|\x8a\x1a\xdf\xe1\xce8\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95\x98I>\x01\xb4w\x1c\xbd\xb5\x18\xeb\x0e]\U000d0db8Mƶ\x18\xf3\t\xd3\xf9\xfbo\x9b\xef\x9a\x1f*\x00\x1d1o\xbf3\x03\x12\xab!\xac\xc1%k+\x00\xa7\x06\\C\xeb\x1f\x9d\xf5\xaa\x8d\xf8WBbj\xf6h1\xfa\xc6\xf8\x8a\x02j9\xb4\x8b>\x855\x1c\x17\xca\xde\x11P\xb9̻1̦\x84\xc9+\xd6\x10\xff\xb2\xb4\xfaь\x1e\xc1\xa6\xa8\xec%\x88\xbcH\xc6uɪx\xb1\\\x01\x90\xf6\x01\xd7\xf0I`\x04\xa5\xb1\xad\x00\xf6ʚ6ߵ\x00\xf3\x01\xddO\x9f?\xdc\u007f\u007f\xab{\x1cT1\x02\xb4H:\x9a\x90\xfd\xe6\xc0\xc0\x10(\x18\x8f\x01\xf6\x87\x93A9P\x91\xcdNi\x86]\xf4\x03l\x95~Ha\x8c\t\xe0\xb7\u007f\xa2f \xf6Qu\xf8\x06(\xe9\x1e\x94D+\x8e`}\a;c\xb1\x19\xb7\x84\xe8\x03F6\x13\x8d\xf2;\x11\xd0\xc16\x03\xfcZnT|\xa0\x15\xc9 \x01\xf7\bc\xe2\xb1\x05ʷ\x05\xbf\x03\xee\rA\xc4\x10\x91\xd0\x15\x11\x9d\x84\x05qQnD\xde\xc0-F\t\x02\xd4\xfbd[\xd1\xd9\x1e#CD\xed;g\xfe>D&\xe1E\x8e\xb4\x8a\xa7LO?\xe3\x18\xa3SVr\x91\xf0\r(\xd7\u00a0\x9e bf'\xb9\x93hم\x1a\xf8\xd5G\x04\xe3v~\r=s\xa0\xf5j\xd5\x19\x9eJF\xfbaH\xce\xf0\xd3*\v\xdfl\x13\xfbH\xab\x16\xf7hWd\xbaZE\xdd\x1bF\xcd)\xe2J\x05Sg\xe0.WL3\xb4\xff\x8bc}\xd1\xeb\x13\xa4\xfc$\xea!\x8e\xc6u\as\x16\xf2U\xdeE\xc8E\x1ee[\xc1\u007f\xa4WL\xc2\xca\xe6\xfd\xed\x1dL\x87\xe6\x14\x9cs^tr\xd8FG\xe2\x85(\xe3v\x18K\xe2\xb2\xca$\"\xba6x\xe38\u007fhkН\x93Ni;\x18\xa6I\xb6\x92\x9f\x06nr\xe3\x80-B\n\xadbl\x1b\xf8\xe0\xe0F\rho\x14\xe1\u007fN\xbb0L\xb5P\xfa2\xf1\xa7\xfd\xeeܱ\xb0u0O\ri1C\xb3R\xbe\r\xa8%_B\x9a\xec3;\xa3s\t\xc0\xceGP\xc7\xca\x1eikN\xe2.\xd5f\x06\xa5b\x87|n\x9b\xa1\xb8\xcb.r\xf0c\xaf\xce[\xc8\xff\xb1\xe9\x1a\xe9\x034B(\x9d\xe1\x9bf\x16\xef\xda\xe9K\x1a]\xc40IU\xae.w\vZtR\trq\x87~\xff\xd5o˷\xe5\x1f&\x00\x95\xa3\xb8\xfcI\xb6\xe4\x03\xb6v\x06\xbaSj\x02\xa0\xb1\xa5\x19X#VFu--\xb0z\xee\xac/W\xa4șR\x9a\x89\xb7T\xf1\xa6\xb53\x9d\x9d\xc1\xeeCZ\x9b\x15J\xc6<\x1a\xf11\x8ay\x17\xc5\xc4/J\xfa\U00037c6f?I\x1f\xe2\f\xab:\x87\xeaX\x89\xf8\xd1K]w\n\xdd\xd1\xe7\t\x80\xaf\x8c\xa5\x19<\xb0\x1a\x16+\x12\x13\x80\x15*)\xa2\xadI1cI\xff\xf0x\xff\xf1w\xf3\xaa\xa1\x16\xd3 \x80uƒ\v\xb2ן\x9f\xbd\x93ێ\x01\b\xf2\x95\x936J\x84k\x16\x95\xe6\x80\xe0\xb3\"\x0f\xa1!Ȉ\x93\x00\x1f\xb7\x01\xb3\x84\xd0H\x0f\x8e\xac#O:\x9dޞX\xe0)\xa8\xc1,\xfeAU(aN\x8e\x85\x80oL\xa7\x04\x1f\xf0\x8a\\\x00G\x95\xa9\xb5\xfc\xd7V\xb2\x87`\xe2\x96\n\x03e\x00\xfbG\xea@N\xa3b\x10:\xba\x01\xd4\x02Z܀#\xde\x03:\xbd'-N\xf1%\xfcl\x1c\x81\xd4K3\x83&\x04\xebg\xd3i-C𥉉i\xdbN˰\x99F\x8f\x93\x8b.\x18秂V\xa4\xa6^\xd6\x05\xba\xaa\x91\x81\xaa\xd09\x9a\xa2\x95ET\\GW-[\xf1\x9dˎ\xed\xaf\xf74\r\x1b>6\x1f\x9c\xd4\xf5v8z\xd0I\xdcك@z\xc0\xbc,鿃\x97\x87\x18\x95\x0f\u007f\x9e?A\xbfi<\x82!\xe6\x11\xed\xdd2\xbf\x03\x9e\x81\x92zI.\x1d\xdcҙ6J$-\xac\x91:ėJI\xd2C\xd0}\xb7he\xe0\x93\xfegG>\xf0\xf9\x94p\x1b#\x16\x16\x04\x9d\x15\x18H\x94p\xaf\xe1\x16[R\xb7\xe8\xe9\xab\xc3\xce\b\xfb\x82!}\x19\xf8\xfdD3\x9c\x98\xd0\xda\x0e\xf7\x99`\xf4\x84\x0e\x82{n\xa9\xe2\xf3b\xd0x\x9d\\\xca*\x86\x00,\x8d\x03<\x9c^\xee\x89\x1d\vM~R\xd8σqX\xd3O\xa6\xda\v\xf2\x13:\xbd\x1b[\xd1k\xc5\xc9+\x85)e\xd1\xe0\xd3\xcc\x03\x91\x00\xaa_\xban\xc8Q\\\xe1\xc8\aY\xb1#\x19/\x83q\x1b\x16\xcb\xebI\x94\a\xebGA\xe7G\x1bAg\xf5\u007f0\x82\xc6\xd4\xe5\x85\x10\x1aL>\xf9hbd\xb8Nk\x8e\x02\xa3/V\xc0\x1aqv\xff,\x19\xc1ђ\x1ci\x8e\xa8\x94|\xac\x89)*\xa0\xd4}\xe4\xa5\xdc\f\xc1\x1c\xc1\xb7H\x00\x93\x80\xe1A\x9f;l8\x99\x8fG5\xfd\xe1\xf1\xbe\xcf\xc1=HY\xe7p\xb8\xe3YD\xf8YJR\xe2\x11C\xf3\xe2\xae\xd7\xf7˴M\xccH\xc1\x00\x82\x95T\xd1 \xb5\x83\xd4>\x10\x8a48\"\x12\x80\x03\xd7Q\x9e\u007f\x93\xf2ONs\xbbr\xc0X\x03\xa6\xfa\x06\u007f\x9d\xbf\u007f\x98\xfe\xc5$]GebU\x91g1\x18\xa8%\x1dn\xc0wU\x03\xe8\xd9\x04\xe9H\xcc\xf9K٢\x96K\xf2\xa1\xcc;\x90\xf3\x9f\xde~\x1e\xc3\f\xe0G〾`k\x15݀L(o\x13j\xef \xd2' \xb6\xf2`-C#\xc7\rGv\xa4l\xf0:\x1a\x1a\xf0\x99\xc0dC;\x02%\x9fi\x06W\x9cB\xf6T\xfc7G\xc3\u007f\xaeFe\xfe&\x05\xe9\x15O\xb9J\x8amk\xe6~\x10\xed\x14L\x91\xe4d]\x93\xa3q4c!\xe0\x04\xfb=\x18Ƕk\xb3' \x8a\xe53K\x89\x8eđ\u009f\xde~>\xa1\xed\x10'\x90Z\xd0\x17x\vR'T\xac\x11ߗ\xf0\x14=b\xa3\x03~\xe1}\xaa\xc6x\xd2`\xb4ڌkk\xa0\xc1\x15\x817-\xc1\x9a\x94*\x12W\x11\xb0\xc6\r\xdb\xdf\x1f\x17{\x18\x82E\x17\x86ldT\xea\xd3\xfb\xbb\xf7\xb3\xa4\x15\xbbP\x1d3)W\xb9\xa5d\xce\xc1d#UN\xf6\xc9\bG\x97\x9c#\x18\xa8\x1a\xd4#\x89\x15\"i\x89\xe8.;\xaee\xe5\xf5k\xa3\xf5\x906\xf4\xcf\b}8L\f\xbfR\x11\xbeȬH\xcb_4\xebaϟϚ\xc5\r\x82\xd3\x14(Z&L\xe5٨\x8al\xf0S\xb3\"\xb7\x92\xb4\x9e\xae\x8d{\x96\xba.\xd8\x11\x8b\xe4\t~\x1a9\xfe\xf4\xbb\xf8\xe7\x17Y\x11\x99\xf9e\xa6ĩ\xdf\xc2\x1e\xde\xc7O_mN\xcf+/\xadJ\xd7\xf3\xcc|\x0eWrH\xac\x1bY5}\x93\xb0˞\xa31ҢH)\x17\xf5櫻-\x03\xd99\xd6gS\xe4>\xb3@-\xf8\u007f/}\xe0\xf1W#\xd7\xc9\v\x82\xf4\xef\xf7w\xdfƙ;\xf9\xea\x88\x1c%\xc4\xc9'\xac\xb9\x17\f\xdfR\x92;K\xa7>\f\xa6\xf6\xc4n\x84In\xe7\\\xcc\xe4\x02\xd6G\x04\n\x85\x887\t\xa8\x1eϐ\xac36\x0f\x94\u007f\xc2\xda\x03:\x02\x84\x16-\x9f\xd33m\x8aT\xa4-J\xae\xb1\\F3_Y\x10\xa0\xb5J\x8e\x94\xd3\\\x8a3]\xcc̛\xdbZ\xac\xfd\xb8\xbd#\xa8\xa7\xd5g\xd1N\xed\xc5\x18}\xce['^\xb2\xa5\xd0\xc1\xec\x88\xea\xb1\xff\x1e\x11\xd7\x13\xb8q\x17\xc8\xecj_\xb5b\xbcu\x19\xcc`J?\x18\xb0F\fއ~6\xf8\x94\xecy\xb1{\v\x18:\u007fq\xff\x16g\xf7\xe8\xa5|\x10\xb2\x8c\xc8\x15~I\aW\x19\xe6\x8e\xc3{\xa8sGx{\xb4F\xdf\xefp܄\xc1\x9e\xb0\xb4.\xe6]\x96E\"R;f\x9dK\x94\x8a\x04\xf4\x97`\x87k\x8ed\xee\xcbXВSUg\x95A\xd17EY\xb5\xfe\x92牻\xe1x\xdfp\xedOJ\xec<\x89\xd8%\x8f\x98\u007fX\x1e\x96Ƶ\x18f 0P1\"PwJ\xe1B\xd1\f\x82\xeb\x0e?\x9e\f\xfd\x96\xbc\xc7\xfa|x\xfd\x9c\xe6\xa4\xfe0/\x00\\\x98.l\x1b\xc4A\x88_\xfb\xec=\x97w\xa7#-\xd8\xd0e\x91\t\xb3\xcf\xf4Q\xa9\xb8b?\xacw\xb7\xa4Q\x9f\x05\xf1\xb1\xfc\xaf\x11\x0e`\x1b\xf4\xe7\xc1y\xe4\x19c\xc1\xb3\xcdAg\xa2\abGص\x87;\x14\xf0@룱{\xfd\xe8L\xed\xc8\x1f\xbaF\xd1\xfbϑ\xb1\x05\xfc\x18\xfd\xfcb{\xf3\x06\xe7MΓ\xa01\xaa\x0fO\x13P\x81\xee\xda\x059\xb6{\xb1\t\xe4\x87I\xf8\xb8\xe7\x8f]\xc4\x0e\xb4\xbd\xd5\xfd\x15B\x92\x93\x9b\xa2\nu\xbce\xe3\x98\t\x06\x84\xf4V\xe1qWԛ\x10\x99\x04\x87\f\x87\xf4\xce[\xfb0\xb5\xe4\xe2\xa7\xd7\xdcRDm\xee\x8c\x1e\xe5\xb8}|J\x1d\xfe\xf8\xfb\x93\x8cC\xea@\xf5 \xa9\xe7\xaf\f\xe0;\x96\xff\xff\x96}\xb2\xb0z\x8d\xd67&\xdcߝ=\xed\xf9vZ\xef\xe5;\xd2\x12sW\xbc\xf7˓\xfa#\x1f\x96\xb4\xf4$7\xb88\xf4|@\x17.+\x1e\xf3\xc1\xd4\x17\xeaF\x94K\xa2\x849Yt\x18\x8e\x1d3\xde\a\xdf\x1e\xfe\x8cr\x03^\xc6k1\xe6>\x89\f\xa5V\xd7s9ajg\\\xf2\xd5c\x89\x83B0H\xfcCտE\xce\x1f\U000470e1\xddoJovo\xd1/\x8b\xfc\x1bR\xfc\x90\xcd\x12{\x9b\xe7[\xd5<\xb2\xa3!X1e'\xf1p\xf8+\xd2U\xba*\xe9\u007f\x16\x8a\xaf\x95щ\xcd\xfa\x19|\xfa<\x81|\xd7\xfa\xb1׃\a\xff\x1b\x00\x00\xff\xff=\x1a\xe4\xa9\u007f\x1b\x00\x00"), diff --git a/config/samples/velero_v1_backupstoragelocation.yaml b/config/samples/velero_v1_backupstoragelocation.yaml index 0383e1b887..7336238cb2 100644 --- a/config/samples/velero_v1_backupstoragelocation.yaml +++ b/config/samples/velero_v1_backupstoragelocation.yaml @@ -1,7 +1,16 @@ apiVersion: velero.io/v1 kind: BackupStorageLocation metadata: - name: backupstoragelocation-sample + creationTimestamp: null + labels: + component: velero + name: default + namespace: velero spec: - # Add fields here - foo: bar + config: + region: minio + s3ForcePathStyle: "true" + s3Url: http://minio.velero.svc:9000 + objectStorage: + bucket: velero + provider: aws \ No newline at end of file diff --git a/go.mod b/go.mod index 1a7f114334..45c6dbd2cc 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,6 @@ require ( github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 // indirect github.com/joho/godotenv v1.3.0 github.com/kubernetes-csi/external-snapshotter/v2 v2.1.0 - github.com/onsi/gomega v1.8.1 github.com/pkg/errors v0.8.1 github.com/prometheus/client_golang v1.0.0 github.com/robfig/cron v0.0.0-20170309132418-df38d32658d8 diff --git a/hack/update-generated-crd-code.sh b/hack/update-generated-crd-code.sh index ee45f7f6f2..d6a803cf14 100755 --- a/hack/update-generated-crd-code.sh +++ b/hack/update-generated-crd-code.sh @@ -30,11 +30,6 @@ if [[ ! -d "${GOPATH}/src/k8s.io/code-generator" ]]; then exit 1 fi -if ! command -v controller-gen > /dev/null; then - echo "controller-gen is missing" - exit 1 -fi - ${GOPATH}/src/k8s.io/code-generator/generate-groups.sh \ all \ github.com/vmware-tanzu/velero/pkg/generated \ diff --git a/pkg/apis/velero/v1/backupstoragelocation_types.go b/pkg/apis/velero/v1/backupstoragelocation_types.go index f030040c89..9b77d37b02 100644 --- a/pkg/apis/velero/v1/backupstoragelocation_types.go +++ b/pkg/apis/velero/v1/backupstoragelocation_types.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. @@ -21,33 +21,82 @@ import ( "k8s.io/apimachinery/pkg/types" ) -// +genclient -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// BackupStorageLocationSpec defines the desired state of a Velero BackupStorageLocation +type BackupStorageLocationSpec struct { + // Provider is the provider of the backup storage. + Provider string `json:"provider"` -// BackupStorageLocation is a location where Velero stores backup objects. -type BackupStorageLocation struct { - metav1.TypeMeta `json:",inline"rated/clientset/versioned/typed/velero/v1/fake/fake_backupstoragelocationspec.go:77` + // Config is for provider-specific configuration fields. + // +optional + Config map[string]string `json:"config,omitempty"` + + StorageType `json:",inline"` + // AccessMode defines the permissions for the backup storage location. // +optional - metav1.ObjectMeta `json:"metadata,omitempty"` + AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"` + // BackupSyncPeriod defines how frequently to sync backup API objects from object storage. A value of 0 disables sync. // +optional - Spec BackupStorageLocationSpec `json:"spec,omitempty"` + // +nullable + BackupSyncPeriod *metav1.Duration `json:"backupSyncPeriod,omitempty"` +} +// BackupStorageLocationStatus defines the observed state of BackupStorageLocation +type BackupStorageLocationStatus struct { + // Phase is the current state of the BackupStorageLocation. // +optional + Phase BackupStorageLocationPhase `json:"phase,omitempty"` + + // LastSyncedTime is the last time the contents of the location were synced into + // the cluster. + // +optional + // +nullable + LastSyncedTime *metav1.Time `json:"lastSyncedTime,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. + // + // Deprecated: this field is no longer updated or used for detecting changes to + // the location's contents and will be removed entirely in v2.0. + // +optional + LastSyncedRevision types.UID `json:"lastSyncedRevision,omitempty"` + + // AccessMode is an unused field. + // + // Deprecated: there is now an AccessMode field on the Spec and this field + // will be removed entirely as of v2.0. + // +optional + AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"` +} + +// +kubebuilder:object:root=true +// +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" + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// BackupStorageLocation is a location where Velero stores backup objects +type BackupStorageLocation struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec BackupStorageLocationSpec `json:"spec,omitempty"` Status BackupStorageLocationStatus `json:"status,omitempty"` } +// +kubebuilder:object:root=true + // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// BackupStorageLocationList is a list of BackupStorageLocations. +// BackupStorageLocationList contains a list of BackupStorageLocation type BackupStorageLocationList struct { metav1.TypeMeta `json:",inline"` - - // +optional metav1.ListMeta `json:"metadata,omitempty"` - - Items []BackupStorageLocation `json:"items"` + Items []BackupStorageLocation `json:"items"` } // StorageType represents the type of storage that a backup location uses. @@ -70,28 +119,7 @@ type ObjectStorageLocation struct { CACert []byte `json:"caCert,omitempty"` } -// BackupStorageLocationSpec defines the specification for a Velero BackupStorageLocation. -type BackupStorageLocationSpec struct { - // Provider is the provider of the backup storage. - Provider string `json:"provider"` - - // Config is for provider-specific configuration fields. - // +optional - Config map[string]string `json:"config,omitempty"` - - StorageType `json:",inline"` - - // AccessMode defines the permissions for the backup storage location. - // +optional - AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"` - - // BackupSyncPeriod defines how frequently to sync backup API objects from object storage. A value of 0 disables sync. - // +optional - // +nullable - BackupSyncPeriod *metav1.Duration `json:"backupSyncPeriod,omitempty"` -} - -// BackupStorageLocationPhase is the lifecyle phase of a Velero BackupStorageLocation. +// BackupStorageLocationPhase is the lifecycle phase of a Velero BackupStorageLocation. // +kubebuilder:validation:Enum=Available;Unavailable type BackupStorageLocationPhase string @@ -117,31 +145,3 @@ const ( // TODO(2.0): remove the AccessMode field from BackupStorageLocationStatus. // TODO(2.0): remove the LastSyncedRevision field from BackupStorageLocationStatus. - -// BackupStorageLocationStatus describes the current status of a Velero BackupStorageLocation. -type BackupStorageLocationStatus struct { - // Phase is the current state of the BackupStorageLocation. - // +optional - Phase BackupStorageLocationPhase `json:"phase,omitempty"` - - // LastSyncedTime is the last time the contents of the location were synced into - // the cluster. - // +optional - // +nullable - LastSyncedTime *metav1.Time `json:"lastSyncedTime,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. - // - // Deprecated: this field is no longer updated or used for detecting changes to - // the location's contents and will be removed entirely in v2.0. - // +optional - LastSyncedRevision types.UID `json:"lastSyncedRevision,omitempty"` - - // AccessMode is an unused field. - // - // Deprecated: there is now an AccessMode field on the Spec and this field - // will be removed entirely as of v2.0. - // +optional - AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"` -} diff --git a/pkg/client/factory.go b/pkg/client/factory.go index 03cad73d77..ff04c69692 100644 --- a/pkg/client/factory.go +++ b/pkg/client/factory.go @@ -19,13 +19,16 @@ package client import ( "os" + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/pkg/errors" "github.com/spf13/pflag" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" ) @@ -42,6 +45,9 @@ type Factory interface { // DynamicClient returns a Kubernetes dynamic client. It uses the following priority to specify the cluster // configuration: --kubeconfig flag, KUBECONFIG environment variable, in-cluster configuration. DynamicClient() (dynamic.Interface, error) + // KubebuilderClient returns a Kubernetes dynamic client. It uses the following priority to specify the cluster + // configuration: --kubeconfig flag, KUBECONFIG environment variable, in-cluster configuration. + KubebuilderClient() (k8sclient.Client, error) // SetBasename changes the basename for an already-constructed client. // This is useful for generating clients that require a different user-agent string below the root `velero` // command, such as the server subcommand. @@ -81,7 +87,7 @@ func NewFactory(baseName string, config VeleroConfig) Factory { // We didn't get the namespace via env var or config file, so use the default. // Command line flags will override when BindFlags is called. if f.namespace == "" { - f.namespace = v1.DefaultNamespace + f.namespace = velerov1api.DefaultNamespace } 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") @@ -137,6 +143,21 @@ func (f *factory) DynamicClient() (dynamic.Interface, error) { return dynamicClient, nil } +func (f *factory) KubebuilderClient() (k8sclient.Client, error) { + clientConfig, err := f.ClientConfig() + if err != nil { + return nil, err + } + + scheme := runtime.NewScheme() + velerov1api.AddToScheme(scheme) + kubebuilderClient, err := k8sclient.New(clientConfig, k8sclient.Options{ + Scheme: scheme, + }) + + return kubebuilderClient, nil +} + func (f *factory) SetBasename(name string) { f.baseName = name } diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index c59df270fe..be8d350879 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -24,11 +24,8 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/cache" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/client" @@ -43,17 +40,6 @@ import ( const DefaultBackupTTL time.Duration = 30 * 24 * time.Hour -var ( - scheme = runtime.NewScheme() -) - -func init() { - _ = clientgoscheme.AddToScheme(scheme) - - _ = velerov1api.AddToScheme(scheme) - // +kubebuilder:scaffold:scheme -} - func NewCreateCommand(f client.Factory, use string) *cobra.Command { o := NewCreateOptions() @@ -163,14 +149,7 @@ func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Facto return err } - clientConfig, err := f.ClientConfig() - if err != nil { - return err - } - - clientKB, err := k8sclient.New(clientConfig, k8sclient.Options{ - Scheme: scheme, - }) + clientKB, err := f.KubebuilderClient() if err != nil { return err } diff --git a/pkg/cmd/cli/backuplocation/create.go b/pkg/cmd/cli/backuplocation/create.go index 78b2d11458..30bd5d5447 100644 --- a/pkg/cmd/cli/backuplocation/create.go +++ b/pkg/cmd/cli/backuplocation/create.go @@ -26,9 +26,6 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - - clientgoscheme "k8s.io/client-go/kubernetes/scheme" k8sclient "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" @@ -38,17 +35,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/cmd/util/output" ) -var ( - scheme = runtime.NewScheme() -) - -func init() { - _ = clientgoscheme.AddToScheme(scheme) - - _ = velerov1api.AddToScheme(scheme) - // +kubebuilder:scaffold:scheme -} - func NewCreateCommand(f client.Factory, use string) *cobra.Command { o := NewCreateOptions() @@ -162,20 +148,12 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { return err } - clientConfig, err := f.ClientConfig() - if err != nil { - return err - } - - clientKB, err := k8sclient.New(clientConfig, k8sclient.Options{ - Scheme: scheme, - }) + clientKB, err := f.KubebuilderClient() if err != nil { return err } if err := clientKB.Create(context.Background(), backupStorageLocation, &k8sclient.CreateOptions{}); err != nil { - fmt.Println("argh") return err } diff --git a/pkg/cmd/cli/backuplocation/get.go b/pkg/cmd/cli/backuplocation/get.go index 0c1c15e095..b9f23fb542 100644 --- a/pkg/cmd/cli/backuplocation/get.go +++ b/pkg/cmd/cli/backuplocation/get.go @@ -39,12 +39,7 @@ func NewGetCommand(f client.Factory, use string) *cobra.Command { err := output.ValidateFlags(c) cmd.CheckError(err) - clientConfig, err := f.ClientConfig() - cmd.CheckError(err) - - clientKB, err := k8sclient.New(clientConfig, k8sclient.Options{ - Scheme: scheme, - }) + clientKB, err := f.KubebuilderClient() cmd.CheckError(err) location := &velerov1api.BackupStorageLocation{} diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/restic/server.go index bf3c167b5d..5dfdf289fd 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/restic/server.go @@ -22,6 +22,8 @@ import ( "strings" "sync" + "k8s.io/apimachinery/pkg/runtime/schema" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -36,7 +38,6 @@ import ( ctrl "sigs.k8s.io/controller-runtime" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/buildinfo" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/cmd" @@ -50,22 +51,12 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/manager" - - clientgoscheme "k8s.io/client-go/kubernetes/scheme" ) var ( - scheme = runtime.NewScheme() - setupLog = ctrl.Log.WithName("setup") + scheme = runtime.NewScheme() ) -func init() { - _ = clientgoscheme.AddToScheme(scheme) - - _ = velerov1api.AddToScheme(scheme) - // +kubebuilder:scaffold:scheme -} - func NewServerCommand(f client.Factory) *cobra.Command { logLevelFlag := logging.LogLevelFlag(logrus.InfoLevel) formatFlag := logging.NewFormatFlag() @@ -195,11 +186,11 @@ func (s *resticServer) run() { var wg sync.WaitGroup - // TODO(carlisia): how to handle the fetching of the bsl informer: - // - options are: 1) get informer for specific obj (below, but w/o namespace or obj name info because we don't know it here) or 2) for a specific kind, which would be CRD? - // - should it go here, or inside the controller? Note that neither resolves issue above - location := &velerov1api.BackupStorageLocation{} - bslInformer, _ := s.mgr.GetCache().GetInformer(location) + gvk := schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "BackupStorageLocation"} + bslInformer, err := s.mgr.GetCache().GetInformerForKind(gvk) + if err != nil { + panic(err) + } backupController := controller.NewPodVolumeBackupController( s.logger, diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 8291f4c161..2029e9ac28 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -70,7 +70,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/restore" "github.com/vmware-tanzu/velero/pkg/util/logging" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" ctrl "sigs.k8s.io/controller-runtime" @@ -110,10 +109,6 @@ const ( defaultBackupTTL = 30 * 24 * time.Hour ) -var ( - scheme = runtime.NewScheme() -) - // list of available controllers for input validation var disableControllerList = []string{ BackupControllerKey, @@ -127,13 +122,6 @@ var disableControllerList = []string{ ServerStatusRequestControllerKey, } -func init() { - _ = clientgoscheme.AddToScheme(scheme) - - _ = velerov1api.AddToScheme(scheme) - // +kubebuilder:scaffold:scheme -} - type serverConfig struct { pluginDir, metricsAddress, defaultBackupLocation string backupSyncPeriod, podVolumeOperationTimeout, resourceTerminatingTimeout time.Duration @@ -316,6 +304,8 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s } } + scheme := runtime.NewScheme() + velerov1api.AddToScheme(scheme) mgr, err := ctrl.NewManager(clientConfig, ctrl.Options{ Scheme: scheme, }) diff --git a/pkg/cmd/velero/velero.go b/pkg/cmd/velero/velero.go index bf45001f8d..db9c09b40c 100644 --- a/pkg/cmd/velero/velero.go +++ b/pkg/cmd/velero/velero.go @@ -22,12 +22,8 @@ import ( "os" "github.com/spf13/cobra" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/klog" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/cmd/cli/backup" "github.com/vmware-tanzu/velero/pkg/cmd/cli/backuplocation" @@ -51,17 +47,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/features" ) -var ( - scheme = runtime.NewScheme() -) - -func init() { - _ = clientgoscheme.AddToScheme(scheme) - - _ = velerov1api.AddToScheme(scheme) - // +kubebuilder:scaffold:scheme -} - func NewCommand(name string) *cobra.Command { // Load the config here so that we can extract features from it. config, err := client.LoadConfig() diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 714aadb855..112df396ce 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -40,8 +40,6 @@ import ( "k8s.io/apimachinery/pkg/version" - . "github.com/onsi/gomega" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/builder" @@ -138,8 +136,6 @@ func TestProcessBackupNonProcessedItems(t *testing.T) { } func TestProcessBackupValidationFailures(t *testing.T) { - g := NewWithT(t) - defaultBackupLocation := builder.ForBackupStorageLocation("velero", "loc-1").Result() tests := []struct { @@ -188,9 +184,9 @@ func TestProcessBackupValidationFailures(t *testing.T) { var fakeClient client.Client if test.backupLocation != nil { - fakeClient = newFakeClient(g, test.backupLocation) + fakeClient = newFakeClient(t, test.backupLocation) } else { - fakeClient = newFakeClient(g) + fakeClient = newFakeClient(t) } c := &backupController{ @@ -225,8 +221,6 @@ func TestProcessBackupValidationFailures(t *testing.T) { } func TestBackupLocationLabel(t *testing.T) { - g := NewWithT(t) - tests := []struct { name string backup *velerov1api.Backup @@ -255,7 +249,7 @@ func TestBackupLocationLabel(t *testing.T) { clientset = fake.NewSimpleClientset(test.backup) sharedInformers = informers.NewSharedInformerFactory(clientset, 0) logger = logging.DefaultLogger(logrus.DebugLevel, formatFlag) - fakeClient = newFakeClient(g) + fakeClient = newFakeClient(t) ) apiServer := velerotest.NewAPIServer(t) @@ -282,8 +276,6 @@ func TestBackupLocationLabel(t *testing.T) { } func TestDefaultBackupTTL(t *testing.T) { - g := NewWithT(t) - var ( defaultBackupTTL = metav1.Duration{Duration: 24 * 30 * time.Hour} ) @@ -317,7 +309,7 @@ func TestDefaultBackupTTL(t *testing.T) { formatFlag := logging.FormatText var ( clientset = fake.NewSimpleClientset(test.backup) - fakeClient = newFakeClient(g) + fakeClient = newFakeClient(t) logger = logging.DefaultLogger(logrus.DebugLevel, formatFlag) sharedInformers = informers.NewSharedInformerFactory(clientset, 0) ) @@ -347,7 +339,6 @@ func TestDefaultBackupTTL(t *testing.T) { } func TestProcessBackupCompletions(t *testing.T) { - g := NewWithT(t) defaultBackupLocation := builder.ForBackupStorageLocation("velero", "loc-1").Bucket("store-1").Result() now, err := time.Parse(time.RFC1123Z, time.RFC1123Z) @@ -795,9 +786,9 @@ func TestProcessBackupCompletions(t *testing.T) { var fakeClient client.Client // add the test's backup storage location if it's different than the default if test.backupLocation != nil && test.backupLocation != defaultBackupLocation { - fakeClient = newFakeClient(g, test.backupLocation) + fakeClient = newFakeClient(t, test.backupLocation) } else { - fakeClient = newFakeClient(g) + fakeClient = newFakeClient(t) } apiServer := velerotest.NewAPIServer(t) diff --git a/pkg/controller/backup_deletion_controller_test.go b/pkg/controller/backup_deletion_controller_test.go index 15e3709f81..f7564663c2 100644 --- a/pkg/controller/backup_deletion_controller_test.go +++ b/pkg/controller/backup_deletion_controller_test.go @@ -38,8 +38,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" core "k8s.io/client-go/testing" - . "github.com/onsi/gomega" - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" @@ -184,10 +182,9 @@ func setupBackupDeletionControllerTest(fakeClient client.Client, objects ...runt } func TestBackupDeletionControllerProcessRequest(t *testing.T) { - g := NewWithT(t) t.Run("missing spec.backupName", func(t *testing.T) { - fakeClient := newFakeClient(g) + fakeClient := newFakeClient(t) td := setupBackupDeletionControllerTest(fakeClient) td.req.Spec.BackupName = "" @@ -209,7 +206,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("existing deletion requests for the backup are deleted", func(t *testing.T) { - fakeClient := newFakeClient(g) + fakeClient := newFakeClient(t) td := setupBackupDeletionControllerTest(fakeClient) // add the backup to the tracker so the execution of processRequest doesn't progress @@ -265,7 +262,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("deleting an in progress backup isn't allowed", func(t *testing.T) { - fakeClient := newFakeClient(g) + fakeClient := newFakeClient(t) td := setupBackupDeletionControllerTest(fakeClient) td.controller.backupTracker.Add(td.req.Namespace, td.req.Spec.BackupName) @@ -290,7 +287,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result() location := builder.ForBackupStorageLocation("velero", "default").Result() - fakeClient := newFakeClient(g, location) + fakeClient := newFakeClient(t, location) td := setupBackupDeletionControllerTest(fakeClient, backup) @@ -322,7 +319,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result() location := builder.ForBackupStorageLocation("velero", "default").Result() - fakeClient := newFakeClient(g, location) + fakeClient := newFakeClient(t, location) td := setupBackupDeletionControllerTest(fakeClient, backup) @@ -361,7 +358,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("unable to find backup", func(t *testing.T) { - fakeClient := newFakeClient(g) + fakeClient := newFakeClient(t) td := setupBackupDeletionControllerTest(fakeClient) @@ -387,7 +384,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("unable to find backup storage location", func(t *testing.T) { - fakeClient := newFakeClient(g) + fakeClient := newFakeClient(t) backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result() td := setupBackupDeletionControllerTest(fakeClient, backup) @@ -417,7 +414,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result() location := builder.ForBackupStorageLocation("velero", "default").AccessMode(velerov1api.BackupStorageLocationAccessModeReadOnly).Result() - fakeClient := newFakeClient(g, location) + fakeClient := newFakeClient(t, location) td := setupBackupDeletionControllerTest(fakeClient, backup) err := td.controller.processRequest(td.req) @@ -450,7 +447,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { restore2 := builder.ForRestore("velero", "restore-2").Phase(velerov1.RestorePhaseCompleted).Backup("foo").Result() restore3 := builder.ForRestore("velero", "restore-3").Phase(velerov1.RestorePhaseCompleted).Backup("some-other-backup").Result() - fakeClient := newFakeClient(g) + fakeClient := newFakeClient(t) td := setupBackupDeletionControllerTest(fakeClient, backup, restore1, restore2, restore3) td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore1) @@ -611,7 +608,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { Backup("some-other-backup"). Result() - fakeClient := newFakeClient(g) + fakeClient := newFakeClient(t) td := setupBackupDeletionControllerTest(fakeClient, backup, restore1, restore2, restore3) td.req = pkgbackup.NewDeleteBackupRequest(backup.Name, string(backup.UID)) td.req.Namespace = "velero" @@ -751,7 +748,6 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { } func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) { - g := NewWithT(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) @@ -863,7 +859,7 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { client := fake.NewSimpleClientset() - fakeClient := newFakeClient(g) + fakeClient := newFakeClient(t) sharedInformers := informers.NewSharedInformerFactory(client, 0) controller := NewBackupDeletionController( diff --git a/pkg/controller/backup_sync_controller_test.go b/pkg/controller/backup_sync_controller_test.go index feb6549d58..804040e033 100644 --- a/pkg/controller/backup_sync_controller_test.go +++ b/pkg/controller/backup_sync_controller_test.go @@ -29,8 +29,6 @@ import ( "k8s.io/apimachinery/pkg/util/validation" core "k8s.io/client-go/testing" - . "github.com/onsi/gomega" - 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" @@ -110,7 +108,6 @@ func defaultLocationsListWithLongerLocationName(namespace string) []*velerov1api } func TestBackupSyncControllerRun(t *testing.T) { - g := NewWithT(t) type cloudBackupData struct { backup *velerov1api.Backup @@ -337,7 +334,7 @@ func TestBackupSyncControllerRun(t *testing.T) { t.Run(test.name, func(t *testing.T) { var ( client = fake.NewSimpleClientset() - fakeClient = newFakeClient(g) + fakeClient = newFakeClient(t) sharedInformers = informers.NewSharedInformerFactory(client, 0) pluginManager = &pluginmocks.Manager{} backupStores = make(map[string]*persistencemocks.BackupStore) @@ -474,7 +471,6 @@ func TestBackupSyncControllerRun(t *testing.T) { } func TestDeleteOrphanedBackups(t *testing.T) { - g := NewWithT(t) baseBuilder := func(name string) *builder.BackupBuilder { return builder.ForBackup("ns-1", name).ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "default")) @@ -566,7 +562,7 @@ func TestDeleteOrphanedBackups(t *testing.T) { t.Run(test.name, func(t *testing.T) { var ( client = fake.NewSimpleClientset() - fakeClient = newFakeClient(g) + fakeClient = newFakeClient(t) sharedInformers = informers.NewSharedInformerFactory(client, 0) ) @@ -619,7 +615,6 @@ func TestDeleteOrphanedBackups(t *testing.T) { } func TestStorageLabelsInDeleteOrphanedBackups(t *testing.T) { - g := NewWithT(t) longLabelName := "the-really-long-location-name-that-is-much-more-than-63-characters" tests := []struct { @@ -661,7 +656,7 @@ func TestStorageLabelsInDeleteOrphanedBackups(t *testing.T) { t.Run(test.name, func(t *testing.T) { var ( client = fake.NewSimpleClientset() - fakeClient = newFakeClient(g) + fakeClient = newFakeClient(t) sharedInformers = informers.NewSharedInformerFactory(client, 0) ) diff --git a/pkg/controller/download_request_controller_test.go b/pkg/controller/download_request_controller_test.go index f73337af7c..4e667dfc5e 100644 --- a/pkg/controller/download_request_controller_test.go +++ b/pkg/controller/download_request_controller_test.go @@ -27,8 +27,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/clock" - . "github.com/onsi/gomega" - "sigs.k8s.io/controller-runtime/pkg/client" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" @@ -125,7 +123,6 @@ func newBackupLocation(name, provider, bucket string) *velerov1api.BackupStorage } func TestProcessDownloadRequest(t *testing.T) { - g := NewWithT(t) defaultBackup := func() *v1.Backup { return builder.ForBackup(v1.DefaultNamespace, "a-backup").StorageLocation("a-location").Result() @@ -253,9 +250,9 @@ func TestProcessDownloadRequest(t *testing.T) { t.Run(tc.name, func(t *testing.T) { var fakeClient client.Client if tc.backupLocation != nil { - fakeClient = newFakeClient(g, tc.backupLocation) + fakeClient = newFakeClient(t, tc.backupLocation) } else { - fakeClient = newFakeClient(g) + fakeClient = newFakeClient(t) } harness := newDownloadRequestTestHarness(t, fakeClient) diff --git a/pkg/controller/gc_controller_test.go b/pkg/controller/gc_controller_test.go index 552c4de3a9..fcf4fffbc3 100644 --- a/pkg/controller/gc_controller_test.go +++ b/pkg/controller/gc_controller_test.go @@ -25,7 +25,6 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - . "github.com/onsi/gomega" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -153,7 +152,6 @@ func TestGCControllerHasUpdateFunc(t *testing.T) { } func TestGCControllerProcessQueueItem(t *testing.T) { - g := NewWithT(t) fakeClock := clock.NewFakeClock(time.Now()) defaultBackupLocation := builder.ForBackupStorageLocation("velero", "default").Result() @@ -255,9 +253,9 @@ func TestGCControllerProcessQueueItem(t *testing.T) { var fakeClient k8sclient.Client if test.backupLocation != nil { - fakeClient = newFakeClient(g, test.backupLocation) + fakeClient = newFakeClient(t, test.backupLocation) } else { - fakeClient = newFakeClient(g) + fakeClient = newFakeClient(t) } controller := NewGCController( diff --git a/pkg/controller/restore_controller_test.go b/pkg/controller/restore_controller_test.go index 8b5b8eb800..0605738ac3 100644 --- a/pkg/controller/restore_controller_test.go +++ b/pkg/controller/restore_controller_test.go @@ -26,7 +26,6 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - . "github.com/onsi/gomega" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" @@ -56,7 +55,6 @@ import ( ) func TestFetchBackupInfo(t *testing.T) { - g := NewWithT(t) tests := []struct { name string @@ -97,7 +95,7 @@ func TestFetchBackupInfo(t *testing.T) { t.Run(test.name, func(t *testing.T) { var ( client = fake.NewSimpleClientset() - fakeClient = newFakeClient(g) + fakeClient = newFakeClient(t) restorer = &fakeRestorer{} sharedInformers = informers.NewSharedInformerFactory(client, 0) logger = velerotest.NewLogger() @@ -233,7 +231,6 @@ func TestProcessQueueItemSkips(t *testing.T) { } func TestProcessQueueItem(t *testing.T) { - g := NewWithT(t) defaultStorageLocation := builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result() @@ -401,7 +398,7 @@ func TestProcessQueueItem(t *testing.T) { t.Run(test.name, func(t *testing.T) { var ( client = fake.NewSimpleClientset() - fakeClient = newFakeClient(g) + fakeClient = newFakeClient(t) restorer = &fakeRestorer{} sharedInformers = informers.NewSharedInformerFactory(client, 0) logger = velerotest.NewLogger() diff --git a/pkg/controller/suite_test.go b/pkg/controller/suite_test.go index 882e1fe35b..01e9747230 100644 --- a/pkg/controller/suite_test.go +++ b/pkg/controller/suite_test.go @@ -17,19 +17,22 @@ limitations under the License. package controller import ( + "testing" + + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client/fake" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - . "github.com/onsi/gomega" "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client" // +kubebuilder:scaffold:imports ) -func newFakeClient(g *WithT, initObjs ...runtime.Object) client.Client { - g.Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) - g.Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) +func newFakeClient(t *testing.T, initObjs ...runtime.Object) client.Client { + err := velerov1api.AddToScheme(scheme.Scheme) + require.NoError(t, err) return fake.NewFakeClientWithScheme(scheme.Scheme, initObjs...) } diff --git a/pkg/restic/common_test.go b/pkg/restic/common_test.go index 2fcddabd79..eb92c0eb6e 100644 --- a/pkg/restic/common_test.go +++ b/pkg/restic/common_test.go @@ -34,8 +34,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" k8sfake "sigs.k8s.io/controller-runtime/pkg/client/fake" - . "github.com/onsi/gomega" - 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" @@ -386,7 +384,6 @@ func TestTempCredentialsFile(t *testing.T) { } func TestTempCACertFile(t *testing.T) { - g := NewWithT(t) var ( fs = velerotest.NewFakeFileSystem() bsl = &velerov1api.BackupStorageLocation{ @@ -404,7 +401,7 @@ func TestTempCACertFile(t *testing.T) { ) //TODO(carlisia): not sure this test makes sense anymore since - fakeClient := newFakeClient(g) + fakeClient := newFakeClient(t) fakeClient.Create(context.Background(), bsl) locationCreated := &velerov1api.BackupStorageLocation{} @@ -531,8 +528,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { } } -func newFakeClient(g *WithT, initObjs ...runtime.Object) client.Client { - g.Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) - g.Expect(velerov1api.AddToScheme(scheme.Scheme)).To(Succeed()) +func newFakeClient(t *testing.T, initObjs ...runtime.Object) client.Client { + err := velerov1api.AddToScheme(scheme.Scheme) + require.NoError(t, err) return k8sfake.NewFakeClientWithScheme(scheme.Scheme, initObjs...) } From 0f4572be7332a597baf8ec994c6d98a8c74237b2 Mon Sep 17 00:00:00 2001 From: Carlisia Date: Thu, 11 Jun 2020 09:13:43 -0700 Subject: [PATCH 18/34] Move all code gen to inside builder container Signed-off-by: Carlisia --- Makefile | 34 +------------------ config/crd/bases/velero.io_backups.yaml | 3 +- .../velero.io_backupstoragelocations.yaml | 3 +- .../bases/velero.io_deletebackuprequests.yaml | 3 +- .../crd/bases/velero.io_downloadrequests.yaml | 3 +- .../crd/bases/velero.io_podvolumebackups.yaml | 3 +- .../bases/velero.io_podvolumerestores.yaml | 3 +- .../bases/velero.io_resticrepositories.yaml | 3 +- config/crd/bases/velero.io_restores.yaml | 3 +- config/crd/bases/velero.io_schedules.yaml | 3 +- .../bases/velero.io_serverstatusrequests.yaml | 3 +- .../velero.io_volumesnapshotlocations.yaml | 3 +- config/crd/crds/crds.go | 22 ++++++------ hack/update-generated-crd-code.sh | 13 +++++++ 14 files changed, 47 insertions(+), 55 deletions(-) diff --git a/Makefile b/Makefile index ff31c8be7d..baaf1a9498 100644 --- a/Makefile +++ b/Makefile @@ -219,7 +219,7 @@ ifneq ($(SKIP_TESTS), 1) @$(MAKE) shell CMD="-c 'hack/verify-all.sh'" endif -update: manifests +update: @$(MAKE) shell CMD="-c 'hack/update-all.sh'" build-dirs: @@ -304,35 +304,3 @@ serve-docs: # a new versioned docs site. The full process is documented in site/README-JEKYLL.md. gen-docs: @hack/gen-docs.sh - -# Kubebuilder -# Produce CRDs that work back to Kubernetes 1.11 (no version conversion) -CRD_OPTIONS ?= "crd:trivialVersions=true" - -# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) -ifeq (,$(shell go env GOBIN)) -GOBIN=$(shell go env GOPATH)/bin -else -GOBIN=$(shell go env GOBIN) -endif - -# Generate manifests e.g. CRD, RBAC etc. -manifests: controller-gen - $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./pkg/apis/velero/v1" output:crd:artifacts:config=config/crd/bases - -# find or download controller-gen -# download controller-gen if necessary -controller-gen: -ifeq (, $(shell which controller-gen)) - @{ \ - set -e ;\ - CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\ - cd $$CONTROLLER_GEN_TMP_DIR ;\ - go mod init tmp ;\ - go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.2.5 ;\ - rm -rf $$CONTROLLER_GEN_TMP_DIR ;\ - } -CONTROLLER_GEN=$(GOBIN)/controller-gen -else -CONTROLLER_GEN=$(shell which controller-gen) -endif diff --git a/config/crd/bases/velero.io_backups.yaml b/config/crd/bases/velero.io_backups.yaml index 9e55ad6872..b527f7513e 100644 --- a/config/crd/bases/velero.io_backups.yaml +++ b/config/crd/bases/velero.io_backups.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.2.5 + controller-gen.kubebuilder.io/version: v0.2.4 creationTimestamp: null name: backups.velero.io spec: @@ -14,6 +14,7 @@ spec: listKind: BackupList plural: backups singular: backup + preserveUnknownFields: false scope: Namespaced validation: openAPIV3Schema: diff --git a/config/crd/bases/velero.io_backupstoragelocations.yaml b/config/crd/bases/velero.io_backupstoragelocations.yaml index 8d193a5234..1eb0951b20 100644 --- a/config/crd/bases/velero.io_backupstoragelocations.yaml +++ b/config/crd/bases/velero.io_backupstoragelocations.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.2.5 + controller-gen.kubebuilder.io/version: v0.2.4 creationTimestamp: null name: backupstoragelocations.velero.io spec: @@ -14,6 +14,7 @@ spec: listKind: BackupStorageLocationList plural: backupstoragelocations singular: backupstoragelocation + preserveUnknownFields: false scope: Namespaced validation: openAPIV3Schema: diff --git a/config/crd/bases/velero.io_deletebackuprequests.yaml b/config/crd/bases/velero.io_deletebackuprequests.yaml index 01503bf05b..951b236555 100644 --- a/config/crd/bases/velero.io_deletebackuprequests.yaml +++ b/config/crd/bases/velero.io_deletebackuprequests.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.2.5 + controller-gen.kubebuilder.io/version: v0.2.4 creationTimestamp: null name: deletebackuprequests.velero.io spec: @@ -14,6 +14,7 @@ spec: listKind: DeleteBackupRequestList plural: deletebackuprequests singular: deletebackuprequest + preserveUnknownFields: false scope: Namespaced validation: openAPIV3Schema: diff --git a/config/crd/bases/velero.io_downloadrequests.yaml b/config/crd/bases/velero.io_downloadrequests.yaml index 491bd11d65..f0bd8debae 100644 --- a/config/crd/bases/velero.io_downloadrequests.yaml +++ b/config/crd/bases/velero.io_downloadrequests.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.2.5 + controller-gen.kubebuilder.io/version: v0.2.4 creationTimestamp: null name: downloadrequests.velero.io spec: @@ -14,6 +14,7 @@ spec: listKind: DownloadRequestList plural: downloadrequests singular: downloadrequest + preserveUnknownFields: false scope: Namespaced validation: openAPIV3Schema: diff --git a/config/crd/bases/velero.io_podvolumebackups.yaml b/config/crd/bases/velero.io_podvolumebackups.yaml index 4dfbdc4f03..54f5641bbb 100644 --- a/config/crd/bases/velero.io_podvolumebackups.yaml +++ b/config/crd/bases/velero.io_podvolumebackups.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.2.5 + controller-gen.kubebuilder.io/version: v0.2.4 creationTimestamp: null name: podvolumebackups.velero.io spec: @@ -14,6 +14,7 @@ spec: listKind: PodVolumeBackupList plural: podvolumebackups singular: podvolumebackup + preserveUnknownFields: false scope: Namespaced validation: openAPIV3Schema: diff --git a/config/crd/bases/velero.io_podvolumerestores.yaml b/config/crd/bases/velero.io_podvolumerestores.yaml index 0a8d377dbe..03b4bf0135 100644 --- a/config/crd/bases/velero.io_podvolumerestores.yaml +++ b/config/crd/bases/velero.io_podvolumerestores.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.2.5 + controller-gen.kubebuilder.io/version: v0.2.4 creationTimestamp: null name: podvolumerestores.velero.io spec: @@ -14,6 +14,7 @@ spec: listKind: PodVolumeRestoreList plural: podvolumerestores singular: podvolumerestore + preserveUnknownFields: false scope: Namespaced validation: openAPIV3Schema: diff --git a/config/crd/bases/velero.io_resticrepositories.yaml b/config/crd/bases/velero.io_resticrepositories.yaml index 822477c3da..538fb2e814 100644 --- a/config/crd/bases/velero.io_resticrepositories.yaml +++ b/config/crd/bases/velero.io_resticrepositories.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.2.5 + controller-gen.kubebuilder.io/version: v0.2.4 creationTimestamp: null name: resticrepositories.velero.io spec: @@ -14,6 +14,7 @@ spec: listKind: ResticRepositoryList plural: resticrepositories singular: resticrepository + preserveUnknownFields: false scope: Namespaced validation: openAPIV3Schema: diff --git a/config/crd/bases/velero.io_restores.yaml b/config/crd/bases/velero.io_restores.yaml index 8d4b802f84..cabfa3067a 100644 --- a/config/crd/bases/velero.io_restores.yaml +++ b/config/crd/bases/velero.io_restores.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.2.5 + controller-gen.kubebuilder.io/version: v0.2.4 creationTimestamp: null name: restores.velero.io spec: @@ -14,6 +14,7 @@ spec: listKind: RestoreList plural: restores singular: restore + preserveUnknownFields: false scope: Namespaced validation: openAPIV3Schema: diff --git a/config/crd/bases/velero.io_schedules.yaml b/config/crd/bases/velero.io_schedules.yaml index ad03a1d072..e612fc11d4 100644 --- a/config/crd/bases/velero.io_schedules.yaml +++ b/config/crd/bases/velero.io_schedules.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.2.5 + controller-gen.kubebuilder.io/version: v0.2.4 creationTimestamp: null name: schedules.velero.io spec: @@ -14,6 +14,7 @@ spec: listKind: ScheduleList plural: schedules singular: schedule + preserveUnknownFields: false scope: Namespaced validation: openAPIV3Schema: diff --git a/config/crd/bases/velero.io_serverstatusrequests.yaml b/config/crd/bases/velero.io_serverstatusrequests.yaml index 731fa4ae64..4989ab948f 100644 --- a/config/crd/bases/velero.io_serverstatusrequests.yaml +++ b/config/crd/bases/velero.io_serverstatusrequests.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.2.5 + controller-gen.kubebuilder.io/version: v0.2.4 creationTimestamp: null name: serverstatusrequests.velero.io spec: @@ -14,6 +14,7 @@ spec: listKind: ServerStatusRequestList plural: serverstatusrequests singular: serverstatusrequest + preserveUnknownFields: false scope: Namespaced validation: openAPIV3Schema: diff --git a/config/crd/bases/velero.io_volumesnapshotlocations.yaml b/config/crd/bases/velero.io_volumesnapshotlocations.yaml index a45b0ea79f..66e2161a22 100644 --- a/config/crd/bases/velero.io_volumesnapshotlocations.yaml +++ b/config/crd/bases/velero.io_volumesnapshotlocations.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.2.5 + controller-gen.kubebuilder.io/version: v0.2.4 creationTimestamp: null name: volumesnapshotlocations.velero.io spec: @@ -14,6 +14,7 @@ spec: listKind: VolumeSnapshotLocationList plural: volumesnapshotlocations singular: volumesnapshotlocation + preserveUnknownFields: false scope: Namespaced validation: openAPIV3Schema: diff --git a/config/crd/crds/crds.go b/config/crd/crds/crds.go index d2c5453046..dc24b08efd 100644 --- a/config/crd/crds/crds.go +++ b/config/crd/crds/crds.go @@ -29,17 +29,17 @@ import ( ) var rawCRDs = [][]byte{ - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xa2u\xd2\xe8k\x10\xa5\xc4W\x8f\x9a\xfes\xeb\xa7\u007fuki\xae\x9e?lы\x0fgOR\xe7\xd7pS9o\x8a\x9fљ\xcaf\xf8\twRK/\x8d>+Ћ\\xq}\x06 \xb46^P\xb7\xa3\u007f\x012\xa3\xbd5J\xa1]\xedQ\xaf\x9f\xaa-n+\xa9r\xb4\xbcBZ\xff\xf9\x1f\xd7\xff\xb4\xfe\x973\x80\xcc\"O\u007f\x90\x05:/\x8a\xf2\x1at\xa5\xd4\x19\x80\x16\x05^\xc3VdOU\xe9\xd6ϨК\xb54g\xaeČ\xd6\xda[S\x95\xd7\xd0\xfc\x10\xa6D<\xc2\x1e~\xe0\xd9ܡ\xa4\xf3?\xb6:?K\xe7\xf9\x87RUV\xa8z%\xeesR\xef+%l\xea=\x03p\x99)\xf1\x1a\xbe\xd0\x12\xa5\xc80?\x03x\x16J\xe6\x8c~XԔ\xa8?\xdem\x1e\xff\xf9>;`!B'@\x8e.\xb3\xb2\xe4qqu\x90\x0e\x04<2\xee`#\x8d\xc1\x1f\x84\xa7\xffJ\x8b\x0e\xb5w\xe0\x0f\b\x99(}e\x11\xcc\x0e~\xac\xb6h5zt\x112@\xa6*\xe7т\xf3\xc2#\b\x0f\x02J#\xb5\a\xa9\xc1\xcb\x02\xe1/\x1f\xef6`\xb6\xbf`\xe6\x1d\b\x9d\x83p\xcedRx\xcc\xe1٨\xaa\xc00\xf7\xaf\xeb\b\xb3\xb4\xa6D\xebe\xa2$\xb5\x96\xe8\xd4}G\xfb\xba\xa0\x8d\x871\x90\x93\xb0`@?\xb2\x1cspL\x14ڇ?H\a\x16\xe36\x99\x80-\xb0@C\x84\x8eH\xaf\xe1\x1e-\x01\x01w0\x95\xcaI\u009e\xd1\x12\x9d2\xb3\xd7\xf2\u007fk\xc8\x0e\xbc\xe1%\x95\xf0\x18\x99\x9b\x9a\xd4\x1e\xad\x16\x8aXV\xe1%\x13\xa2\x10o`\x91րJ\xb7\xa0\xf1\x10\xb7\x86\u007f7\x16AꝹ\x86\x83\xf7\xa5\xbb\xbe\xba\xdaK\x9f\x94%3EQi\xe9߮X\xe4\xe5\xb6\xf2ƺ\xab\x1c\x9fQ]9\xb9_\t\x9b\x1d\xa4nj\x98w%J\xb9b\xc45\xebʺ\xc8\xff!q\xdd]\xb40\xf5o$d\xce[\xa9\xf7u7\xcb\xf2(\xddI\xa8\x838\x85i\x01\xff\x86\xbc\xd4ET\xf9\xf9\xf6\xfe\xa1-j\xd2ui\xce\xd4n\xa6\xb9\x86\xf0D(\xa9wh\x03\xe3v\xd6\x14\f\x11u\x1ed\x8d\xc5TI\xd4]\xa2\xbbj[HO\x9c\xfe\x9f\n\x1d\x89\xb3Y\xc3\r\x9b\f\xd8\"TeNR\xb8\x86\x8d\x86\x1bQ\xa0\xba\x11\x0e\u007fs\xb2\x13\x85݊H:O\xf8\xb6\xa5\xeb\x0e\fԪ\xbb\x93M\x1a\xe4P\xd0\xf8\xfb\x12\xb3\x8eb\xd0\x1c\xb9\x93\x19\x8b?\xec\x8cm\fB0:\xeb\x16\xc0!\xa5\f\v\xedD\xa5\xfc#+\xb2{0?\xa3\xf32\xeb\x8e9B\xe7\xd3\xe0\x94\x84\x0e:x9\xa0?\xa0%Y\xe1\x1fX\xed\x8e \x023\xd0a\xce:'\x9e\x10DĚ\x95W)(M\xb2/\x0e\xb6o\t\xd1\xf5\x11\x9c@ͭ1\nE\xd7\x06\xe0k\xa6\xaa\x1c\xf3\xda\xe0\xba\xc9]\xdd\xf6\x86\xf3Q$\xa4&\xcd \xe3O\x88\xe9\xe6W\xb6\xb5\xc2boc$\x9dR\ahlE\x0f8\xc0\x10j\xd2c\xd1\xc3jD\x94\"\xecJ)\xb1Ux\r\xdeV\xc7K\x87y\xc2Z\xf16H\x89t\x14/#D=:\xda\x06%3>Cj\v\xc0\xb4\xf8\x03\x91\xe1`\xcc\xd3\xf4\xd6\xff\x8dF4\x16\f2\xf6``\x8b\a\xf1,\x8d\x8d\x9b\x8d\xc7\xc8\x16\x01_1\xab<\xf6e[x\xc8\xe5n\x87\x96\xa0\x94\a\xe1Ѕck\x8c\x04c\xeaI͎\xb1\xad\x87\u007f\xc32a1\xecw\feRR\xcd\xc8\xf4\xa9\x1b\x1a\xf9\x18:\x97\xcf2\xaf\x84\x02\xa9\x9d\x17:\v\xfb\x105N\xc7\xfb\x80qv\xf6\xb0\rf-\xe1L\xb4\xef\x988\xa3\x11\x8c\x85\x82\xacy\u007f\xa8\x1b\x84\x0f\xa3\xdb\xdd\n\xb25&\x88\xa1\xad\x14\xba\xb8PΖ\xb3\xd1\xeb\xcb\x11\xc05\x17\xc2ٯ\xc4\x16\x158T\x98yc\x87\xc80\xcd\xd4\xd0\xe6m\xd4\b\xed\x06\xacUc\u007fi\x8bmCeFa\x02\xbc\x1cdv\b\xc72\xc9\vC\x81ܠc\xfd\x15e\xa9ކ7\aӜ\x0emB\x85\x9b6\xa9\xccǰ\xfajݴY;״\x19\x8bץe\xcd\xfa?\x0f)\x93\xe1>Y07\xbd\x89\xef)\x98DDI\xae\xf5f\aX\x94\xfe\xed\x12\xa4O\xbd\xe4I\b\x8e\xfcF\xc9S\xaf\xfd\x87cĩ2\xbd9\x9e\xf7\x8e2\xfd+\xb9P/\xfd\x87a\x02\x1b\xfb\xfbh\xeb\x172\xe0s{\xce%\xc8]̀\xfc\x12vRy\xb4G\x9c\x98ڮ\x99\xe6į%\xc1\xfcIE\xad\x10>;ܾ\x92w䚄\xcd\"j\x1cO\r>e\U000aaec7\xe9$T\xe0`PZ,B\x88\xf9\xc0\x14lz\xd8\xf3\xf9\xf8\xe5\x13\xe6\xe3D\x81%\x12\xd6\xdb\xc2\xc7#4\xdb\xcbF\x17y\xd9\x06\xa2\x93RG\x17!]p\t\x02\x9e\xf0-x\x17B\x031D\xd024x\x16\xa2E\xceY\xb0@=\xe1\x1b\x03\x89i\x88\x99\xb9\xcbX\x1f\xda\x13\xbe\xcd\x0f:\"\x1ba#]L\xab\x10\xfd\xa8\x83\t\xc01\xecR\x92\x01'\x91\x92\x85\x99\xdb\x14,5\x11\xa9%j\x9f\xbc\xbd\x9aMM\xde#0\xf2\xc2\x05\xa6\x90\xb4\x1fd\xb9h\x83d:\xc1!\xebDJ\"=\n%\xf3z\x99 \xdf\x1b}\t_\x8c\xdf\xe81g\xb5\xdbn_\xa5\x8b\xb9\xbbO\x06\xdd\x17\xe3\xb9\xe7݉\x18P>\x99\x84a\x1a\xab\x90\x0ef\x98\xf6\xdf\xceE\xcd\nqh\x9b\x10a\xd5,\x91\x0e6\x9ab\x88@\xab\x90M\f\x8bMY\xfbn+*\xc7\xc9&m\xf4\x8a\x0f\xbb\xf5\xd0:\x91\xc4\v\x05\xb9ͅ>Z\xf5\x92a\xb9E\x10\x1f\xe8\\\b\xb3CfT\x89\fs\xc8+&\"g\xf6\x84ǽ̠@\xbb\x1f?\bڭ$\x9b\xbdd\xf9E\xb64\xb4\x93\xe4i\xc9ќZ4\xc6\xf9\x1c\x1a+\xd2\xcd\xd91\x89\xb53\x03\aSy\xe3\x03\xe7\xf6\xc1\x87$\xfb\r3\xd4\x14y\xce7)B\xdd-\xb6ދ)\xdf?\xb7\x03J\xe1\x8c+\x04'\xe8\xfe\x8f\x8e*\x16\xda\xff\x87RH;\xab\xa1\x1f\xf9JDagf\xcc\n\xb5\x17!\xf8\xd2\x01q\xf3Y\xa8\xe3\x84\xf0\xc0\xb6\fY\rT\xe1\x186\xbb\x9e\xa7q\t/\a\xe3©\xb8\x93\xa8r\x90S\x9e\x16\xb5\xf3'|;\xbf\xec\xe9\xf8\xf9F\x9f\x87㹧\xb1\xe9,\x9f\x01l\xb4z\x83s\x9ey\xfe\xf5\xae\xcb\"\xa9[0\x88\xefǖ9\xb3\x14ͥS\x9c\xa6\xd5w0䊎c\xbb@\xe6J\xe3\xfcB$\xee\x8c\xf3!C\xd7q\x1e\arC\xd31M\xcc\t\x81\u0605{/c\xd3\r\a\x19\xb2\xa3T%q\xc9\xe1`\x82\xb3\a1\x8f \x85Rp\xde\xe8h\xb0\x8f\xe7\xe1ڃ\x97\x10\x19\xbb\x05\x13\x10I\x14Jk2tnJ\x1cf-\xefL\u00adN\xb6\x89\x10T\x84K\x84\xa9\xe4^jK\xddF\"\xcdIn\xf6\xedk+\aH\xaaM\xffO\x8b\xd9i\x18\x01_2\x17\x85г\x87E\x0f\xb9\x9b0/\xa9B\x04\x13\\v\xbb\xafX\x8d\x97zzQh\xbe\xed\x01[H\xbda\xe0\xf0\xe1]\x8fcH&\x11Ow\xa9o\xd2̆\xccuG\xd0\xcd\xd2\xf4S\xeeC\xed\xe5\x80\x16;\x9c\xeag\x86ٝ\xd3Ʒ\xc2\xf3e\x84\x0ex\\8\xd8I\xeb|\x1bI\xc7\x17[\xef\x1f\xa3\xe8[k\xbf\"D\xf9)\xcck%\x80\x0e\xe6%\xdd\x14\x8e\\\xce\r5\xbe\x06A\x90;\x90\x1ePg\xa6Ҝ\xc4 %\xe5\x05\x02I\x831\x9d=dC[\xa2\xd8\xd4PWŒ\x8d\xafXz\xa4\x9e\xc8u\xb4\a\xffMȩLUj'\xb1\xc9\xcb\x02M5q\xa85\xadæ\x870\xafs\xc5[\x88WYT\x05\x88\x82\x88\xbd\x88\xa2t2\xcb\x02\xbb\xfc\x85\x17!=[w\x82ʦ\xde\x1bR\x8aR\xa1_\x16\rlqg,뢓9\xd6Gf\xe4\xb9\xd1 `'\xa4\xaa\xec\"\x8bv\x02E\x97{\xf6Q\xc9\xdf\xc7i_\xb2슷?\x9b\xa6\\\xe4\xaaMY\xd5\xd2.u\xd4\xee,\xbe\xa7\x8bTZI2c\xde\xd7K\x8a\xa2$\xf4\xdbw7\xa9E\x9b\xefnR\xaf}w\x93:\xed\xbb\x9b\xf4\xddM\x9al\xdfݤ\xefnҟ\xd5M\x9a\xc6d\xc5y\xab\xc1\x9ffV\x9f\xbdB\x1dGl\x14r\xbcտ\t\xb5\xd7\xcb\xea\xf26\xc3s\x06\xea.cI\xf7\x8a+\xce\xfb|n\xae\xfe\x1b3_\x17\xea\x91\xf0'\xe1\r\x85\xa5\x93\xa5{\v\n\xf1\x86j3\xe7\xcbK\xe6\x8aJ\xba5\x89uaG*J4i\x89\xde\xeeS%;\xb9\x99\xed\n\x06\xa1T\xbb6E؆(ߨ^q\xb6\xf4c\xa6\xe0c\xbals\x9cBG\xae}\x97D\xb6Sb\xf8\x8d)4Y\x971^\x8d\x11o2Ћ\xe7\x0f\xeb\xee/\xde\xc4\xda\fx\x91\xfe\xd0\xdb\x00\x17MRȢ\xf7\xed\xe2\xc8$S\xf1\xf9\xc01\xe5\xc0X\xd0R]\x0e\xd6\xc5\xd4/+\xda䄟\xca\x10\x14\x9d\xa4oS\xae\xfd\x92ڍ\xaf\xae\xd8\xe8\xd6d\f\x1a\xd9\xd3.;\x96\x96\x90.\xaf\xc9\xe8\xd6\\\x8c\x1c2\v*1N\xae\xb4\x98\x8f\xb7&\xab*\xbe\xa2\x96\"\xd5IL\x1d\xb8\x13\x15\x14\v|\x8e\xf9j\x89\xaf\xaa\x91\xe0˼\t\xacO\xaa\x8chU=L\x80\\V\x0f\xb1\x80$s\xb5\x0f'W<\x1cW\x19Llb\xae\xcea\xbc\x86a\x02\xe8`uÒʅ\t\x98uM\xc3;\xd6+\xccT)\xbcO%\xe1\xaf\xf5=\xc7j\x0ef*\rf<\xd3)\xacfj\t\x96W\x10\xcc\xd0\xe7+\xab\x05\xeaz\x80\xc15O\xad\x11\xe8V\x01\f\x82\\X\x190r\xf7?\brA=\xc0̍\xff \xd8ɃqB\"F\u007frZ\x94\xee`҃\xadI?\xe9\xbe;v \xb8Hϵ2e\xaa\xbc\x86\xdd\xdf\n?\xc1|\x83\xbbG6\xf2\xfc\x14&k\x1e\x02ES\x9e\x9c\x9f\xe3wB?\xbcg\xb0ἱb\x8f\x9fM\xd6zi;\xb6\xff\xee\xd8γ\xc8\xc8\xd4\x14ҧ:\b\x91\xde\xe7u\xa7\x0e\xf9\x8e1\xcb\x16߹5\xd1\x17a\xd8\xe7\xf7\xa8\xe6y\xaf&7\xf1\xf0\xf09 \xeee\x81\xebOU\b\xe4V\xa5\xb0\x0e\x89~iCaҖ\xfe<\x98\x97\x1e\xc2\xcaĝ\xfep\x8c\xafE\xce\xe1q\xb4\xb8\x18\xeb\xf0\x96/\tX\"Ӵ8>\x0e\xcfi\xf9\xa2-\xa6\x84\xc0\xc6\xec\xc6f\xf56\xd8z\xc8L\xde~\xa8hy\xaf'j\xc3\xc6y\xf8\xf1\xa7\x17\xbers\xcf?yPz\xcc\x1d3\xbe\x95\xe5\x17f\x01@\x10Ɠ_\x80\xc6\xf4V\xe7\t\xfd\x14On\xfa\xe3\xf9)\xb5\xcd\x03R\x9cV\xab\x1fs\xbe\bW'\xd0\x06,Z\x03,\xcccg\x80`a\x0e\xf8\x8c\x1a\x8c\xe6|\x19\xbf\xe0\n\x0f\xf9\x8f\xe7\xf4\xe3\xd7\x16\x8c\x98\x8e\xabJeD\x9e47\xbd3\x8d\xcf\xc3\x1f\xd8\x1e\xd9g\xb4\x17n\x14\"?U\xdd\x19;\xb4\xfdc\xc9\xda\x19[\b\u007f\r\xb9\xf0\xb8\x1a\x00\xb8\xc0\x8e\r\x88\x14'\x8fg\x9en\xf2\x90\xa0\x1d\x9cwNoiC\xe2\xb9@\xe7\xc4>\xbd\xd9|!s\xb4GM\x87\xdc@\x96(\xbabM\xe2\xb2\xfb~1Dt\"\xf3\x14\xff\x06\xd4b\b\xdb\x1au\xd1\xd79e\xf6\x14a\xf3\xc0\xf8b<\xda\xe7aC\"\xb5\xc7=v\xdd#|-\xa5\x9d\xb7\xe5\xb7\xf50\xa2\b\x87\xee\xac\xe1\xcd\a\x14Pɽ$\x83H\x8c\xdd\v\xbb\x15{\\eFQ\x1c%\x8d>\xc6\xe8\xb7\xe1k\x80:\xf0u\x84ކ\xfe\xd6\x1e\x99\x02\xc1(\xcc\x01J\xfaX\xc2e{\x9a\x16\x05\n\xdf\xda\xec\x1e\x82\xb6A\x90,r)z\xd0H\x9c1\x1b[RIj\xd2\xf4\xe9\v\xc9\xf6\xccx\xe2 \xdbC}\x13E\x91ԏ\u007f\xac\xaa\xae\xeb\xcaDzD\x16\n\xbe\x05\x13\t\xffR\xf4y%\xcd\xd3\x0f\xd2P\xd8\x1c\xae\xb6\xa8\xe6\xaaz\"\xefZ\xb8N\xa2a\xb8G\t\x89-~\xc2\x1dyR\n\xbe\x1aP\x8d3j\xda\n\xc0x\x1f\xd4d\xb1\xe4%\x80\r^9\xf4=r\xbdG\xdf<\xa5-n\x13\xf5\x0e\xb9x\x98\xfd\x1f\xbei\xbem\xbe\xaf\x00,c9\xfe\x85\x06\x145Cl\xc1\xa7\xbe\xaf\x00\xbc\x19\xb0\x05\x87=*n\x8d}J\x91\xf1τ\xa2\xd2\x1c\xb0G\x0e\r\x85J\"\xda\xecx\xcf!\xc5\x16N\x1b\xe3\xf9)\xa8\xf1B\x9f\x8a\xa9\x9f\x8a\xa9\xfb\xd1T\xd9\xedI\xf4\xe7\xb74~\xa1I+\xf6\x89M\xbf\x1ePQ\x10\xf2\xfb\xd4\x1b^U\xa9\x00Ć\x88-\xdc氢\xb1\xe8*\x80\x83\xe9ɕ\xfb\x8f\x81\x86\x88\xfeǻ\x9b\xc7\xef\x1el\x87\x83\x19\x85\x00\x0e\xc52Ţ\xb7\x16$\x90\x80\x81\xc9\x15h\x98\"\x80\xe0\x11\x02\xc3\x10\x18a\fG\x9a\xc9d\xe4\x10\x91\x95fD\xf9;+\x90\xa3\xec\xc2\xf9\xc7\x1cݨ\x03.\x97\x04\nh\x870%\x16\x1dH\x89\x1c\xc2\x0e\xb4#\x01\xc6\xc8(\xe8\xc7\"93\vY\xc5x\b\xdb?\xd0j\x03\x0f\xc8\xd9\bH\x17R\xefr\x1d\x1d\x90\x15\x18m\xd8{\xfa\xfbhY\xf2\xfd\xb2\xcb\xde\xe8\x9c\xc1\xf9#\xaf\xc8\xde\xf4\x99k\xc2\xff\x83\xf1\x0e\x06\xf3\x02\x8c\xd9\a$\u007ff\xad\xa8H\x03\xbff8\xe4w\xa1\x85N5J\xbb\xd9\xecI疰a\x18\x92'}ٔ¦m\xd2\xc0\xb2qx\xc0~#\xb4\xaf\rێ\x14\xad&ƍ\x89T\x97\xc0}\xe9\x88fp\xff\xe3\xa9\u007f\xe4\xe3Y\xa4\xfa\x92+A\x94\xc9\xef\x8f\xe2R\xa4or\xcf\x05:\xa6y<6\xc6\u007f\u009bE\x99\xca\xfd\xe7\x87/0;-)X2/\xb4O\xc7\xe4\x04>\x83\"\xbfC\x1e\x13\xb7\xe30\x14\x8b\xe8]\f\xe4\xb5,lO\xe8\x97\xd0%m\aR\x99\xcb/私\xeb2\x18`\x8b\x90\xa23\x8a\xae\x81\x1b\x0f\xd7f\xc0\xfe\xda\b\xfe\xe7\xd83a\xa93\xd2\xf7\xc1\x9fϳ\xa5\xe2H\xeb(\x9e\x87\xcdj\x86V\xda\xf2!\xa2\xcd9\xcb\xe0\xf2Yڑ-m\x00\xbb\xc0\xf0ܑ\xed\xe6\xb6\\\x10=6ps&^k\xd8\xfc\x8d\x06\xf2TY\xca߸,\x94<\x11\xe3\xa2\xd6\xea33\xefRP\xa3I\xfe\x15\x87rb&a\x133z\x9d\xec\x94)\xb0v\xe8k\xee\x8é\xe5\xf2ދp>\x17\x95\xf2[2\xe4\x05\x8c\u007f\x99\x8e\x81vF\xe1\x199\x97\xb8\r)\xcf\x0et\xe0\xd2\x05\xaf\tE\x87cRr\xfa\"\a\x8b\"ͅ\x16)\x0e\xaf\xa2y3\x0f\xf9˿:\xb3\xed\xb1\x05儫\xf93\xcc\xe6e\xb1\x13;#\xaf\x92\xbd\xb8\xf4]\xd6X\xe3\x8d\xe3\\\xc6\xf7\x80\x17\xb8>\r\x97^j\xb8\xc5\xe7W\xb2\x1b\u007f\xc7a\xcf(\xf2j\xebn$U~v_\xc1d\xa5\xe0.D\xa7\x17\xc4\xd5iU\xa0\xd7Ӌ\xa1l\x00H\x1ek\xee\f\xach`\xb3\x9fQ\x9f\xaa\xd8X\x8bQ\xd1\xdd^\xbe\x17>|X\xfc\xf8\xcb\xd2\x06\xefh|\xee\xc0o\xbfW\xa3Ut\x8fs\x1cY\xf8O\x00\x00\x00\xff\xff\xbe\x16\xd7\"m\t\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WO\x8fܶ\x0f\xbd\xfbS\x10\xf9\x1d\xf2+\x10{\x9a\x16\x05\x8a\xb9\xb5\x9b\x1c\x82\xa6A0\xbb\xddKуF\xe6\xd8\xeaʒ*R\xb3\xd9~\xfa\x82\x92=\u007f<\x9e\xdd\xf4й\x99\xa2\xa8\xa7\xc7GRS\xd5u]\xa9`\xee1\x92\xf1n\r*\x18\xfc\xc2\xe8䋚\x87\x1f\xa91~\xb5\u007f\xbbEVo\xab\a\xe3\xda5\xdc$b?l\x90|\x8a\x1a\xdf\xe1\xce8\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95\x98I>\x01\xb4w\x1c\xbd\xb5\x18\xeb\x0e]\U000d0db8Mƶ\x18\xf3\t\xd3\xf9\xfbo\x9b\xef\x9a\x1f*\x00\x1d1o\xbf3\x03\x12\xab!\xac\xc1%k+\x00\xa7\x06\\C\xeb\x1f\x9d\xf5\xaa\x8d\xf8WBbj\xf6h1\xfa\xc6\xf8\x8a\x02j9\xb4\x8b>\x855\x1c\x17\xca\xde\x11P\xb9̻1̦\x84\xc9+\xd6\x10\xff\xb2\xb4\xfaь\x1e\xc1\xa6\xa8\xec%\x88\xbcH\xc6uɪx\xb1\\\x01\x90\xf6\x01\xd7\xf0I`\x04\xa5\xb1\xad\x00\xf6ʚ6ߵ\x00\xf3\x01\xddO\x9f?\xdc\u007f\u007f\xab{\x1cT1\x02\xb4H:\x9a\x90\xfd\xe6\xc0\xc0\x10(\x18\x8f\x01\xf6\x87\x93A9P\x91\xcdNi\x86]\xf4\x03l\x95~Ha\x8c\t\xe0\xb7\u007f\xa2f \xf6Qu\xf8\x06(\xe9\x1e\x94D+\x8e`}\a;c\xb1\x19\xb7\x84\xe8\x03F6\x13\x8d\xf2;\x11\xd0\xc16\x03\xfcZnT|\xa0\x15\xc9 \x01\xf7\bc\xe2\xb1\x05ʷ\x05\xbf\x03\xee\rA\xc4\x10\x91\xd0\x15\x11\x9d\x84\x05qQnD\xde\xc0-F\t\x02\xd4\xfbd[\xd1\xd9\x1e#CD\xed;g\xfe>D&\xe1E\x8e\xb4\x8a\xa7LO?\xe3\x18\xa3SVr\x91\xf0\r(\xd7\u00a0\x9e bf'\xb9\x93hم\x1a\xf8\xd5G\x04\xe3v~\r=s\xa0\xf5j\xd5\x19\x9eJF\xfbaH\xce\xf0\xd3*\v\xdfl\x13\xfbH\xab\x16\xf7hWd\xbaZE\xdd\x1bF\xcd)\xe2J\x05Sg\xe0.WL3\xb4\xff\x8bc}\xd1\xeb\x13\xa4\xfc$\xea!\x8e\xc6u\as\x16\xf2U\xdeE\xc8E\x1ee[\xc1\u007f\xa4WL\xc2\xca\xe6\xfd\xed\x1dL\x87\xe6\x14\x9cs^tr\xd8FG\xe2\x85(\xe3v\x18K\xe2\xb2\xca$\"\xba6x\xe38\u007fhkН\x93Ni;\x18\xa6I\xb6\x92\x9f\x06nr\xe3\x80-B\n\xadbl\x1b\xf8\xe0\xe0F\rho\x14\xe1\u007fN\xbb0L\xb5P\xfa2\xf1\xa7\xfd\xeeܱ\xb0u0O\ri1C\xb3R\xbe\r\xa8%_B\x9a\xec3;\xa3s\t\xc0\xceGP\xc7\xca\x1eikN\xe2.\xd5f\x06\xa5b\x87|n\x9b\xa1\xb8\xcb.r\xf0c\xaf\xce[\xc8\xff\xb1\xe9\x1a\xe9\x034B(\x9d\xe1\x9bf\x16\xef\xda\xe9K\x1a]\xc40IU\xae.w\vZtR\trq\x87~\xff\xd5o˷\xe5\x1f&\x00\x95\xa3\xb8\xfcI\xb6\xe4\x03\xb6v\x06\xbaSj\x02\xa0\xb1\xa5\x19X#VFu--\xb0z\xee\xac/W\xa4șR\x9a\x89\xb7T\xf1\xa6\xb53\x9d\x9d\xc1\xeeCZ\x9b\x15J\xc6<\x1a\xf11\x8ay\x17\xc5\xc4/J\xfa\U00037c6f?I\x1f\xe2\f\xab:\x87\xeaX\x89\xf8\xd1K]w\n\xdd\xd1\xe7\t\x80\xaf\x8c\xa5\x19<\xb0\x1a\x16+\x12\x13\x80\x15*)\xa2\xadI1cI\xff\xf0x\xff\xf1w\xf3\xaa\xa1\x16\xd3 \x80uƒ\v\xb2ן\x9f\xbd\x93ێ\x01\b\xf2\x95\x936J\x84k\x16\x95\xe6\x80\xe0\xb3\"\x0f\xa1!Ȉ\x93\x00\x1f\xb7\x01\xb3\x84\xd0H\x0f\x8e\xac#O:\x9dޞX\xe0)\xa8\xc1,\xfeAU(aN\x8e\x85\x80oL\xa7\x04\x1f\xf0\x8a\\\x00G\x95\xa9\xb5\xfc\xd7V\xb2\x87`\xe2\x96\n\x03e\x00\xfbG\xea@N\xa3b\x10:\xba\x01\xd4\x02Z܀#\xde\x03:\xbd'-N\xf1%\xfcl\x1c\x81\xd4K3\x83&\x04\xebg\xd3i-C𥉉i\xdbN˰\x99F\x8f\x93\x8b.\x18秂V\xa4\xa6^\xd6\x05\xba\xaa\x91\x81\xaa\xd09\x9a\xa2\x95ET\\GW-[\xf1\x9dˎ\xed\xaf\xf74\r\x1b>6\x1f\x9c\xd4\xf5v8z\xd0I\xdcك@z\xc0\xbc,鿃\x97\x87\x18\x95\x0f\u007f\x9e?A\xbfi<\x82!\xe6\x11\xed\xdd2\xbf\x03\x9e\x81\x92zI.\x1d\xdcҙ6J$-\xac\x91:ėJI\xd2C\xd0}\xb7he\xe0\x93\xfegG>\xf0\xf9\x94p\x1b#\x16\x16\x04\x9d\x15\x18H\x94p\xaf\xe1\x16[R\xb7\xe8\xe9\xab\xc3\xce\b\xfb\x82!}\x19\xf8\xfdD3\x9c\x98\xd0\xda\x0e\xf7\x99`\xf4\x84\x0e\x82{n\xa9\xe2\xf3b\xd0x\x9d\\\xca*\x86\x00,\x8d\x03<\x9c^\xee\x89\x1d\vM~R\xd8σqX\xd3O\xa6\xda\v\xf2\x13:\xbd\x1b[\xd1k\xc5\xc9+\x85)e\xd1\xe0\xd3\xcc\x03\x91\x00\xaa_\xban\xc8Q\\\xe1\xc8\aY\xb1#\x19/\x83q\x1b\x16\xcb\xebI\x94\a\xebGA\xe7G\x1bAg\xf5\u007f0\x82\xc6\xd4\xe5\x85\x10\x1aL>\xf9hbd\xb8Nk\x8e\x02\xa3/V\xc0\x1aqv\xff,\x19\xc1ђ\x1ci\x8e\xa8\x94|\xac\x89)*\xa0\xd4}\xe4\xa5\xdc\f\xc1\x1c\xc1\xb7H\x00\x93\x80\xe1A\x9f;l8\x99\x8fG5\xfd\xe1\xf1\xbe\xcf\xc1=HY\xe7p\xb8\xe3YD\xf8YJR\xe2\x11C\xf3\xe2\xae\xd7\xf7˴M\xccH\xc1\x00\x82\x95T\xd1 \xb5\x83\xd4>\x10\x8a48\"\x12\x80\x03\xd7Q\x9e\u007f\x93\xf2ONs\xbbr\xc0X\x03\xa6\xfa\x06\u007f\x9d\xbf\u007f\x98\xfe\xc5$]GebU\x91g1\x18\xa8%\x1dn\xc0wU\x03\xe8\xd9\x04\xe9H\xcc\xf9K٢\x96K\xf2\xa1\xcc;\x90\xf3\x9f\xde~\x1e\xc3\f\xe0G〾`k\x15݀L(o\x13j\xef \xd2' \xb6\xf2`-C#\xc7\rGv\xa4l\xf0:\x1a\x1a\xf0\x99\xc0dC;\x02%\x9fi\x06W\x9cB\xf6T\xfc7G\xc3\u007f\xaeFe\xfe&\x05\xe9\x15O\xb9J\x8amk\xe6~\x10\xed\x14L\x91\xe4d]\x93\xa3q4c!\xe0\x04\xfb=\x18Ƕk\xb3' \x8a\xe53K\x89\x8eđ\u009f\xde~>\xa1\xed\x10'\x90Z\xd0\x17x\vR'T\xac\x11ߗ\xf0\x14=b\xa3\x03~\xe1}\xaa\xc6x\xd2`\xb4ڌkk\xa0\xc1\x15\x817-\xc1\x9a\x94*\x12W\x11\xb0\xc6\r\xdb\xdf\x1f\x17{\x18\x82E\x17\x86ldT\xea\xd3\xfb\xbb\xf7\xb3\xa4\x15\xbbP\x1d3)W\xb9\xa5d\xce\xc1d#UN\xf6\xc9\bG\x97\x9c#\x18\xa8\x1a\xd4#\x89\x15\"i\x89\xe8.;\xaee\xe5\xf5k\xa3\xf5\x906\xf4\xcf\b}8L\f\xbfR\x11\xbeȬH\xcb_4\xebaϟϚ\xc5\r\x82\xd3\x14(Z&L\xe5٨\x8al\xf0S\xb3\"\xb7\x92\xb4\x9e\xae\x8d{\x96\xba.\xd8\x11\x8b\xe4\t~\x1a9\xfe\xf4\xbb\xf8\xe7\x17Y\x11\x99\xf9e\xa6ĩ\xdf\xc2\x1e\xde\xc7O_mN\xcf+/\xadJ\xd7\xf3\xcc|\x0eWrH\xac\x1bY5}\x93\xb0˞\xa31ҢH)\x17\xf5櫻-\x03\xd99\xd6gS\xe4>\xb3@-\xf8\u007f/}\xe0\xf1W#\xd7\xc9\v\x82\xf4\xef\xf7w\xdfƙ;\xf9\xea\x88\x1c%\xc4\xc9'\xac\xb9\x17\f\xdfR\x92;K\xa7>\f\xa6\xf6\xc4n\x84In\xe7\\\xcc\xe4\x02\xd6G\x04\n\x85\x887\t\xa8\x1eϐ\xac36\x0f\x94\u007f\xc2\xda\x03:\x02\x84\x16-\x9f\xd33m\x8aT\xa4-J\xae\xb1\\F3_Y\x10\xa0\xb5J\x8e\x94\xd3\\\x8a3]\xcc̛\xdbZ\xac\xfd\xb8\xbd#\xa8\xa7\xd5g\xd1N\xed\xc5\x18}\xce['^\xb2\xa5\xd0\xc1\xec\x88\xea\xb1\xff\x1e\x11\xd7\x13\xb8q\x17\xc8\xecj_\xb5b\xbcu\x19\xcc`J?\x18\xb0F\fއ~6\xf8\x94\xecy\xb1{\v\x18:\u007fq\xff\x16g\xf7\xe8\xa5|\x10\xb2\x8c\xc8\x15~I\aW\x19\xe6\x8e\xc3{\xa8sGx{\xb4F\xdf\xefp܄\xc1\x9e\xb0\xb4.\xe6]\x96E\"R;f\x9dK\x94\x8a\x04\xf4\x97`\x87k\x8ed\xee\xcbXВSUg\x95A\xd17EY\xb5\xfe\x92牻\xe1x\xdfp\xedOJ\xec<\x89\xd8%\x8f\x98\u007fX\x1e\x96Ƶ\x18f 0P1\"PwJ\xe1B\xd1\f\x82\xeb\x0e?\x9e\f\xfd\x96\xbc\xc7\xfa|x\xfd\x9c\xe6\xa4\xfe0/\x00\\\x98.l\x1b\xc4A\x88_\xfb\xec=\x97w\xa7#-\xd8\xd0e\x91\t\xb3\xcf\xf4Q\xa9\xb8b?\xacw\xb7\xa4Q\x9f\x05\xf1\xb1\xfc\xaf\x11\x0e`\x1b\xf4\xe7\xc1y\xe4\x19c\xc1\xb3\xcdAg\xa2\abGص\x87;\x14\xf0@룱{\xfd\xe8L\xed\xc8\x1f\xbaF\xd1\xfbϑ\xb1\x05\xfc\x18\xfd\xfcb{\xf3\x06\xe7MΓ\xa01\xaa\x0fO\x13P\x81\xee\xda\x059\xb6{\xb1\t\xe4\x87I\xf8\xb8\xe7\x8f]\xc4\x0e\xb4\xbd\xd5\xfd\x15B\x92\x93\x9b\xa2\nu\xbce\xe3\x98\t\x06\x84\xf4V\xe1qWԛ\x10\x99\x04\x87\f\x87\xf4\xce[\xfb0\xb5\xe4\xe2\xa7\xd7\xdcRDm\xee\x8c\x1e\xe5\xb8}|J\x1d\xfe\xf8\xfb\x93\x8cC\xea@\xf5 \xa9\xe7\xaf\f\xe0;\x96\xff\xff\x96}\xb2\xb0z\x8d\xd67&\xdcߝ=\xed\xf9vZ\xef\xe5;\xd2\x12sW\xbc\xf7˓\xfa#\x1f\x96\xb4\xf4$7\xb88\xf4|@\x17.+\x1e\xf3\xc1\xd4\x17\xeaF\x94K\xa2\x849Yt\x18\x8e\x1d3\xde\a\xdf\x1e\xfe\x8cr\x03^\xc6k1\xe6>\x89\f\xa5V\xd7s9ajg\\\xf2\xd5c\x89\x83B0H\xfcCտE\xce\x1f\U000470e1\xddoJovo\xd1/\x8b\xfc\x1bR\xfc\x90\xcd\x12{\x9b\xe7[\xd5<\xb2\xa3!X1e'\xf1p\xf8+\xd2U\xba*\xe9\u007f\x16\x8a\xaf\x95щ\xcd\xfa\x19|\xfa<\x81|\xd7\xfa\xb1׃\a\xff\x1b\x00\x00\xff\xff=\x1a\xe4\xa9\u007f\x1b\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y͒۸\x11\xbe\xeb)\xbaf\x0f\x93\xad2\xa9\xd8\xf9\xa9\x94n\xbb3ٔ\x92\xdd\xf1\x945\xf1\xc5\xe5\x03D4EdH4\x034$+\xa9\xbc{\xaa\x01R\x12%J\xa3qe\x1d]l\x02\x8d\xc6\xd7_\xff\xa0\x81\x99dY6Q\xad\xf9\x88\xce\x1b\xb23P\xad\xc1/\x8cV\xbe|\xfe\xfc'\x9f\x1b\x9a\xae\xdf.\x91\xd5\xdbɳ\xb1z\x06w\xc135\x1f\xd0Sp\x05\xdeci\xacaCv\xd2 +\xadX\xcd&\x00\xcaZb%\xc3^>\x01\n\xb2쨮\xd1e+\xb4\xf9sX\xe22\x98Z\xa3\x8b;\xf4\xfb\xaf\u007f\x9b\xbf\xcb\xff0\x01(\x1c\xc6\xe5O\xa6AϪig`C]O\x00\xacjp\x06-\xe95աA\x87\x9eɡ\xcf\xd7X\xa3\xa3\xdc\xd0ķXȮ+G\xa1\x9d\xc1~\"-\xee\x10%k\x1eI\u007f\x8cz>$=q\xaa6\x9e\xff6:\xfd\xb3\xf1\x1cE\xda:8U\x8f\xe0\x88\xb3\xde\xd8U\xa8\x95;\x9d\x9f\x00\xf8\x82Z\x9c\xc1\x83@iU\x81z\x02\xb0V\xb5\xd1\xd1\xe0\x04\x8eZ\xb4?<\xce?\xfenQTب4\b\xd0:jѱ\xe9m\x90߁\xfbvc\x00\x1a}\xe1L\x1b5\u00ad\xa8J2\xa0\xc5a\xe8\x81+\x84\x8ev\xd4\xe0\xe36@%pe<8l\x1dz\xb4Ʌ\ajAD\x94\x05Z\xfe\x03\v\xcea\x81N\x94\x80\xaf(\xd4Z\xbc\xbcF\xc7ఠ\x955\xff\xdai\xf6\xc0\x14\xb7\xac\x15cGa\xff3\x96\xd1YU\v\t\x01߀\xb2\x1a\x1a\xb5\x05\x87\xb2\a\x04{\xa0-\x8a\xf8\x1c~!\x87`lI3\xa8\x98[?\x9bNW\x86\xfb\x80-\xa8i\x825\xbc\x9dư3\xcb\xc0\xe4\xfcT\xe3\x1a\xeb\xa97\xabL\xb9\xa22\x8c\x05\a\x87S՚,\x02\xb71^\xf3F\u007f\xe7\xba\xe8\xf6\xb7\aHy+n\xf3\xec\x8c]\xed\x86c\x14\x9d\xe5]\x82\b\x8c\a\xd5-K\xf8\xf7\xf4ʐ\xb0\xf2\xe1ϋ'\xe87\x8d.\x18r\x1e\xd9\xde/\xf3{\xe2\x85(cKt\xc9q\xa5\xa3&jD\xab[2\x96\xe3GQ\x1b\xb4C\xd2}X6\x86\xc5\xd3\xff\f\xe8Y\xfc\x93\xc3]L[X\"\x84V+F\x9d\xc3\xdc\u009dj\xb0\xbeS\x1e\u007fuڅa\x9f\t\xa5/\x13\u007fXm\x86\x82\x89\xad\xddp_\rF=t\x9cߋ\x16\vq\x98\xb0&\vMi\x8a\x98\x03P\x92\x03u\"\x9f\x1f(\x1eKN\xf9-U\xf1\x1c\xda\x05\x93S+\xfc\x99\x8a\x834?\x83\xeaDZ\x15=,)a)Q\xb1S\r>I\x1e\xa9\x04\xa8\xfb\xa5\x9b\n\x1d\xc6\x15R\x86L!\xa1D\xde0\xb9\xad\xa8\x8d\xa6\xe8\xfch\xfd(\xed\xd1P\xd2\x17\xe1?R\x17\xf4\x0eKth%\xa4S\xf6\xb7\x14k\x04+c\xfb\xd0O\xd5\x11\x98N\xd0/\x13\xda1h稆\xb3\xf5p\x14\xe8\x0f\x8f\xf3\xbe\x06\xf6\x8cv\x90\xf9xNj\x84ȯ4X\xebG\xc5Ջ\xbb\xde\xce˴M\xac\bL\xa0\xa05X࠴\x82\xb1\x9eQ\xe948\xa2\x12@\x12\xc7a'\xff&\xe5\u007fWf\xf6\xe5X\xa8\x06\x95\xce\x17\xf8\xeb\xe2\xfd\xc3\xf4/\x94\xb0\x8e\xeaTE\x81^\xd4(\xc6\x06-\xbf\x01\x1f\x8a\n\x94\x17\x13\x8cC\xbd\x90\x99\xbcQ֔\xe89\xefv@\xe7?\xbd\xfb<\xc6\x19\xc0O\xe4\x00\xbf\xa8\xa6\xad\xf1\r\x98\xc4\xf2\xae\xa0\xf5\xf1a|\"b\xa7\x0f6\x86+3n\xb8\x928\xea\f\xdeDCY=#Pgh@\xa8\xcd3\xce\xe0F2\xf8\x00\xe2\xbf%u\xfes3\xaa\xf37)EnD\xe4&\x01\u06ddY\x87\x19\xb7\aȕb`gV+t8\xcef,\xc4R\xe0\xbe\arb\xbb\xa5\x03\x05Q\xad\xf8,\xd5\x19\xd4'\x80?\xbd\xfb|\x06\xed\x90'0V\xe3\x17x\a\xc6&VZ\xd2\xdf\xe7\xf0\x14#bkY}\x91}\x8a\x8a^U\x92k\x14\xcf@\xaeə(8\x9a\xb7\xa1\xaeղ\xc6\x19\xb0\vǓgӠA\xef\xd5\xear\x1e\xfc\x92d\xd2\r\xab[\x00jI\x81wW\xac.!:\xf3o}\xe7\xf1\xeb/x\x95\xf2\x97A<\x8a\xc4X\\\xed\x92\xf2R`A\xbc\xbd\x84\xe6x\x8b\f\x1eps26\xb7\x8f\x8eV\x0e\xfd\xb1\x0f\xb2\xdeQ'\xedw\x06?\xc5\b\xb8\xda\xe0n\x83\xcb6wBPQ\xddG.\xb1\xaa\xc1\x86f\x89N\f_n\x19}\xcf@\x9f\xe8\xa77\xd4\xd8\xf3\xeeyۯ\xef\xabUR\xd4u\xf0\x85\xb2\xf1IF\xa2\x93\t\xb4\xf1m\xadN[\xf8ކx\xecIpJ\x86\xec\xe3\xa2\xcf.I\xe98\xf7\x9a;u\x84sOv\xb4#\xebS\xc1X\xfe\xe3\xefϞ\x8f\xc62\xae\x06\xa5\xb0\x9b\x15\n\u007f\x14\xfd\xffk\xddg\x0f_\xcf\xca\xf1u\xa5k1\x10}\xa9jE\xc5c5\xeb\xb0\xfc\x9c\x96\x9b\xe1&ߢҌPs4\xb4\u007f\x91\u007f\xbb\xff\x8a.ʺ\x17\xf88\x01\xc9,}\xb0y\xf7\x18Ս\xec\x0f,UH\xaf\x85\xfa\xe1\xf8\t\xfe\xe6f\xf0\xa2\x1e?\v\xb2ڤ?\x1f\xc0\xa7\xcf\x13螨>\xf68d\xf0\xbf\x01\x00\x00\xff\xffV\xe6G\x99\xbd\x18\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VMo\xe36\x13\xbe\xfbW\f\xf6=\xec\xe5\x95\xdcmQ\xa0\xf0\xadM[`\xd1$X8\x8b\\\x8a\x1e(rdOC\x91,g\xe8\xd4\xfd\xf5\x05)ɖm\xd9\x1b,P\xdd4\x9c\x8fg\x9e\xf9 \x17UU-T\xa0g\x8cLޭ@\x05¿\x05]\xfe\xe3\xfa\xe5\a\xae\xc9/w\x1f\x1a\x14\xf5a\xf1Bά\xe0.\xb1\xf8n\x8d\xecS\xd4\xf83\xb6\xe4HȻE\x87\xa2\x8c\x12\xb5Z\x00(缨,\xe6\xfc\v\xa0\xbd\x93\xe8\xad\xc5Xm\xd0\xd5/\xa9\xc1&\x915\x18K\x841\xfe\xee\x9b\xfa\xdb\xfa\xfb\x05\x80\x8eX\xcc?S\x87,\xaa\v+p\xc9\xda\x05\x80S\x1d\xae \"\v\xe9\x88\xc13\x89\x8f\x84\\\xef\xd0b\xf45\xf9\x05\a\xd49\xec&\xfa\x14Vp<\xe8\xad\aH}:\xeb\xe2h=:ڗ#K,\xbf\xcd\x1e\xdf\x13KQ\t6Ee瀔c&\xb7IV\xc5\v\x85\x1c\x80\xb5\x0f\xb8\x82nj%(\x8df\x01\xb0S\x96LI\xb9G\xe7\x03\xba\x1f?}|\xfe\xeeIo\xb1S\xbd\x10 D\x1f0\n\x8dI\xe4oR\xc0\x83\f\xc0 \xebH\xa1x\x84\xf7\xd9U\xaf\x03&\x97\f\x19d\x8b0\x10\x8f\x06\xb8\x84\x01߂l\x89!b\x88\xc8\xe8\xfa\"N\xdcBVQ\x0e|\xf3'j\xa9\xe1\tcv\x02\xbc\xf5ɚ\\\xe7\x1dF\x81\x88\xdao\x1c\xfds\xf0\xcc \xbe\x84\xb4Jp\xe0p\xfc\xc8\tF\xa7l&!\xe1\xffA9\x03\x9d\xdaC\xc4\x1c\x03\x92\x9bx+*\\Ã\x8f\b\xe4Z\xbf\x82\xadH\xe0\xd5r\xb9!\x19[V\xfb\xaeK\x8ed\xbf,\x8dGM\x12\x1fyip\x87vɴ\xa9T\xd4[\x12Ԓ\".U\xa0\xaa\x00w\xa5c\xeb\xce\xfc/\x0e\xfd\xcd\xef'He\x9f\xcb\xc6\x12\xc9m\x0e\xe2\xd2FWy\xcf]\x04Ġ\x06\xb3\x1e\xff\x91\xde,ʬ\xac\u007fy\xfa\fc\xd0R\x82S\xce\v\xdbG3>\x12\x9f\x89\"\xd7b\xec\v\xd7F\xdf\x15\x8f\xe8L\xf0\xe4\xa4\xfchK\xe8NI\xe7\xd4t$\xb9\xd2\u007f%d\xc9\xf5\xa9\xe1\xae\f.4\b)\x18%hj\xf8\xe8\xe0Nuh\xef\x14\xe3\u007fN{f\x98\xabL闉\x9f\xee\x9bSŞ\xad\x83x\\\a\xb3\x15:\x1f\xf0\xa7\x80:\x17,\xb3\x96\r\xa9%]f\x00Z\x1fA]\xe8\xd7\x13\xc7sÙ\xbfF\xe9\x97\x14\x9e\xc4G\xb5\xc1{\xaf'c~\x05\xd5Os\x16#\xac\xbc\xc3\xfaA\xc5y\xc53\xcf\x00\xb2U2\x99PQ\xe4\x0ec>\x93\xc7U\xca\v\xed*\x8f\xabSN㯥w\x9c\xde\xdf\xcc\xe5a\xc6 \xa7\xb2\xf5\xaf\xe0[A7u9\xa2l\xf0\"\x89\x98ܛA\xf6K\xf7\xa3ɭ\xd5\x12ƛ\x00\xd7g\xca#\xcfm\xb2v\xf0Ti\xdf\x05%\xd4X\x1c\a\xb9\xf5\xf1\x02\"\xf5>\xf6\xfdT\u007f\x1d\xbf;oS\x87\x87\xbb\xe1&\xf2\xe7S\xddi\x83\xf4\x82\x01DN\x01\xe2\xe9\x1d7\xfd\x86\x9e`\b\xde\f\x00\x86\xa6\xe5\x9c\xe7\x1b\xb1\xe7\xe2RēmX\xcd7\xff\x89\xc6\\G\x9d(\x9cW\xf3\xe4\xf0\x8c\xaf/.\x03Q\x92\xf8\xed련\x8f\xc4\xea\x14#:\x19\x9c\x94\x9b\xf0\xab\x16\x82U,\x93\xb1ȏ\x9c\x9bu\xbe\xbf\xd4\x1f!eW Y0\x9d\xa2W\xc5s\xf3\xd2\xfa\xd8)YA^\xedU6:;\xcfO,\xd5X\\\x81\xc4t~x}# \xb3\xda\xdc\xce\xe0\xa1\xd7\xe9\xaf\xc2\xc1\x00T\xe3\x93\\!\xb6\\\x8a7\xa8\xbd\x89(l\x15\xdf\xc6\xf3)k̕\x15\xdf\x1a\x1c]\xea\xceCT\xf0\x88\xaf\x17\xb25*s>s\x15/ޤ\xaeVp\x17\x9c7\xf53:\x13l\x89_p#\xb5\xf4\xd2\xe8E\x8d^T\u008b\xd5\x02@hm\xbc\xa0eG\u007f\x01J\xa3\xbd5J\xa1]nQ\x17oa\x8d\xeb U\x85\x96O\xc8\xe7\xef\u007f[\xfc\xae\xf8\xc3\x02\xa0\xb4\xc8ۿ\xca\x1a\x9d\x17u\xb3\x02\x1d\x94Z\x00hQ\xe3\n,:o,\xbab\x8f\n\xad)\xa4Y\xb8\x06K:lkMhVн\x88{\x92 Q\x89縝W\x94t\xfe\xbb\xfe\xea\xf7\xd2y~Ө`\x85\xea\x0e\xe3E'\xf56(a\xdb\xe5\x05\x80+M\x83+x\xa4c\x1aQb\xb5\x00\xd8\v%+\xd6!\x1el\x1aԷO\x0f\xaf\xbf\u007f)wX\x8b\xb8\bP\xa1+\xadl\x98.\v\x00ҁ\x80WV\x80Na\xa4\xc1\xef\x84\a\x8b\x8dE\x87\xda;\xf0;\x04\xd14J\x96|\n\x98Mb\t\xed\x1e\a\x1bk\xea\x8e\xd7Z\x94o\xa1\x01o@\x80\x17v\x8b\x1e\xbe\vk\xb4\x1a=:(Up\x1em\x91\xd84\xd64h\xbd\xcc\xc8\xd1ӳ\x95v\xedH\x87O\xa4d\xa4\x81\x8a\xac\x03\xa3\xa8鎱\x02\xc7\x00\x80ـ\xdfIש\xc4j\xf4\xd8\x02\x91\b\rf\xfd\x0f,}\x01/h\x89\t\xb8\x9d\t\xaa\"\x93ڣ%HJ\xb3\xd5\xf2\x9f-gG\nґJxLW\x99\x1f\xa9=Z-\x14]O\xc0k\x10\xba\x82Z\x1c\xc0\"\x9d\x01A\xf7\xb81\x89+\xe0\a\xbe\x12\xbd1+\xd8y߸\xd5\xcd\xcdV\xfa\xec\x1d\xa5\xa9력?ܰ\x8d\xcbu\xf0ƺ\x9b\n\xf7\xa8n\x9c\xdc.\x85-w\xd2c\xe9\x83\xc5\x1b\xd1\xc8%\v\xae\xd99\x8a\xba\xfaM{Y\x9fz\x92\xfa\x03\x19\x94\xf3V\xeam\xbb̶{\x12w\xb2\xe1h9q[\x94\xbf\x83\x97\x96\b\x95\xe7\xfb\x97\xaf}\xab\x92n\x889\xa3\xdd3\xb4\x0ex\x02J\xea\r\xdaxql[\xc4\x11u\xd5\x18\xa9=\xff)\x95D=\x04݅u-=\xdd\xf4\x8f\x01\x1d\x99\xae)\xe0\x8ec\x04\xac\x11BS\t\x8fU\x01\x0f\x1a\xeeD\x8d\xeaN8\xfc\xd9a'\x84ݒ =\x0f|?\xb4\r\t#Z\xedr\x8eA\x937\x94\xbc\xfb\xa5\xc1r\xe0\x19\xb4In\xb2\x1bo\x8c\x1d8?m)z,\xa7ܒ\x9e\xe8\xdb\x14\x82\x86\xebGB\xfc\xa9%#[\xa1ブ?\x06\xe4\x18\x19}\x12\xc7\xe1\xc2\xf6\xe2e\xff!\x13(\x8eV'\x11\xa4\a?J\x15*\xac\xda0\xe9f%\xbd\x1f\x91s\x16\x11R\x93\x8dS\xd4&qu\xf7\x96\x03\xa4\x98\x90\x92\xecL\xea\xc8\r\xa4f\x15'\x90\xa5Gz\xacGb\xcd\xe8\x04\x9c\x96\xc4Z\xe1\n\xbc\r\xc7g\xc7}\xc2Zq\x98\x84\"\xa7\xd1ːh\xa9\x93\x9b+Y\xf2\x95\xb5\xce\xcc`\xfc\x9apH\xd2\xdc\xc5\xccs\x19\x1a\x0f\xd3{\xb2\x1b\xa1\x83\xf7\x1d\xfa\x1dڜЖ\x9c\x9e\xab\x912]\x96L\x19e\x8d\x1d<䆥\xd1NVh\xa3c\x1e\x01\x06\x0f\x9b1\xc0A\xa9k\xf2m\x11\x94\x8fiȆ\x11\xb6\x17 \xb56F\xa1\xd0SX]\xea>\x0f#\xf2#\xabi='\x9b\x8d\xc9G\x8c\xf4\x8aQ.F|R\x1c\xb0n\xfc\xe1\x1a\x84R}\a\x14\xb6\x03\xf0\x975\xa8\v\x1d\xeb\xe1\x98\xfa\x8cc\x9dFhl\x1c}\x8c:KKt)\xb2\xfe\x1f\x00\xa6\xc4\x1a\xd5\v*,\xbd\xb1\xb3`}ߧ\x8c@Q^\xdc\u007f.\x86o\xbc\x81\x8dT\x1e-\xbcK\xbf\x1b)\xf0\xbeC\x9dp\xa2\x8aD\xeaJ\xeee\x15\x84\x1aXY\x0f\xa5\x0eL0\x16\xb4T\xd7#\x9e\x84q\xde=\xc0\x14\xfe\xc2\xc2\v\xf5\x93|\xf0T\x8a\xa5\xa7\x16\xbe\xdc\xdd\u007fPq\xe4\xba\xf6f\x06\xb6\xe3\r\x11\xb9\x9c\xbe\x18~p\x19;\xaa\x90\xa4Ś\xeb\xae\t\xce\x00_\xd9\xca:*\xd6\xf7\xf6\xf1\xcb\u0600\xe0\xb4\x11\x8d\x84\xbc\x9d\x11$\xf9D{\xbd\x94]r\"\x9e\xe4\f\xa9r\xbe\x06\x01ox\x88E6\xd5\xf1\r\x85\xd2\xcc\xc2\"\x97\xe7|\xd1ox`\xa2TqOr\x9d\xbb\x94\xf8\xbc\xe1\xe1ԫ#u\xe9\xbcT\xfdD\xbdi\x81\xa5\xe2z,\xab\xca\xdd\x15\x9eR\x92\x1eo\xa6\x85\x85yO\xcdOF\xe4B\xb1[\x00{] C\xfc\xc9E8ɾv26x3R;d\xdb\xcb\xfd\xcd+u\xaa-\xf3hQ\x0f\xfa\x1a\x1e\x8d\xa7\x9f\xfb\x0fI5\xbb\xd0\xe3\f\xda=_\f\xbaG\xe3\x99\xf6\xbf\x82$\nu! \x91\x98\rT\xc7\xd8Fz\xf5\xdb\x1f\xc7уn5\xeb7\xa3\x84tԂ\x18\x9b5\xe7\xb65\x1e\x11\x99\xd7\xc1qǢ\x8d^rD\xca\xdcg\x98\xb6\x97&]\x86\xd2\xd8\x01^'\x0e\x9a\xe1\xb9FH\xc7\u007f\xa5F,\ue26d\xb4\x12%VP\x05\x86\x80[A\xe1q+K\xa8\xd1n\xe7\xe4l(N\x9d\xbe\xba\x99H\x12\x9f\v\xee\xf6t\x16\xcaO\n;\xd5\xf4AK\xb2\xf5\x13of\xafw\xb2W\xbbL*\x0eߜ\xe0&\xb5\x17U%c\x86y:\x13\x9f\xce\xe03\xce\x19\xf1ДhEC\x96\xfd/\n\xa7l(\xff\x86FH\xeb\n\xb8偔\x9a\xbe\xd9>}\xaa<\xfa\xac\x89\xabt@\x98\uf162PO\x81C\x03*\x0e\xfc\x93,\xcdf\x94Ѯ\xe1}g\\\x8c\xe2\x1b\x89\x8a\xa7\x10Wox\xb8\xba\x1ex\x1e\xc8\xe9Pz\xf5\xa0\xafb\x92\x18\xf9A\xdb\xf0\x19\xad\x0ep\xc5ﮊQ\x12\x9cd;\x9b\x18g,\xe2䫶\xd2\xfdA4\x8d\xd4\xdb\xe3{\xbe\xcc\x16f\xec``\x03\x8fG\xa7\r\f\xa1_\x96\x0eJ\xf8\xf1qq\xcc7Q\xec\xdb\xbd\xceW\xf2\xcf-\xd9D\x1f\xd8S\x96*\xc5V\x81\xa7ױ\xe5p\xf1\xe9\xb4h\xdc\xcex\xf8f/E\x9aj\x99P5\xd6\xec\xa9\x1f\xfc\xf6\u007f\xd4ѹr\x87UPxvh\xf3\xd2#nЉ\x8fg\x8fR\xa5Wp]Z\xa7\xf3\x9f\xd0\xea\xd2$\xf8\x19\xb7RI'\xb5:\xcbщT8qu\x06 \x94\xd2NP\xb7\xa5\u007f\x01\x12\xad\x9c\xd1Y\x86f\xb5C\xb5~,7\xb8)e\x96\xa2\xe1\x15\xaa\xf5\x0f\xbf]\xffn\xfd\x873\x80\xc4 O\xbf\x979Z'\xf2\xe2\nT\x99eg\x00J\xe4x\x056\xd9cZfh\xd7\a\xcc\xd0\xe8\xb5\xd4g\xb6\xc0\x84V\xdb\x19]\x16W\xd0\xfc\xe0'\x05L\xfc.\xee\xc2|\xeeʤu\u007f\xe9t\u007f\x91\xd6\xf1OEV\x1a\x91\xb5\xd6\xe3^+ծ̄i\xfa\xcf\x00l\xa2\v\xbc\x82\xaf\xb4T!\x12L\xcf\x00\x0e\"\x93)o\xc4/\xae\vT\x9f\xbe\xdd>\xfc\x9e\x16ʅ\xef\x04H\xd1&F\x16<\xae\xc6\x01\xa4\x05\x01\x0f\xbc\v0\x81\xde\xe0\xf6\u0081\xc1\u00a0E\xe5hDapU\xa1\x91\x826\x01&@\x81F\xeaT&\xf0\x83H\x1e\xcb\xc2O\xb5{]f)l\x10L\xa9\xd6alat\x81\xc6ɊF\xd4ZbQ\xf7\xf50\xfd@[\xf1c %A@\vn\x8f\x10؉)\x93'\x17\xa0\xb7\xe0\xf6\xd26x3IZ`\x81\x86\b\x05z\xf3\x0fL\xdc\x1a\xee\xd0\x10\x90\n\xdbD\xab\x03\x1a\xdaw\xa2wJ\xfe\xab\x86l\xc1i^2\x13\x0e\x03˪&\x95C\xa3DFL(\xf1\x02\x84J!\x17G0Hk@\xa9Z\xd0x\x88]\xc3_\xb5A\x90j\xab\xaf`\xef\\a\xaf./w\xd2U\x8a\x90\xe8\x13\xa7\x1b}%9yڣ\"-2\xa5\"\f{\x10!\x18\x8fu\xaf\u007f\x90v\xfc\x03\xe6\x05)\xe3$j\xf7a\x10\xa1FTIk/\xe2M\x05\xd6&K\aK\x05z\x18\xbb\xc2\xe8\x83L\x83=\xe8Qo\x8a\x82\x1e\xa5\xad(3\xf7\xa0\xb32G{\xaf\u007fB\xebdr:\xae\x87\xfc\xe7\xc1i\x15g\xd1\x12E\xdd\x1e\r)\x1e\xff\xc06l\x00*\xb0FXLو\x89G\x04\x01\x1b\xbfo\xb2\x86Y\x06\x85N\xe1\xe0ׁͱB\xb8ϋ\x86\x1f\x1b\xad3\x14\xea\xe4w|N\xb22Ŵ\xf6M\x03\xd4\xe8\xed\xf2\xe6d\n\xfbp!\x15I\x13yLBR5\xbf\x92w\x19ܤ0\b\xa4\xfeRy\x88 \x99\x95a\xabC\x9b\x91\x0e\xf3A\f'\xe4\xce7\x8a\x11\xc4&\xc3+p\xa6<\x95\x96j\xbe0F\x1cG\xa9T\xc56\xf1D\xaag\x04\xa3\x9c\xc9\x04\x89<\xb5\xe9e:\xfd\x02H\xb4\xd7\xfaq\x9e,\u007f\xa6Q\x8d[\x81\x84CF\xd8\xe0^\x1c\xa46\xb6\x1f\x89\xe03&\xa5\xc3a\x1d\x11\x0eR\xb9ݢ!H\xc5^X\xb4\x95\x91\x18'ϔ\xdaS3S,>\xd9O\xc3^b\x14\xd3`l\vlNG`\x02#L6\xb7,@\xaaT\x1edZ\x8a\f\xa4\xb2N\xa8\xc4\xefKԸ\r\xed\v\xa6Y\u007f\x82\xb97\xa3\x15\xfeė\x8eG\xd2\nA\x1b\xc8\xc9\xfd\x9e\x0e\xb5\xa3k\xc0\xe8\xf67\x82\xec\x997\xd6`(`\x0e\x8b\xa5\xec\xec\x1a{q1\x01\xbc\xe6\x8e\x0f\xda2\xb1\xc1\f,f\x988m\xc6\xc82\xcft\xdf\xe2l\xe1\b=\a\xacbc\xf7i\xcb\xcd\x06'\x81\x02\x99\xfc\xa7\xbdL\xf6>\xbe\"\x99bH\x90j\xb4l\vDQd\xc7\xf1\xcd¼$\x84\x85\xa6\xcdA\xd3f\rC\x1f氉hZ\x94=mڌe\xedҹ\x16\x91w2WN\xe3E\x02}{2\xf9\xb5\x05\x9a\b,\xe9lu\xbb\x05\xcc\vw\xbc\x00\xe9\xaa\xdey\x98\x14\x0658\xfc\"\x18\xf5\x12}\xb8\xed\xcf}e}x\x05.\xd5(\xfc_3\x89\x9d\xcd]\xf05\v\x18\xf4\xa5=\xef\x02\xe4\xb6fPz\x01[\x999\xe4\xb8g\x1aŖ\xeb\x9b\xe5\xd4k\x91%\xcekR˅K\xf67\xf5\x11rv|\x8fB\xfd\xe9>V\xaeN\x12]'?\v\x198\xc3 \r\xe6>oq\xcf:\xd0\xf4p\xa4\xf6\xe9\xebgL\xa7\t\x05\xb1\x12y\xb2\x9dO=\x94\xdbˇc@\xfcfB@U\x9f\xb0|>\xea\x02\x04<\xe2\xd1GAB\x011J\xd0R\xa3\a\x89S\"qb\x8cM\xc4#\x1e\x19P\xc8uȄ\x17\r\xdf\x1e\xf1\x187\xb0GJ\xc2,d\x02\x17\xf0\xb4\xd7\xd6{\xe4\xadđ\xe4p\xb7I\v\xe7\x8fx<\xbf8\xb1K\xe7\xb7\xea܇\b}\xad\x8f\x00[G\x1cZeG8\xe7\xd9\xe7?/\x9c\x8a\x96\xceȁ|\xe1\x1b\x1f\x88\xd3I\xb6\x8a&hj}\xf9H!\xf44\xf6\x91\xb2Yh\xeb\x16 \xf4M[\xe73\xa2\x9d\x80wY\xbe\r\xbc\\\x85<\x1b\x88\xadC\x03\xd6iS]\xf5\x91\x91쥍\x89\x8bv\xee\xc0A\x8c\xad\xb3w\x1e,\x1d\xe6\xce\x1b\xfd\xf6\xf6\xf7\xdc\xdf\x01\xd2\xdfs\x10\x13\x0e`\x18rat\x82\xd6ΉM\x94\x85\x9fIl\xd6IM\xe1\x0fK|\xb76+\xacs\xc9֪-\t\x85\x89\x9c\x8b\x8f\x137ϭ\xbc,\x99\x0f\xfa\u007f^d\x97c\a\xac\xf5y.T\x94\x03;A\xf4\xdaϭT,\x80\xf2G\x14\xb3+\xd9\\,\x89\\\x83\xf0}?\xc1@.\xd5-/\x02\x1f\xdf$|\xa8\x8d.\xbe\xec\xf8p]\xcdnXPw\f_\x92\x8e\xb5B\xf3}\x85\xc1\x0e'O\xb3\xfa\x8b\xc2f\xa5];\xf5A\x90\v\x9d~\xb0\xb0\x95ƺ\x06\xd9h\x98\xd2\xf2%\xe9\u06dd\xe5ԍ1/<\xca\xfd\xe8綒q{\xfdT_\xe8\x8f_\xfc\x0e5\xbe\x1eC\x90[\x90\x0eP%\xbaT\x9c4\"c\xc0\x8bxv\xc4\v2\xc4\xfa\xbd\xa6\xa1*\xf3XB\xacX\x12\xa5\x9a\xc9/\xb5'\xfcI\xc8\xec\xad\xd8\xe8d\x8e\xba\x9cq\xccM\xeb\xd6#\xf8\xb9\x9dJ\x8d\\<˼\xccA\xe4Ĉh\x92S\xb4!s\xec\xca\x00<\t\xe9\xd8#\x11dvONG\x83Lt^d\xe8\x106\xb8Ն\xf5\xdd\xca\x14k\xd7\x1f\xe4B\x8f\xdf;\xf6\x9b\x80\xad\x90Yi\xa2\xad\xeeBn,;!\x05\xc3\xf3\xba\a\x9fX\x14VL\xbe\xa8tttH;\xe7\t\n\xb3$\xa0\xfdf\xf0\xb5\xc3\xc7\xc2H\x92E=\x17A\xce@\xe4\xf8\xb2\x1bA\x06\x11\x15\xea8\x16B\xce\xc0d,\xdeC\xc8\xf7\x102\x0e\xee{\b\xf9\x1eBη\xf7\x10\xf2=\x84\x8c\x99\xf0\x1eB\xbe\x87\x90\xcbQ\xf8߇\x90\xf3\x98\xad8\xf79\xfas\x046Q%\x04\xd3\xc8N\xae\x12\xaaa\xae\xb3\xd2:4\xf15\xb7\xb7\xc3\xf3\x06\xea\xaf\x13?d\xc5\x0fs\x86e\xa3)\xb7h\\V]\x84K\xcaV)\x8a\xafן\x8d\x8e#\x8bk\xc7\xea\xb4\xe3J\xb9\xe6\n\xb8\xba5\xc8u\xf1TU\x84i\x99F\x99\xcdO\x9f'w\xf7\x8dF\xd47:\x81\xac<\xadz0=\xb2\x93\xa1\x94\xfd\n\xbe\xe2\xd3Iߍ\"\xc4\xfb\xb94\x9f\x95\xc7\xf4\xa1\xfe\xdaM즚\xef\xe3\xf0%ȉ\x02tu\xb67\xb8\x97\xa9\xa1\x13\u007f\x03\xcf_xX\xf8\xb5<5\xb4|\xfcJh'\xbf\xe9\xfd6\xa2\x85\x13\x1a8\xac}\x03J\xd2\xebj>y\xf4\xb1\xf9\x8f\x97^\x85O\x1c\x1d\xfcݡEs\xc0\xb4%+\xc13\x85\x9eF\xf3D\x92`\xe1B&\xb0\xfd\xad\xa3\xf3s\xfe\xa7\xfa\x94\x11\xff\x9bh\xe5c@{\x05\u007f\xfb\xfb\x19\x04/\xf2P\xe1A\x9d\xff\r\x00\x00\xff\xff\xcd6\xa5\xa7\x1eJ\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xe44\x10\xbd\xe7W\x94\x96\xc3^H\x9a\x01!\xa1\xdcP\xc3a\x04\x8cFӫ\xb9 \x0e\x8eS\xe9.Ʊ\x83\xab\xdcC\xf3\xeb\x91\xed\xa4?\xd2靝\xc3\xe6\x96J}<\xbfzUqQ\x96e\xa1\x06zF\xcf\xe4l\rj \xfcW\xd0\xc67\xae^~\xe2\x8a\xdcj\u007fנ\xa8\xbb\xe2\x85l[\xc3:\xb0\xb8\xfe\t\xd9\x05\xaf\xf1\x17\xecȒ\x90\xb3E\x8f\xa2Z%\xaa.\x00\x94\xb5NT4s|\x05\xd0ΊwƠ/\xb7h\xab\x97\xd0`\x13ȴ\xe8S\x85\xa9\xfe\xfe\xbb\xea\xfb\xea\xc7\x02@{L៨G\x16\xd5\x0f5\xd8`L\x01`U\x8f50\xfa\x18$J\x02{\xfc' \vW{4\xe8]E\xae\xe0\x01u,\xbc\xf5.\f5\x9c>\xe4\xf8\x11T>\xd0&\xa5ڤTO9U\xfaj\x88\xe5\xb7[\x1e\xbf\xd3\xe85\x98\xe0\x95Y\x06\x94\x1c\x98\xec6\x18\xe5\x17]\n\x00\xd6n\xc0\x1a\x1e\"\xacAil\v\x80\xbd2Ԧ\xf3g\xa0n@\xfb\xf3\xe3\xfd\xf3\x0f\x1b\xbd\xc3^e#@\x8b\xac=\r\xc9o\t$\x10\x83\x82\xb1\x14\x88\x03\xa552\x83\x0eޣ\x15\xc8P\x80l\xe7|\x9fʍ\x89\x01Tむ\xec\x10\x9e\x13w#\xf8jt\x18\xbc\x1b\xd0\vML\xa6\x90\x93\x8e\x8e\xb6\x19Ə\xf1\x10\xd9\aڨ\x1c\xe4Tc\xec?\xb6\xc0\xe9\x80\xe0:\x90\x1d1x\x1c<2Z\xb9D\x978\xe9@Yp\xcdߨ\xa5\x1aO\xcf\xc0;\x17L\x1b\xe5\xb6G/\xe0Q\xbb\xad\xa5\xff\x8e\x999\xd2\x10K\x1a%S\xa3\xa7\x87\xac\xa0\xb7\xcaD\xfa\x03~\vʶЫ\x03x\x8c5 سlɅ+\xf8\xc3yL\x04ְ\x13\x19\xb8^\xad\xb6$\xd3\xe4h\xd7\xf7\xc1\x92\x1cVI\xff\xd4\x04q\x9eW-\xeeѬ\x98\xb6\xa5\xf2zG\x82Z\x82Ǖ\x1a\xa8L\xc0m\x1a\x9c\xaao\xbf\xf1\xe3\x98\xf1\xc73\xa4r\x88\x82a\xf1d\xb7Gs\xd2\xf2Mޣ\x8e\xb3\x1arX\xc6\u007f\xa27\x9a\"+O\xbfn>\xc1T4\xb5\xe0\x92\xf3\xc4\xf6)\x8cO\xc4G\xa2\xc8v\xe8s\xe3:\xef\xfa\x94\x11m;8\xb2YK\xda\x10\xdaK\xd294=\tO*\x8d\xfd\xa9`\x9d\xf6\a4\bah\x95`[\xc1\xbd\x85\xb5\xeaѬ\x15\xe3W\xa7=2\xcce\xa4\xf4m\xe2\xcf\xd7ޥcf\xebh\x9ev\xd2b\x87\x16\xa6w3\xa0\x8e=\x8b\xc4\xc5X\xeaH\xa71\x80\xceyPK!՛\x18\x92\xf7\xbbP\x8c;\"\xe3\x98m\x8e8\x83o\xe1XZ\x15ɾS\x8c\x97\xa6\x19\x9a\xc7\xe81\xafl\xa8C}\xd0\x06s\x82\xbc)\xf0-\x10\xf1A\x1b\xfay\xbd\x12\x1e\xf0\xf5\xca\xf6\xe8]ܓi\x15\x9f?\x8b\xfd\x87\xfc\x13ؒ\xe5ϟ&\xfb\xa4\xdf\xca\xf9\xca=[\xb5c\x1a\xf0\xc1\xda8\x91\xceF\xf3,)\\n\xe4\xd9W\x12\xec\xafp,\"\xb9\xb7\x9dK\xbfe\x15K*\xc9s\x82cS\xc7\x1a\x19\xd1U\xba[=\xcd\xcf|\x15}\x01\x81\xf9I\xbf\xf6\xf7\a\xc6\xd5A\x1e\x17j\x96\t˂9V\xba2/N̈,\x18\xa3\x1a\x835\x88\x0f\xf3\xc8\x1c\xa7\xbcW\x87KUL2:]b>+\x90+\xf7\xa8\xfd\xd7\x1d\xda[\n\x87W\xc5K\xbd\xc9i\xa09\xdc\n\\\x1foc\xf3!ɲ\xac!n\xddR芥/ b\xa1KY\xaa\v\xb7\x83+\x126\xe7\x9e\xd3\xec_\b~\xba,̑\xdf(\xbe\xd0ԙ\xe9t\xf9\xbc;\xbd%a\x97\xe3e3}\x18Oў\x9d\x9c\xc5y\xb5\x9d\xb88\xed\xd6x\xcd\x1a\x04ۇ\xf9U\xf3Ç\x8b;cz\xd5ζ\x94o\xca\xf0\xe7_EΊ\xed\xf3\x84#\x1a\xff\x0f\x00\x00\xff\xff\a2\x1f\n\xa8\v\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f$5\f\xbdׯ\xb0\x96\xc3^\xa8j\x06\x84\x84\xea\xb6\x1a8\xacXV\xa3\x9de.\x88C:qu\x9bI%!v\x1a\x86_\x8f\xf2Q\xd3]=ݻ D\xdd\xe2r\xec\xe7\xe7g+]\xdf\xf7\x9d\n\xf4\x80\x91ɻ\x11T \xfcS\xd0\xe5\x13\x0f\x8f\xdf\xf1@~s\xb8٢\xa8\x9b\ue45c\x19\xe16\xb1\xf8\xf9\x03\xb2OQ\xe3\xf78\x91#!\xef\xba\x19E\x19%j\xec\x00\x94s^T6s>\x02h\xef$zk1\xf6;t\xc3c\xda\xe26\x915\x18K\x86%\xff\xe1\xab\xe1\xeb\xe1\xdb\x0e@G,\xd7?Ҍ,j\x0e#\xb8dm\a\xe0Ԍ#\x1c\xbcM3\xb2S\x81\xf7^\xac\xd75\xd9p@\x8b\xd1\x0f\xe4;\x0e\xa8s\xee]\xf4)\x8cp\xfcQC4\\\xb5\xa6\x87\x12\xed\xbeE{ע\x15\aK,?~\xc2\xe9\x1d\xb1\x14\xc7`ST\xf6*\xb2\xe2\xc3\xe4vɪxͫ\x03`\xed\x03\x8e\xf0>C\fJ\xa3\xe9\x00\x0eʒ)\x0e\x15\xb4\x0f\xe8\xdeܽ}\xf8\xe6^\xefqV\xd5\b`\x90u\xa4P\xfc\xae\xa0\x05bP\xb0\xa4\x83?\xf6\x18\x11\x1e\n5\xc0\xe2#rC\xd6B\x02,\x10yh\xa6\x10}\xc0(\xb40\x98\xbf\x13\t=\xdb\xce\xf0\xbc\u0380\xab\x0f\x98,\x1ad\x90=Bk=\x1a\xe0R\f\xf8\tdO\f\x11CDF'\xc7^,\x9f\x9f@9\xf0\xdb\xdfP\xcb\x00\xf7\x18s\x10\xe0\xbdO\xd6d\xa5\x1d0\nD\xd4~\xe7\xe8\xaf\xe7\xc8\f\xe2KJ\xab\x04[Ӗ\x8f\x9c`t\xcaf\xaa\x13~\t\xca\x19\x98\xd5\x13D\xcc9 \xb9\x93hŅ\a\xf8\xc9G\x04r\x93\x1fa/\x12x\xdclv$\xcb\xd0h?\xcfɑ\x01\xf2\xd24#HL5ySZ\xb3\x1c\xb5\xa0\xb4\xc6 hޟ?V^\xbdZ\xbd7\xcaQ{WǔG\xf8\xe5\u05eeFE\xf3\xb0\xe0\xc8ƿ\x03\x00\x00\xff\xff\xcf^\xca\x05\xed\t\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xecwà\x18C\xe7\xf2(\xf3J(\x90\xday\xa1\xb3@\x87\xa8q:\xa5\x03\xc6\xc5\xd9\xc36\xb8\xb5\x843\xf1\xbe\xe3\xe2\x8cF0\x16\n\xf2\xe6\xfd\xa9n\x10>\x8c\x92\xbb\x15\xe4kLPC[)t\xf1E9{\xceƮ\xafG\x00\xd7R\b{\xbf\x12[T\xe0Pa\xe6\x8d\x1dbôPØ\xf7Q#\xbc\x1b\xf0V\x8d\xff%\x12ێʌ\xc2\x04x=\xc8\xec\x10\xb6e\xd2\x17\x86\x02\xb9A\xc7\xf6+\xcaR\xbd\x0f\x13\aӒ\x0ec\u0084\x9b1i̧\xb0\xfaf\u074cY?\u05cc\x19\x8f\xd7\xe5e-\xfa\xdf\x0f+\x93\xe3>[17\xbd\x85\x1f\xa9\x98\xc4DI\xa1\xf5f\aX\x94\xfe\xfd\x1a\xa4OO)\x92\x10\x9c\x18\x8e\xb2\xa7~\xf7oN\x10\xe7\xea\xf4\xe6t\xdd\a\xea\xf4/\x94B\xfd\xeaߌ\x10\xd8\xd9?F_\xbfP\x00?\xb5\xd7\\\x83\xdc\xd5\x02ȯa'\x95G{\"\x89)rʹ$~)\v\xe6w*\x1a\x85\xf0\xd9\xe1\xfe\x8d\xa2#\xd7\xd4s\x16q\xe3ti\x88)ST\xdd\xddL'\xa1\x02'\x83\xd2b\x11R\xcc'\xe6`\xf3\x84#\x9f\xcf_\xbf`>\xce\x14X\xa2a=\x12>\x9f\xa0\xd9~m\f\x91\x97\x11\x10\x83\x94:\xbb\b\xe5\x82k\x10\xf0\x82\xef!\xba\x10\x1aH \x82^C\x93g!Z\xe4\x9a\x05+\xd4\v\xbe3\x90X\x86\x98Y\xbbL\xf4a\xbc\xe0\xfb\xfc\xa4\x13\xb6\x116\xd2Ų\n\xf1\x8f\x1e0\x038\x87]\xca2\xe0\"R\xf20sD\xc1R\x17\x91F\xe2\xf6\xd9\xe4\xd5bj\xea\x1eA\x90W.\b\x85\xb4\xfd \xcbE\x04\x92\xeb\x04\x87l\x13\xa9\x88\xf4,\x94\xcc\xeb\xd7\x04\xfd\xde\xe8k\xf8j\xfcF\x8f\x05\xab\xddq\xff&]\xac\xdd}1\xe8\xbe\x1a\xcfO>\x9c\x89\x01\xe5\xb3Y\x18\x96\xb1\t\xe9\xe0\x86\x89\xfev-jV\x89\xc3\u0604\f\xab\x16\x89t\xb0єC\x04^\x85jbxٔ\xb7\uf3a2r\\l\xd2F\xafx\xb3[\x0f\xbd'\xb2x\xa1\"\xb7\xa5\xd0G\xab~ex\xdd\"\x88O\xb4/\x84ա2\xaaD\x869\xe4\x153\x91+{\xc2\xe3^fP\xa0ݏo\x04\xedQ\x92\xcf^\xf2\xfaE\xbe4\x8c\xb3\xf4i\xc9֜Ft\xc6\xf9\x1c\x1a+\xb2\xcd\xd99I\xb43\x13\aKy\xe3\x13\xe7\xe8\xe0M\x92\xe3\x86\x19n\x8a<\xe7\x83\x16\xa1\x1e\x16{\xefŜ\xef\xef\xdb\x01\xa5\xb0\xc7\x15\x82\vt\xffK[\x15+\xed\xffA)\xa4\x9d\xb5\xd0\xcf|b\xa2\xb0\xb32V\x85\xda/!\xf8\xd2\x01I\xf3(\xd4iAx\x80,C^\x03U؆ͮ\x17i\\\xc3\xeb\xc1\xb8\xb0+\xee$\xaa\x1c\xe4T\xa4E\xe3\xf2\x05\xdf/\xaf{6~\xb9їa{\xeeYl\xda\xcbg\x00\x1b\xad\xde\xe1\x92W^~{\xe8\xb2H\xeb\x16L\xe2\xe3\xb3e\xc1,esi\x17\xa7e\xf5\x19\f\x85\xa2\xe3\xd8.й\xd28\xbf\x10\x89\a\xe3|\xa8\xd0u\x82ǁ\xda\xd0tN\x13kB v\xe1\xdc\xcb\xd8t\xc2A\x8e\xec\xa4TIRr8X\xe0\xecA\xcc#H\xa1\x14\\66\x1a\xfc\xe3e8\xf6\xe0W\x88\x8cÂ\t\x88\xa4\n\xa55\x19:7\xa5\x0e\xb3\x9ew\xa6\xe0V\x17\xdbDH*\xc2!\xc2Tq/\x8d\xa5a#\xb1\xe6\xac0\xfb\xfe\xadU\x03$Ӧ\xff\xa7\xd5\xec<\x8c\x80Ϡ\x8bB\xe8\xd9͢\x87\xdc]X\x97L!\x82\t!\xbb\xddWl\xc6K#\xbd\xa84\xbf\xee\x06[H\xbda\xe0\xf0\xe9C\xb7cH.\x11\xcf\x0f\xa9\xef\xd2ʆ\xcd\xf5\x83`\x9b\xa5\xe9\x97܇\xc6\xeb\x01-v$կ\fs8\xa7\x8do\xa5\xe7\xcb\x18\x1d\xf0\xb8r\xb0\x93\xd6\xf96\x92\x8e\x0f\xb6>>G\xd1\xf7\xd6~C\x8a\xf2簮U\x00:\x98\xd7tR8r874\xf8\x18\x04A\xee@z@\x9d\x99Js\x11\x83\x8c\x94_\x10X\x1a\x9c\xe9\xec&\x1b\xc6\x12æ\x81\xba*\x96\x10\xbeb\xed\x91z\xa2\xd6ў\xfc\xa3\x90S\x95\xaa4\xce\x12\x93\x97\x05\x9ajbSkFGLOa]爷\x10o\xb2\xa8\n\x10\x051{\x11Gig\x96\x05v\xe5\v\xafBz\xf6\xee\x04\x95]\xbd7d\x14\xa5B\xbf,\x1b\xd8\xe2\xceX\xb6E's\xac\xb7\xcc(s\xa3A\xc0NHU\xd9E\x1e\xed\f\x8e.\x8f죑\u007fLо\xe4\xb5+&\u007f\xb6L\xb9(T\x9b\xf2\xaa\xa5]\x1a\xa8=X\xfc\xc8\x10\xa9\xb4\x92t\xc6|l\x94\x14UI\xe8\xf7\xefaR\x8b7\xdfä\xde\xf8\x1e&u\xc6\xf70\xe9{\x9849\xbe\x87I\xdfä\xdfk\x984\x8dɊ\xebV\x83?ͼ}\xf6\bu\x1c\xb1Q\xc8\xf1T\xff.\xf4^/\xeb\xcb\xdb\f\xaf\x19軌-\xdd+\xee8\xef˹9\xfao\xdc|ݨGʟ\x9474\x96N\xb6\xee-h\xc4\x1b\xea͜o/\x99k*\xe9\xf6$֍\x1d\xa9)ѤW\xf4\xa8O\x9d\xec\x14f\xb6;\x18\x84R\xed\xde\x14a\x1b\xa6\xfcJ\xfd\x8a\xb3\xad\x1f3\r\x1f\xd3m\x9b\xe3\x1c:\t\xed\xbb,\xb2\x9d\x16\xc3_\x99C\x93}\x19\xe3\xdd\x18\xf1$\x03\xbd8~Zw\u007f\xf1&\xf6f\xc0\xab\xf4\x87\x1e\x01\xdc4I)\x8b\u07b7\x9b#\x93N\xc5\xeb\x03\xa7\x9c\x03cAKu=\xd8\x17S߬h\xb3\x13\xfe\\\x86\xa4\xe8,{\x9b\n\xed\x97\xf4n|s\xc7F\xb7'c\xd0ɞwر\xb4\x85tyOF\xb7\xe7bd\x93YЉqv\xa7\xc5|\xbe5\xd9U\xf1\r\xbd\x14\xa9ObjÝ\xe8\xa0X\x10s\xccwK|S\x8f\x04\x1f\xe6M`}VgD\xab\xeba\x02\xe4\xb2~\x88\x05,\x99\xeb}8\xbb\xe3\xe1\xb4\xcb`\x82\x88\xb9>\x87\xf1\x1e\x86\t\xa0\x83\xdd\rK:\x17&`\xd6=\r\x1fد0ӥ\xf01\x9d\x84\xbf4\xf6\x1c\xeb9\x98\xe94\x98\x89L\xa7\xb0\x9a\xe9%X\xdeA0ßo\xec\x16\xa8\xfb\x01\x06\xdfyn\x8f@\xb7\v`\x10\xe4\xc2\u0380\x91\xb3\xffA\x90\v\xfa\x01fN\xfc\a\xc1Nn\x8c\x13\x1a1\xfa\x93Ӣt\a\x93.lM\xc6I\x8fݹ\x03\xc9E\xba\xae\x95)S\xe55\xec>)|\x05\xf3\x1d\x1e\x9e\xd9\xc9\xf3U\x98\xac\xb9\b\x14]y\n~N\xef\t\xfd\xf0\x91Ɇ\xf3Ɗ=\xfed\xb2\xd6M\xdb1\xfa\xbbs;\xd7\"\xa3PSJ\x9f\xfa D\xba\x9f\xd7]:\x14;\xc6*[\xbc\xe7\xd6d_\x84a_ޣ\x96罚$\xe2\xe9駀\xb8\x97\x05\xae\xbfT!\x91[\x95\xc2:$\xfe%\x82¢-\xfdy0\xaf=\x84\x95\x89\x94\xfep\x8a\xafE\xae\xe1q\xb6\xb8\x18\xebp\x97/)XbӴ:>\x0f\xafiŢ-\xa1\x84\xc4\xc6\xec\xc6V\xf5\bl]d\xa6h?t\xb4|\xd4\x15\xb5a\xe7<|\xf9\xd3\v_\xb9\xb9\xeb\x9f<)]\xe6\x8e\x15\xdf\xca\xf2\r\xb3\x00 (\xe3\xd97@cy\xabs\xc3~J&w\xfd\xf9|\x95\xda\xe6\x01).\xab\u05579_\x85\xab\vh\x03\x1e\xad\x01\x16\xd6q0@\xb00\a<\xa2\x06\xa3\xb9^\xc67\xb8\xc2=\xff\xd35\xfd\xfc\xb5\x05#\x96\xe3\xaaR\x19\x91'\xcbM\xf7L\xe3\xf5\xf0'\xf6G\xf6\x88\xf6ʍB䫪;c\x87\xc8?լ\x9d\xb1\x85\xf0\xb7\x90\v\x8f\xab\x01\x80\v\xfc\u0600Jq\xf1x\xe6\xea&O\t\xd6\xc1u\xe7t\x976\x14\x9e\vtN\xecӝ\xcdWrG{Դ\xc9\rT\x89b(\xd6\x14.\xbb\xf7\x17CF'2O\xf9o@-\xa6\xb0\xadYW}\x9bSfO\x196O\x8c7ƣ\u007f\x1ev$R{\xdcc7<·R\xday_~_O#\x8ep\xea\xce\x16\xde|@\x01\x95\xdcKr\x88$ؽ\xb0[\xb1\xc7Uf\x14\xe5Q\xd2\xe8S\x8c\xfe6r\rP\a\xbe\x8e\xd0#\xe8\xc7\xf6̔\bFe\x0eP\xd2\xc7\x12\xae\xe3\x8eJ\x12,\xc4_\x8d\xed\x1f\xd6\x14R\x1b\x1b\xc2\x17\x0e\xa1\xd3\xd2\xc5\xfe\x9c\xaf\xb5N\xe2\xfb@3\xeaө\x96\xaf¤L\xc3\xfb\xfc\xd0)\xc6\n\xbe\xe2\xe9\x16\x15\x0e&0\u007f\xae\xbf\xa2ћ\xb0\xd1\x0f\xd6\xec)\xe2\xeb\xfdt\x97\xbcR\xef\x97\aa\xbd\x14J\xbd\a\xf0#o\xed=\xfe\x82\xe4\x17F6\x82!\x06F̦y\x18'5!\xa5\xd4A\xd6|\x98\xb05\x95\xef\x18\\c\xb0=\x89\xa7\xf7\xad)O\xc4T7\x90]\x88\xb4\x03\xa2\xf3+\xdc\xed\x8c\xf5!~]\xad@\xee\xe2\xc6҃Jޙ+_\xe1k\f }\x93\xc55\xbaɱ\xa0E\xe1X7=\u007f\x11\x82\xcb\xce\"\xcb(>\xc1\x1b\xe7\x85\xea\xf9\x80o.w\xf1~Mڅ\xf9_z\xdbY\x8fɛ\xf6\xec\xbaϵ*\xb6hIS\x19X\xe0\x17\x1f\xed\x05\xaf\xa7\x86S\xba-\xa2\x86W+\xbd'\u007f\xd3.\b\x82'\x0f\xa3\x148\x03;1xgx\xdc\xe7\xf1\xaf\xc6\v\xb5\x19Kh\xbb!`=5\x91Ë\xfbD\x19\x12ÖI\x1f$'4wH\x97V\x92ಃ\xd0{R k\xaa\xfd!i\xe0\xc8N1\\ī\b!(U\xb5'\x95\x8e\x855_Y\xdd\xcaLc\xa9-o\xa1*\xb2\x17\xa8\xca\xe1\x93\xe7\xf0\xad\x98\xf8\xa9\x9f\x9bx\x17x\xb5\xb3\xa6XE\xfes\xcd\xec:V\xec\xac4\x142qN\x13\xaf㍀e\xb1\x97%j\x10.\xe22\xdbw2%\xc8\xf1D\xcd\v\xeb\x97\x05a\x8f\x9d\xa93\xf1\x17\xc3\xc5|\r\x8fX\n2\xb6\xbe\x11[S\xc0\xdd釖\xae)GO_\x15\n\x1f\x85\t\xa2\xa7\xbc\x96\xbf\xeea,\x86;x=\x88\x9d\x80\xaa\x13@uQ\xff\xfb\xc4N\xcdw\x96\xee磨\xe7\x93\xc9'\a)d\xc1\r\xbc\x14\xfb\xfcA\xee\xfa\xf9EY*\x99\x11\xb6\u007f\xfc\x95\x0eH\x8e\v\xa2\x8a\xabɀ\x82\xa3\x87:6\x80/XZ\xcc\xc8*\xfb\xc8?(\xa4\xfd\xde!v#\x95\xabŁ]7Et\x9f\xbdǢ\x1cx\xd7D\x8e\xd8,\x1as|\"M\xe8\x11\x90\xbef\x95@\xc5N\x80Ѥp1!u\xa8q\x0e!\xf5\xa21B\\\x95\x91\x03\xdaUC[Q\x9ds} U\xaf\xc2R\xa2=m=\xff\x11'\rd!q\xfd\xc7\xe6!\xad4$\xe1\xf7wJD\x06\xfc\xf8ɣ\xe6cv\x9f\x9a\xff\x98}\xab\xf8\xf1\xbac\xe8'co\x99\xb7L;\xa2\x12\x9f4\x05\x02\x91eH\xba\xfb\xf5\xf4;v\x97\x97\xfcO\xfaT\x1d\xff\x9b\x19\x1d\xf6Rw\v\xff\xf9_\x17\x10\xebL\xcf\t\x0fz\xf8\xff\x01\x00\x00\xff\xff\xff\x8cC\xfd\xf8O\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcX\xcdr\xdb6\x10\xbe\xeb)v܃/\x15\x95\xa4=txs\x94v&S'\xf1X\x89{h;\x13\bXJ\xa8A\x80\xc5\x02rԧ\xef,\bR\"E\xd9\xceLS\xde\x00,\x17\xdf~\xfbK\xce\xe6\xf3\xf9L4\xfa\x0e=igK\x10\x8d\xc6/\x01-\xaf\xa8\xb8\xff\x89\n\xed\x16\xbb\x97k\f\xe2\xe5\xec^[U\xc22Rp\xf5-\x92\x8b^\xe2\x1b\xac\xb4\xd5A;;\xab1\b%\x82(g\x00\xc2Z\x17\x04o\x13/\x01\xa4\xb3\xc1;c\xd0\xcf7h\x8b\xfb\xb8\xc6u\xd4F\xa1O7t\xf7\xef^\x14\xaf\x8a\x1fg\x00\xd2cz\xfd\xa3\xae\x91\x82\xa8\x9b\x12l4f\x06`E\x8d%\xac\x85\xbc\x8f\r\x05\xe7\xc5\x06\x8d\x93\xed]\xc5\x0e\rzWh7\xa3\x06%_\xbd\xf1.6%\x1c\x0eZ\r\x19Vk\xd2\xeb\xa4l\xd5*\xbb\xce\xcaҹ\xd1\x14~=/s\xad)$\xb9\xc6D/\xcc9XI\x84\xb4\xddD#\xfc\x19\xa1\x19@\xe3\x91\xd0\xef\U00013f77\xee\xc1\xfe\xa2\xd1(*\xa1\x12\x86p\x06@\xd25X\xc2{F\xdf\b\x89j\x06\xb0\x13F\xab\xf4~k\x8fk\xd0^ݼ\xbd\xfba%\xb7X\x8bv\x13@!I\xaf\x9b$7m\th\x02\x01\x1d\x18xآG\xb8K\xa4\x01#Eʰ\xb3F\x00\xb7\xfe\ve\xa0\xbcn\xbck\xd0\a\xdd\x11\xcb\xcfQ`\xf5{#,\x97\f\xb6\x95\x01š\x84\x04a\x8b\x90\x03\x02\x15P2\x04\\\x05a\xab\t<&\x96l8\xf8\xa8\aT\x81\xb0\x19V\x01+f\xd2\x13\xd0\xd6E\xa38\xfev\xe8\x03x\x94nc\xf5?\xbdf\x82\xe0ҕF\x04\xcc\xde\xec\x1em\x03z+\f\xd3\x1c\xf1{\x10VA-\xf6\xe0\x91\xef\x80h\x8f\xb4%\x11*\xe0\x9d\xf3\b\xdaV\xae\x84m\b\r\x95\x8b\xc5F\x87.\x95\xa4\xab\xebhu\xd8/RB\xe8u\f\xce\xd3B\xe1\x0e͂\xf4f.\xbc\xdc\xea\x802D\x8f\v\xd1\xe8y\x02n\xdb\xe8\xae\xd5w>\xe7\x1d]\x1e!\r{\x0e\f\n^\xdbM\xbf\x9dB\xfb,\xef\x1cԭ\xcf\xdb\xd7Z\xfc\azy\x8bY\xb9\xfdy\xf5\x11\xbaK\x93\v\x86\x9c'\xb6\x0f\xafсx&J\xdb\n}\xeb\xb8ʻ:iD\xab\x1a\xa7mH\vi4\xda!\xe9\x14\u05f5\x0e\xec\xe9\xbf#R`\xff\x14\xb0L\x05\x05\xd6\b\xb1Q\"\xa0*\u0b45\xa5\xa8\xd1,\x05\xe17\xa7\x9d\x19\xa69S\xfa4\xf1\xc7up(ز\xd5ow%j\xd2C\x93Y\xbajP\x0e\xf2D!iϱ\x1cD\xc0\x94\x019i\a\x94\x9e\xafo\xe7\x937%\xb0\x94H\xf4\xce)\x1c\ue3e0^\xf5b\x03l\r\xfaZS\xea$P9\x9f\xf6\xda\x1a\x02\xb9\xf6\x8d\x94B_\u007f\x8a\xd1\t\xdaX\x8f!\xcc\xe1\x16\x85\xfa`\xcd~\xf2\xe07\xaf\xc3\xf8\x82Iw\xf1\xd3\xc2Z\xed\xad\xbcA\xaf\x9dz\xd4\xdc\xd7#\xe1\xde\xe8\xad{\x80*\x85\xad\rf\xcfu\x85\xf6V\x8e\xebf\xf7\\ݼ\xedjh\x9b\x1c9\x9727\x05\\\xe5\x9ct\x15\xbc\x00\xa5I\xac\rRR9\xa6\x87;#\x9f\x96\x10||\xb6\xd1\xd2\xd9JoƦ\n\xa5R;\x17\xe6\xe6LT<\xaat\xc4\xd52\xdd\xc1\x85\x86#\xa0\xf1n\xa7\x15\xfa9G\xbe\xae\xb4\xcc\x18\xa2o\xbbN\x95:\xdeغ\xc9܁\xbe\xf8\xe4\xb0~\xd4e\x1f\x8e%\xfb\x96\x97Q\xe4p%\f\\\xf5\b,r8\v?\x8e+`\x8fJg-{)8\x10\xbd=\x974v\xde\xe8\xd5s\t\xc6\xcf:\xca{\f\xa7\xfb\xe3\xa8Kb\xccdʣv\x15\x1cD\xc2\xc4\xed\xe3\x00\x9e\xf0\x19\x80\x14K\xf4O\xa3X^\xb1X\x1f\xf1\x02\x96W\xb0\x8eV\x19\xec\xb0\x81?4m\"}\xa53\xb8\x82pQ?-x-\x8c\xe7\xa6G\xc7磙q\x93\x85z\xbb\xbbu\x1a\xb8\xc6\x05{:5O\xac\x98\xb2`>L\xd7\xc1Iw\xe9\x93\r3\x88\x10\xe9+[fzgИ\xdc:\x8d\xd9G]\xf3\x1b7ȋ\xa3\x0e\xc93\x97\x85h#\xa1j\v^\x01\u007fXx\xc3\x13\x94\xe4ɦd\x8c<\xcc\xd0I\xd8X\xf7\xc0/\x1fiK\n\xc0\xd9dW\x9a\x0exFm\a\xaet\xf4\xa0\x8d\xe1\xb1\xc9c\xedv\xe9\x93a\xf8\xf0\x8c\xe3\xd1\xecA\x10\x13\xb1{U\xbc(.\xfe\xe7\xeek\x04\x05n\xa7\xa8nq\xa7\xc7\xdf\v\xa7l^\x9f\xc8w\xd1\xdb7L^|\xee\x06\xb1\x85\xcfb\x9fO̯\xb4\xe1\xb1q\"\xd4\x0f\xdfB\xed\xc7\x01\x05\b\xbaƴz\xbd\xba\xbe\xa4\xf4I\xcb#\xef\x89\xd2\av\x1f%\x80\xfc\t\xe1\xf2\xa4\x1b)\xa0\x9fpv\xef+M`\x1d\x18g7\x83Th\x9f<\xf7\x82\xf3І\x8e\xf3\xa0\x90GV.\xb3r+\xec\x06\x0f\xdf2\x19\xfb\x11J\x0e\x8cS\xa4\xc3\xe88D\x83\xb6ӡ\xf0\f\x1f\xf2\x17\xfb\xa3\xfe\xbb\x1e\x88v\xae\x1b2ܣξ4\xa79\xf9\f\xaeG\xd2]\x13a\"\xe7|\xd5\u007f2F5[A\x8f\x1b|\xc3\x12\x9d\x9d2z\x8f6\x1cjO\n\xa8\xa9\xfa\xf3\xbc!\xf8j'tB}r\xf2Ɋ3ggl\x99\xa8\xb9\xa3\xad\xc3\u007f\x9a\x97\x87U\xaa\x89\xf3\xfc_&\x1d\x00\xb4\x05\xf6\x88ȜUy\xe7Pȹ\x826\x01\xd5\xfb\xf1?\x99\x8b\x8b\xc1\x8f\x95\xb4\x94ζ\xc3)\x95\xf0\xfb\x9f\xb3V+\xaa\xbb\x0e\ao\xfe\x1b\x00\x00\xff\xff\x81\xab\x99~\xd3\x12\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96\xcdr$5\f\x80\xef\xfd\x14\xaa\xe5\xb0\x17\xa6\x87\x00\a\xaao\x90]\xaaR@*\x95,\xb9P\x1c<\xb6fZ\xc4m\x1bI\x9e\x10\x9e\x9e\xb2\xbb;\xf3\x93Ne9l\xdf,\xcb\xfa\xf9$\xb9ݬV\xab\xc6$\xbaG\x16\x8a\xa1\x03\x93\b\xffQ\fe%\xed\xc3\x0f\xd2R\\\xef/6\xa8\xe6\xa2y\xa0\xe0:\xb8̢q\xb8E\x89\x99-~\xc0-\x05R\x8a\xa1\x19P\x8d3j\xba\x06\xc0\x84\x10\xd5\x14\xb1\x94%\x80\x8dA9z\x8f\xbc\xdaah\x1f\xf2\x067\x99\xbcC\xae\x1ef\xff\xfbo\xdao\xdb\xef\x1b\x00\xcbX\x8f\u007f\xa2\x01E͐:\b\xd9\xfb\x06 \x98\x01;p\xe8Qqc\xecCN\x8c\u007fg\x14\x95v\x8f\x1e9\xb6\x14\x1bIh\x8b\xe3\x1dǜ:8l\x8c秠Ƅ>TS?US\xb7\xa3\xa9\xba\xebI\xf4\x97\xd74~\xa5I+\xf9\xcc\xc6/\aT\x15\x84\xc2.{Ë*\r@b\x14\xe4=\xfe\x1e\x1eB|\f?\x13z'\x1dl\x8d\x17l\x00\xc4Ƅ\x1d\\\x97\xa8\x93\xb1\xe8\x1a\x80\xbd\xf1\xe4*\x9e1\x8f\x980\xfcxsu\xffݝ\xedq0\xa3\x10\xc0\xa1X\xa6T\xf5\x96r\x00\x1200E\x02\x1a\xa7\x00!\x06\x84\xc80DF\x18\xa3\x95v2\x998&d\xa5\x99`\xf9\x8e\xfa\xe7Yv\xe6\xfc}\x89n\xd4\x01W:\x06\x05\xb4G\x98\xea\x8e\x0e\xa4F\x0eq\vړ\x00c\xc5\x12\xc6\x1e:2\vE\xc5\x04\x88\x9b\xbf\xd0j\vw\x05\x1d\vH\x1f\xb3w\xa5\xcd\xf6\xc8\n\x8c6\xee\x02\xfd\xfblYJ~ť7:\x17x\xfe((r0\xbep\xcd\xf85\x98\xe0`0O\xc0X|@\x0eG֪\x8a\xb4\xf0[\x81Ca\x1b;\xe8U\x93t\xeb\xf5\x8et\x9e\x18\x1b\x87!\aҧu\xed{\xdad\x8d,k\x87{\xf4k\xa1\xddʰ\xedI\xd1jf\\\x9bD\xab\x1ax\xa8\x03\xd3\x0e\xee+\x9e\xc6K\xde\x1fE\xaaO\xa5\x13D\x99\xc2\xeeY\\{\xf8U\xee\xa5\u007f\xc72\x8f\xc7\xc6\xf8\x0fx\x8b\xa8P\xb9\xfdx\xf7\tf\xa7\xb5\x04\xa7\xcc+\xed\xc319\x80/\xa0(l\x91\xc7\xc2m9\x0e\xd5\"\x06\x97\"\x05\xad\v\xeb\t\xc3)tɛ\x81T\xe6\xf6+\xf5i\xe1\xb2\xde\x1b\xb0A\xc8\xc9\x19E\xd7\xc2U\x80K3\xa0\xbf4\x82_\x1c{!,\xab\x82\xf4m\xf0\xc7\xd7ݩ\xe2H\xebY<\xdfE\x8b\x15Z\x18˻\x84\xb6Ԭ\x80+giK\xb6\x8e\x01l#\xc3cO\xb6\x9f\xc7\xf2\x84\xe8\xf3\x00\xb7G⥁-\xdfh\xa0\xdc*\xa7\xf2W\x92\x85Z'b<\xe9\xb5Ց\x997)\xa8\xd1,\xff\x8bC=1\x93\xb0\x99\x19\x83Nv\xea-\xb0t\xe8srG\xe6\xc8r\x9e\xf7I8\x1f\xabJ\xfdk\x19\n\x02&t\xb1\xb1\x16\x93\xa2\xbb>\u007fN\xbc{w\xf2.\xa8K\x1b\x83\xa3\xf15\x04\u007f\xfcٌV\xd1\xdd\xcfq\x14\xe1\u007f\x01\x00\x00\xff\xff\xcb0\x9b\f\x8c\t\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WOoܶ\x13\xbd\xebS\f\xf2;\xe4W \xd26m\x0f\x85n\xad\x93\x02A\xd3 \xb0\x13_\x8a\x1e\xb8\xe4\xacĚ\"Y\xcep\x1d\xf7\xd3\x17CI\xfbG+\xdb\xe9\xa1{\xd3p8|||oȭ꺮T\xb4\xb7\x98\xc8\x06߂\x8a\x16\xbf0z\xf9\xa2\xe6\xeeGjl\xd8\xec_o\x91\xd5\xeb\xea\xcez\xd3\xc2U&\x0e\xc35R\xc8I\xe3\x1b\xdcYo\xd9\x06_\r\xc8\xca(Vm\x05\xa0\xbc\x0f\xac$L\xf2\t\xa0\x83\xe7\x14\x9c\xc3Tw蛻\xbc\xc5m\xb6\xce`*+\xcc\xeb\xef\xbfm\xbek~\xa8\x00t\xc22\xfd\x93\x1d\x90X\r\xb1\x05\x9f\x9d\xab\x00\xbc\x1a\xb0\x05\x13\xee\xbd\v\xca$\xfc+#15{t\x98BcCE\x11\xb5,ڥ\x90c\vǁq\xee\x04h\xdc̛\xa9\xcc\xf5X\xa6\x8c8K\xfc\xeb\xda\xe8{;eD\x97\x93r\x97 \xca Y\xdfe\xa7\xd2\xc5p\x05\x10\x13\x12\xa6=~\xf6w>\xdc\xfb_,:C-\xec\x94#\xac\x00H\x87\x88-|\x10\x94Qi4\x15\xc0^9k\n\x15#\xee\x10\xd1\xff\xf4\xf1\xdd\xed\xf77\xba\xc7A\x8dA\x00\x83\xa4\x93\x8d%o\x89\x1b,\x81\x82\t\x05p8\x00\x03\xe5A%\xb6;\xa5\x19v)\f\xb0U\xfa.ǩ&@\xd8\xfe\x89\x9a\x818$\xd5\xe1+\xa0\xac{PRmL\x04\x17:\xd8Y\x87\xcd4%\xa6\x101\xb1\x9dY\x96߉\xbe\x0e\xb1\x05\xe0\x97\xb2\xa31\a\x8c(\n\t\xb8G\x98t\x81\x06\xa8\xec\x16\xc2\x0e\xb8\xb7\x04\t\v\x95~\xd4\xd8IY\x90\x14\xe5'\xe4\r\xdc\b݉\x80\xfa\x90\x9d\x11\x19\xee11$ԡ\xf3\xf6\xefCe\x12^dI\xa7x\x16\xc2\xfc\xb3\x9e1y\xe5\xe4,2\xbe\x02\xe5\r\f\xea\x01\x12\x16v\xb2?\xa9VR\xa8\x81\xdfBB\xb0~\x17Z\xe8\x99#\xb5\x9bMgyv\x94\x0eÐ\xbd\xe5\x87M\xf1\x85\xddf\x0e\x896\x06\xf7\xe86d\xbbZ%\xdd[F\xcd9\xe1FE[\x17\xe0\xbe\x18\xaa\x19\xcc\xff\xd2d?zy\x82\x94\x1fD=\xc4\xc9\xfa\xee\x10.:\u007f\x94w\xd1\xf9(\x8fqڈ\xffH\xaf\x84\x84\x95\xeb\xb77\x9f`^\xb4\x1c\xc19\xe7\xa3N\x0e\xd3\xe8H\xbc\x10e\xfd\x0e\xd3xpEeR\x11\xbd\x89\xc1z.\x1f\xdaY\xf4\xe7\xa4S\xde\x0e\x96i\x96\xad\x9cO\x03W\xa5\xaf\xc0\x16!G\xa3\x18M\x03\xef<\\\xa9\x01ݕ\"\xfc\xcfi\x17\x86\xa9\x16J\x9f'\xfe\xb4\x1d\x9e'\x8el\x1d\xc2s\xbfZ=\xa1\x85\x95o\"j9/!M\xe6ٝ\xd5\xc5\x02\xb0\v\t\xd4\xd1\xd9\x13m\xcdI\xdd5o\x16P*u\xc8\xe7\xb1\x05\x8aO%E\x16\xbe\xef\xd5y\v\xf9?6]#}\x80&\bcg\xf8\xa6Y\xd4{l\xf55\x8d\xaeb\x98\xa5*[\x17\x1e\xc5\xe8\xd2zN\xd1,\x17\x95\x1f\xfa<\xac\x15\xaf\xe1\xe7\x82\xf4}\xe8\x9e\x18\xbd\n\x9eE\xd0O\xa4\xdc\x06\x97\a\xbc\xf1*R\x1f\x9e̜/\xcd\xc3E\xb2L\xbbFi\xb5\xf8\x18\xa4i\xf8\x1a)\xbbՅV\x858\xff\xca\xc5\xf9\x1c\xcbr\xf7\xcc,˄\xb1\xe3\"ȅ\x9d<2ұ\r\xdc[\xeeᾷ\xba_\xa9\neZ9 \xe9/DA\xdb\xe2\xd8\u007f\a[tl\x13^ȣ.\xa2\xb9\b\n\xe4j\xad\xf8\xc2s\xeb\x85\xeb\xc9\v\xcf:\x96\x15g\xfajϖ\xec\x99T\x9dSB\xcfS\x8dr[-'|\x8dig\xc5\u007f\xbe~\xff\xa4s\xdf\x1c\xf3\xca\x1bLY?\xe2\x88\tk\xb2\x9dܭ2&\xde-\xceZ\x120\xfeN\xef\xf8gO\r\xbfD\x9bN\x9e,\x8f@{{H\x1b\x1b\v\xfa\xf1\x8aX\xbe^J9\xa4r\xedj\xe5/\xb0m\x11\f:d4\xb0}\x18;\xe3\x031\x0eK\xbc\xbb\x90\x06\xc5-\xc8\xc5Q\xb3\xbd\x10\x8a\xbc/\xd5\xd6a\v\x9c\xf2\xba\x8aV6\x1b{E\x17\xb6:\xdb\xe7G\xc9X;\xfe\x83\xb9\x9e8\u007fx\xa4\x83\xd5\xf0\x01\xef/b\x1fS\xd0H\x84Kc<\x82~E܋\xd0\xf1a\xfe\xfa\xf8U\xa4XO\x0f\xf12\x00P\x9e\xb5愺\xe9\xcd8E\x8e\x8eQZcd4\x1f\x96O\xf1\x17/\xce\xde\xd6\xe5S\ao\xec\xf8/\x02~\xff\xa3\x1a\xab\xa2\xb9\x9dqH\xf0\x9f\x00\x00\x00\xff\xff\xbbظ3\xc4\f\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y_s۸\x11\u007fק\xd8\xc9=\xb87\x13RMz\xd3\xe9\xe8\xedb7\x1d\xb7w\x8e'\xf2\xe5%\x93\x87\x15\xb1\x12Q\x83\x00\x8a\x05\xa5\xa8\x9d~\xf7\xce\x02\xa4$J\xb4,_{)_l\x82\x8b\xc5\xfe\xdf\xdfB\x93\xa2(&\xe8\xf5'\n\xac\x9d\x9d\x01zM_#Yy\xe3\xf2\xf1O\\j7]\xbfYP\xc47\x93Gm\xd5\f\xae[\x8e\xae\xf9H\xec\xdaP\xd1\r-\xb5\xd5Q;;i(\xa2\u0088\xb3\t\x00Z\xeb\"\xca2\xcb+@\xe5l\f\xce\x18\nŊl\xf9\xd8.h\xd1j\xa3(\xa4\x13\xfa\xf3\u05ff/ߖ?L\x00\xaa@i\xfb\x83n\x88#6~\x06\xb65f\x02`\xb1\xa1\x19x\xa7\xd6δ\r-\xb0zl=\x97k2\x14\\\xa9݄=Ur\xe8*\xb8\xd6\xcf`\xff!\xef\xed\x04\xca\xca\xdc;\xf5)\xb1y\x97ؤ/Fs\xfc\xdb\xd8ן4\xc7D\xe1M\x1bМ\n\x91>\xb2\xb6\xab\xd6`8\xf9<\x01\xf0\x81\x98\u009a~\xb1\x8f\xd6m\xec{MF\xf1\f\x96h\x98&\x00\\9O3\xb8\x13)=V\xa4&\x00k4Z%Sd\xb9\x9d'\xfb\xe3\xfd\xed\xa7?̫\x9a\x1a̋\xc2\xd9y\nQ\xf7\xea\xc9s\xe0\xd8\xdd\x1a\x80\"\xae\x82\xf6\x89#\\\t\xabL\x03J\\I\f\xb1&\xe8\x1cB\n8\x1d\x03n\t\xb1\xd6\f\x81\x92\x0e6;\xf7\x80-\b\tZp\x8b\xbfS\x15K\x98\x8b\x9e\x81\x81k\xd7\x1a%\xfe_S\x88\x10\xa8r+\xab\xff\xb9\xe3\xcc\x10]:\xd2`\xa4ξ\xfd\xa3m\xa4`ш\x11Zz\rh\x154\xb8\x85@r\x06\xb4\xf6\x80[\"\xe1\x12~v\x81@ۥ\x9bA\x1d\xa3\xe7\xd9t\xbaұ\x0f\xe5\xca5Mku\xdcNS@\xeaE\x1b]\u0a625\x99)\xebU\x81\xa1\xaau\xa4*\xb6\x81\xa6\xe8u\x91\x04\xb7)\x92\xcbF}\x17\xba\xb8\xe7\xab\x03I\xe3V\xdc\xc61h\xbb\xda-\xa7\x00{\xd2\xee\x12`\xa0\x19\xb0ۖ\xe5ߛW\x96\xc4*\x1f\xff<\u007f\x80\xfe\xd0䂡͓\xb5\xf7\xdbxox1\x94\xb6K\n\xd9q\xcb\xe0\x9ađ\xac\xf2Nۘ^*\xa3\xc9\x0e\x8d\xce\xed\xa2\xd1Q<\xfd\x8f\x968\x8a\u007fJ\xb8N\t\r\v\x82\xd6+\x8c\xa4J\xb8\xb5p\x8d\r\x99kd\xfa\xcd\xcd.\x16\xe6BL\xfa\xbc\xe1\x0f\xebА0[k\xb7\xdc\x17\x8aQ\x0f\x1d\xe5\xfe\xdcS%\xfe\x12\xa3\xc9>\xbd\xd4UJ\x01X\xba\x00xL^\x1e\xb0\x1dKMyrU\x98G\x17pE?\xb9\xea ɟ\x90\xe9\xdd؎^*\xa9m9M\xa9c\r\x9c)\x8fX\x02\x98~릦@iG \x8e\xba\x92@r\xac\xa3\v[a+\xfbI\x95G\xfbG\x8d.\x8fu\x8a\xce\xca\u007f\xe7\x14\x8d\x89+\x1b!֘c\xf2ޥ\xcc\b\xad\xb5\x92\x05\xce^,\x80w\xea\xec\xf9\x1dg\x84@K\nd%\xa3r\xf1\xf1.\x95\xa8\x88\xda\xf6\x99\x97K7Dwb\xbeE60)\x18:\xfa\x9c\xb3\xe1\xc9z<*\xe9\x8f\xf7\xb7}\r\xee\x8d\xd4\xc9\x1c\x8fO|\xb8\xf90\xcbRI\b\xadR%\x95.\xb7Ԃ9\x04l\xe4\xce)1\x99\xcc\xd1\xe6\xe0\x88\x0e\xaa\x1a\xedHa\x85\x04Z\x92u\x97\xad\xf4\xb2\xf2\xea\xa5\xd9z\f\x1b\xfag\x04>\x1c\x17\x86\xffS\x13\xbeH\xad\x84ڟU\xeb\xee \x9eϪ%\xf3C\xb0\x14)i\xa6\\ŢTE>\xf2ԭ)\xac5m\xa6\x1b\x17\x1e\xb5]\x15\x12\x88E\x8e\x04\x9e\xa6\x11`\xfa]\xfa\xf3\xab\xb4H\xc8\xfc2U\x12\xe9\xb7\xd0G\xce\xe1\xe9\x8b\xd5\xe9q\xe5\xa5]\xe9j\xde!\x9f㝒\x12\x9bZWu?$\xec\xab\xe7h\x8e4\xa8r\xc9E\xbb\xfd\xcd\xc3V\f\xd9\x06\x91g[tch\x81V\xc9\xff\xac9\xca\xfa\x8b-\xd7\xea\v\x92\xf4\x97ۛo\x13̭~qF\x8e\x02\xe2\x1c\x13\xde\xdd*1\xdfRS8\v\xa7>\x0eH{`7\x82$w4\x17#\xb9\x88\xab\x13\x00\x85J\xa5\x8b\x064\xf7g@\xd6\x19\x9d\a\xc2?\xe0\x8a\x01\x03\x01B\x83^\xfc\xf4H\xdb\"7i\x8fZz\xac\xb4\xd1\x0e\xaf,\b\xd0{\xa3G\xdai\u05ca;\xb8\xd8!o\x19kq\xc5\xe3\xfa\x8eX=\xef>k\xed<^\x8c\xc1\xe7\xee\xe8\x8cKv\x10:\xba=P=\x8d\xdf\x13\xe0\xfa\x84\xddd\n\x14tu(Z1>\xba\f(\x04\xd2\x0f\x16\xbcS\x83\xf7a\x9c\r>e}\x9e\x9d\xde\"Ɩ/\x9e\xdf\x12uo\xbd\\\x0fb\xc7#a\x85_3\xc1UN\xb0\xe3\xf0\x9a\xea\x9c\v\xafO\xe9ӅHPY\xac\xa8\x1b\x89\xc7.\x866\xc8\xfd\t\xa7C\x18\x1c0\xcb\xfbR\xdd\x15^\xa4\x12\xb4\x13ԹDmHA\u007fGv\xbc\xe7\x84\xe7!\x8f\x05-\xa5T\xb5\xde8T\xfdPԉ\xd6_\xf2<\xc84\x9c\xee\x1b\xae\xf8I\x8e-\x93JS\xf2\x88\xfa\xc7\xeda\xe9B\x83q\x06\n#\x15#\fmk\f.\f\xcd \x86\xf6\xf8㓩\xdf\x103\xaeΧ\xd7ϙ&χ\xdd\x06\xc0\x85k\xe3n@\x1c\xa4\xf8\x15w\xd1s\xf9t:2\x82\rC\x16\x050s\a\x1f\x8dI;\x0e\xd3z\u007f\x89\x9a\xe4Y\x90\xb8\xe5\xbf\xcdp\x00_#\x9f7νP\x8c%Ϯ\x06\x9d\xc9\x1eH\x13a\xdb\x1c\x9fP\xc0\x1dmN\xd6n\xed}p\xab@|\x1c\x1aE\x1f?'\xca\x16\xf0>\xc5\xf9\xc5\xfav\a\x9cW\xb9#\x82ڙ>=]D\x03\xb6m\x16\x14D\xef\xc56\x12\x0f\x8b\xf0\xe9̟\xa6\x88\xbd\xd1\x0ev\xf7W\b\x99O7\x14Uh\xd3-\x9b\xe4Lt\xa04{\x83\xa7SQ\xafBB\x12\x922\x92\xd2\xfbh\xed\xd3\xd4SH\x9f^rK\x91\xa4\xb9qv\x14\xe3\xf6\xf9\xa9m\xfc\xe3\x0fO\"\x0em#\xad\x06E\xbd\xfb*\x06|'\xfc\xff\u05fc\x9fl\xacl\xd1s\xed\xe2\xed\xcdYo\xcfwd}\x94\xefAK\xaa]\xe9ޯ#\xea]>li\xf9\xc9apq\xeaq\xc4\x10/k\x1e\xf3\x01\xe93}#\xf1%U\u009c<\x06\x8c\xa7\x81\x99\ue0ef\x8f\u007fey\r\xacӵ\x98`\x9f\f\x86\xf2\xa8\xcb\xd2N\x04ڹ\x90c\xf5\x94\xe3\xa0\x11\f\n\xffP\xf4oQ\xf3G\xe2\xe1hi\xff\x93ӛ\xfd[\x8aˢ\xfb\x89)}\xe8\xd4R\a\x87w\xb7\xaa\xdd\xca\x1e\x86`%\x90\x9d\xd4\xdd\xf1\x8fL\xaf\xf2UI\xff\xabQz\xad\x9c\xcdh\x96g\xf0\xf9\xcb\x04\xba\xbb\xd6O\xbd\x1c\xb2\xf8\x9f\x00\x00\x00\xff\xff\x80\xb6\xf7)\x9e\x1b\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y_s۸\x11\u007fק\xd8\xf1=\xb87\x13RMz\xd3\xe9\xe8\xed\xcen:n\xef\x1cO\xe4\xcbK&\x0f\x10\xb1\x14Q\x93\x00\x8b]HV;\xfd\xee\x9d\x05HI\x94(Y\xce\xf4rzI\b,\x16\xbf\xfd\xed\x1f,\xe0I\x96e\x13՚O\xe8\xc98;\x03\xd5\x1a|f\xb4\xf2E\xf9\xd3_(7n\xbaz\xbb@Vo'O\xc6\xea\x19\xdc\x04b\xd7|Dr\xc1\x17x\x8b\xa5\xb1\x86\x8d\xb3\x93\x06Yi\xc5j6\x01P\xd6:V2L\xf2\tP8\xcb\xde\xd55\xfal\x896\u007f\n\v\\\x04Sk\xf4q\x87~\xff\xd5\x1f\xf3w\xf9\x0f\x13\x80\xc2c\\\xfeh\x1a$VM;\x03\x1b\xeaz\x02`U\x833h\x9d^\xb9:4\xe8\x91\xd8y\xa4|\x855z\x97\x1b7\xa1\x16\v\xd9u\xe9]hg\xb0\x9bH\x8b;Dɚ\a\xa7?E=\x1f\x93\x9e8U\x1b\xe2\u007f\x8cN\xffl\x88\xa3H[\a\xaf\xea\x11\x1cq\x96\x8c]\x86Z\xf9\xe3\xf9\t@\xeb\x91Я\xf0W\xfbd\xddھ7Xk\x9aA\xa9j\x92i*\\\x8b3\xb8\x17\xa4\xad*PO\x00V\xaa6:\U00091c3b\x16\xed\x8f\x0fw\x9f\xfe4/*lT\x1a\x14ͮEϦ7Q~{\xdeݎ\x01h\xa4\u009b6j\x84kQ\x95d@\x8b?\x91\x80+\x84\xce+\xa8\x81\xe26\xe0J\xe0\xca\x10x\x8c6\xd8\xe4\xe1=\xb5 \"ʂ[\xfc\x13\v\xcea.vz\x02\xaa\\\xa8\xb5\x04\xc1\n=\x83\xc7\xc2-\xad\xf9\xf7V3\x01\xbb\xb8e\xad\x18;\x86\xfb\x9f\xb1\x8cުZH\b\xf8\x06\x94\xd5Ш\rx\x94= \xd8=mQ\x84r\xf8\xc5y\x04cK7\x83\x8a\xb9\xa5\xd9t\xba4\xdc\xc7s\xe1\x9a&XÛi\x8cJ\xb3\b\xec\xdc\xf1,!\xf2+\xa5\xca?(\xae^\xdc\xf5\xfa\xaeL\xdbĊ\xc0\x0e\x14\xb4\x06\v\x1c\x94V0\x96\x18\x95N\x83#*\x01$q\xbf\xfb2\xc6\x19\xc0{\xe7\x01\x9fU\xd3\xd6\xf8\x06Lby[\xd0\xfa\xf80\x94\x88\xd8ꃵ\xe1ʌ\x1b\xae$\x8e:\x83\xd7\xd1PVO\b\xae34 \xd4\xe6\tgp%\x19\xbc\a\xf1?\x92:\xff\xbd\x1a\xd5\xf9\x87\x94\"W\"r\x95\x80mϬ\xfd\x8c\xdb\x01\xe4J1\xb07\xcb%z\x1cg3\x16b)p߃\xf3b\xbbu{\n\xa2Z\xf1Y\xaa3\xa8\x8f\x00\u007f~\xf7\xe5\x04\xda!O`\xac\xc6gx\a\xc6&VZ\xa7\xbf\xcf\xe11F\xc4Ʋz\x96}\x8a\xca\x11Zp\xb6ތ\xa3uP\xa9\x15\x02\xb9\x06a\x8du\x9d\xa5^A\xc3Zm\xc4\xfe\xde]\x12a\nZ\xe5y\xd8\r\x8cj}\xfcp\xfba\x96PI\b-c\x1d\x93S\xa64r\xe6\xcba\x9fN.\x89\xc9HGH\xc1\xc1\x0e\x8aJّ\xb2\x06\xb1i\x88\xec\x96AΒ\xfc\xfa\xb5\xd9zxl\xf7\xbf\x91\xe3\xfb\xb00\xfcN\x87\xe0Ef\xc5\xd6\xf9E\xb3\xee\xf7\xe2\xf9\xacY\xd2\xc4{\x8b\x8c\xd12\xed\n\x12\xa3\nl\x99\xa6n\x85~ep=];\xffd\xec2\x93@\xccR$\xd04\xb6\xe1\xd3\xef\xe2?_eE\xec\x8c/3%\x8a~\v{d\x1f\x9a\xbeڜ\xbe\xaf\xbb\xf4T\xba\x9ew\x8d\xc7\xe1JI\x89ue\x8a\xaao\xd2w\xd5s4G\x1a\xa5S\xc9Uv\U000db1ed\x10\x19\xbc\xe0\xd9d\xdd]0SV\xcb\xff\xc9\x10\xcb\xf8\xab\x99\v\xe6\x82$\xfd\xf5\xee\xf6\xdb\x04s0\xaf\xce\xc8ц4\xc5D\xeb\xee\xb4\xd0W\x1a\xf4g\xbb\xa9\x8f\x03Ѿ\v\x1c\xe9\xe3\xb62\x177rdUK\x95\xe3\xbb۳\b\xe6[\xb1~\xf7\x1d\xe5]\xfb\xd6k\x92\x10=ӷ\x9dD\x92ԜE\x91\xfa\xee\xb1.\xb8Ð:\x868\"\x1d\xe8W!\x91됴9\xfbH\xb2\xf1\x0e~ \xd1:=\xf8\x1e\xfaw0\xb5#}0\x9c\x8cx\xf12Ê\x03]~\x9d\x89\xe2=g)?\xb9S\x12\xcf\uebfa\xd0\x14N\x9a\xb9\xe1\xe3\xcd9\xcf\xdd\x1c\xcb\xc7\x17\x02\xaf\x13.6\r\xc6\xdbBD\x00kE\xfd\x16\xc7~\x83=mia\xac\x84\xa2\ful\xb6\xa4\x0f,\x95\xa9Q\xc3\xf6\xe9\b\x1e\xe5>\x17\xaf\xcc\xd7ǵ\xb2W\x13\bu\xbc\xe7\x8d\x00>\\U:\xdf(\x9e\x81\\\x933Qp0oC]\xabE\x8d3`\x1f\x0e'O\xa6A\x83Djy>\x0f~I2\xe9\x86\xd5-\x00\xb5p\x81\xb7W\xac.!:\xf3\xaf\xa9\xf3\xf8\xe5\x17\xbcJ\xd1y\x10\x0f\"1\x16Wۤ<\x17X\x10o/\xa19\xdc\"\x83{\\\x1f\x8d\xdd\xd9\a\xef\x96\x1e\xe9\xd0\aY﨣\xf6;\x83\xf71\x02.6\xb8\xdb\xe0\xbc͝\x10T\xae\xee#ױ\xaa\xc1\x86f\x81^\f_l\x18\xa9g\xa0O\xf4\xe3\x1bj\xecyw\xbc\xed\xd6\xf7\xd5*)\xea:\xf8B\xd9\xf8$#\xd1\xc9\x0e\xb4\xa1\xb6V\xc7-|oC<\xf6$8%Cvq\xd1g\x97\xa4t\x9c{͝:¹uv\xb4#\xebS\xc1X\xfe\xf3\x0f'\xcfGc\x19\x97\x83R\xd8\xcd\n\x85?\x89\xfe\xff\xb7\ue4c7/\xb1\xf2|Y\xe9\x9a\x0fD_\xaaZQ\xf1X\xcd\xda/?\xc7\xe5f\xb8ɷ\xa84#\xd4\x1c\f\xed\x1e\xec\xdf\uefa2\x8b\xb2\xee\x81>N@2K\xefm\xde=Fu#\xbb\x03K\x15\xd2k\xa1\xbe?|\xa1\xbf\xba\x1a<\xb8\xc7\xcf\xc2Ym\xd2_\x17\xe0\xf3\x97\ttOT\x9fz\x1c2\xf8\xbf\x00\x00\x00\xff\xff6\x10(\x86\xdc\x18\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4W\xcdn\xe36\x10\xbe\xfb)\x06\xdb\xc3^*\xb9i{(tk\xd3.\x104\t\x16\xce6\x97\xa2\a\x8a\x1a\xd9\xd3P$\xcb\x19:u\x9f\xbe %ٲ-{\x83\x05V7\x0e\x873\xdf|\xf3CjQ\x14\xc5Byz\xc6\xc0\xe4l\x05\xca\x13\xfe+hӊ˗\x9f\xb8$\xb7\xdc\xde\xd4(\xeaf\xf1B\xb6\xa9\xe06\xb2\xb8n\x85\xecb\xd0\xf8+\xb6dI\xc8\xd9E\x87\xa2\x1a%\xaaZ\x00(k\x9d\xa8$\xe6\xb4\x04\xd0\xceJp\xc6`(\xd6h˗Xc\x1d\xc94\x18\xb2\x87\xd1\xff\xf6\xbb\xf2\xfb\xf2\xc7\x05\x80\x0e\x98\x8f\u007f\xa2\x0eYT\xe7+\xb0ј\x05\x80U\x1dV\x10\x90\x85t@\xef\x98\xc4\x05B.\xb7h0\xb8\x92܂=\xea\xe4v\x1d\\\xf4\x15\x1c6\xfa\xd3\x03\xa4>\x9cU6\xb4\x1a\r\xed\xf2\x96!\x96\xdfg\xb7\xef\x89%\xabx\x13\x832s@\xf26\x93]G\xa3\u0099Br\xe0\x032\x86-\xfea_\xac{\xb5\x1f\bM\xc3\x15\xb4\xca0.\x00X;\x8f\x15<&\xa8^il\x16\x00[e\xa8Ɍ\xf4\xe0\x9dG\xfb\xf3ǻ\xe7\x1f\x9e\xf4\x06;\xd5\v\x93e\xe71\b\x8d1\xa6o\x92߽\f\xa0Aց|\xb6\b\uf4e9^\a\x9a\x94Qd\x90\r\u0090\x17l\x80\xb3\x1bp-Ȇ\x18\x02\xe6\x18l\x9f\xe3\x89YH*ʂ\xab\xffF-%<\xa58\x03\x03o\\4M*\x83-\x06\x81\x80ڭ-\xfd\xb7\xb7\xcc .\xbb4Jp\xa0x\xfc\xc8\n\x06\xabL\"!ⷠl\x03\x9d\xdaA\xc0\xe4\x03\xa2\x9dX\xcb*\\\u0083\v\bd[W\xc1F\xc4s\xb5\\\xaeIƊ֮\xeb\xa2%\xd9-s]R\x1d\xc5\x05^6\xb8E\xb3dZ\x17*\xe8\r\tj\x89\x01\x97\xcaS\x91\x81\xdb\\\xd0e\xd7|\x13\x86\xf2\xe7\xf7\x13\xa4\xb2Kic\td\xd7{q\xae\xb2\x8b\xbc\xa7\"\x03bPñ\x1e\xff\x81\xde$J\xac\xac~{\xfa\x04\xa3Ӝ\x82c\xce3ۇc| >\x11E\xb6\xc5\xd0'\xae\r\xae\xcb\x16\xd16ޑ\x95\xbcІ\xd0\x1e\x93α\xeeHR\xa6\xff\x89Ȓ\xf2S\xc2m\xeek\xa8\x11\xa2o\x94`S\u009d\x85[ա\xb9U\x8c_\x9d\xf6\xc40\x17\x89\xd2\xcf\x13?\x1dGNJ=[{\xf18-f3t\xda\xffO\x1euJXb-\x1d\xa4\x96t\xee\x01h]\x00u\xa6_N\f\xcf5g\xfaj\xa5_\xa2\u007f\x12\x17\xd4\x1a\uf75e\xb4\xf9\x05T\xbf̝\x18a\xa5\x11\xd77*\xce+\x9eX\x06\x90\x8d\x92I\x87\x8a\"\xbbo\xf3\x998.R\x9eiW\xa9]\xad\xb2\x1a?\xe4ڱzw5\x96\x87\x99\x03)\x94\x8d{\x05\xd7\nک\xc9\x11e\x8dgA\x84h\xdf\f\xb2\x9f\xc9wM*\xad\x960\\\x05\xb8:Q\x1eyn\xa31\x83\xa5B\xbb\xce+\xa1\xda\xe0\xd8ȭ\vg\x10\xa9\xb7\xb1\xeb\xbb\xfa\xcb\xf8\xdd:\x13;\xdc\xdf\rW\x91?\x1f\xebN\v\xa4\x17\f R\b\x10\x8e\xaf\xc0\xe97\xd4\x04\x83w\xcd\x00`(ZNq\xbe\x11{J.\x05<\x9a\x86\xc5|\xf1\x1fi\xccUԑ\xc2i6\x8f6O\xf8\xfa\xec0\x10%\x91\xdf>\x0e\xb2\xfaH\xac\x8e!\xa0\x95\xc1H\xbe\t\xbfh \x18\xc52i\x8b\xf4\x06\xba\x9a\xe7\xfbs\xfd\x11R2\x05\x92\x04\xd3.zU<\xd7/\xad\v\x9d\x92\n\xd2h/ҡ\x93\xfd\xf4\x02S\xb5\xc1\n$\xc4\xd3\xcd\xcb\x13\x01\x99\xd5\xfaz\x04\x0f\xbdN\u007f\x15\x0e\a@\xd5.\xca\x05b\xf3\xa5x\x85ګ\x88\xfcF\xf1u<\x1f\x93\xc6\\Z\xf1\xad\xce\xd1\xc6\xee\xd4E\x01\x8f\xf8z&[\xa1jN{\xae\x80G's\x1b\x17b\x9a\xa9\xe5\x13\xd1\xe1\x89}sX\xe5\xba+\x86'u\xde\x00\xc8/\xd3f\x92b\xee{s\x90\x1c\x1aDi\x8d^\xb0y<}R\xbf{w\xf4B\xceK\xedlC\xfd\xff\x00\xfc\xf9ע\xb7\x8a\xcd\xf3\x88#\t\xff\x0f\x00\x00\xff\xfft\x8f\x1aC\x8e\f\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xd4YMo\xf3\xb8\xf1\xbf\xfbS\f\xf2?<\xbb@\xac\xfc\x9f\xb6\x87·4O\x16\bv7\r\x92\xa7\xe9\xa1聖\xc66\x1b\x8a\xd4\xf2ʼn[\xf4\xbb\x173$\xf5bɲ\x17\xedb[]\fS\xc3\xe1̏\xf3\xae\xc5r\xb9\\\x88F\xbe\xa2u\xd2\xe8\x15\x88F\xe2\x87GM\xff\\\xf1\xf6{WHs\xb3\xff\xbcF/>/ޤ\xaeVp\x17\x9c7\xf53:\x13l\x89_p#\xb5\xf4\xd2\xe8E\x8d^T\u008b\xd5\x02@hm\xbc\xa0eG\u007f\x01J\xa3\xbd5J\xa1]nQ\x17oa\x8d\xeb U\x85\x96O\xc8\xe7\xef\xff\xbf\xf8M\xf1\xbb\x05@i\x91\xb7\u007f\x955:/\xeaf\x05:(\xb5\x00Т\xc6\x15Xt\xdeXt\xc5\x1e\x15ZSH\xb3p\r\x96t\xd8֚Ь\xa0{\x11\xf7$A\xa2\x12\xcfq;\xaf(\xe9\xfc\xf7\xfd\xd5\x1f\xa4\xf3\xfc\xa6Q\xc1\n\xd5\x1dƋN\xeamP¶\xcb\v\x80ƢC\xbb\xc7?\xe97m\xde\xf5w\x12U\xe5V\xb0\x11\xca\xd1kW\x9a\x06W\xf0HR4\xa2\xc4j\x01\xb0\x17JV\xacb\x94\xcb4\xa8o\x9f\x1e^\u007f\xfbR\xee\xb0\x16q\x11\xa0BWZ\xd90]\x96\x0f\xa4\x03\x01\xaf\xac\x1f\t\xc1\x17\x01~')\x0eX7\xfep\rB\xa9\xbe\x03\n\xdb\x01\xf8\xeb\x1aԅ\x8e\xf5pL}ƱN#46\x8e>F\x9d\xa5%\xba\x14Y\xff\v\x00Sb\x8d\xea\x05\x15\x96\xde\xd8Y\xb0~\xe8SF\xa0(/\xee?\x17\xc37\xde\xc0F*\x8f\x16ޥߍ\x14xߡN8QE\"u%\xf7\xb2\nB\r\xac\xac\x87R\a&\x18\vZ\xaa\xeb\x11O\xc28\xef\x1e`\n\u007fd\xe1\x85\xfaY>x*\xc5\xd2S\v_\xee\xee?\xa88r]\xf73\x03\xdb\xf1\x86\x88\\N_\f?\xb8\x8c\x1dUH\xd2b\xcdu\xd7\x04g\x80\xafle\x1d\x15\xeb{\xfb\xf8el@pڈFB\xde\xce\b\x92|\xa2\xbd^\xca.9\x11Or\x86T9_\x83\x807<\xc4\"\x9b\xea\xf8\x86Bifa\x91\xcbs\xbe\xe87<0Q\xaa\xb8'\xb9\xce]J|\xde\xf0p\xeaՑ\xbat^\xaa~\xa2\u07b4\xc0Rq=\x96U\xe5\xee\nO)I\x8f7\xd3\xc2¼\xa7\xe6'#r\xa1\xd8-\x80\xbd.\x90!\xfe\xe4\"\x9cd_;\x19\x1b\xbc\x19\xa9\x1d\xb2\xed\xe5\xfe\xe6\x95:Ֆy\xb4\xa8\a}\r\x8f\xc6\xd3\xcf\xfd\x87\xa4\x9a]\xe8q\x06\xed\x9e/\x06ݣ\xf1L\xfboA\x12\x85\xba\x10\x90H\xcc\x06\xaacl#\xbd\xfa\xed\x8f\xe3\xe8A\xb7\x9a\xf5\x9bQB:jA\x8c͚s\xdb\x1a\x8f\x88\xcc\xeb\xe0\xb8c\xd1F/9\"e\xee3L\xdbK\x93.Ci\xec\x00\xaf\x13\a\xcd\xf0\\#\xa4\xe3\xbfR#\x16\xf7\xc4VZ\x89\x12+\xa8\x02C\xc0\xad\xa0\xf0\xb8\x95%\xd4h\xb7sr6\x14\xa7N_\xddL$\x89\xcf\x05w{:\v\xe5'\x85\x9dj\xfa\xa0%\xd9\xfa\x897\xb3\xd7;٫]&\x15\x87oNp\x93ڋ\xaa\x921\xc3<\x9d\x89Og\xf0\x19\xe7\x8cxhJ\xb4\xa2!\xcb\xfe\a\x85S6\x94\u007fB#\xa4u\x05\xdc\xf2\xbcJM\xdfl\x9f>U\x1e}\xd6\xc4U: \xcc\xf7BQ\xa8\xa7\xc0\xa1\x01\x15\a\xfeI\x96f3\xcah\xd7\xf0\xbe3.F\xf1\x8dD\xc5S\x88\xab7<\\]\x0f<\x0f\xe4t(\xbdz\xd0W1I\x8c\xfc\xa0m\xf8\x8cV\a\xb8\xe2wW\xc5(\tN\xb2\x9dM\x8c3\x16q\xf2U[\xe9\xfe(\x9aF\xea\xed\xf1=_f\v3v0\xb0\x81ǣ\xd3\x06\x86\xd0/K\a%\xfc\xf8\xb88\xe6\x9b(\xf6m\x1e6jo\n\xb8Շ\x11WG\x1d\xe3D\xa9;\xec I\xa4w\xa9\x14E\xa5ijb\xa6}Fi\xb0\xe0D\x1d\xf9O\x0f\r&@O\x1c\x9f^\xe7+\xf9\xe7\x96l\xa2\x0f\xec)K\x95b\xab\xc0\xd3\xeb\xd8r\xb8\xf8tZ4ng<|\xb3\x97\"M\xb5L\xa8\x1ak\xf6\xd4\x0f~\xfb\x1f\xea\xe8\\\xb9\xc3*(<;\xb4y\xe9\x11\x9e\x1f\xdbd\xb6c[\xe8ph;\xb9\x8cV\x15=p8\x1eJ-L\xe2K\x97<\xd5D\xb7\f\xa39\x18\xc73Y\n'.\x94%:\xb7\t*w<<\xf6\xa7\xf6:\x92K\xd7J{\xe1\x18i*C,{ï\xb3\x13:/|pggtL\x05\xa5h|\xb0\xa9H-\x83\xb5\xacT|g6\xa31\xdd\x05S:\xb4\xd6\xd83\xd3\x1e&\x89\xee^\x9a\xa0\xb9V#\xbb\xe5\xbdP\xa3sb\x9b\xc7<\xefh\x11\xb6\xa8)\xc2N\x8c8R\x1d\x80\x1fX\x86\xf4\x91`اR$\x15\xa5\xa7\xf6+\x8a\xc6a\xb3u\xe2S9\x93\b\xc4\xf6ĝI\xedq\x8b\xc3L\xbc\x11R\x05\x8b\xcf(\xdc\xf0\xdb\xc1H\xfd\xef\xfa\x94\xa9\xb4\x8b\x9a\xc7\xceC\x04\x87U\x1a;{i\xf1\xe4l\x92\xfaP!G\x9d\xdf\xc9\xf8\xdb섛w\xc3'\xa2\xc8\xfe\xd77\x87\xd6\x03\x9f'eA\x1d\xeac\xc6Kx\xc4\xf7\xd1\x1a)\x8f\xd5k\xfb\xe9hD𠟬\xd9R\xe6\x1d\xbd\xba3u\xa3pl\x05Kx\x12\xd6K\xa1\xd4!\xb2?q\xea\xa58u\x1f\xb6\xee\xcf\x1b\xf3\xeb\x11\xf1р\x85̺\xe3\x97M\xf0\x1b9\x1e\xad\xa5/]k\x85\xdf\xfe:\x83\x92wa\xb5\xd4\xdbyu\xff\x9c\x88&\xbc7\xed\xff\xe5\xfc7\v8\xf4\xe0\x13\xb3\xbc\x9f\xeb\xc1\x13\xb1\xf4h\xa9\xfb\xc4\xfb\xb9\xfb\xc7h-\xd3']~Aݧ\xddc\xd5\xc3>\x89\x92V\xba\x00-\xca\x12\x1b\x9f&\x98\xfd\x8f\xbbWW\xfc'\u007f\xbd忥ѱ\xfar+\xf8\xcb_\x17\x90\x10x\xcdr\xd0\xe2\xbf\x02\x00\x00\xff\xffF\x9c\x18\xb7\x0e\x1f\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\x1cMo,9\xf1\x9e_Q\n\x87\aRf\xc2\x03\x0ehno\xf3\xb2\"\xe2\xf1\xf6i\x13rA\x1c<\xdd5\x19\x93n\xbb\xb1ݓ\f\x88\xff\x8e\xaa\xec\xfe\x9c\xfepg\x13\xb4\xac\xe2S\xe2\xb1\xcb\xe5\xfav\xb9\xdcg\xab\xd5\xeaL\x14\xf2\x1e\x8d\x95Zm@\x14\x12\x9f\x1d*\xfaϮ\x1f\xffh\xd7R_\x1e>nщ\x8fg\x8fR\xa5\x1b\xb8*\xad\xd3\xf9\x8fhui\x12\xfc\x8c;\xa9\xa4\x93Z\x9d\xe5\xe8D*\x9c\u061c\x01\b\xa5\xb4\x13\xd4m\xe9_\x80D+gt\x96\xa1Y=\xa0Z?\x96[ܖ2K\xd1\xf0\n\xd5\xfa\x87߮\u007f\xb7\xfe\xc3\x19@b\x90\xa7\xdf\xc9\x1c\xad\x13y\xb1\x01Uf\xd9\x19\x80\x129n\xc0&{L\xcb\f\xed\xfa\x80\x19\x1a\xbd\x96\xfa\xcc\x16\x98\xd0j\x0fF\x97\xc5\x06\x9a\x1f\xfc\xa4\x80\x89\xdf\xc5m\x98\xcf]\x99\xb4\xeeϝ\xee/\xd2:\xfe\xa9\xc8J#\xb2\xd6z\xdck\xa5z(3a\x9a\xfe3\x80\u00a0Es\xc0\xbf\xaaG\xa5\x9f\xd4\xf7\x12\xb3\xd4n`'2K?\xdbD\x17\xb8\x81\xaf\x84I!\x12L\xcf\x00\x0e\"\x93)\xef\xd3\xe3\xa6\vT\x9f\xbe\xdd\xdc\xff\x9e\xf0ȅ\xef\x04H\xd1&F\x16<\xaeF\x11\xa4\x05\x01\xf7\xbcI0\x81\x1d\xe0\xf6\u0081A\xc6E9\x1aQ\x18\\UX\xa6\xa0M\x80\tP\xa0\x91:\x95\t|'\x92Dz\xf0S\xed^\x97Y\n[\x04S\xaau\x18[\x18]\xa0q\xb2\"!\xb5\x96\xd4\xd4}=L?\xd0V\xfc\x18HINЂ\xdb#\x04nc\xca\xd4\xcb\x05\xe8\x1d\xb8\xbd\xb4\r\xdeL\x92\x16X\xa0!B\x81\xde\xfe\x03\x13\xb7\x86[\xa2\xb3\xb1\x15\xb6\x89V\a4\xb4\xefD?(\xf9\xaf\x1a\xb2\x05\xa7y\xc9L8\f\x1c\xad\x9aT\x0e\x8d\x12\x191\xa1\xc4\v\x10*\x85\\\x1c\xc1 \xad\x01\xa5jA\xe3!v\r\u007f\xd1\x06A\xaa\x9d\xde\xc0\u07b9\xc2n./\x1f\xa4\xab\xf4$\xd1y^*鎗,\xedr[:m\xece\x8a\a\xcc.\xad|X\t\x93\xec\xa5\xc3ĕ\x06/E!W\x8c\xb8b5Y\xe7\xe9\xaf*.\xda\x0f-Lݑ\xc4\xc6:#\xd5C\xdd\xcdBh\xcb\xc4\x163\xb0\x98a\xe2\xb4\x19#\xcb<\xd3}\x8b\xb3\x85#\xf4\x1c\xb0\x8a\x8dݧ-7\x1b\x9c\x04\nd\xf2\x9f\xf62\xd9\xfb\xf8\x8ad\x8a!A\xaaѲ-\x10E\x91\x1d\xc77\v\xf3\x92\x10\x16\x9a6\aM\x9b5\f}\x98\xc3&\xa2iQ\xf6\xb4i3\x96\xb5K\xe7ZD\xde\xc9\\9\x8d\x17\t\xf4\xcd\xc9\xe4\xd7\x16h\"\xb0\xa4\xb3\xd5\xcd\x0e0/\xdc\xf1\x02\xa4\xabz\xe7aR\x18\xd4\xe0\xf0\x8b`\xd4K\xf4\xe1\xa6?\xf7\x95\xf5\xe1\x15\xb8T\xa3\xf0\u007f\xcd$v6\xb7\xc1\xd7,`З\xf6\xbc\v\x90\xbb\x9aA\xe9\x05\xecd\xe6\x90\xe3\x9ei\x14[\xaeo\x96S\xafE\x968\xafI-\x17.\xd9_\xd7G\xc8\xd9\xf1=\n\xf5\xa7\xfbX\xb9:It\x9d\xfc,d\xe0\f\x834\x98\xfb\xbc\xc5\x1d\xeb@\xd3Ñڧ\xaf\x9f1\x9d&\x14\xc4J\xe4\xc9v>\xf5Pn/\x1f\x8e\x01\xf1\x9b\t\x01U}\xc2\xf2\xf9\xa8\v\x10\xf0\x88G\x1f\x05\t\x05\xc4(AK\x8d\x1e$N\x89ĉ16\x11\x8fxd@!\xd7\x151?^4|{\xc4c\xdc\xc0\x1e)\t\xb3\x90\t\xf04\xa5\x0e&\b\xa7L\x96\x90\x118s\xc9\x1a\x02N\xc7l\x12\x96\x98\x9b\xaaU\x9cx\xd1vk6vҸ\x8fx\xfc`=\xc3H;\xf6\xb2\x88\xde0\x19`\xb0\xc8zTe2\xefE&\xd3z)\xaf\x0f7j*\xea\uedaf\xdaݨ\v\xb8~\x96\x96\xd0S)|\xd6h\xbfj\xc7=oFX\x8f\xfe\x8b\xc8꧲\xea)o\xe6\x89\x1e\xed\x04i\x94\xd0\xfbv\xe3O\x985\xab\xa4\x85\x1bEg\xa5@\x17Ns3\xccx\xb1d\x94\xf2\xd2r&Ti\xb5bG\xbb\x1eX+\x1af`\x8f6\x1d\xee\xb4\xd1k-\x1b\r\x95\x0et\x1e\xb5;\xf2=\x1e\x82O\xdfg\"\xc1\x14Ғ\x89*\xa2!Zg\x84\xc3\a\x99@\x8e\xe6\x01\xa1 _\x10ˍh\xfb\xec\xdbb\x99\x8b\r\r\xaa\x16\f}\x1a\x83Ҋ\xf4:j\\\xc5\xfe\x88\xc1\x83\xf9\xe8\xe9\xc11{c\a\xcdqL\x04\xb5E\x9ar\xc2Vd\xdf\x16y\x89E\xdc9\x8d\x1f#\xda\tx\x97\xe5\xdb\xc0\xcbUȳ\x81\xd894`\x9d6\xd5U\x1f\x19\xc9^ژ\xb8h\xe7\x0e\x1c\xc4\xd8:{\xe7\xc1\xd2a\xee\xbc\xd1oo\u007f\xcf\xfd\x1d \xfd=\a1\xe1\x00\x86!\x17F'h\xed\x9c\xd8DY\xf8\x99\xc4f\x9d\xd4\x14\xfe\xb0\xc4wk\xb3\xc2:\x97l\xadڒP\x98ȹ\xf88q\xfd\xdc\xca˒\xf9\xa0\xff\xe7Ev9v\xc0Z\x9f\xe7BE9\xb0\x13D\xaf\xfc\xdcJ\xc5\x02(\u007fD1\x0f%\x9b\x8b%\x91k\x10\xbe\x9fO0\x90KuË\xc0\xc77\t\x1fj\xa3\x8b/;>\\U\xb3\x1b\x16\xd4\x1d×\xa4c\xad\xd0|_a\xb0\xc3\xc9Ӭ\xfe\xa2\xb0Yi\xd7N}\x10\xe4B\xa7\x1f,줱\xaeA6\x1a\xa6\xb4|I\xfavg9um\xcc\v\x8fr?\xf8\xb9\xadd\xdc^?\xd5\x17\xfa\xe3\x17\xbfC\x8d\xaf\xc7\x10\xe4\x0e\xa4\x03T\x89.\x15'\x8d\xc8\x18\xf0\"\x9e\x1d\xf1\x82\f\xb1~\xafi\xa8\xca<\x96\x10+\x96D\xa9f\xf2K\xed\t\xdf\v\x99\xbd\x15\x1b\x9d\xccQ\x973\x8e\xb9i\xddz\x04?\xb7S\xa9\x91\x8bg\x99\x979\x88\x9c\x18\x11Mr\x8a6d\x8e]\x19\x80'!\x1d{$\x82\xcc\xee\xc9\xe9h\x90\x89\u038b\f\x1d\xc2\x16wڰ\xbe[\x99b\xed\xfa\x83\\\xe8\xf1{\xc7~\x13\xb0\x132+M\xb4\xd5]ȍe'\xa4`x^\xf7\xe0\x13\x8b\u008a\xc9\x17\x95\x8e\x8e\x0ei\xe7G\u007f\x8e\xc0&\xaa\x84`\x1a\xd9\xc9UB5\xccUVZ\x87&\xbe\xe6\xf6fx\xde@\xfdu⇬\xf8aΰl4\xe5\x16\x8d˪\x8bpI\xd9*E\xf1\xf5\xfa\xb3\xd1qdq\xedX\x9dv\\)\xd7\\\x01W\xb7\x06\xb9.\x9e\xaa\x8a\x90\x87\xadFX:p˿\xf8hW\x03u\xeb\xb082\xaf\xb0\xfd\x19\xd5'G\x95X\xcd\x14VM\x97p\xebj\x8d\x11*v\x12\xf0]\xf2\x99N\x19\xf1ϐz\xb3\xb5O\xe3\x15O\xe1F\x0e\x9d8|\\w\u007fq:\xd4?\xc1\x93t\xfb\xc1M\xf1\x1b\x14:.\xaa\x87vat%\x8b\xe1\x8dW\x9f\xaa\xa0\r(\x99\r\xd74\x10\xc1\xab\xf9\x1dr\xc3\x0f\x85?\x94\xbeH\u007f\xe7\x8eI\xb15R/\xae\x8c\xea\xd6=\x8d\x1a\xf8\xe5\x17xK\xca\xca\xe3k\x9f\xe6J\x95\x96T<\xb5\xab\x99&@\xc6\xd69ŝxgk\x9a^P\xc9\x14]W\xf9*וq\xb5JoQ\xa1\xb4\xa0.\xa9[o4\x03wY5R$\x99b*\x8f\x16\xd7\x1b\x85ڞ\x99\xfdDT\x19\x8dV\x0f̀\x1e\xa8c\x9a\xaf\x19\x9a#\u007f\a\x95W\xa9\x14zA}\xd0\xeb\xd6\x11\xbfV\xd4=U\xed\x13Q\xe3\x13\x11\x97\xcfa\x1aQų\xacv'\x82\x86/\xacө\xabpF\xd7^Z\x9dӭ\xbd\x19\x05\x1bS\x933Rq3\ns\xb2\x12'\xb6\xcef\x14\xfa\xac\xfb\x9e\x91\x9cɟ\xad\x12\x85\xdd\xeb\xeaI\xebl\xe4w\xdb\x1d?p\xf4\xaa\x1e\xb4&\x99.\xd3\x1a\xfe\xf0\xf6\xf8\xe5\xff\x11\xbeݳs\xe1\x87~I\xf3\x042\xb8\x8f*\x94뿐\x1c~\x9d\f?\xfd(f\x9d6\xe2\x01\xbf\xe8\xa4\xf5\xf9\x86)\x9at\xc7w^\xe7\a\xe6WɖP\x954\x1c\xb6\x86\x1d\xf5\xc159\xd6\xf0B\xb89\xaf\x12\xa6\xc3r1\xa9\xb9\xcee\xb3\x9b\xba\xbb\xfb\xe27\xe2d\x8e\xebϥ?\x06\xaf\na,\x12m\xab\r\xfaI\xdb1\x03\xb1\xd7O\x90\xe9\xb0\xfb\xef\xfa\xf8\x1b\xe4l.\x9f\xb7\x17\xef¿\x8e\xae\x04\xb2\"\u05fc\b\xdf\x0f\xcfkE\xde-\xa6\xf1\xd1oLv\xc7 \tku\"\x05\xbf\t\x95.<\xe3x\x8b\a\xbd\xe3\x0eaD\xe9\x87\xfc\xd8j\xe8\x01\xfd\xaa~\xcd\u007f6\x03\xd4:\xe1J;\xff9\x04\x1e\x06\x89(\\iB\x924)\r?\xed%\x10\xe8_\xc0.\xff B&\xac\xf3\x825\xf9݁/\xf5\xb0&J\xb7\xceg[+̓'a\xf9\x8b\x03>\xd7*m\xf7S2\xed6\xf2́\x9d6\xb9p\x1bH\x85\xc3\x15\xc1\xee\xfd>i\x99F\x99\xcdO\x9f'w\xf7\x8dF\xd47:\x81\xac<\xadz0=\xb2\x93\xa1\x94\xfd\n\xbe\xe2\xd3Iߵ\"\xc4\xfb\xb94\x9f\x95\xc7\xf4\xbe\xfe\xdaM즚\xef\xe3\xf0%ȉ\x02tu\xb67\xb8\x97\xa9\xa1\x13\u007f\x03\xcf_xX\xf8\xb5<5\xb4|\xfcJh'\xbf\xe9\xfd6\xa2\x85\x13\x1a8\xac}\x03J\xd2\xebj\xbe\x88\xf4\xb1\xf9\x8f\x97^\x85/ \x1d\xfc\xdd!\u007fr(m\xc9J\xf0L\xa1\xa7\xd1<\x91$X\xb8\x90\tl\u007f\n\xe9\xfc\x9c\xff\xa9\xbet\xc4\xff&Z\xf9\x18\xd0n\xe0o\u007f?\x83\xe0E\xee+<\xa8\xf3\xbf\x01\x00\x00\xff\xff|l\xf6\x1b=J\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x93\xe34\x10\xbd\xfbWt-\x87\xbd`\x87\x01\x0e\x94o\xd4\x00US\xc0\xd4\xd4d\x99\v\xc5A\x91\xdaI3\xb2dԭ\f\xe1\xd7S\x92\xecI\xe28;\xbb\x87\xf5\xcd\xed\xfexz\xfd\xba\xad\xaa\xae\xebJ\r\xf4\x84\x81ɻ\x16\xd4@\xf8\xaf\xa0Ko\xdc<\xff\xc0\r\xf9\xd5\xfef\x83\xa2n\xaagr\xa6\x85\xdb\xc8\xe2\xfbGd\x1f\x83Ɵ\xb0#GB\xdeU=\x8a2JT[\x01(缨d\xe6\xf4\n\xa0\xbd\x93\xe0\xad\xc5Po\xd15\xcfq\x83\x9bH\xd6`\xc8\x15\xa6\xfa\xfbo\x9ao\x9b\xef+\x00\x1d0\x87\u007f\xa0\x1eYT?\xb4\u08b5\x15\x80S=\xb6\xc0\x18R\x90(\x89\x1c\xf0\x9f\x88,\xdc\xec\xd1b\xf0\r\xf9\x8a\aԩ\xf06\xf88\xb4p\xfcP\xe2GP\xe5@\xeb\x9cj\x9dS=\x96T\xf9\xab%\x96_\xafy\xfcF\xa3\xd7`cPv\x19Pv`r\xdbhUXt\xa9\x00\x86\x80\xf9\xc3\x1f\xee\xd9\xf9\x17\xf7\v\xa15\xdcB\xa7,c\x05\xc0\xda\x0f\xd8\xc2}B=(\x8d\xa6\x02\xd8+K&\xd3S\xce\xe1\at?>\xdc=}\xb7\xd6;\xecU1\x02\x18d\x1dh\xc8~Kg\x00bP0\"\x01\xf1\xa0\xb4Ff\xd01\x04t\x02\x05)\x90\xeb|\xe8s\xb911\x80\xda\xf8( ;\x84\xa7L\xedx\xb6ft\x18\x82\x1f0\bMD琣\xcc^m3\x8c\xef\xd3!\x8a\x0f\x98$,\xe4\\c\x94\a\x1a\xe0|@\xf0\x1dȎ\x18\x02f\xf6\x9c\x9c\xa3˜t\xa0\x1c\xf8\xcdߨ\xa5\x19O\xcf\xc0;\x1f\xadIj\xdcc\x10\b\xa8\xfd\xd6\xd1\u007f\xaf\x999ѐJZ%\x93\x0e\xa6\x87\x9c`p\xca&\xfa#~\r\xca\x19\xe8\xd5\x01\x02\xa6\x1a\x10\xddI\xb6\xec\xc2\r\xfc\xee\x03f\x02[؉\fܮV[\x92i\xb0\xb4\xef\xfb\xe8H\x0e\xab<\x1e\xb4\x89\xe2\x03\xaf\f\xeeѮ\x98\xb6\xb5\nzG\x82Zb\xc0\x95\x1a\xa8\xce\xc0]\x9e\xab\xa67_\x85q\n\xf9\xfd\tR9$\xc1\xb0\x04r\xdbWs\x96\xfaUޓ̋\x1aJX\xc1\u007f\xa47\x99\x12+\x8f?\xaf?\xc0T4\xb7\xe0\x9c\xf3\xcc\xf61\x8c\x8f\xc4'\xa2\xc8u\x18J\xe3\xba\xe0\xfb\x9c\x11\x9d\x19<\xb9\xa2%m\t\xdd9\xe9\x1c7=\tO*M\xfdi\xe06\xaf\x17\xd8 \xc4\xc1(A\xd3\xc0\x9d\x83[գ\xbdU\x8c_\x9c\xf6\xc40\u05c9ҷ\x89?݊玅\xadW\xf3\xb4\xb2\x16;\xb40\xbd\xeb\x01u\xeaY\".\xc5RG:\x8f\x01t>\x80Z\ni\xdeĐ\xbd?\vŸ#\n\x8e\xd9\xe6H3\xf8\x16\x8e\xa5U\x91\xed;\xc5xn\x9a\xa1yH\x1e\xf3ʖ:\xd4\am\xb1$(\x9b\x02\xdf\x02\x91\x1et\xb1\x9f\u05eb\xe1\x1e_.l\x0f\xc1\xa7=\x99W\xf1\xe9\xb3\xd8\u007f(\xff\x88-9\xfe\xf8i\x8aO\xfe뜮ܓU;\xa6\x81\x10\x9dK\x13\xe9]2ϒ\xc2\xf9F\x9e}%\xc1\xfe\x02\xc7\"\x92;\xd7\xf9\xfc\xd7V\xa9\xa4\x922'86u\xacQ\x10]\xa4\xbb\xd6\xd3\xf2\xccW\xd1'\x10X\x9e\xfc\xe7\xff\xfc\xc0\xb4:(\xe0B\xcd:cY0\xa7J\x17\xe6ʼn\x19\x91Ek\xd5\xc6b\v\x12\xe2<\xb2ĩ\x10\xd4\xe1\\\x15\x93\x8c\x8ew\x9c\x8f\n\xe4\xc2=i\xffe\x87\xee\x9a\xc2\xe1E\xf1RoJ\x1a\xd8\x1c\xae\x05\u07be^\xd6\xe6CRd\xd9Bں\xb5\xd0\x05K\x9f@\xc4B\x97\x8aT\x17n\a\x17$\xacO=\xa7\xd9?\x13\xfctY\x98#\xbfR|\xa1\xa93\xd3\xf1nzs|\xcb®ǻh\xfe0\x9e\u009c\x9c\x9c\xc5\a\xb5\x9d\xb88\xee\xd6t\xcd\x1a\x04\xcd\xfd\xfc&\xfa\xee\xddٕ2\xbfj\xef\f\x95\x8b4\xfc\xf9WU\xb2\xa2y\x9ap$\xe3\xff\x01\x00\x00\xff\xff\x8e\xadi\xa0\xc7\v\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V\xc1\x8e\xdc6\f\xbd\xfb+\x88\xf4\x90K\xed\xe9\xb6=\x14\xbe\x05\xdb\x16\b\x9a\x06\x8bl2\x97\xa2\a\x8dDϰ+K\xaaH9\xdd~}!Y\xde\x19\xcf\xce$-\x8a\xf8&\x9a\"\x1f\x1f\x1f\t5m\xdb6*\xd0\x16#\x93w=\xa8@\xf8\x97\xa0\xcb'\xee\x1e~\xe0\x8e\xfcf\xba١\xa8\x9b恜\xe9\xe16\xb1\xf8\xf1\x1d\xb2OQ\xe3\x8f8\x90#!\xef\x9a\x11E\x19%\xaao\x00\x94s^T6s>\x02h\xef$zk1\xb6{t\xddC\xda\xe1.\x915\x18K\x86%\xff\xf4M\xf7m\xf7}\x03\xa0#\x96\xeb\xefiD\x165\x86\x1e\\\xb2\xb6\x01pj\xc4\x1e&oӈ\xecT\xe0\x83\x17\xeb\xf5\x9c\xac\x9b\xd0b\xf4\x1d\xf9\x86\x03\xea\x9c{\x1f}\n=\x1c\u007f\xcc!*\xae\xb9\xa6m\x89v_\xa3\xbd\xa9ъ\x83%\x96_>\xe1\xf4\x86X\x8ac\xb0)*{\x15Y\xf1ar\xfbdU\xbc\xe6\xd5\x00\x84\x88\x8cq\xc2\x0f\xee\xc1\xf9\x8f\xeegBk\xb8\x87AY\xc6\x06\x80\xb5\x0f\xd8\xc3\xdb\\AP\x1aM\x030)K\xa6ܟk\xf2\x01ݫ\xbb\xd7\xdb\xef\xee\xf5\x01G5\x1b\x01\f\xb2\x8e\x14\x8aߕb\x80\x18\x14,h\xe0\xe3\x01#¶0\a,>\"W\xe05$\xc0R\x01w\xd5\x14\xa2\x0f\x18\x85\x16\x82\xf3w\xa2\xb0'\xdb\x19\x9e\x97\x19\xf0\xec\x03&k\n\x19\xe4\x80P\x95\x81\x06\xb8\x14\x03~\x009\x10C\xc4\u0094\x93c\xab\x96\xcf\x0f\xa0\x1c\xf8\xdd\x1f\xa8\xa5\x83\xfb\xccfd\xe0\x83O\xd6d!N\x18\x05\"j\xbfw\xf4\xf7Sd\x06\xf1%\xa5U\x82\xb5\xa7\xcbGN0:e3\xd5\t\xbf\x06\xe5\f\x8c\xea\x11\"\xe6\x1c\x90\xdcI\xb4\xe2\xc2\x1d\xfc\xea#\x02\xb9\xc1\xf7p\x10\t\xdco6{\x92e\xa6\xb4\x1f\xc7\xe4H\x1e7e2h\x97\xc4G\xde\x18\x9c\xd0n\x98\xf6\xad\x8a\xfa@\x82ZRč\n\xd4\x16\xe0nV\xf9h\xbe\x8au\x00\xf9\xe5\tRy\xcc\xe2`\x89\xe4\xf6O\xe6\"\xf1\xab\xbcgm\xcfm\x9f\xaf\xcd\xf8\x8f\xf4fSf\xe5\xddO\xf7\xefaIZZ\xb0漰}\xbc\xc6G\xe23Q\xe4\x06\x8cs\xe3\x86\xe8\xc7\x12\x11\x9d\t\x9e\x9c\x94\x83\xb6\x84nM:\xa7\xddH\x92;\xfdgB\x96ܟ\x0en\xcbf\x81\x1dB\nF\t\x9a\x0e^;\xb8U#\xda[\xc5\xf8\xc5i\xcf\fs\x9b)\xfd<\xf1\xa7\vq\xed8\xb3u\x1c\xa2\xba\xaa.v\xe8\xf2\xa4\xde\aԫA\xc91h\xa0:\xb9\x83\x8f\xa0Vl\xd6)\xbe\x1c\xad;q\xbd4\xc00o\xf0\x81\xf6k\x1b\x802\xa6l\u007fe\xef\xaeܻJυZoK\x8e,\xc7\\@\x88~\"\x83\xb1]j\xab\x18R\xacE\x96\xdd\xd85\x97r\x9d1\\\v+\xe1\xce\xe1\xad\x10\xdcU\xa7\x8c!Ӻ\\\x9a\xf7\x0e\xd6\xf5W\x96\xa1\xda\xe3\xe5\xdc\xcf\xea\xcc\n\xa6\x88\xab)l\x9fB\u007fV\x1d\xa2$\xf1\u007f\xd5G\xb9T=wU#:ňNjD\xf0\xc3\n\xbe\xfa\xff\x1a\t\a\xc5\xf8I~/Ǿ\xcb\xf7\x16\xca-\r\xa8\x1f-\xce\xd1\xca6\u007f&\xa8\u007f\x8d4\u007f\xe8\xd2x\x0e\xaa\x85W\x93\"\xabv\x16\x9f\xfd\xf9\xe0ԕ\u007fW\xfa{\xa1mg\xa6\xe3\x03\xe7\xe6x*\xe4\xb5˃\xe6f~!\xe4\xa5iz\x90\x98\xe6\xe4Ui\xd5rԂ\xd2\x1a\x83\xa0y{\xfe\x96y\xf1b\xf5\x1c)G\xed\xdd<\xa6\xdc\xc3o\xbf7sT4\xdb\x05G6\xfe\x13\x00\x00\xff\xff\x9f\xfe\xa5\x85\f\n\x00\x00"), } var CRDs = crds() diff --git a/hack/update-generated-crd-code.sh b/hack/update-generated-crd-code.sh index d6a803cf14..890835939d 100755 --- a/hack/update-generated-crd-code.sh +++ b/hack/update-generated-crd-code.sh @@ -30,6 +30,11 @@ if [[ ! -d "${GOPATH}/src/k8s.io/code-generator" ]]; then exit 1 fi +if ! command -v controller-gen > /dev/null; then + echo "controller-gen is missing" + exit 1 +fi + ${GOPATH}/src/k8s.io/code-generator/generate-groups.sh \ all \ github.com/vmware-tanzu/velero/pkg/generated \ @@ -39,4 +44,12 @@ ${GOPATH}/src/k8s.io/code-generator/generate-groups.sh \ --output-base ../../.. \ $@ +# Generate manifests e.g. CRD, RBAC etc. +controller-gen \ + crd:crdVersions=v1beta1,preserveUnknownFields=false,trivialVersions=true \ + rbac:roleName=manager-role \ + paths=./pkg/apis/velero/v1/... \ + paths=./pkg/controller/... \ + output:crd:artifacts:config=config/crd/bases + go generate ./config/crd/crds From 1e884b2f5b94a16b00d9e0750623046c5bf6fb0b Mon Sep 17 00:00:00 2001 From: Carlisia Date: Thu, 11 Jun 2020 13:40:06 -0700 Subject: [PATCH 19/34] Address code review Signed-off-by: Carlisia --- design/backup-resource-list.md | 2 +- hack/update-all.sh | 2 + pkg/client/factory.go | 8 ++-- pkg/cmd/cli/backup/create.go | 8 ++-- pkg/cmd/cli/backuplocation/create.go | 7 +-- pkg/cmd/cli/backuplocation/get.go | 14 +++--- pkg/cmd/cli/restic/server.go | 10 ---- pkg/cmd/server/server.go | 9 ++-- pkg/controller/backup_controller.go | 8 ++-- pkg/controller/backup_controller_test.go | 9 ++-- pkg/controller/backup_deletion_controller.go | 8 ++-- .../backup_deletion_controller_test.go | 47 +++++++------------ pkg/controller/backup_sync_controller.go | 22 +++------ pkg/controller/download_request_controller.go | 8 ++-- pkg/controller/gc_controller.go | 8 ++-- pkg/controller/gc_controller_test.go | 4 +- .../pod_volume_backup_controller.go | 43 +++++------------ .../pod_volume_restore_controller.go | 33 +++---------- .../restic_repository_controller.go | 8 ++-- pkg/controller/restore_controller.go | 8 ++-- pkg/restic/common.go | 18 +++++-- pkg/restic/common_test.go | 11 +---- pkg/restic/repository_manager.go | 28 +++-------- 23 files changed, 123 insertions(+), 200 deletions(-) diff --git a/design/backup-resource-list.md b/design/backup-resource-list.md index aa56631962..ad71e08502 100644 --- a/design/backup-resource-list.md +++ b/design/backup-resource-list.md @@ -57,7 +57,7 @@ The Backupper currently initialises a map to track the `backedUpItems` (https:// This property will be moved to the [Backup request struct](https://github.com/heptio/velero/blob/16910a6215cbd8f0bde385dba9879629ebcbcc28/pkg/backup/request.go#L11), allowing the BackupController to access it after a successful backup. `backedUpItems` currently uses the `schema.GroupResource` as a key for the resource. -In order to record the API group, version and kind for the resource, this key will be constructed from the object's `schema.SchemeGroupVersionKind` in the format `{group}/{version}/{kind}` (e.g. `apps/v1/Deployment`). +In order to record the API group, version and kind for the resource, this key will be constructed from the object's `schema.GroupVersionKind` in the format `{group}/{version}/{kind}` (e.g. `apps/v1/Deployment`). The `backedUpItems` map is kept as a flat structure internally for quick lookup. When the backup is ready to upload, `backedUpItems` will be converted to a nested structure representing the metadata file above, grouped by `schema.SchemeGroupVersionKind`. diff --git a/hack/update-all.sh b/hack/update-all.sh index 714ba36b07..b5fd7c2b6f 100755 --- a/hack/update-all.sh +++ b/hack/update-all.sh @@ -24,3 +24,5 @@ for f in ${HACK_DIR}/update-*.sh; do fi $f done + + diff --git a/pkg/client/factory.go b/pkg/client/factory.go index ff04c69692..e46b81dc1c 100644 --- a/pkg/client/factory.go +++ b/pkg/client/factory.go @@ -19,7 +19,7 @@ package client import ( "os" - k8sclient "sigs.k8s.io/controller-runtime/pkg/client" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" "github.com/pkg/errors" "github.com/spf13/pflag" @@ -47,7 +47,7 @@ type Factory interface { DynamicClient() (dynamic.Interface, error) // KubebuilderClient returns a Kubernetes dynamic client. It uses the following priority to specify the cluster // configuration: --kubeconfig flag, KUBECONFIG environment variable, in-cluster configuration. - KubebuilderClient() (k8sclient.Client, error) + KubebuilderClient() (kbclient.Client, error) // SetBasename changes the basename for an already-constructed client. // This is useful for generating clients that require a different user-agent string below the root `velero` // command, such as the server subcommand. @@ -143,7 +143,7 @@ func (f *factory) DynamicClient() (dynamic.Interface, error) { return dynamicClient, nil } -func (f *factory) KubebuilderClient() (k8sclient.Client, error) { +func (f *factory) KubebuilderClient() (kbclient.Client, error) { clientConfig, err := f.ClientConfig() if err != nil { return nil, err @@ -151,7 +151,7 @@ func (f *factory) KubebuilderClient() (k8sclient.Client, error) { scheme := runtime.NewScheme() velerov1api.AddToScheme(scheme) - kubebuilderClient, err := k8sclient.New(clientConfig, k8sclient.Options{ + kubebuilderClient, err := kbclient.New(clientConfig, kbclient.Options{ Scheme: scheme, }) diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index be8d350879..fe310f9627 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -21,6 +21,8 @@ import ( "fmt" "time" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/spf13/cobra" "github.com/spf13/pflag" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -34,8 +36,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/cmd/util/output" veleroclient "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" v1 "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" - - k8sclient "sigs.k8s.io/controller-runtime/pkg/client" ) const DefaultBackupTTL time.Duration = 30 * 24 * time.Hour @@ -149,7 +149,7 @@ func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Facto return err } - clientKB, err := f.KubebuilderClient() + client, err := f.KubebuilderClient() if err != nil { return err } @@ -161,7 +161,7 @@ func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Facto if o.StorageLocation != "" { location := &velerov1api.BackupStorageLocation{} - if err := clientKB.Get(context.Background(), k8sclient.ObjectKey{ + if err := client.Get(context.Background(), kbclient.ObjectKey{ Namespace: f.Namespace(), Name: o.StorageLocation, }, location); err != nil { diff --git a/pkg/cmd/cli/backuplocation/create.go b/pkg/cmd/cli/backuplocation/create.go index 30bd5d5447..53ea54f080 100644 --- a/pkg/cmd/cli/backuplocation/create.go +++ b/pkg/cmd/cli/backuplocation/create.go @@ -26,7 +26,8 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - k8sclient "sigs.k8s.io/controller-runtime/pkg/client" + + kbclient "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/client" @@ -148,12 +149,12 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { return err } - clientKB, err := f.KubebuilderClient() + client, err := f.KubebuilderClient() if err != nil { return err } - if err := clientKB.Create(context.Background(), backupStorageLocation, &k8sclient.CreateOptions{}); err != nil { + if err := client.Create(context.Background(), backupStorageLocation, &kbclient.CreateOptions{}); err != nil { return err } diff --git a/pkg/cmd/cli/backuplocation/get.go b/pkg/cmd/cli/backuplocation/get.go index b9f23fb542..c0dbe4148b 100644 --- a/pkg/cmd/cli/backuplocation/get.go +++ b/pkg/cmd/cli/backuplocation/get.go @@ -19,9 +19,10 @@ package backuplocation import ( "context" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - k8sclient "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/client" @@ -39,15 +40,14 @@ func NewGetCommand(f client.Factory, use string) *cobra.Command { err := output.ValidateFlags(c) cmd.CheckError(err) - clientKB, err := f.KubebuilderClient() + client, err := f.KubebuilderClient() cmd.CheckError(err) - location := &velerov1api.BackupStorageLocation{} - var locations *velerov1api.BackupStorageLocationList - locations = new(velerov1api.BackupStorageLocationList) + locations := new(velerov1api.BackupStorageLocationList) if len(args) > 0 { + location := &velerov1api.BackupStorageLocation{} for _, name := range args { - err = clientKB.Get(context.Background(), k8sclient.ObjectKey{ + err = client.Get(context.Background(), kbclient.ObjectKey{ Namespace: f.Namespace(), Name: name, }, location) @@ -55,7 +55,7 @@ func NewGetCommand(f client.Factory, use string) *cobra.Command { locations.Items = append(locations.Items, *location) } } else { - err := clientKB.List(context.Background(), locations, &k8sclient.ListOptions{ + err := client.List(context.Background(), locations, &kbclient.ListOptions{ Namespace: f.Namespace(), }) cmd.CheckError(err) diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/restic/server.go index 5dfdf289fd..2fa86b2996 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/restic/server.go @@ -22,8 +22,6 @@ import ( "strings" "sync" - "k8s.io/apimachinery/pkg/runtime/schema" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -186,12 +184,6 @@ func (s *resticServer) run() { var wg sync.WaitGroup - gvk := schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "BackupStorageLocation"} - bslInformer, err := s.mgr.GetCache().GetInformerForKind(gvk) - if err != nil { - panic(err) - } - backupController := controller.NewPodVolumeBackupController( s.logger, s.veleroInformerFactory.Velero().V1().PodVolumeBackups(), @@ -200,7 +192,6 @@ func (s *resticServer) run() { s.secretInformer, s.kubeInformerFactory.Core().V1().PersistentVolumeClaims(), s.kubeInformerFactory.Core().V1().PersistentVolumes(), - bslInformer, s.mgr.GetClient(), os.Getenv("NODE_NAME"), ) @@ -218,7 +209,6 @@ func (s *resticServer) run() { s.secretInformer, s.kubeInformerFactory.Core().V1().PersistentVolumeClaims(), s.kubeInformerFactory.Core().V1().PersistentVolumes(), - bslInformer, s.mgr.GetClient(), os.Getenv("NODE_NAME"), ) diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 2029e9ac28..2cfe75338b 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -70,10 +70,9 @@ import ( "github.com/vmware-tanzu/velero/pkg/restore" "github.com/vmware-tanzu/velero/pkg/util/logging" - _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" ctrl "sigs.k8s.io/controller-runtime" - k8sclient "sigs.k8s.io/controller-runtime/pkg/client" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/manager" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" @@ -364,7 +363,7 @@ func (s *server) run() error { } bsl := &velerov1api.BackupStorageLocation{} - if err := s.mgr.GetAPIReader().Get(context.Background(), k8sclient.ObjectKey{ + if err := s.mgr.GetAPIReader().Get(context.Background(), kbclient.ObjectKey{ Namespace: s.namespace, Name: s.config.defaultBackupLocation, }, bsl); err != nil { @@ -468,7 +467,7 @@ func (s *server) validateBackupStorageLocations() error { defer pluginManager.CleanupClients() locations := &velerov1api.BackupStorageLocationList{} - if err := s.mgr.GetAPIReader().List(context.Background(), locations, &k8sclient.ListOptions{ + if err := s.mgr.GetAPIReader().List(context.Background(), locations, &kbclient.ListOptions{ Namespace: s.namespace, }); err != nil { return errors.WithStack(err) @@ -570,7 +569,7 @@ func (s *server) initRestic() error { secretsInformer, s.sharedInformerFactory.Velero().V1().ResticRepositories(), s.veleroClient.VeleroV1(), - s.mgr.GetCache(), + s.mgr.GetClient(), s.kubeClient.CoreV1(), s.kubeClient.CoreV1(), s.logger, diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index b8ee7fe891..741e88310b 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -68,7 +68,7 @@ type backupController struct { backupper pkgbackup.Backupper lister velerov1listers.BackupLister client velerov1client.BackupsGetter - k8sClient client.Client + kbclient client.Client clock clock.Clock backupLogLevel logrus.Level newPluginManager func(logrus.FieldLogger) clientmgmt.Manager @@ -94,7 +94,7 @@ func NewBackupController( backupLogLevel logrus.Level, newPluginManager func(logrus.FieldLogger) clientmgmt.Manager, backupTracker BackupTracker, - k8sClient client.Client, + kbclient client.Client, defaultBackupLocation string, defaultVolumesToRestic bool, defaultBackupTTL time.Duration, @@ -115,7 +115,7 @@ func NewBackupController( backupLogLevel: backupLogLevel, newPluginManager: newPluginManager, backupTracker: backupTracker, - k8sClient: k8sClient, + kbclient: kbclient, defaultBackupLocation: defaultBackupLocation, defaultVolumesToRestic: defaultVolumesToRestic, defaultBackupTTL: defaultBackupTTL, @@ -375,7 +375,7 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg // validate the storage location, and store the BackupStorageLocation API obj on the request storageLocation := &velerov1api.BackupStorageLocation{} - if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ + if err := c.kbclient.Get(context.Background(), client.ObjectKey{ Namespace: request.Namespace, Name: request.Spec.StorageLocation, }, storageLocation); err != nil { diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 112df396ce..dd83ce24b1 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -24,7 +24,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" - //"path/filepath" "sort" "strings" "testing" @@ -194,7 +193,7 @@ func TestProcessBackupValidationFailures(t *testing.T) { discoveryHelper: discoveryHelper, client: clientset.VeleroV1(), lister: sharedInformers.Velero().V1().Backups().Lister(), - k8sClient: fakeClient, + kbclient: fakeClient, snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), defaultBackupLocation: defaultBackupLocation.Name, clock: &clock.RealClock{}, @@ -261,7 +260,7 @@ func TestBackupLocationLabel(t *testing.T) { discoveryHelper: discoveryHelper, client: clientset.VeleroV1(), lister: sharedInformers.Velero().V1().Backups().Lister(), - k8sClient: fakeClient, + kbclient: fakeClient, snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), defaultBackupLocation: test.backupLocation.Name, clock: &clock.RealClock{}, @@ -323,7 +322,7 @@ func TestDefaultBackupTTL(t *testing.T) { c := &backupController{ genericController: newGenericController("backup-test", logger), discoveryHelper: discoveryHelper, - k8sClient: fakeClient, + kbclient: fakeClient, snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), defaultBackupTTL: defaultBackupTTL.Duration, clock: clock.NewFakeClock(now), @@ -813,7 +812,7 @@ func TestProcessBackupCompletions(t *testing.T) { discoveryHelper: discoveryHelper, client: clientset.VeleroV1(), lister: sharedInformers.Velero().V1().Backups().Lister(), - k8sClient: fakeClient, + kbclient: fakeClient, snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), defaultBackupLocation: defaultBackupLocation.Name, defaultVolumesToRestic: test.defaultVolumesToRestic, diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 60ab1be740..837e37d5f7 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -66,7 +66,7 @@ type backupDeletionController struct { backupTracker BackupTracker resticMgr restic.RepositoryManager podvolumeBackupLister velerov1listers.PodVolumeBackupLister - k8sClient client.Client + kbclient client.Client snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister csiSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister csiSnapshotContentLister snapshotv1beta1listers.VolumeSnapshotContentLister @@ -89,7 +89,7 @@ func NewBackupDeletionController( backupTracker BackupTracker, resticMgr restic.RepositoryManager, podvolumeBackupLister velerov1listers.PodVolumeBackupLister, - k8sClient client.Client, + kbclient client.Client, snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, csiSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister, csiSnapshotContentLister snapshotv1beta1listers.VolumeSnapshotContentLister, @@ -107,7 +107,7 @@ func NewBackupDeletionController( backupTracker: backupTracker, resticMgr: resticMgr, podvolumeBackupLister: podvolumeBackupLister, - k8sClient: k8sClient, + kbclient: kbclient, snapshotLocationLister: snapshotLocationLister, csiSnapshotLister: csiSnapshotLister, csiSnapshotContentLister: csiSnapshotContentLister, @@ -217,7 +217,7 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR // Don't allow deleting backups in read-only storage locations location := &velerov1api.BackupStorageLocation{} - if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ + if err := c.kbclient.Get(context.Background(), client.ObjectKey{ Namespace: backup.Namespace, Name: backup.Spec.StorageLocation, }, location); err != nil { diff --git a/pkg/controller/backup_deletion_controller_test.go b/pkg/controller/backup_deletion_controller_test.go index f7564663c2..7a0c75e0c8 100644 --- a/pkg/controller/backup_deletion_controller_test.go +++ b/pkg/controller/backup_deletion_controller_test.go @@ -22,8 +22,6 @@ import ( "testing" "time" - "sigs.k8s.io/controller-runtime/pkg/client" - snapshotv1beta1api "github.com/kubernetes-csi/external-snapshotter/v2/pkg/apis/volumesnapshot/v1beta1" snapshotFake "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/clientset/versioned/fake" snapshotv1beta1informers "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/informers/externalversions" @@ -37,6 +35,7 @@ import ( "k8s.io/apimachinery/pkg/util/clock" "k8s.io/apimachinery/pkg/util/sets" core "k8s.io/client-go/testing" + "sigs.k8s.io/controller-runtime/pkg/client" velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" @@ -125,6 +124,7 @@ func TestBackupDeletionControllerProcessQueueItem(t *testing.T) { type backupDeletionControllerTestData struct { client *fake.Clientset + fakeClient client.Client sharedInformers informers.SharedInformerFactory volumeSnapshotter *velerotest.FakeVolumeSnapshotter backupStore *persistencemocks.BackupStore @@ -132,13 +132,14 @@ type backupDeletionControllerTestData struct { req *velerov1.DeleteBackupRequest } -func setupBackupDeletionControllerTest(fakeClient client.Client, objects ...runtime.Object) *backupDeletionControllerTestData { +func setupBackupDeletionControllerTest(t *testing.T, objects ...runtime.Object) *backupDeletionControllerTestData { req := pkgbackup.NewDeleteBackupRequest("foo", "uid") req.Namespace = "velero" req.Name = "foo-abcde" var ( client = fake.NewSimpleClientset(append(objects, req)...) + fakeClient = newFakeClient(t, objects...) sharedInformers = informers.NewSharedInformerFactory(client, 0) volumeSnapshotter = &velerotest.FakeVolumeSnapshotter{SnapshotsTaken: sets.NewString()} pluginManager = &pluginmocks.Manager{} @@ -147,6 +148,7 @@ func setupBackupDeletionControllerTest(fakeClient client.Client, objects ...runt data := &backupDeletionControllerTestData{ client: client, + fakeClient: fakeClient, sharedInformers: sharedInformers, volumeSnapshotter: volumeSnapshotter, backupStore: backupStore, @@ -182,11 +184,8 @@ func setupBackupDeletionControllerTest(fakeClient client.Client, objects ...runt } func TestBackupDeletionControllerProcessRequest(t *testing.T) { - t.Run("missing spec.backupName", func(t *testing.T) { - fakeClient := newFakeClient(t) - td := setupBackupDeletionControllerTest(fakeClient) - + td := setupBackupDeletionControllerTest(t) td.req.Spec.BackupName = "" err := td.controller.processRequest(td.req) @@ -206,8 +205,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("existing deletion requests for the backup are deleted", func(t *testing.T) { - fakeClient := newFakeClient(t) - td := setupBackupDeletionControllerTest(fakeClient) + td := setupBackupDeletionControllerTest(t) // add the backup to the tracker so the execution of processRequest doesn't progress // past checking for an in-progress backup. this makes validation easier. @@ -262,8 +260,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("deleting an in progress backup isn't allowed", func(t *testing.T) { - fakeClient := newFakeClient(t) - td := setupBackupDeletionControllerTest(fakeClient) + td := setupBackupDeletionControllerTest(t) td.controller.backupTracker.Add(td.req.Namespace, td.req.Spec.BackupName) @@ -287,9 +284,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result() location := builder.ForBackupStorageLocation("velero", "default").Result() - fakeClient := newFakeClient(t, location) - - td := setupBackupDeletionControllerTest(fakeClient, backup) + td := setupBackupDeletionControllerTest(t, location, backup) td.client.PrependReactor("patch", "deletebackuprequests", func(action core.Action) (bool, runtime.Object, error) { return true, nil, errors.New("bad") @@ -319,9 +314,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result() location := builder.ForBackupStorageLocation("velero", "default").Result() - fakeClient := newFakeClient(t, location) - - td := setupBackupDeletionControllerTest(fakeClient, backup) + td := setupBackupDeletionControllerTest(t, location, backup) td.client.PrependReactor("patch", "deletebackuprequests", func(action core.Action) (bool, runtime.Object, error) { return true, td.req, nil @@ -358,9 +351,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("unable to find backup", func(t *testing.T) { - fakeClient := newFakeClient(t) - - td := setupBackupDeletionControllerTest(fakeClient) + td := setupBackupDeletionControllerTest(t) err := td.controller.processRequest(td.req) require.NoError(t, err) @@ -384,10 +375,9 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("unable to find backup storage location", func(t *testing.T) { - fakeClient := newFakeClient(t) backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result() - td := setupBackupDeletionControllerTest(fakeClient, backup) + td := setupBackupDeletionControllerTest(t, backup) err := td.controller.processRequest(td.req) require.NoError(t, err) @@ -414,8 +404,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result() location := builder.ForBackupStorageLocation("velero", "default").AccessMode(velerov1api.BackupStorageLocationAccessModeReadOnly).Result() - fakeClient := newFakeClient(t, location) - td := setupBackupDeletionControllerTest(fakeClient, backup) + td := setupBackupDeletionControllerTest(t, location, backup) err := td.controller.processRequest(td.req) require.NoError(t, err) @@ -447,8 +436,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { restore2 := builder.ForRestore("velero", "restore-2").Phase(velerov1.RestorePhaseCompleted).Backup("foo").Result() restore3 := builder.ForRestore("velero", "restore-3").Phase(velerov1.RestorePhaseCompleted).Backup("some-other-backup").Result() - fakeClient := newFakeClient(t) - td := setupBackupDeletionControllerTest(fakeClient, backup, restore1, restore2, restore3) + 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) @@ -469,7 +457,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }, } - require.NoError(t, fakeClient.Create(context.Background(), location)) + require.NoError(t, td.fakeClient.Create(context.Background(), location)) snapshotLocation := &velerov1.VolumeSnapshotLocation{ ObjectMeta: metav1.ObjectMeta{ @@ -608,8 +596,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { Backup("some-other-backup"). Result() - fakeClient := newFakeClient(t) - td := setupBackupDeletionControllerTest(fakeClient, backup, restore1, restore2, restore3) + 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" @@ -631,7 +618,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }, }, } - require.NoError(t, fakeClient.Create(context.Background(), location)) + require.NoError(t, td.fakeClient.Create(context.Background(), location)) snapshotLocation := &velerov1.VolumeSnapshotLocation{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index c6a47df5cb..ec7086e377 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -38,14 +38,13 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" "sigs.k8s.io/controller-runtime/pkg/client" - k8sclient "sigs.k8s.io/controller-runtime/pkg/client" ) type backupSyncController struct { *genericController backupClient velerov1client.BackupsGetter - k8sClient client.Client + kbclient client.Client podVolumeBackupClient velerov1client.PodVolumeBackupsGetter backupLister velerov1listers.BackupLister csiSnapshotClient *snapshotterClientSet.Clientset @@ -59,7 +58,7 @@ type backupSyncController struct { func NewBackupSyncController( backupClient velerov1client.BackupsGetter, - k8sClient client.Client, + kbclient client.Client, podVolumeBackupClient velerov1client.PodVolumeBackupsGetter, backupLister velerov1listers.BackupLister, syncPeriod time.Duration, @@ -78,7 +77,7 @@ func NewBackupSyncController( c := &backupSyncController{ genericController: newGenericController("backup-sync", logger), backupClient: backupClient, - k8sClient: k8sClient, + kbclient: kbclient, podVolumeBackupClient: podVolumeBackupClient, namespace: namespace, defaultBackupLocation: defaultBackupLocation, @@ -124,7 +123,7 @@ func (c *backupSyncController) run() { c.logger.Debug("Checking for existing backup storage locations to sync into cluster") locationList := &velerov1api.BackupStorageLocationList{} - if err := c.k8sClient.List(context.Background(), locationList, &client.ListOptions{ + if err := c.kbclient.List(context.Background(), locationList, &client.ListOptions{ Namespace: c.namespace, }); err != nil { c.logger.WithError(errors.WithStack(err)).Error("Error getting backup storage locations from lister") @@ -306,16 +305,9 @@ func (c *backupSyncController) run() { c.deleteOrphanedBackups(location.Name, backupStoreBackups, log) - locationUpdate := &velerov1api.BackupStorageLocation{} - if err = c.k8sClient.Get(context.Background(), k8sclient.ObjectKey{ - Namespace: c.namespace, - Name: location.Name, - }, locationUpdate); err != nil { - log.WithError(errors.WithStack(err)).Error("Error fetching backup location for update") - continue - } - locationUpdate.Status.LastSyncedTime = &metav1.Time{Time: time.Now().UTC()} - if err := c.k8sClient.Update(context.Background(), locationUpdate); err != nil { + statusPatch := client.MergeFrom(location.DeepCopyObject()) + location.Status.LastSyncedTime = &metav1.Time{Time: time.Now().UTC()} + if err := c.kbclient.Status().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/download_request_controller.go b/pkg/controller/download_request_controller.go index ff262e4322..25d4b5d869 100644 --- a/pkg/controller/download_request_controller.go +++ b/pkg/controller/download_request_controller.go @@ -49,7 +49,7 @@ type downloadRequestController struct { downloadRequestLister velerov1listers.DownloadRequestLister restoreLister velerov1listers.RestoreLister clock clock.Clock - k8sClient client.Client + kbclient client.Client backupLister velerov1listers.BackupLister newPluginManager func(logrus.FieldLogger) clientmgmt.Manager newBackupStore func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) @@ -60,7 +60,7 @@ func NewDownloadRequestController( downloadRequestClient velerov1client.DownloadRequestsGetter, downloadRequestInformer velerov1informers.DownloadRequestInformer, restoreLister velerov1listers.RestoreLister, - k8sClient client.Client, + kbclient client.Client, backupLister velerov1listers.BackupLister, newPluginManager func(logrus.FieldLogger) clientmgmt.Manager, logger logrus.FieldLogger, @@ -70,7 +70,7 @@ func NewDownloadRequestController( downloadRequestClient: downloadRequestClient, downloadRequestLister: downloadRequestInformer.Lister(), restoreLister: restoreLister, - k8sClient: k8sClient, + kbclient: kbclient, backupLister: backupLister, // use variables to refer to these functions so they can be @@ -163,7 +163,7 @@ func (c *downloadRequestController) generatePreSignedURL(downloadRequest *v1.Dow } backupLocation := &velerov1api.BackupStorageLocation{} - if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ + if err := c.kbclient.Get(context.Background(), client.ObjectKey{ Namespace: backup.Namespace, Name: backup.Spec.StorageLocation, }, backupLocation); err != nil { diff --git a/pkg/controller/gc_controller.go b/pkg/controller/gc_controller.go index 24ff715c45..64315cca3c 100644 --- a/pkg/controller/gc_controller.go +++ b/pkg/controller/gc_controller.go @@ -48,7 +48,7 @@ type gcController struct { backupLister velerov1listers.BackupLister deleteBackupRequestLister velerov1listers.DeleteBackupRequestLister deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter - k8sClient client.Client + kbclient client.Client clock clock.Clock } @@ -59,7 +59,7 @@ func NewGCController( backupInformer velerov1informers.BackupInformer, deleteBackupRequestLister velerov1listers.DeleteBackupRequestLister, deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter, - k8sClient client.Client, + kbclient client.Client, ) Interface { c := &gcController{ genericController: newGenericController("gc-controller", logger), @@ -67,7 +67,7 @@ func NewGCController( backupLister: backupInformer.Lister(), deleteBackupRequestLister: deleteBackupRequestLister, deleteBackupRequestClient: deleteBackupRequestClient, - k8sClient: k8sClient, + kbclient: kbclient, } c.syncHandler = c.processQueueItem @@ -134,7 +134,7 @@ func (c *gcController) processQueueItem(key string) error { log.Info("Backup has expired") loc := &velerov1api.BackupStorageLocation{} - if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ + if err := c.kbclient.Get(context.Background(), client.ObjectKey{ Namespace: ns, Name: backup.Spec.StorageLocation, }, loc); err != nil { diff --git a/pkg/controller/gc_controller_test.go b/pkg/controller/gc_controller_test.go index fcf4fffbc3..38d6f5a738 100644 --- a/pkg/controller/gc_controller_test.go +++ b/pkg/controller/gc_controller_test.go @@ -34,7 +34,7 @@ import ( "k8s.io/apimachinery/pkg/watch" core "k8s.io/client-go/testing" - k8sclient "sigs.k8s.io/controller-runtime/pkg/client" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" @@ -251,7 +251,7 @@ func TestGCControllerProcessQueueItem(t *testing.T) { sharedInformers = informers.NewSharedInformerFactory(client, 0) ) - var fakeClient k8sclient.Client + var fakeClient kbclient.Client if test.backupLocation != nil { fakeClient = newFakeClient(t, test.backupLocation) } else { diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index c40ece0ef6..c51f0c4ef6 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -17,7 +17,6 @@ limitations under the License. package controller import ( - "context" "encoding/json" "fmt" "os" @@ -58,7 +57,7 @@ type podVolumeBackupController struct { pvcLister corev1listers.PersistentVolumeClaimLister pvLister corev1listers.PersistentVolumeLister backupLocationInformer k8scache.Informer - client client.Client + kbClient client.Client nodeName string processBackupFunc func(*velerov1api.PodVolumeBackup) error @@ -75,21 +74,19 @@ func NewPodVolumeBackupController( secretInformer cache.SharedIndexInformer, pvcInformer corev1informers.PersistentVolumeClaimInformer, pvInformer corev1informers.PersistentVolumeInformer, - backupLocationInformer k8scache.Informer, - client client.Client, + kbClient client.Client, nodeName string, ) Interface { c := &podVolumeBackupController{ - genericController: newGenericController("pod-volume-backup", logger), - podVolumeBackupClient: podVolumeBackupClient, - podVolumeBackupLister: podVolumeBackupInformer.Lister(), - podLister: corev1listers.NewPodLister(podInformer.GetIndexer()), - secretLister: corev1listers.NewSecretLister(secretInformer.GetIndexer()), - pvcLister: pvcInformer.Lister(), - pvLister: pvInformer.Lister(), - backupLocationInformer: backupLocationInformer, - client: client, - nodeName: nodeName, + genericController: newGenericController("pod-volume-backup", logger), + podVolumeBackupClient: podVolumeBackupClient, + podVolumeBackupLister: podVolumeBackupInformer.Lister(), + podLister: corev1listers.NewPodLister(podInformer.GetIndexer()), + secretLister: corev1listers.NewSecretLister(secretInformer.GetIndexer()), + pvcLister: pvcInformer.Lister(), + pvLister: pvInformer.Lister(), + kbClient: kbClient, + nodeName: nodeName, fileSystem: filesystem.NewFileSystem(), clock: &clock.RealClock{}, @@ -102,7 +99,6 @@ func NewPodVolumeBackupController( podInformer.HasSynced, secretInformer.HasSynced, pvcInformer.Informer().HasSynced, - backupLocationInformer.HasSynced, ) c.processBackupFunc = c.processBackup @@ -235,24 +231,11 @@ func (c *podVolumeBackupController) processBackup(req *velerov1api.PodVolumeBack ) // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic - //TODO(carlisia): before the client was being passed to the restic package and the - // "getting" of the bsl was being done there; is that better? - location := &velerov1api.BackupStorageLocation{} - if err := c.client.Get(context.Background(), client.ObjectKey{ - Namespace: req.Namespace, - Name: req.Spec.BackupStorageLocation, - }, location); err != nil { - return err - } - - //TODO(carlisia): would it be better to fetch the cert here? - //if location.Spec.ObjectStorage != nil { - // caCert = location.Spec.ObjectStorage.CACert - //} - caCert, err := restic.GetCACert(location) + location, err := restic.GetCACert(c.kbClient, req.Namespace, req.Spec.BackupStorageLocation) if err != nil { log.WithError(err).Error("Error getting caCert") } + caCert := location.Spec.ObjectStorage.CACert var caCertFile string if caCert != nil { caCertFile, err = restic.TempCACertFile(caCert, req.Spec.BackupStorageLocation, c.fileSystem) diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 2e69093e94..b280a32d42 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -17,7 +17,6 @@ limitations under the License. package controller import ( - "context" "encoding/json" "fmt" "io/ioutil" @@ -60,7 +59,7 @@ type podVolumeRestoreController struct { pvcLister corev1listers.PersistentVolumeClaimLister pvLister corev1listers.PersistentVolumeLister backupLocationInformer k8scache.Informer - client client.Client + kbClient client.Client nodeName string processRestoreFunc func(*velerov1api.PodVolumeRestore) error @@ -77,8 +76,7 @@ func NewPodVolumeRestoreController( secretInformer cache.SharedIndexInformer, pvcInformer corev1informers.PersistentVolumeClaimInformer, pvInformer corev1informers.PersistentVolumeInformer, - backupLocationInformer k8scache.Informer, - client client.Client, + kbClient client.Client, nodeName string, ) Interface { c := &podVolumeRestoreController{ @@ -89,8 +87,7 @@ func NewPodVolumeRestoreController( secretLister: corev1listers.NewSecretLister(secretInformer.GetIndexer()), pvcLister: pvcInformer.Lister(), pvLister: pvInformer.Lister(), - backupLocationInformer: backupLocationInformer, - client: client, + kbClient: kbClient, nodeName: nodeName, fileSystem: filesystem.NewFileSystem(), @@ -104,7 +101,6 @@ func NewPodVolumeRestoreController( podInformer.HasSynced, secretInformer.HasSynced, pvcInformer.Informer().HasSynced, - backupLocationInformer.HasSynced, ) c.processRestoreFunc = c.processRestore @@ -300,18 +296,11 @@ func (c *podVolumeRestoreController) processRestore(req *velerov1api.PodVolumeRe defer os.Remove(credsFile) // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic - location := &velerov1api.BackupStorageLocation{} - if err := c.client.Get(context.Background(), client.ObjectKey{ - Namespace: req.Namespace, - Name: req.Spec.BackupStorageLocation, - }, location); err != nil { - return err - } - - caCert, err := restic.GetCACert(location) + location, err := restic.GetCACert(c.kbClient, req.Namespace, req.Spec.BackupStorageLocation) if err != nil { log.WithError(err).Error("Error getting caCert") } + caCert := location.Spec.ObjectStorage.CACert var caCertFile string if caCert != nil { caCertFile, err = restic.TempCACertFile(caCert, req.Spec.BackupStorageLocation, c.fileSystem) @@ -323,7 +312,7 @@ func (c *podVolumeRestoreController) processRestore(req *velerov1api.PodVolumeRe } // execute the restore process - if err := c.restorePodVolume(req, credsFile, caCertFile, volumeDir, log); err != nil { + if err := c.restorePodVolume(req, location, credsFile, caCertFile, volumeDir, log); err != nil { log.WithError(err).Error("Error restoring volume") return c.failRestore(req, errors.Wrap(err, "error restoring volume").Error(), log) } @@ -342,7 +331,7 @@ func (c *podVolumeRestoreController) processRestore(req *velerov1api.PodVolumeRe return nil } -func (c *podVolumeRestoreController) restorePodVolume(req *velerov1api.PodVolumeRestore, credsFile, caCertFile, volumeDir string, log logrus.FieldLogger) error { +func (c *podVolumeRestoreController) restorePodVolume(req *velerov1api.PodVolumeRestore, location *velerov1api.BackupStorageLocation, credsFile, caCertFile, volumeDir string, log logrus.FieldLogger) error { // 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)) @@ -360,14 +349,6 @@ func (c *podVolumeRestoreController) restorePodVolume(req *velerov1api.PodVolume // Running restic command might need additional provider specific environment variables. Based on the provider, we // set resticCmd.Env appropriately (currently for Azure and S3 based backuplocations) - location := &velerov1api.BackupStorageLocation{} - if err := c.client.Get(context.Background(), client.ObjectKey{ - Namespace: req.Namespace, - Name: req.Spec.BackupStorageLocation, - }, location); err != nil { - return err - } - if strings.HasPrefix(req.Spec.RepoIdentifier, "azure") { env, err := restic.AzureCmdEnv(location) if err != nil { diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index 5fa3516925..17e266cf2b 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -47,7 +47,7 @@ type resticRepositoryController struct { resticRepositoryClient velerov1client.ResticRepositoriesGetter resticRepositoryLister velerov1listers.ResticRepositoryLister - k8sClient client.Client + kbclient client.Client repositoryManager restic.RepositoryManager defaultMaintenanceFrequency time.Duration @@ -59,7 +59,7 @@ func NewResticRepositoryController( logger logrus.FieldLogger, resticRepositoryInformer velerov1informers.ResticRepositoryInformer, resticRepositoryClient velerov1client.ResticRepositoriesGetter, - k8sClient client.Client, + kbclient client.Client, repositoryManager restic.RepositoryManager, defaultMaintenanceFrequency time.Duration, ) Interface { @@ -67,7 +67,7 @@ func NewResticRepositoryController( genericController: newGenericController("restic-repository", logger), resticRepositoryClient: resticRepositoryClient, resticRepositoryLister: resticRepositoryInformer.Lister(), - k8sClient: k8sClient, + kbclient: kbclient, repositoryManager: repositoryManager, defaultMaintenanceFrequency: defaultMaintenanceFrequency, @@ -160,7 +160,7 @@ func (c *resticRepositoryController) initializeRepo(req *v1.ResticRepository, lo // confirm the repo's BackupStorageLocation is valid loc := &velerov1api.BackupStorageLocation{} - if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ + if err := c.kbclient.Get(context.Background(), client.ObjectKey{ Namespace: req.Namespace, Name: req.Spec.BackupStorageLocation, }, loc); err != nil { diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index 9837eb818c..06c29f93b4 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -83,7 +83,7 @@ type restoreController struct { restorer pkgrestore.Restorer backupLister velerov1listers.BackupLister restoreLister velerov1listers.RestoreLister - k8sClient client.Client + kbclient client.Client snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister restoreLogLevel logrus.Level defaultBackupLocation string @@ -101,7 +101,7 @@ func NewRestoreController( podVolumeBackupClient velerov1client.PodVolumeBackupsGetter, restorer pkgrestore.Restorer, backupLister velerov1listers.BackupLister, - k8sClient client.Client, + kbclient client.Client, snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, logger logrus.FieldLogger, restoreLogLevel logrus.Level, @@ -118,7 +118,7 @@ func NewRestoreController( restorer: restorer, backupLister: backupLister, restoreLister: restoreInformer.Lister(), - k8sClient: k8sClient, + kbclient: kbclient, snapshotLocationLister: snapshotLocationLister, restoreLogLevel: restoreLogLevel, defaultBackupLocation: defaultBackupLocation, @@ -400,7 +400,7 @@ func (c *restoreController) fetchBackupInfo(backupName string, pluginManager cli } location := &velerov1api.BackupStorageLocation{} - if err := c.k8sClient.Get(context.Background(), client.ObjectKey{ + if err := c.kbclient.Get(context.Background(), client.ObjectKey{ Namespace: c.namespace, Name: backup.Spec.StorageLocation, }, location); err != nil { diff --git a/pkg/restic/common.go b/pkg/restic/common.go index 7d98ba1213..0a58220273 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -17,11 +17,15 @@ limitations under the License. package restic import ( + "context" "fmt" "os" "strings" "time" + "sigs.k8s.io/controller-runtime/pkg/client" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/pkg/errors" corev1api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -284,12 +288,20 @@ func TempCACertFile(caCert []byte, bsl string, fs filesystem.Interface) (string, return name, nil } -func GetCACert(loc *velerov1api.BackupStorageLocation) ([]byte, error) { - if loc.Spec.ObjectStorage == nil { +func GetCACert(client client.Client, namespace, name string) (*velerov1api.BackupStorageLocation, error) { + location := &velerov1api.BackupStorageLocation{} + if err := client.Get(context.Background(), kbclient.ObjectKey{ + Namespace: namespace, + Name: name, + }, location); err != nil { + return nil, err + } + + if location.Spec.ObjectStorage == nil { return nil, nil } - return loc.Spec.ObjectStorage.CACert, nil + return location, nil } // NewPodVolumeRestoreListOptions creates a ListOptions with a label selector configured to diff --git a/pkg/restic/common_test.go b/pkg/restic/common_test.go index eb92c0eb6e..7ce835a00b 100644 --- a/pkg/restic/common_test.go +++ b/pkg/restic/common_test.go @@ -400,21 +400,14 @@ func TestTempCACertFile(t *testing.T) { } ) - //TODO(carlisia): not sure this test makes sense anymore since fakeClient := newFakeClient(t) fakeClient.Create(context.Background(), bsl) - locationCreated := &velerov1api.BackupStorageLocation{} - err := fakeClient.Get(context.Background(), client.ObjectKey{ - Namespace: bsl.Namespace, - Name: bsl.Name, - }, locationCreated) - require.NoError(t, err) - // expect temp file to be created with cacert value - caCert, err := GetCACert(locationCreated) + location, err := GetCACert(fakeClient, bsl.Namespace, bsl.Name) require.NoError(t, err) + caCert := location.Spec.ObjectStorage.CACert fileName, err := TempCACertFile(caCert, "default", fs) require.NoError(t, err) diff --git a/pkg/restic/repository_manager.go b/pkg/restic/repository_manager.go index 76980bbd42..edb404d53e 100644 --- a/pkg/restic/repository_manager.go +++ b/pkg/restic/repository_manager.go @@ -29,8 +29,7 @@ import ( corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" - kbcache "sigs.k8s.io/controller-runtime/pkg/cache" - k8sclient "sigs.k8s.io/controller-runtime/pkg/client" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" @@ -87,7 +86,7 @@ type repositoryManager struct { secretsLister corev1listers.SecretLister repoLister velerov1listers.ResticRepositoryLister repoInformerSynced cache.InformerSynced - kbCache kbcache.Cache + kbClient kbclient.Client log logrus.FieldLogger repoLocker *repoLocker repoEnsurer *repositoryEnsurer @@ -105,7 +104,7 @@ func NewRepositoryManager( secretsInformer cache.SharedIndexInformer, repoInformer velerov1informers.ResticRepositoryInformer, repoClient velerov1client.ResticRepositoriesGetter, - kbCache kbcache.Cache, + kbClient kbclient.Client, pvcClient corev1client.PersistentVolumeClaimsGetter, pvClient corev1client.PersistentVolumesGetter, log logrus.FieldLogger, @@ -116,7 +115,7 @@ func NewRepositoryManager( secretsLister: corev1listers.NewSecretLister(secretsInformer.GetIndexer()), repoLister: repoInformer.Lister(), repoInformerSynced: repoInformer.Informer().HasSynced, - kbCache: kbCache, + kbClient: kbClient, pvcClient: pvcClient, pvClient: pvClient, log: log, @@ -245,19 +244,12 @@ func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error { cmd.PasswordFile = file - location := &velerov1api.BackupStorageLocation{} - if err := rm.kbCache.Get(context.Background(), k8sclient.ObjectKey{ - Namespace: rm.namespace, - Name: backupLocation, - }, location); err != nil { - return err - } - // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic - caCert, err := GetCACert(location) + location, err := GetCACert(rm.kbClient, rm.namespace, backupLocation) if err != nil { return err } + caCert := location.Spec.ObjectStorage.CACert var caCertFile string if caCert != nil { caCertFile, err = TempCACertFile(caCert, backupLocation, rm.fileSystem) @@ -270,20 +262,12 @@ func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error { cmd.CACertFile = caCertFile if strings.HasPrefix(cmd.RepoIdentifier, "azure") { - if !rm.kbCache.WaitForCacheSync(rm.ctx.Done()) { - return errors.New("timed out waiting for cache to sync") - } - env, err := AzureCmdEnv(location) if err != nil { return err } cmd.Env = env } else if strings.HasPrefix(cmd.RepoIdentifier, "s3") { - if !rm.kbCache.WaitForCacheSync(rm.ctx.Done()) { - return errors.New("timed out waiting for cache to sync") - } - env, err := S3CmdEnv(location) if err != nil { return err From fd849b29f392b3317af64d4013c41cc11108efea Mon Sep 17 00:00:00 2001 From: Carlisia Date: Thu, 11 Jun 2020 13:59:02 -0700 Subject: [PATCH 20/34] Fix imports/aliases Signed-off-by: Carlisia --- pkg/controller/download_request_controller.go | 17 ++-- .../download_request_controller_test.go | 42 +++++----- pkg/controller/gc_controller_test.go | 12 +-- pkg/controller/restore_controller_test.go | 82 +++++++++---------- 4 files changed, 76 insertions(+), 77 deletions(-) diff --git a/pkg/controller/download_request_controller.go b/pkg/controller/download_request_controller.go index 25d4b5d869..9538563dde 100644 --- a/pkg/controller/download_request_controller.go +++ b/pkg/controller/download_request_controller.go @@ -32,7 +32,6 @@ import ( "k8s.io/client-go/tools/cache" "sigs.k8s.io/controller-runtime/pkg/client" - v1 "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" velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" @@ -88,7 +87,7 @@ func NewDownloadRequestController( AddFunc: func(obj interface{}) { key, err := cache.MetaNamespaceKeyFunc(obj) if err != nil { - downloadRequest := obj.(*v1.DownloadRequest) + downloadRequest := obj.(*velerov1api.DownloadRequest) c.logger.WithError(errors.WithStack(err)). WithField("downloadRequest", downloadRequest.Name). Error("Error creating queue key, item not added to queue") @@ -124,9 +123,9 @@ func (c *downloadRequestController) processDownloadRequest(key string) error { } switch downloadRequest.Status.Phase { - case "", v1.DownloadRequestPhaseNew: + case "", velerov1api.DownloadRequestPhaseNew: return c.generatePreSignedURL(downloadRequest, log) - case v1.DownloadRequestPhaseProcessed: + case velerov1api.DownloadRequestPhaseProcessed: return c.deleteIfExpired(downloadRequest) } @@ -137,7 +136,7 @@ const signedURLTTL = 10 * time.Minute // generatePreSignedURL generates a pre-signed URL for downloadRequest, changes the phase to // Processed, and persists the changes to storage. -func (c *downloadRequestController) generatePreSignedURL(downloadRequest *v1.DownloadRequest, log logrus.FieldLogger) error { +func (c *downloadRequestController) generatePreSignedURL(downloadRequest *velerov1api.DownloadRequest, log logrus.FieldLogger) error { update := downloadRequest.DeepCopy() var ( @@ -146,7 +145,7 @@ func (c *downloadRequestController) generatePreSignedURL(downloadRequest *v1.Dow ) switch downloadRequest.Spec.Target.Kind { - case v1.DownloadTargetKindRestoreLog, v1.DownloadTargetKindRestoreResults: + case velerov1api.DownloadTargetKindRestoreLog, velerov1api.DownloadTargetKindRestoreResults: restore, err := c.restoreLister.Restores(downloadRequest.Namespace).Get(downloadRequest.Spec.Target.Name) if err != nil { return errors.Wrap(err, "error getting Restore") @@ -182,7 +181,7 @@ func (c *downloadRequestController) generatePreSignedURL(downloadRequest *v1.Dow return err } - update.Status.Phase = v1.DownloadRequestPhaseProcessed + update.Status.Phase = velerov1api.DownloadRequestPhaseProcessed update.Status.Expiration = &metav1.Time{Time: c.clock.Now().Add(persistence.DownloadURLTTL)} _, err = patchDownloadRequest(downloadRequest, update, c.downloadRequestClient) @@ -190,7 +189,7 @@ func (c *downloadRequestController) generatePreSignedURL(downloadRequest *v1.Dow } // deleteIfExpired deletes downloadRequest if it has expired. -func (c *downloadRequestController) deleteIfExpired(downloadRequest *v1.DownloadRequest) error { +func (c *downloadRequestController) deleteIfExpired(downloadRequest *velerov1api.DownloadRequest) error { log := c.logger.WithField("key", kube.NamespaceAndName(downloadRequest)) log.Info("checking for expiration of DownloadRequest") if downloadRequest.Status.Expiration.Time.After(c.clock.Now()) { @@ -222,7 +221,7 @@ func (c *downloadRequestController) resync() { } } -func patchDownloadRequest(original, updated *v1.DownloadRequest, client velerov1client.DownloadRequestsGetter) (*v1.DownloadRequest, error) { +func patchDownloadRequest(original, updated *velerov1api.DownloadRequest, client velerov1client.DownloadRequestsGetter) (*velerov1api.DownloadRequest, error) { origBytes, err := json.Marshal(original) if err != nil { return nil, errors.Wrap(err, "error marshalling original download request") diff --git a/pkg/controller/download_request_controller_test.go b/pkg/controller/download_request_controller_test.go index 4e667dfc5e..4ae18e01c6 100644 --- a/pkg/controller/download_request_controller_test.go +++ b/pkg/controller/download_request_controller_test.go @@ -87,19 +87,19 @@ func newDownloadRequestTestHarness(t *testing.T, fakeClient client.Client) *down } } -func newDownloadRequest(phase v1.DownloadRequestPhase, targetKind v1.DownloadTargetKind, targetName string) *v1.DownloadRequest { +func newDownloadRequest(phase velerov1api.DownloadRequestPhase, targetKind velerov1api.DownloadTargetKind, targetName string) *v1.DownloadRequest { return &v1.DownloadRequest{ ObjectMeta: metav1.ObjectMeta{ Name: "a-download-request", - Namespace: v1.DefaultNamespace, + Namespace: velerov1api.DefaultNamespace, }, - Spec: v1.DownloadRequestSpec{ - Target: v1.DownloadTarget{ + Spec: velerov1api.DownloadRequestSpec{ + Target: velerov1api.DownloadTarget{ Kind: targetKind, Name: targetName, }, }, - Status: v1.DownloadRequestStatus{ + Status: velerov1api.DownloadRequestStatus{ Phase: phase, }, } @@ -109,7 +109,7 @@ func newBackupLocation(name, provider, bucket string) *velerov1api.BackupStorage return &velerov1api.BackupStorageLocation{ ObjectMeta: metav1.ObjectMeta{ Name: name, - Namespace: v1.DefaultNamespace, + Namespace: velerov1api.DefaultNamespace, }, Spec: velerov1api.BackupStorageLocationSpec{ Provider: provider, @@ -153,14 +153,14 @@ func TestProcessDownloadRequest(t *testing.T) { }, { name: "backup contents request for nonexistent backup returns an error", - downloadRequest: newDownloadRequest("", v1.DownloadTargetKindBackupContents, "a-backup"), + downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindBackupContents, "a-backup"), backup: builder.ForBackup(v1.DefaultNamespace, "non-matching-backup").StorageLocation("a-location").Result(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectedErr: "backup.velero.io \"a-backup\" not found", }, { name: "restore log request for nonexistent restore returns an error", - downloadRequest: newDownloadRequest("", v1.DownloadTargetKindRestoreLog, "a-backup-20170912150214"), + downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindRestoreLog, "a-backup-20170912150214"), restore: builder.ForRestore(v1.DefaultNamespace, "non-matching-restore").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(), backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), @@ -168,42 +168,42 @@ func TestProcessDownloadRequest(t *testing.T) { }, { name: "backup contents request for backup with nonexistent location returns an error", - downloadRequest: newDownloadRequest("", v1.DownloadTargetKindBackupContents, "a-backup"), + downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindBackupContents, "a-backup"), backup: defaultBackup(), backupLocation: newBackupLocation("non-matching-location", "a-provider", "a-bucket"), expectedErr: "backupstoragelocations.velero.io \"a-location\" not found", }, { name: "backup contents request with phase '' gets a url", - downloadRequest: newDownloadRequest("", v1.DownloadTargetKindBackupContents, "a-backup"), + downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindBackupContents, "a-backup"), backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, }, { name: "backup contents request with phase 'New' gets a url", - downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, v1.DownloadTargetKindBackupContents, "a-backup"), + downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, velerov1api.DownloadTargetKindBackupContents, "a-backup"), backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, }, { name: "backup log request with phase '' gets a url", - downloadRequest: newDownloadRequest("", v1.DownloadTargetKindBackupLog, "a-backup"), + downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindBackupLog, "a-backup"), backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, }, { name: "backup log request with phase 'New' gets a url", - downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, v1.DownloadTargetKindBackupLog, "a-backup"), + downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, velerov1api.DownloadTargetKindBackupLog, "a-backup"), backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, }, { name: "restore log request with phase '' gets a url", - downloadRequest: newDownloadRequest("", v1.DownloadTargetKindRestoreLog, "a-backup-20170912150214"), + downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindRestoreLog, "a-backup-20170912150214"), restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(), backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), @@ -211,7 +211,7 @@ func TestProcessDownloadRequest(t *testing.T) { }, { name: "restore log request with phase 'New' gets a url", - downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, v1.DownloadTargetKindRestoreLog, "a-backup-20170912150214"), + downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, velerov1api.DownloadTargetKindRestoreLog, "a-backup-20170912150214"), restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(), backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), @@ -219,7 +219,7 @@ func TestProcessDownloadRequest(t *testing.T) { }, { name: "restore results request with phase '' gets a url", - downloadRequest: newDownloadRequest("", v1.DownloadTargetKindRestoreResults, "a-backup-20170912150214"), + downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindRestoreResults, "a-backup-20170912150214"), restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(), backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), @@ -227,7 +227,7 @@ func TestProcessDownloadRequest(t *testing.T) { }, { name: "restore results request with phase 'New' gets a url", - downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, v1.DownloadTargetKindRestoreResults, "a-backup-20170912150214"), + downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, velerov1api.DownloadTargetKindRestoreResults, "a-backup-20170912150214"), restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(), backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), @@ -235,12 +235,12 @@ func TestProcessDownloadRequest(t *testing.T) { }, { name: "request with phase 'Processed' is not deleted if not expired", - downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseProcessed, v1.DownloadTargetKindBackupLog, "a-backup-20170912150214"), + downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseProcessed, velerov1api.DownloadTargetKindBackupLog, "a-backup-20170912150214"), backup: defaultBackup(), }, { name: "request with phase 'Processed' is deleted if expired", - downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseProcessed, v1.DownloadTargetKindBackupLog, "a-backup-20170912150214"), + downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseProcessed, velerov1api.DownloadTargetKindBackupLog, "a-backup-20170912150214"), backup: defaultBackup(), expired: true, }, @@ -261,7 +261,7 @@ func TestProcessDownloadRequest(t *testing.T) { // Set .status.expiration properly for processed requests. Since "expired" is relative to the controller's // clock time, it's easier to do this here than as part of the test case definitions. - if tc.downloadRequest != nil && tc.downloadRequest.Status.Phase == v1.DownloadRequestPhaseProcessed { + if tc.downloadRequest != nil && tc.downloadRequest.Status.Phase == velerov1api.DownloadRequestPhaseProcessed { if tc.expired { tc.downloadRequest.Status.Expiration = &metav1.Time{Time: harness.controller.clock.Now().Add(-1 * time.Minute)} } else { @@ -312,7 +312,7 @@ func TestProcessDownloadRequest(t *testing.T) { assert.True(t, velerotest.TimesAreEqual(harness.controller.clock.Now().Add(signedURLTTL), output.Status.Expiration.Time), "expiration does not match") } - if tc.downloadRequest != nil && tc.downloadRequest.Status.Phase == v1.DownloadRequestPhaseProcessed { + if tc.downloadRequest != nil && tc.downloadRequest.Status.Phase == velerov1api.DownloadRequestPhaseProcessed { res, err := harness.client.VeleroV1().DownloadRequests(tc.downloadRequest.Namespace).Get(tc.downloadRequest.Name, metav1.GetOptions{}) if tc.expired { diff --git a/pkg/controller/gc_controller_test.go b/pkg/controller/gc_controller_test.go index 38d6f5a738..6259396bda 100644 --- a/pkg/controller/gc_controller_test.go +++ b/pkg/controller/gc_controller_test.go @@ -199,15 +199,15 @@ func TestGCControllerProcessQueueItem(t *testing.T) { deleteBackupRequests: []*api.DeleteBackupRequest{ { ObjectMeta: metav1.ObjectMeta{ - Namespace: api.DefaultNamespace, + Namespace: velerov1api.DefaultNamespace, Name: "foo", Labels: map[string]string{ api.BackupNameLabel: "backup-1", api.BackupUIDLabel: "", }, }, - Status: api.DeleteBackupRequestStatus{ - Phase: api.DeleteBackupRequestPhaseInProgress, + Status: velerov1api.DeleteBackupRequestStatus{ + Phase: velerov1api.DeleteBackupRequestPhaseInProgress, }, }, }, @@ -220,15 +220,15 @@ func TestGCControllerProcessQueueItem(t *testing.T) { deleteBackupRequests: []*api.DeleteBackupRequest{ { ObjectMeta: metav1.ObjectMeta{ - Namespace: api.DefaultNamespace, + Namespace: velerov1api.DefaultNamespace, Name: "foo", Labels: map[string]string{ api.BackupNameLabel: "backup-1", api.BackupUIDLabel: "", }, }, - Status: api.DeleteBackupRequestStatus{ - Phase: api.DeleteBackupRequestPhaseProcessed, + Status: velerov1api.DeleteBackupRequestStatus{ + Phase: velerov1api.DeleteBackupRequestPhaseProcessed, }, }, }, diff --git a/pkg/controller/restore_controller_test.go b/pkg/controller/restore_controller_test.go index 0605738ac3..a7d8cd6929 100644 --- a/pkg/controller/restore_controller_test.go +++ b/pkg/controller/restore_controller_test.go @@ -254,7 +254,7 @@ func TestProcessQueueItem(t *testing.T) { { name: "restore with both namespace in both includedNamespaces and excludedNamespaces fails validation", location: defaultStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "another-1", "*", api.RestorePhaseNew).ExcludedNamespaces("another-1").Result(), + restore: NewRestore("foo", "bar", "backup-1", "another-1", "*", velerov1api.RestorePhaseNew).ExcludedNamespaces("another-1").Result(), backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), @@ -263,7 +263,7 @@ func TestProcessQueueItem(t *testing.T) { { name: "restore with resource in both includedResources and excludedResources fails validation", location: defaultStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "*", "a-resource", api.RestorePhaseNew).ExcludedResources("a-resource").Result(), + restore: NewRestore("foo", "bar", "backup-1", "*", "a-resource", velerov1api.RestorePhaseNew).ExcludedResources("a-resource").Result(), backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), @@ -271,14 +271,14 @@ func TestProcessQueueItem(t *testing.T) { }, { name: "new restore with empty backup and schedule names fails validation", - restore: NewRestore("foo", "bar", "", "ns-1", "", api.RestorePhaseNew).Result(), + restore: NewRestore("foo", "bar", "", "ns-1", "", velerov1api.RestorePhaseNew).Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), expectedValidationErrors: []string{"Either a backup or schedule must be specified as a source for the restore, but not both"}, }, { name: "new restore with backup and schedule names provided fails validation", - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Schedule("sched-1").Result(), + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseNew).Schedule("sched-1").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), expectedValidationErrors: []string{"Either a backup or schedule must be specified as a source for the restore, but not both"}, @@ -286,15 +286,15 @@ func TestProcessQueueItem(t *testing.T) { { name: "valid restore with schedule name gets executed", location: defaultStorageLocation, - restore: NewRestore("foo", "bar", "", "ns-1", "", api.RestorePhaseNew).Schedule("sched-1").Result(), + restore: NewRestore("foo", "bar", "", "ns-1", "", velerov1api.RestorePhaseNew).Schedule("sched-1").Result(), backup: defaultBackup().StorageLocation("default").ObjectMeta(builder.WithLabels(api.ScheduleNameLabel, "sched-1")).Phase(api.BackupPhaseCompleted).Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseInProgress), - expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).Schedule("sched-1").Result(), + expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseInProgress).Schedule("sched-1").Result(), }, { name: "restore with non-existent backup name fails", - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "*", api.RestorePhaseNew).Result(), + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "*", velerov1api.RestorePhaseNew).Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), expectedValidationErrors: []string{"Error retrieving backup: backup.velero.io \"backup-1\" not found"}, @@ -303,28 +303,28 @@ func TestProcessQueueItem(t *testing.T) { { name: "restorer throwing an error causes the restore to fail", location: defaultStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Result(), + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseNew).Result(), backup: defaultBackup().StorageLocation("default").Result(), restorerError: errors.New("blarg"), expectedErr: false, expectedPhase: string(api.RestorePhaseInProgress), expectedFinalPhase: string(api.RestorePhasePartiallyFailed), expectedRestoreErrors: 1, - expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).Result(), + expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseInProgress).Result(), }, { name: "valid restore gets executed", location: defaultStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Result(), + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseNew).Result(), backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseInProgress), - expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).Result(), + expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseInProgress).Result(), }, { name: "restoration of nodes is not supported", location: defaultStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "nodes", api.RestorePhaseNew).Result(), + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "nodes", velerov1api.RestorePhaseNew).Result(), backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), @@ -336,7 +336,7 @@ func TestProcessQueueItem(t *testing.T) { { name: "restoration of events is not supported", location: defaultStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events", api.RestorePhaseNew).Result(), + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events", velerov1api.RestorePhaseNew).Result(), backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), @@ -348,7 +348,7 @@ func TestProcessQueueItem(t *testing.T) { { name: "restoration of events.events.k8s.io is not supported", location: defaultStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events.events.k8s.io", api.RestorePhaseNew).Result(), + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events.events.k8s.io", velerov1api.RestorePhaseNew).Result(), backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), @@ -360,7 +360,7 @@ func TestProcessQueueItem(t *testing.T) { { name: "restoration of backups.velero.io is not supported", location: defaultStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "backups.velero.io", api.RestorePhaseNew).Result(), + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "backups.velero.io", velerov1api.RestorePhaseNew).Result(), backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), @@ -372,7 +372,7 @@ func TestProcessQueueItem(t *testing.T) { { name: "restoration of restores.velero.io is not supported", location: defaultStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "restores.velero.io", api.RestorePhaseNew).Result(), + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "restores.velero.io", velerov1api.RestorePhaseNew).Result(), backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), @@ -384,7 +384,7 @@ func TestProcessQueueItem(t *testing.T) { { name: "backup download error results in failed restore", location: defaultStorageLocation, - restore: NewRestore(api.DefaultNamespace, "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Result(), + restore: NewRestore(api.DefaultNamespace, "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseNew).Result(), expectedPhase: string(api.RestorePhaseInProgress), expectedFinalPhase: string(api.RestorePhaseFailed), backupStoreGetBackupContentsErr: errors.New("Couldn't download backup"), @@ -469,7 +469,7 @@ func TestProcessQueueItem(t *testing.T) { // these are the fields that we expect to be set by // the controller - res.Status.Phase = api.RestorePhase(phase) + res.Status.Phase = velerov1api.RestorePhase(phase) backupName, found, err := unstructured.NestedString(patchMap, "spec", "backupName") if found { @@ -553,9 +553,9 @@ func TestProcessQueueItem(t *testing.T) { } type StatusPatch struct { - Phase api.RestorePhase `json:"phase"` - ValidationErrors []string `json:"validationErrors"` - Errors int `json:"errors"` + Phase velerov1api.RestorePhase `json:"phase"` + ValidationErrors []string `json:"validationErrors"` + Errors int `json:"errors"` } type Patch struct { @@ -575,7 +575,7 @@ func TestProcessQueueItem(t *testing.T) { expected := Patch{ Status: StatusPatch{ - Phase: api.RestorePhase(test.expectedPhase), + Phase: velerov1api.RestorePhase(test.expectedPhase), ValidationErrors: test.expectedValidationErrors, }, } @@ -600,7 +600,7 @@ func TestProcessQueueItem(t *testing.T) { expected = Patch{ Status: StatusPatch{ - Phase: api.RestorePhaseCompleted, + Phase: velerov1api.RestorePhaseCompleted, Errors: test.expectedRestoreErrors, }, } @@ -608,7 +608,7 @@ func TestProcessQueueItem(t *testing.T) { if test.expectedFinalPhase != "" { expected = Patch{ Status: StatusPatch{ - Phase: api.RestorePhase(test.expectedFinalPhase), + Phase: velerov1api.RestorePhase(test.expectedFinalPhase), Errors: test.expectedRestoreErrors, }, } @@ -653,10 +653,10 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) { restore := &api.Restore{ ObjectMeta: metav1.ObjectMeta{ - Namespace: api.DefaultNamespace, + Namespace: velerov1api.DefaultNamespace, Name: "restore-1", }, - Spec: api.RestoreSpec{ + Spec: velerov1api.RestoreSpec{ ScheduleName: "schedule-1", }, } @@ -740,7 +740,7 @@ func TestMostRecentCompletedBackup(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "a", }, - Status: api.BackupStatus{ + Status: velerov1api.BackupStatus{ Phase: "", }, }, @@ -748,32 +748,32 @@ func TestMostRecentCompletedBackup(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "b", }, - Status: api.BackupStatus{ - Phase: api.BackupPhaseNew, + Status: velerov1api.BackupStatus{ + Phase: velerov1api.BackupPhaseNew, }, }, { ObjectMeta: metav1.ObjectMeta{ Name: "c", }, - Status: api.BackupStatus{ - Phase: api.BackupPhaseInProgress, + Status: velerov1api.BackupStatus{ + Phase: velerov1api.BackupPhaseInProgress, }, }, { ObjectMeta: metav1.ObjectMeta{ Name: "d", }, - Status: api.BackupStatus{ - Phase: api.BackupPhaseFailedValidation, + Status: velerov1api.BackupStatus{ + Phase: velerov1api.BackupPhaseFailedValidation, }, }, { ObjectMeta: metav1.ObjectMeta{ Name: "e", }, - Status: api.BackupStatus{ - Phase: api.BackupPhaseFailed, + Status: velerov1api.BackupStatus{ + Phase: velerov1api.BackupPhaseFailed, }, }, } @@ -786,8 +786,8 @@ func TestMostRecentCompletedBackup(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "foo", }, - Status: api.BackupStatus{ - Phase: api.BackupPhaseCompleted, + Status: velerov1api.BackupStatus{ + Phase: velerov1api.BackupPhaseCompleted, StartTimestamp: &metav1.Time{Time: now}, }, }) @@ -796,8 +796,8 @@ func TestMostRecentCompletedBackup(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "bar", }, - Status: api.BackupStatus{ - Phase: api.BackupPhaseCompleted, + Status: velerov1api.BackupStatus{ + Phase: velerov1api.BackupPhaseCompleted, StartTimestamp: &metav1.Time{Time: now.Add(time.Second)}, }, } @@ -806,7 +806,7 @@ func TestMostRecentCompletedBackup(t *testing.T) { assert.Equal(t, expected, mostRecentCompletedBackup(backups)) } -func NewRestore(ns, name, backup, includeNS, includeResource string, phase api.RestorePhase) *builder.RestoreBuilder { +func NewRestore(ns, name, backup, includeNS, includeResource string, phase velerov1api.RestorePhase) *builder.RestoreBuilder { restore := builder.ForRestore(ns, name).Phase(phase).Backup(backup) if includeNS != "" { @@ -824,7 +824,7 @@ func NewRestore(ns, name, backup, includeNS, includeResource string, phase api.R type fakeRestorer struct { mock.Mock - calledWithArg api.Restore + calledWithArg velerov1api.Restore } func (r *fakeRestorer) Restore( From e0f85520f2423c2a1e6a17875f187cffcea8bf9d Mon Sep 17 00:00:00 2001 From: Carlisia Date: Thu, 11 Jun 2020 14:23:20 -0700 Subject: [PATCH 21/34] More code reviews Signed-off-by: Carlisia --- pkg/cmd/cli/restic/server.go | 3 +++ pkg/controller/pod_volume_backup_controller.go | 18 ++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/restic/server.go index 2fa86b2996..63dc624377 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/restic/server.go @@ -22,6 +22,8 @@ import ( "strings" "sync" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -147,6 +149,7 @@ func newResticServer(logger logrus.FieldLogger, factory client.Factory) (*restic } ctrl.SetLogger(zap.New(zap.UseDevMode(true))) + velerov1api.AddToScheme(scheme) mgr, err := ctrl.NewManager(clientConfig, ctrl.Options{ Scheme: scheme, }) diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index c51f0c4ef6..22600bf050 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -43,22 +43,20 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/kube" - k8scache "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" ) type podVolumeBackupController struct { *genericController - podVolumeBackupClient velerov1client.PodVolumeBackupsGetter - podVolumeBackupLister listers.PodVolumeBackupLister - secretLister corev1listers.SecretLister - podLister corev1listers.PodLister - pvcLister corev1listers.PersistentVolumeClaimLister - pvLister corev1listers.PersistentVolumeLister - backupLocationInformer k8scache.Informer - kbClient client.Client - nodeName string + podVolumeBackupClient velerov1client.PodVolumeBackupsGetter + podVolumeBackupLister listers.PodVolumeBackupLister + secretLister corev1listers.SecretLister + podLister corev1listers.PodLister + pvcLister corev1listers.PersistentVolumeClaimLister + pvLister corev1listers.PersistentVolumeLister + kbClient client.Client + nodeName string processBackupFunc func(*velerov1api.PodVolumeBackup) error fileSystem filesystem.Interface From b7a3246d1b5b701edcc7c4645f3fb75f4eab39a6 Mon Sep 17 00:00:00 2001 From: Carlisia Date: Tue, 16 Jun 2020 07:51:46 -0700 Subject: [PATCH 22/34] Add waitforcachesync Signed-off-by: Carlisia --- pkg/cmd/server/server.go | 2 ++ pkg/controller/backup_sync_controller.go | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 2cfe75338b..04c3e2053f 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -636,8 +636,10 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string backupSyncControllerRunInfo := func() controllerRunInfo { backupSyncContoller := controller.NewBackupSyncController( + s.ctx, s.veleroClient.VeleroV1(), s.mgr.GetClient(), + s.mgr.GetCache(), s.veleroClient.VeleroV1(), s.sharedInformerFactory.Velero().V1().Backups().Lister(), s.config.backupSyncPeriod, diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index ec7086e377..e7028e17bb 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -37,14 +37,17 @@ import ( "github.com/vmware-tanzu/velero/pkg/persistence" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" + "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" ) type backupSyncController struct { *genericController + ctx context.Context backupClient velerov1client.BackupsGetter kbclient client.Client + kbCache cache.Cache podVolumeBackupClient velerov1client.PodVolumeBackupsGetter backupLister velerov1listers.BackupLister csiSnapshotClient *snapshotterClientSet.Clientset @@ -57,8 +60,10 @@ type backupSyncController struct { } func NewBackupSyncController( + ctx context.Context, backupClient velerov1client.BackupsGetter, kbclient client.Client, + kbCache cache.Cache, podVolumeBackupClient velerov1client.PodVolumeBackupsGetter, backupLister velerov1listers.BackupLister, syncPeriod time.Duration, @@ -76,6 +81,7 @@ func NewBackupSyncController( c := &backupSyncController{ genericController: newGenericController("backup-sync", logger), + ctx: ctx, backupClient: backupClient, kbclient: kbclient, podVolumeBackupClient: podVolumeBackupClient, @@ -95,6 +101,11 @@ func NewBackupSyncController( c.resyncFunc = c.run c.resyncPeriod = 30 * time.Second + if !c.kbCache.WaitForCacheSync(c.ctx.Done()) { + logger.Info("Timed out waiting for caches to sync") + return c + } + return c } From 47144543282dddde522f0dac42c5ca2bce98df3f Mon Sep 17 00:00:00 2001 From: Carlisia Date: Wed, 17 Jun 2020 09:21:13 -0700 Subject: [PATCH 23/34] Have manager register ALL controllers This will allow for proper cache management. Signed-off-by: Carlisia --- pkg/cmd/server/server.go | 58 +++++++++++++++--------- pkg/controller/backup_sync_controller.go | 11 ----- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 04c3e2053f..ac5635c2d1 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -25,7 +25,6 @@ import ( "os" "reflect" "strings" - "sync" "time" "github.com/pkg/errors" @@ -614,7 +613,6 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger.Info("Starting controllers") ctx := s.ctx - var wg sync.WaitGroup go func() { metricsMux := http.NewServeMux() @@ -636,10 +634,8 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string backupSyncControllerRunInfo := func() controllerRunInfo { backupSyncContoller := controller.NewBackupSyncController( - s.ctx, s.veleroClient.VeleroV1(), s.mgr.GetClient(), - s.mgr.GetCache(), s.veleroClient.VeleroV1(), s.sharedInformerFactory.Velero().V1().Backups().Lister(), s.config.backupSyncPeriod, @@ -901,33 +897,53 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string for i := range controllers { controllerRunInfo := controllers[i] - - wg.Add(1) - go func() { - controllerRunInfo.controller.Run(ctx, controllerRunInfo.numWorkers) - wg.Done() - }() + // Adding the controllers to the manager will register them as a runnable, + // so the manager will ensure the cache is started and ready before all controller are started + s.mgr.Add(controllerRunnable(controllerRunInfo.controller, controllerRunInfo.numWorkers)) } - s.logger.Info("Server started successfully") + s.logger.Info("Server starting...") - wg.Add(1) - go func() { - defer wg.Done() - // +kubebuilder:scaffold:builder - if err := s.mgr.Start(ctrl.SetupSignalHandler()); err != nil { - s.logger.Fatal("Problem starting manager", err) - } - }() + if err := s.mgr.Start(s.ctx.Done()); err != nil { + s.logger.Fatal("Problem starting manager", err) + } - <-ctx.Done() + s.logger.Info("Server started successfully") s.logger.Info("Waiting for all controllers to shut down gracefully") - wg.Wait() return nil } +// controllerRunnable will turn an existing controller into a Runnable +func controllerRunnable(p controller.Interface, numWorkers int) manager.Runnable { + return manager.RunnableFunc(func(stop <-chan struct{}) error { + ctx, cancel := contextForChannel(stop) + defer cancel() + + return p.Run(ctx, numWorkers) + }) +} + +// contextForChannel derives a child context from a parent channel. +// +// The derived context's Done channel is closed when the returned cancel function +// is called or when the parent channel is closed, whichever happens first. +// +// Note the caller must *always* call the CancelFunc, otherwise resources may be leaked. +func contextForChannel(parentCh <-chan struct{}) (context.Context, context.CancelFunc) { + ctx, cancel := context.WithCancel(context.Background()) + + go func() { + select { + case <-parentCh: + cancel() + case <-ctx.Done(): + } + }() + return ctx, cancel +} + func (s *server) runProfiler() { mux := http.NewServeMux() mux.HandleFunc("/debug/pprof/", pprof.Index) diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index e7028e17bb..ec7086e377 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -37,17 +37,14 @@ import ( "github.com/vmware-tanzu/velero/pkg/persistence" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" - "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" ) type backupSyncController struct { *genericController - ctx context.Context backupClient velerov1client.BackupsGetter kbclient client.Client - kbCache cache.Cache podVolumeBackupClient velerov1client.PodVolumeBackupsGetter backupLister velerov1listers.BackupLister csiSnapshotClient *snapshotterClientSet.Clientset @@ -60,10 +57,8 @@ type backupSyncController struct { } func NewBackupSyncController( - ctx context.Context, backupClient velerov1client.BackupsGetter, kbclient client.Client, - kbCache cache.Cache, podVolumeBackupClient velerov1client.PodVolumeBackupsGetter, backupLister velerov1listers.BackupLister, syncPeriod time.Duration, @@ -81,7 +76,6 @@ func NewBackupSyncController( c := &backupSyncController{ genericController: newGenericController("backup-sync", logger), - ctx: ctx, backupClient: backupClient, kbclient: kbclient, podVolumeBackupClient: podVolumeBackupClient, @@ -101,11 +95,6 @@ func NewBackupSyncController( c.resyncFunc = c.run c.resyncPeriod = 30 * time.Second - if !c.kbCache.WaitForCacheSync(c.ctx.Done()) { - logger.Info("Timed out waiting for caches to sync") - return c - } - return c } From 041adf8a12bee02b64cbc40cfdc713ac396397b4 Mon Sep 17 00:00:00 2001 From: Carlisia Date: Wed, 17 Jun 2020 13:40:14 -0700 Subject: [PATCH 24/34] Status subresource is now enabled; cleanup Signed-off-by: Carlisia --- config/crd/bases/velero.io_backupstoragelocations.yaml | 10 ++++++++++ config/crd/crds/crds.go | 2 +- pkg/apis/velero/v1/backupstoragelocation_types.go | 9 ++++----- pkg/cmd/server/server.go | 2 -- pkg/cmd/util/output/backup_storage_location_printer.go | 2 +- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/config/crd/bases/velero.io_backupstoragelocations.yaml b/config/crd/bases/velero.io_backupstoragelocations.yaml index 1eb0951b20..0a4eb4fb1d 100644 --- a/config/crd/bases/velero.io_backupstoragelocations.yaml +++ b/config/crd/bases/velero.io_backupstoragelocations.yaml @@ -8,6 +8,14 @@ metadata: creationTimestamp: null name: backupstoragelocations.velero.io spec: + additionalPrinterColumns: + - JSONPath: .status.phase + description: Backup Storage Location status such as Available/Unavailable + name: Phase + type: string + - JSONPath: .metadata.creationTimestamp + name: Age + type: date group: velero.io names: kind: BackupStorageLocation @@ -16,6 +24,8 @@ spec: singular: backupstoragelocation preserveUnknownFields: false scope: Namespaced + subresources: + status: {} validation: openAPIV3Schema: description: BackupStorageLocation is a location where Velero stores backup diff --git a/config/crd/crds/crds.go b/config/crd/crds/crds.go index dc24b08efd..ea51c0d1f9 100644 --- a/config/crd/crds/crds.go +++ b/config/crd/crds/crds.go @@ -30,7 +30,7 @@ import ( var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xecwà\x18C\xe7\xf2(\xf3J(\x90\xday\xa1\xb3@\x87\xa8q:\xa5\x03\xc6\xc5\xd9\xc36\xb8\xb5\x843\xf1\xbe\xe3\xe2\x8cF0\x16\n\xf2\xe6\xfd\xa9n\x10>\x8c\x92\xbb\x15\xe4kLPC[)t\xf1E9{\xceƮ\xafG\x00\xd7R\b{\xbf\x12[T\xe0Pa\xe6\x8d\x1dbôPØ\xf7Q#\xbc\x1b\xf0V\x8d\xff%\x12ێʌ\xc2\x04x=\xc8\xec\x10\xb6e\xd2\x17\x86\x02\xb9A\xc7\xf6+\xcaR\xbd\x0f\x13\aӒ\x0ec\u0084\x9b1i̧\xb0\xfaf\u074cY?\u05cc\x19\x8f\xd7\xe5e-\xfa\xdf\x0f+\x93\xe3>[17\xbd\x85\x1f\xa9\x98\xc4DI\xa1\xf5f\aX\x94\xfe\xfd\x1a\xa4OO)\x92\x10\x9c\x18\x8e\xb2\xa7~\xf7oN\x10\xe7\xea\xf4\xe6t\xdd\a\xea\xf4/\x94B\xfd\xeaߌ\x10\xd8\xd9?F_\xbfP\x00?\xb5\xd7\\\x83\xdc\xd5\x02ȯa'\x95G{\"\x89)rʹ$~)\v\xe6w*\x1a\x85\xf0\xd9\xe1\xfe\x8d\xa2#\xd7\xd4s\x16q\xe3ti\x88)ST\xdd\xddL'\xa1\x02'\x83\xd2b\x11R\xcc'\xe6`\xf3\x84#\x9f\xcf_\xbf`>\xce\x14X\xa2a=\x12>\x9f\xa0\xd9~m\f\x91\x97\x11\x10\x83\x94:\xbb\b\xe5\x82k\x10\xf0\x82\xef!\xba\x10\x1aH \x82^C\x93g!Z\xe4\x9a\x05+\xd4\v\xbe3\x90X\x86\x98Y\xbbL\xf4a\xbc\xe0\xfb\xfc\xa4\x13\xb6\x116\xd2Ų\n\xf1\x8f\x1e0\x038\x87]\xca2\xe0\"R\xf20sD\xc1R\x17\x91F\xe2\xf6\xd9\xe4\xd5bj\xea\x1eA\x90W.\b\x85\xb4\xfd \xcbE\x04\x92\xeb\x04\x87l\x13\xa9\x88\xf4,\x94\xcc\xeb\xd7\x04\xfd\xde\xe8k\xf8j\xfcF\x8f\x05\xab\xddq\xff&]\xac\xdd}1\xe8\xbe\x1a\xcfO>\x9c\x89\x01\xe5\xb3Y\x18\x96\xb1\t\xe9\xe0\x86\x89\xfev-jV\x89\xc3\u0604\f\xab\x16\x89t\xb0єC\x04^\x85jbxٔ\xb7\uf3a2r\\l\xd2F\xafx\xb3[\x0f\xbd'\xb2x\xa1\"\xb7\xa5\xd0G\xab~ex\xdd\"\x88O\xb4/\x84ա2\xaaD\x869\xe4\x153\x91+{\xc2\xe3^fP\xa0ݏo\x04\xedQ\x92\xcf^\xf2\xfaE\xbe4\x8c\xb3\xf4i\xc9֜Ft\xc6\xf9\x1c\x1a+\xb2\xcd\xd99I\xb43\x13\aKy\xe3\x13\xe7\xe8\xe0M\x92\xe3\x86\x19n\x8a<\xe7\x83\x16\xa1\x1e\x16{\xefŜ\xef\xef\xdb\x01\xa5\xb0\xc7\x15\x82\vt\xffK[\x15+\xed\xffA)\xa4\x9d\xb5\xd0\xcf|b\xa2\xb0\xb32V\x85\xda/!\xf8\xd2\x01I\xf3(\xd4iAx\x80,C^\x03U؆ͮ\x17i\\\xc3\xeb\xc1\xb8\xb0+\xee$\xaa\x1c\xe4T\xa4E\xe3\xf2\x05\xdf/\xaf{6~\xb9їa{\xeeYl\xda\xcbg\x00\x1b\xad\xde\xe1\x92W^~{\xe8\xb2H\xeb\x16L\xe2\xe3\xb3e\xc1,esi\x17\xa7e\xf5\x19\f\x85\xa2\xe3\xd8.й\xd28\xbf\x10\x89\a\xe3|\xa8\xd0u\x82ǁ\xda\xd0tN\x13kB v\xe1\xdc\xcb\xd8t\xc2A\x8e\xec\xa4TIRr8X\xe0\xecA\xcc#H\xa1\x14\\66\x1a\xfc\xe3e8\xf6\xe0W\x88\x8cÂ\t\x88\xa4\n\xa55\x19:7\xa5\x0e\xb3\x9ew\xa6\xe0V\x17\xdbDH*\xc2!\xc2Tq/\x8d\xa5a#\xb1\xe6\xac0\xfb\xfe\xadU\x03$Ӧ\xff\xa7\xd5\xec<\x8c\x80Ϡ\x8bB\xe8\xd9͢\x87\xdc]X\x97L!\x82\t!\xbb\xddWl\xc6K#\xbd\xa84\xbf\xee\x06[H\xbda\xe0\xf0\xe9C\xb7cH.\x11\xcf\x0f\xa9\xef\xd2ʆ\xcd\xf5\x83`\x9b\xa5\xe9\x97܇\xc6\xeb\x01-v$կ\fs8\xa7\x8do\xa5\xe7\xcb\x18\x1d\xf0\xb8r\xb0\x93\xd6\xf96\x92\x8e\x0f\xb6>>G\xd1\xf7\xd6~C\x8a\xf2簮U\x00:\x98\xd7tR8r874\xf8\x18\x04A\xee@z@\x9d\x99Js\x11\x83\x8c\x94_\x10X\x1a\x9c\xe9\xec&\x1b\xc6\x12æ\x81\xba*\x96\x10\xbeb\xed\x91z\xa2\xd6ў\xfc\xa3\x90S\x95\xaa4\xce\x12\x93\x97\x05\x9ajbSkFGLOa]爷\x10o\xb2\xa8\n\x10\x051{\x11Gig\x96\x05v\xe5\v\xafBz\xf6\xee\x04\x95]\xbd7d\x14\xa5B\xbf,\x1b\xd8\xe2\xceX\xb6E's\xac\xb7\xcc(s\xa3A\xc0NHU\xd9E\x1e\xed\f\x8e.\x8f죑\u007fLо\xe4\xb5+&\u007f\xb6L\xb9(T\x9b\xf2\xaa\xa5]\x1a\xa8=X\xfc\xc8\x10\xa9\xb4\x92t\xc6|l\x94\x14UI\xe8\xf7\xefaR\x8b7\xdfä\xde\xf8\x1e&u\xc6\xf70\xe9{\x9849\xbe\x87I\xdfä\xdfk\x984\x8dɊ\xebV\x83?ͼ}\xf6\bu\x1c\xb1Q\xc8\xf1T\xff.\xf4^/\xeb\xcb\xdb\f\xaf\x19軌-\xdd+\xee8\xef˹9\xfao\xdc|ݨGʟ\x9474\x96N\xb6\xee-h\xc4\x1b\xea͜o/\x99k*\xe9\xf6$֍\x1d\xa9)ѤW\xf4\xa8O\x9d\xec\x14f\xb6;\x18\x84R\xed\xde\x14a\x1b\xa6\xfcJ\xfd\x8a\xb3\xad\x1f3\r\x1f\xd3m\x9b\xe3\x1c:\t\xed\xbb,\xb2\x9d\x16\xc3_\x99C\x93}\x19\xe3\xdd\x18\xf1$\x03\xbd8~Zw\u007f\xf1&\xf6f\xc0\xab\xf4\x87\x1e\x01\xdc4I)\x8b\u07b7\x9b#\x93N\xc5\xeb\x03\xa7\x9c\x03cAKu=\xd8\x17S߬h\xb3\x13\xfe\\\x86\xa4\xe8,{\x9b\n\xed\x97\xf4n|s\xc7F\xb7'c\xd0ɞwر\xb4\x85tyOF\xb7\xe7bd\x93YЉqv\xa7\xc5|\xbe5\xd9U\xf1\r\xbd\x14\xa9ObjÝ\xe8\xa0X\x10s\xccwK|S\x8f\x04\x1f\xe6M`}VgD\xab\xeba\x02\xe4\xb2~\x88\x05,\x99\xeb}8\xbb\xe3\xe1\xb4\xcb`\x82\x88\xb9>\x87\xf1\x1e\x86\t\xa0\x83\xdd\rK:\x17&`\xd6=\r\x1fد0ӥ\xf01\x9d\x84\xbf4\xf6\x1c\xeb9\x98\xe94\x98\x89L\xa7\xb0\x9a\xe9%X\xdeA0ßo\xec\x16\xa8\xfb\x01\x06\xdfyn\x8f@\xb7\v`\x10\xe4\xc2\u0380\x91\xb3\xffA\x90\v\xfa\x01fN\xfc\a\xc1Nn\x8c\x13\x1a1\xfa\x93Ӣt\a\x93.lM\xc6I\x8fݹ\x03\xc9E\xba\xae\x95)S\xe55\xec>)|\x05\xf3\x1d\x1e\x9e\xd9\xc9\xf3U\x98\xac\xb9\b\x14]y\n~N\xef\t\xfd\xf0\x91Ɇ\xf3Ɗ=\xfed\xb2\xd6M\xdb1\xfa\xbbs;\xd7\"\xa3PSJ\x9f\xfa D\xba\x9f\xd7]:\x14;\xc6*[\xbc\xe7\xd6d_\x84a_ޣ\x96罚$\xe2\xe9駀\xb8\x97\x05\xae\xbfT!\x91[\x95\xc2:$\xfe%\x82¢-\xfdy0\xaf=\x84\x95\x89\x94\xfep\x8a\xafE\xae\xe1q\xb6\xb8\x18\xebp\x97/)XbӴ:>\x0f\xafiŢ-\xa1\x84\xc4\xc6\xec\xc6V\xf5\bl]d\xa6h?t\xb4|\xd4\x15\xb5a\xe7<|\xf9\xd3\v_\xb9\xb9\xeb\x9f<)]\xe6\x8e\x15\xdf\xca\xf2\r\xb3\x00 (\xe3\xd97@cy\xabs\xc3~J&w\xfd\xf9|\x95\xda\xe6\x01).\xab\u05579_\x85\xab\vh\x03\x1e\xad\x01\x16\xd6q0@\xb00\a<\xa2\x06\xa3\xb9^\xc67\xb8\xc2=\xff\xd35\xfd\xfc\xb5\x05#\x96\xe3\xaaR\x19\x91'\xcbM\xf7L\xe3\xf5\xf0'\xf6G\xf6\x88\xf6ʍB䫪;c\x87\xc8?լ\x9d\xb1\x85\xf0\xb7\x90\v\x8f\xab\x01\x80\v\xfc\u0600Jq\xf1x\xe6\xea&O\t\xd6\xc1u\xe7t\x976\x14\x9e\vtN\xecӝ\xcdWrG{Դ\xc9\rT\x89b(\xd6\x14.\xbb\xf7\x17CF'2O\xf9o@-\xa6\xb0\xadYW}\x9bSfO\x196O\x8c7ƣ\u007f\x1ev$R{\xdcc7<·R\xday_~_O#\x8ep\xea\xce\x16\xde|@\x01\x95\xdcKr\x88$ؽ\xb0[\xb1\xc7Uf\x14\xe5Q\xd2\xe8S\x8c\xfe6r\rP\a\xbe\x8e\xd0#\xe8\xc7\xf6̔\bFe\x0eP\xd2\xc7\x12\xae\xe3\x8eJ\x12,\xc4_\x8d\xed\x1f\xd6\x14R\x1b\x1b\xc2\x17\x0e\xa1\xd3\xd2\xc5\xfe\x9c\xaf\xb5N\xe2\xfb@3\xeaө\x96\xaf¤L\xc3\xfb\xfc\xd0)\xc6\n\xbe\xe2\xe9\x16\x15\x0e&0\u007f\xae\xbf\xa2ћ\xb0\xd1\x0f\xd6\xec)\xe2\xeb\xfdt\x97\xbcR\xef\x97\aa\xbd\x14J\xbd\a\xf0#o\xed=\xfe\x82\xe4\x17F6\x82!\x06F̦y\x18'5!\xa5\xd4A\xd6|\x98\xb05\x95\xef\x18\\c\xb0=\x89\xa7\xf7\xad)O\xc4T7\x90]\x88\xb4\x03\xa2\xf3+\xdc\xed\x8c\xf5!~]\xad@\xee\xe2\xc6҃Jޙ+_\xe1k\f }\x93\xc55\xbaɱ\xa0E\xe1X7=\u007f\x11\x82\xcb\xce\"\xcb(>\xc1\x1b\xe7\x85\xea\xf9\x80o.w\xf1~Mڅ\xf9_z\xdbY\x8fɛ\xf6\xec\xbaϵ*\xb6hIS\x19X\xe0\x17\x1f\xed\x05\xaf\xa7\x86S\xba-\xa2\x86W+\xbd'\u007f\xd3.\b\x82'\x0f\xa3\x148\x03;1xgx\xdc\xe7\xf1\xaf\xc6\v\xb5\x19Kh\xbb!`=5\x91Ë\xfbD\x19\x12ÖI\x1f$'4wH\x97V\x92ಃ\xd0{R k\xaa\xfd!i\xe0\xc8N1\\ī\b!(U\xb5'\x95\x8e\x855_Y\xdd\xcaLc\xa9-o\xa1*\xb2\x17\xa8\xca\xe1\x93\xe7\xf0\xad\x98\xf8\xa9\x9f\x9bx\x17x\xb5\xb3\xa6XE\xfes\xcd\xec:V\xec\xac4\x142qN\x13\xaf㍀e\xb1\x97%j\x10.\xe22\xdbw2%\xc8\xf1D\xcd\v\xeb\x97\x05a\x8f\x9d\xa93\xf1\x17\xc3\xc5|\r\x8fX\n2\xb6\xbe\x11[S\xc0\xdd釖\xae)GO_\x15\n\x1f\x85\t\xa2\xa7\xbc\x96\xbf\xeea,\x86;x=\x88\x9d\x80\xaa\x13@uQ\xff\xfb\xc4N\xcdw\x96\xee磨\xe7\x93\xc9'\a)d\xc1\r\xbc\x14\xfb\xfcA\xee\xfa\xf9EY*\x99\x11\xb6\u007f\xfc\x95\x0eH\x8e\v\xa2\x8a\xabɀ\x82\xa3\x87:6\x80/XZ\xcc\xc8*\xfb\xc8?(\xa4\xfd\xde!v#\x95\xabŁ]7Et\x9f\xbdǢ\x1cx\xd7D\x8e\xd8,\x1as|\"M\xe8\x11\x90\xbef\x95@\xc5N\x80Ѥp1!u\xa8q\x0e!\xf5\xa21B\\\x95\x91\x03\xdaUC[Q\x9ds} U\xaf\xc2R\xa2=m=\xff\x11'\rd!q\xfd\xc7\xe6!\xad4$\xe1\xf7wJD\x06\xfc\xf8ɣ\xe6cv\x9f\x9a\xff\x98}\xab\xf8\xf1\xbac\xe8'co\x99\xb7L;\xa2\x12\x9f4\x05\x02\x91eH\xba\xfb\xf5\xf4;v\x97\x97\xfcO\xfaT\x1d\xff\x9b\x19\x1d\xf6Rw\v\xff\xf9_\x17\x10\xebL\xcf\t\x0fz\xf8\xff\x01\x00\x00\xff\xff\xff\x8cC\xfd\xf8O\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcX\xcdr\xdb6\x10\xbe\xeb)v܃/\x15\x95\xa4=txs\x94v&S'\xf1X\x89{h;\x13\bXJ\xa8A\x80\xc5\x02rԧ\xef,\bR\"E\xd9\xceLS\xde\x00,\x17\xdf~\xfbK\xce\xe6\xf3\xf9L4\xfa\x0e=igK\x10\x8d\xc6/\x01-\xaf\xa8\xb8\xff\x89\n\xed\x16\xbb\x97k\f\xe2\xe5\xec^[U\xc22Rp\xf5-\x92\x8b^\xe2\x1b\xac\xb4\xd5A;;\xab1\b%\x82(g\x00\xc2Z\x17\x04o\x13/\x01\xa4\xb3\xc1;c\xd0\xcf7h\x8b\xfb\xb8\xc6u\xd4F\xa1O7t\xf7\xef^\x14\xaf\x8a\x1fg\x00\xd2cz\xfd\xa3\xae\x91\x82\xa8\x9b\x12l4f\x06`E\x8d%\xac\x85\xbc\x8f\r\x05\xe7\xc5\x06\x8d\x93\xed]\xc5\x0e\rzWh7\xa3\x06%_\xbd\xf1.6%\x1c\x0eZ\r\x19Vk\xd2\xeb\xa4l\xd5*\xbb\xce\xcaҹ\xd1\x14~=/s\xad)$\xb9\xc6D/\xcc9XI\x84\xb4\xddD#\xfc\x19\xa1\x19@\xe3\x91\xd0\xef\U00013f77\xee\xc1\xfe\xa2\xd1(*\xa1\x12\x86p\x06@\xd25X\xc2{F\xdf\b\x89j\x06\xb0\x13F\xab\xf4~k\x8fk\xd0^ݼ\xbd\xfba%\xb7X\x8bv\x13@!I\xaf\x9b$7m\th\x02\x01\x1d\x18xآG\xb8K\xa4\x01#Eʰ\xb3F\x00\xb7\xfe\ve\xa0\xbcn\xbck\xd0\a\xdd\x11\xcb\xcfQ`\xf5{#,\x97\f\xb6\x95\x01š\x84\x04a\x8b\x90\x03\x02\x15P2\x04\\\x05a\xab\t<&\x96l8\xf8\xa8\aT\x81\xb0\x19V\x01+f\xd2\x13\xd0\xd6E\xa38\xfev\xe8\x03x\x94nc\xf5?\xbdf\x82\xe0ҕF\x04\xcc\xde\xec\x1em\x03z+\f\xd3\x1c\xf1{\x10VA-\xf6\xe0\x91\xef\x80h\x8f\xb4%\x11*\xe0\x9d\xf3\b\xdaV\xae\x84m\b\r\x95\x8b\xc5F\x87.\x95\xa4\xab\xebhu\xd8/RB\xe8u\f\xce\xd3B\xe1\x0e͂\xf4f.\xbc\xdc\xea\x802D\x8f\v\xd1\xe8y\x02n\xdb\xe8\xae\xd5w>\xe7\x1d]\x1e!\r{\x0e\f\n^\xdbM\xbf\x9dB\xfb,\xef\x1cԭ\xcf\xdb\xd7Z\xfc\azy\x8bY\xb9\xfdy\xf5\x11\xbaK\x93\v\x86\x9c'\xb6\x0f\xafсx&J\xdb\n}\xeb\xb8ʻ:iD\xab\x1a\xa7mH\vi4\xda!\xe9\x14\u05f5\x0e\xec\xe9\xbf#R`\xff\x14\xb0L\x05\x05\xd6\b\xb1Q\"\xa0*\u0b45\xa5\xa8\xd1,\x05\xe17\xa7\x9d\x19\xa69S\xfa4\xf1\xc7up(ز\xd5ow%j\xd2C\x93Y\xbajP\x0e\xf2D!iϱ\x1cD\xc0\x94\x019i\a\x94\x9e\xafo\xe7\x937%\xb0\x94H\xf4\xce)\x1c\ue3e0^\xf5b\x03l\r\xfaZS\xea$P9\x9f\xf6\xda\x1a\x02\xb9\xf6\x8d\x94B_\u007f\x8a\xd1\t\xdaX\x8f!\xcc\xe1\x16\x85\xfa`\xcd~\xf2\xe07\xaf\xc3\xf8\x82Iw\xf1\xd3\xc2Z\xed\xad\xbcA\xaf\x9dz\xd4\xdc\xd7#\xe1\xde\xe8\xad{\x80*\x85\xad\rf\xcfu\x85\xf6V\x8e\xebf\xf7\\ݼ\xedjh\x9b\x1c9\x9727\x05\\\xe5\x9ct\x15\xbc\x00\xa5I\xac\rRR9\xa6\x87;#\x9f\x96\x10||\xb6\xd1\xd2\xd9JoƦ\n\xa5R;\x17\xe6\xe6LT<\xaat\xc4\xd52\xdd\xc1\x85\x86#\xa0\xf1n\xa7\x15\xfa9G\xbe\xae\xb4\xcc\x18\xa2o\xbbN\x95:\xdeغ\xc9܁\xbe\xf8\xe4\xb0~\xd4e\x1f\x8e%\xfb\x96\x97Q\xe4p%\f\\\xf5\b,r8\v?\x8e+`\x8fJg-{)8\x10\xbd=\x974v\xde\xe8\xd5s\t\xc6\xcf:\xca{\f\xa7\xfb\xe3\xa8Kb\xccdʣv\x15\x1cD\xc2\xc4\xed\xe3\x00\x9e\xf0\x19\x80\x14K\xf4O\xa3X^\xb1X\x1f\xf1\x02\x96W\xb0\x8eV\x19\xec\xb0\x81?4m\"}\xa53\xb8\x82pQ?-x-\x8c\xe7\xa6G\xc7磙q\x93\x85z\xbb\xbbu\x1a\xb8\xc6\x05{:5O\xac\x98\xb2`>L\xd7\xc1Iw\xe9\x93\r3\x88\x10\xe9+[fzgИ\xdc:\x8d\xd9G]\xf3\x1b7ȋ\xa3\x0e\xc93\x97\x85h#\xa1j\v^\x01\u007fXx\xc3\x13\x94\xe4ɦd\x8c<\xcc\xd0I\xd8X\xf7\xc0/\x1fiK\n\xc0\xd9dW\x9a\x0exFm\a\xaet\xf4\xa0\x8d\xe1\xb1\xc9c\xedv\xe9\x93a\xf8\xf0\x8c\xe3\xd1\xecA\x10\x13\xb1{U\xbc(.\xfe\xe7\xeek\x04\x05n\xa7\xa8nq\xa7\xc7\xdf\v\xa7l^\x9f\xc8w\xd1\xdb7L^|\xee\x06\xb1\x85\xcfb\x9fO̯\xb4\xe1\xb1q\"\xd4\x0f\xdfB\xed\xc7\x01\x05\b\xbaƴz\xbd\xba\xbe\xa4\xf4I\xcb#\xef\x89\xd2\av\x1f%\x80\xfc\t\xe1\xf2\xa4\x1b)\xa0\x9fpv\xef+M`\x1d\x18g7\x83Th\x9f<\xf7\x82\xf3І\x8e\xf3\xa0\x90GV.\xb3r+\xec\x06\x0f\xdf2\x19\xfb\x11J\x0e\x8cS\xa4\xc3\xe88D\x83\xb6ӡ\xf0\f\x1f\xf2\x17\xfb\xa3\xfe\xbb\x1e\x88v\xae\x1b2ܣξ4\xa79\xf9\f\xaeG\xd2]\x13a\"\xe7|\xd5\u007f2F5[A\x8f\x1b|\xc3\x12\x9d\x9d2z\x8f6\x1cjO\n\xa8\xa9\xfa\xf3\xbc!\xf8j'tB}r\xf2Ɋ3ggl\x99\xa8\xb9\xa3\xad\xc3\u007f\x9a\x97\x87U\xaa\x89\xf3\xfc_&\x1d\x00\xb4\x05\xf6\x88ȜUy\xe7Pȹ\x826\x01\xd5\xfb\xf1?\x99\x8b\x8b\xc1\x8f\x95\xb4\x94ζ\xc3)\x95\xf0\xfb\x9f\xb3V+\xaa\xbb\x0e\ao\xfe\x1b\x00\x00\xff\xff\x81\xab\x99~\xd3\x12\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcXM\x8fۼ\x11\xbe\xfbW\f\xb6\x87\xbd\xd4r\x92\xf6P\xe8\xe68-\x90v\x935\xd6\xc9\xf6\xd0\x16\bM\x8elv)R\xe5\x90\u07b8E\xff\xfb\x8b!%ْ\xb5\x1f\x01\u07bc<\x99\xe4\x98|\xe6\x99Oj6\x9f\xcfg\xa2\xd1\xf7\xe8I;[\x82h4~\x0fhyF\xc5ß\xa8\xd0nqx\xbb\xc5 \xde\xce\x1e\xb4U%\xac\"\x05W\xdf!\xb9\xe8%~\xc0J[\x1d\xb4\xb3\xb3\x1a\x83P\"\x88r\x06 \xacuA\xf02\xf1\x14@:\x1b\xbc3\x06\xfd|\x87\xb6x\x88[\xdcFm\x14\xfatCw\xff\xe1M\xf1\xae\xf8\xe3\f@zL\u007f\xff\xa2k\xa4 \xea\xa6\x04\x1b\x8d\x99\x01XQc\t[!\x1fbC\xc1y\xb1C\xe3d\xbe\xab8\xa0A\xef\n\xedfԠLH\x94J\xf0\x84Y{m\x03\xfa\x953\xb1ΰ\xe6\xf0\xd7\xcd\xed\xe7\xb5\b\xfb\x12\n\n\"D*\x9a\xbd L\x90\x15\x92\xf4\xba\t\t\xd8\xfbt\x1fl\xf2\x85p\xd3\xde\b\xf9_@Q\xeeA\x10,\x0fB\x1b\xb15\xb8\xf8jE\xf7;\x9d\x96a\xaf\xfb\xd3ñ\xc1\x12(xmwc(\x1d\x93\xc5\x05\vgG-w\xe7\a)\x11x\xba\xf3.6%\x9ch\xc8ҭ\x11\xb2\x01\xb3*\xad&\x9d\"i\xdfh\n\u007f{Z\xe6FSHr\x8d\x89^\x98\xa7\x8c\x90DH\xdb]4\xc2?!4\x03h<\x12\xfa\x03~\xb5\x0f\xd6=ڿh4\x8aJ\xa8\x84I\f\x91t\xac\xd7gF\xdf\b\x89\x8a\xd7\xe2ַ~\xd7j\x94\xe9/\xe1\u007f\xff\x9f\x01\x1c\x84\xd1*\x1d\x9f7]\x83v\xb9\xfex\xff\x87\x8d\xdcc-\xf2\xe2\xa4]G\x8a\x82&\x10\xd0a\x85\xc7=z\x84\xfb\xc4)\xb0\"H\xadV\xed\x89\x00n\xfbo\x94\x81\xday\xe3]\x83>\xe8\x0e%\x8f\xb3(\xeb\xd7FX\xae\x19l\x96\x01\xc5q\x85\x04a\x8f\xd0F\a*\xa0\xa4\b\xb8\n\xc2^\x13xL$\xdap2a\x0f\xa8\x02a[X\x05l\x98hO@{\x17\x8d\xe2`<\xa0\x0f\xe0Q\xba\x9d\xd5\xff\xedO&\b.]iD\xc0\xd6\xd8\xddH\xc1c\x85a\x9a#\xfe\x1e\x84UP\x8b#x\xe4; ڳӒ\b\x15\xf0\xc9y\x04m+W\xc2>\x84\x86\xca\xc5b\xa7C\x97W\xa4\xab\xebhu8.Rv\xd0\xdb\x18\x9c\xa7\x85\xc2\x03\x9a\x05\xe9\xdd\\x\xb9\xd7\x01e\x88\x1e\x17\xa2\xd1\xf3\x04\xdc\xe6P\xaf\xd5\xefzg\xb8>C:\n\xac<\x92\xe7?\xc9;\xfb|\xb6y\xfe[\xc6\u007f\xa2\x97\x97\x98\x95\xbb?o\xbe@wi2\xc1\x90\xf3\xc4\xf6\xe9ot\"\x9e\x89ҶB\x9f\rWyW\xa7\x13Ѫ\xc6i\x1b\xd2D\x1a\x8dvH:\xc5m\xad\x03[\xfa?\x11)\xb0}\nX\xa5\xec\n[\x84\xd8pܫ\x02>ZX\x89\x1a\xcdJ\x10\xfetڙa\x9a3\xa5/\x13\u007f^\x14\x86\x82\x99\xad~\xb9\xcbד\x16\x9a\x8c\xd2M\x83r\x10'\nI{\xf6\xe5 \x02\xa6\bh\x83v@\xe9\xd3\xe9\xef\xe9\xe0M\x01,%\x12}r\n\x87\xeb#\xa8\xcb^l\x80\xadA_kJe\x15*\xe7\xd3Z\xce!Ц\xc6ѡ\xd0\xe7\x9fb\xb4\x836\xd6c\bs\xb8C\xa1n\xad9Nn\xfc\xdd\xeb0\xbe`\xd2\\<2\xac\xcd\xd1\xca5z\xedԳ\xea\xbe\x1f\t\xf7J\xef\xdd#T\xc9mm0G\xce+t\xb4r\x9c7\xbb\xb1\\\u007f\xecrh\x0e\x8e6\x96Zn\nX\xb61\xe9*x\x03J\x13\xd7UJG\x8e\xe9\xe16\x81wK\b>\xbeZi\xe9l\xa5wcUϛ\x87i\xafx\xf6\xd0\x11W\xabt\a'\x1a\xf6\x80ƻ\x83V\xe8\xe7\xec\xf9\xbaҲ\xc5\x10}\xae:U*\x88c\xed&c\a\xfa\xe4Ӻ\xf5\xb3&\xbb=\x97<52\x19E뮄\x81\xb3\x1e\x81Evg\xe1\xc7~\x05lQ\xe9\xace+\x05\a\xa2\xd7\xe7\x9a\xc6\xc6\x1b\xfd\xf5\xa9\x00㱍\xf2\x01\xc3\xe5\xfa\xd8\xeb\x92\x183\x99\xe2(ς\x83H\x98\xb8}\x1e\xc0\v6\x03\x90b\x85\xfee\x14\xab%\x8b\xf5\x1e/`\xb5\x84m\xb4\xca`\x87\xe5q\x8f\x96˷\xae\x8e\\C\xbe\xdcl&΄\x8eǔ\x1c\xda\x02ܱ9\x85\xbdr\xbe\x16\xa1\x84\xed\xf1\"\xa8_T\xad\xf1X\xe9\xef/\xaa\xb6Nb\x1d\xc1\x8d\b{Ж\xb4B\x10\x13tOd\xd9n\xf4\x01|\xdb\xe4@\xfaAcp\x06\xe1\xa4~\x99\xf02\x8c׆G\xc7糑\xb1n\x85z\xbd\xbbyj\xb8\xc6\t{:4/\xb4\x98\xd2`>\f\xd7\xc1Nw\xe9\x8b\x053\xb7\xbe?V2\xf3k\xe5\xbc0\xb9m\xea\xc2Ϫ\xe6O.\x90Wg\x15\x92{.\v\xd1FB\x95\x13^\x01\xff\xb4\xf0\x81;(ɝM\xc9\x18\xb9\x99\xa1\v\xb7\xb1\xee\x91\xff|vZ:\x00\x9cMz\xa5\xee\x80{\xd4\xdcp\xa5\xadGm\f\xb7M\x1ekwH/\x8a\xe1\xe0\x1eǣ9\xf2K\xceUpxW\xbc)\xae~\xe3\xeak\x04\x05.\xa7\xa8\xee\xf0\xa0\xc7\xef\x85K6o.\xe4;\xef\xed\v&O\xbeu\x8d\xd8·b\xdf.ԯ\xb4\xe1\xb6q\xc2\xd5Oo\xa1\xfc8\xa0\x00Aטf\xef77ה\xde\xf7\xdc\xf2^\x1c\xfa\xc8\xe6\xa3\x04\x90\x9f\x10\xae\xedt#\x05\xf4\x13\xc6\xeem\xa5\t\xac\x03\xe3\xecn\x10\ny\xb4}/8\x0f\xd9u\x9c\a\x85ܲr\x9a\x95{awxz˴\xd8\xcfP\xb2c\\\"\x1dz\xc7\xc9\x1b\xb4\x9dv\x85Wؐ\x1f\xee\xcf\xda\xeff ڙn\xc8p\x8f\xba\xb5\xa5\xb9\x8c\xc9Wp=\x92\xee\x8a\b\x139\xe7\xab~\x956*}=y>DzD\xa7\xa7\x8cޣ\r\xa7ܓ\x1cj*\xff\xbc\xae\t^\x0e>\xb9\x9c\xef\x8c?Ǽ\xa8\xcbD\xce\x1d-\x9d>Z\xbd=\xcd\xda\xefJ\xf9\x13M\xda\x00\xc8\t\xf6\x8c\xc86\xaaڕS\"\xe7\f\xda\x04T\x9fǟl\xae\xae\x06\xdf]\xd2T:\x9b\x9bS*\xe1\x1f\xff\x9a\xe5SQ\xddw8x\xf1\x97\x00\x00\x00\xff\xff\xbd\xf1$\xc1\xe0\x13\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96\xcdr$5\f\x80\xef\xfd\x14\xaa\xe5\xb0\x17\xa6\x87\x00\a\xaao\x90]\xaaR@*\x95,\xb9P\x1c<\xb6fZ\xc4m\x1bI\x9e\x10\x9e\x9e\xb2\xbb;\xf3\x93Ne9l\xdf,\xcb\xfa\xf9$\xb9ݬV\xab\xc6$\xbaG\x16\x8a\xa1\x03\x93\b\xffQ\fe%\xed\xc3\x0f\xd2R\\\xef/6\xa8\xe6\xa2y\xa0\xe0:\xb8̢q\xb8E\x89\x99-~\xc0-\x05R\x8a\xa1\x19P\x8d3j\xba\x06\xc0\x84\x10\xd5\x14\xb1\x94%\x80\x8dA9z\x8f\xbc\xdaah\x1f\xf2\x067\x99\xbcC\xae\x1ef\xff\xfbo\xdao\xdb\xef\x1b\x00\xcbX\x8f\u007f\xa2\x01E͐:\b\xd9\xfb\x06 \x98\x01;p\xe8Qqc\xecCN\x8c\u007fg\x14\x95v\x8f\x1e9\xb6\x14\x1bIh\x8b\xe3\x1dǜ:8l\x8c秠Ƅ>TS?US\xb7\xa3\xa9\xba\xebI\xf4\x97\xd74~\xa5I+\xf9\xcc\xc6/\aT\x15\x84\xc2.{Ë*\r@b\x14\xe4=\xfe\x1e\x1eB|\f?\x13z'\x1dl\x8d\x17l\x00\xc4Ƅ\x1d\\\x97\xa8\x93\xb1\xe8\x1a\x80\xbd\xf1\xe4*\x9e1\x8f\x980\xfcxsu\xffݝ\xedq0\xa3\x10\xc0\xa1X\xa6T\xf5\x96r\x00\x1200E\x02\x1a\xa7\x00!\x06\x84\xc80DF\x18\xa3\x95v2\x998&d\xa5\x99`\xf9\x8e\xfa\xe7Yv\xe6\xfc}\x89n\xd4\x01W:\x06\x05\xb4G\x98\xea\x8e\x0e\xa4F\x0eq\vړ\x00c\xc5\x12\xc6\x1e:2\vE\xc5\x04\x88\x9b\xbf\xd0j\vw\x05\x1d\vH\x1f\xb3w\xa5\xcd\xf6\xc8\n\x8c6\xee\x02\xfd\xfblYJ~ť7:\x17x\xfe((r0\xbep\xcd\xf85\x98\xe0`0O\xc0X|@\x0eG֪\x8a\xb4\xf0[\x81Ca\x1b;\xe8U\x93t\xeb\xf5\x8et\x9e\x18\x1b\x87!\aҧu\xed{\xdad\x8d,k\x87{\xf4k\xa1\xddʰ\xedI\xd1jf\\\x9bD\xab\x1ax\xa8\x03\xd3\x0e\xee+\x9e\xc6K\xde\x1fE\xaaO\xa5\x13D\x99\xc2\xeeY\\{\xf8U\xee\xa5\u007f\xc72\x8f\xc7\xc6\xf8\x0fx\x8b\xa8P\xb9\xfdx\xf7\tf\xa7\xb5\x04\xa7\xcc+\xed\xc319\x80/\xa0(l\x91\xc7\xc2m9\x0e\xd5\"\x06\x97\"\x05\xad\v\xeb\t\xc3)tɛ\x81T\xe6\xf6+\xf5i\xe1\xb2\xde\x1b\xb0A\xc8\xc9\x19E\xd7\xc2U\x80K3\xa0\xbf4\x82_\x1c{!,\xab\x82\xf4m\xf0\xc7\xd7ݩ\xe2H\xebY<\xdfE\x8b\x15Z\x18˻\x84\xb6Ԭ\x80+giK\xb6\x8e\x01l#\xc3cO\xb6\x9f\xc7\xf2\x84\xe8\xf3\x00\xb7G⥁-\xdfh\xa0\xdc*\xa7\xf2W\x92\x85Z'b<\xe9\xb5Ց\x997)\xa8\xd1,\xff\x8bC=1\x93\xb0\x99\x19\x83Nv\xea-\xb0t\xe8srG\xe6\xc8r\x9e\xf7I8\x1f\xabJ\xfdk\x19\n\x02&t\xb1\xb1\x16\x93\xa2\xbb>\u007fN\xbc{w\xf2.\xa8K\x1b\x83\xa3\xf15\x04\u007f\xfcٌV\xd1\xdd\xcfq\x14\xe1\u007f\x01\x00\x00\xff\xff\xcb0\x9b\f\x8c\t\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WOoܶ\x13\xbd\xebS\f\xf2;\xe4W \xd26m\x0f\x85n\xad\x93\x02A\xd3 \xb0\x13_\x8a\x1e\xb8\xe4\xacĚ\"Y\xcep\x1d\xf7\xd3\x17CI\xfbG+\xdb\xe9\xa1{\xd3p8|||oȭ꺮T\xb4\xb7\x98\xc8\x06߂\x8a\x16\xbf0z\xf9\xa2\xe6\xeeGjl\xd8\xec_o\x91\xd5\xeb\xea\xcez\xd3\xc2U&\x0e\xc35R\xc8I\xe3\x1b\xdcYo\xd9\x06_\r\xc8\xca(Vm\x05\xa0\xbc\x0f\xac$L\xf2\t\xa0\x83\xe7\x14\x9c\xc3Tw蛻\xbc\xc5m\xb6\xce`*+\xcc\xeb\xef\xbfm\xbek~\xa8\x00t\xc22\xfd\x93\x1d\x90X\r\xb1\x05\x9f\x9d\xab\x00\xbc\x1a\xb0\x05\x13\xee\xbd\v\xca$\xfc+#15{t\x98BcCE\x11\xb5,ڥ\x90c\vǁq\xee\x04h\xdc̛\xa9\xcc\xf5X\xa6\x8c8K\xfc\xeb\xda\xe8{;eD\x97\x93r\x97 \xca Y\xdfe\xa7\xd2\xc5p\x05\x10\x13\x12\xa6=~\xf6w>\xdc\xfb_,:C-\xec\x94#\xac\x00H\x87\x88-|\x10\x94Qi4\x15\xc0^9k\n\x15#\xee\x10\xd1\xff\xf4\xf1\xdd\xed\xf77\xba\xc7A\x8dA\x00\x83\xa4\x93\x8d%o\x89\x1b,\x81\x82\t\x05p8\x00\x03\xe5A%\xb6;\xa5\x19v)\f\xb0U\xfa.ǩ&@\xd8\xfe\x89\x9a\x818$\xd5\xe1+\xa0\xac{PRmL\x04\x17:\xd8Y\x87\xcd4%\xa6\x101\xb1\x9dY\x96߉\xbe\x0e\xb1\x05\xe0\x97\xb2\xa31\a\x8c(\n\t\xb8G\x98t\x81\x06\xa8\xec\x16\xc2\x0e\xb8\xb7\x04\t\v\x95~\xd4\xd8IY\x90\x14\xe5'\xe4\r\xdc\b݉\x80\xfa\x90\x9d\x11\x19\xee11$ԡ\xf3\xf6\xefCe\x12^dI\xa7x\x16\xc2\xfc\xb3\x9e1y\xe5\xe4,2\xbe\x02\xe5\r\f\xea\x01\x12\x16v\xb2?\xa9VR\xa8\x81\xdfBB\xb0~\x17Z\xe8\x99#\xb5\x9bMgyv\x94\x0eÐ\xbd\xe5\x87M\xf1\x85\xddf\x0e\x896\x06\xf7\xe86d\xbbZ%\xdd[F\xcd9\xe1FE[\x17\xe0\xbe\x18\xaa\x19\xcc\xff\xd2d?zy\x82\x94\x1fD=\xc4\xc9\xfa\xee\x10.:\u007f\x94w\xd1\xf9(\x8fqڈ\xffH\xaf\x84\x84\x95\xeb\xb77\x9f`^\xb4\x1c\xc19\xe7\xa3N\x0e\xd3\xe8H\xbc\x10e\xfd\x0e\xd3xpEeR\x11\xbd\x89\xc1z.\x1f\xdaY\xf4\xe7\xa4S\xde\x0e\x96i\x96\xad\x9cO\x03W\xa5\xaf\xc0\x16!G\xa3\x18M\x03\xef<\\\xa9\x01ݕ\"\xfc\xcfi\x17\x86\xa9\x16J\x9f'\xfe\xb4\x1d\x9e'\x8el\x1d\xc2s\xbfZ=\xa1\x85\x95o\"j9/!M\xe6ٝ\xd5\xc5\x02\xb0\v\t\xd4\xd1\xd9\x13m\xcdI\xdd5o\x16P*u\xc8\xe7\xb1\x05\x8aO%E\x16\xbe\xef\xd5y\v\xf9?6]#}\x80&\bcg\xf8\xa6Y\xd4{l\xf55\x8d\xaeb\x98\xa5*[\x17\x1e\xc5\xe8\xd2zN\xd1,\x17\x95\x1f\xfa<\xac\x15\xaf\xe1\xe7\x82\xf4}\xe8\x9e\x18\xbd\n\x9eE\xd0O\xa4\xdc\x06\x97\a\xbc\xf1*R\x1f\x9e̜/\xcd\xc3E\xb2L\xbbFi\xb5\xf8\x18\xa4i\xf8\x1a)\xbbՅV\x858\xff\xca\xc5\xf9\x1c\xcbr\xf7\xcc,˄\xb1\xe3\"ȅ\x9d<2ұ\r\xdc[\xeeᾷ\xba_\xa9\neZ9 \xe9/DA\xdb\xe2\xd8\u007f\a[tl\x13^ȣ.\xa2\xb9\b\n\xe4j\xad\xf8\xc2s\xeb\x85\xeb\xc9\v\xcf:\x96\x15g\xfajϖ\xec\x99T\x9dSB\xcfS\x8dr[-'|\x8dig\xc5\u007f\xbe~\xff\xa4s\xdf\x1c\xf3\xca\x1bLY?\xe2\x88\tk\xb2\x9dܭ2&\xde-\xceZ\x120\xfeN\xef\xf8gO\r\xbfD\x9bN\x9e,\x8f@{{H\x1b\x1b\v\xfa\xf1\x8aX\xbe^J9\xa4r\xedj\xe5/\xb0m\x11\f:d4\xb0}\x18;\xe3\x031\x0eK\xbc\xbb\x90\x06\xc5-\xc8\xc5Q\xb3\xbd\x10\x8a\xbc/\xd5\xd6a\v\x9c\xf2\xba\x8aV6\x1b{E\x17\xb6:\xdb\xe7G\xc9X;\xfe\x83\xb9\x9e8\u007fx\xa4\x83\xd5\xf0\x01\xef/b\x1fS\xd0H\x84Kc<\x82~E܋\xd0\xf1a\xfe\xfa\xf8U\xa4XO\x0f\xf12\x00P\x9e\xb5愺\xe9\xcd8E\x8e\x8eQZcd4\x1f\x96O\xf1\x17/\xce\xde\xd6\xe5S\ao\xec\xf8/\x02~\xff\xa3\x1a\xab\xa2\xb9\x9dqH\xf0\x9f\x00\x00\x00\xff\xff\xbbظ3\xc4\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y_s۸\x11\u007fק\xd8\xc9=\xb87\x13RMz\xd3\xe9\xe8\xedb7\x1d\xb7w\x8e'\xf2\xe5%\x93\x87\x15\xb1\x12Q\x83\x00\x8a\x05\xa5\xa8\x9d~\xf7\xce\x02\xa4$J\xb4,_{)_l\x82\x8b\xc5\xfe\xdf\xdfB\x93\xa2(&\xe8\xf5'\n\xac\x9d\x9d\x01zM_#Yy\xe3\xf2\xf1O\\j7]\xbfYP\xc47\x93Gm\xd5\f\xae[\x8e\xae\xf9H\xec\xdaP\xd1\r-\xb5\xd5Q;;i(\xa2\u0088\xb3\t\x00Z\xeb\"\xca2\xcb+@\xe5l\f\xce\x18\nŊl\xf9\xd8.h\xd1j\xa3(\xa4\x13\xfa\xf3\u05ff/ߖ?L\x00\xaa@i\xfb\x83n\x88#6~\x06\xb65f\x02`\xb1\xa1\x19x\xa7\xd6δ\r-\xb0zl=\x97k2\x14\\\xa9݄=Ur\xe8*\xb8\xd6\xcf`\xff!\xef\xed\x04\xca\xca\xdc;\xf5)\xb1y\x97ؤ/Fs\xfc\xdb\xd8ן4\xc7D\xe1M\x1bМ\n\x91>\xb2\xb6\xab\xd6`8\xf9<\x01\xf0\x81\x98\u009a~\xb1\x8f\xd6m\xec{MF\xf1\f\x96h\x98&\x00\\9O3\xb8\x13)=V\xa4&\x00k4Z%Sd\xb9\x9d'\xfb\xe3\xfd\xed\xa7?̫\x9a\x1a̋\xc2\xd9y\nQ\xf7\xea\xc9s\xe0\xd8\xdd\x1a\x80\"\xae\x82\xf6\x89#\\\t\xabL\x03J\\I\f\xb1&\xe8\x1cB\n8\x1d\x03n\t\xb1\xd6\f\x81\x92\x0e6;\xf7\x80-\b\tZp\x8b\xbfS\x15K\x98\x8b\x9e\x81\x81k\xd7\x1a%\xfe_S\x88\x10\xa8r+\xab\xff\xb9\xe3\xcc\x10]:\xd2`\xa4ξ\xfd\xa3m\xa4`ш\x11Zz\rh\x154\xb8\x85@r\x06\xb4\xf6\x80[\"\xe1\x12~v\x81@ۥ\x9bA\x1d\xa3\xe7\xd9t\xbaұ\x0f\xe5\xca5Mku\xdcNS@\xeaE\x1b]\u0a625\x99)\xebU\x81\xa1\xaau\xa4*\xb6\x81\xa6\xe8u\x91\x04\xb7)\x92\xcbF}\x17\xba\xb8\xe7\xab\x03I\xe3V\xdc\xc61h\xbb\xda-\xa7\x00{\xd2\xee\x12`\xa0\x19\xb0ۖ\xe5ߛW\x96\xc4*\x1f\xff<\u007f\x80\xfe\xd0䂡͓\xb5\xf7\xdbxox1\x94\xb6K\n\xd9q\xcb\xe0\x9ađ\xac\xf2Nۘ^*\xa3\xc9\x0e\x8d\xce\xed\xa2\xd1Q<\xfd\x8f\x968\x8a\u007fJ\xb8N\t\r\v\x82\xd6+\x8c\xa4J\xb8\xb5p\x8d\r\x99kd\xfa\xcd\xcd.\x16\xe6BL\xfa\xbc\xe1\x0f\xebА0[k\xb7\xdc\x17\x8aQ\x0f\x1d\xe5\xfe\xdcS%\xfe\x12\xa3\xc9>\xbd\xd4UJ\x01X\xba\x00xL^\x1e\xb0\x1dKMyrU\x98G\x17pE?\xb9\xea ɟ\x90\xe9\xdd؎^*\xa9m9M\xa9c\r\x9c)\x8fX\x02\x98~릦@iG \x8e\xba\x92@r\xac\xa3\v[a+\xfbI\x95G\xfbG\x8d.\x8fu\x8a\xce\xca\u007f\xe7\x14\x8d\x89+\x1b!֘c\xf2ޥ\xcc\b\xad\xb5\x92\x05\xce^,\x80w\xea\xec\xf9\x1dg\x84@K\nd%\xa3r\xf1\xf1.\x95\xa8\x88\xda\xf6\x99\x97K7Dwb\xbeE60)\x18:\xfa\x9c\xb3\xe1\xc9z<*\xe9\x8f\xf7\xb7}\r\xee\x8d\xd4\xc9\x1c\x8fO|\xb8\xf90\xcbRI\b\xadR%\x95.\xb7Ԃ9\x04l\xe4\xce)1\x99\xcc\xd1\xe6\xe0\x88\x0e\xaa\x1a\xedHa\x85\x04Z\x92u\x97\xad\xf4\xb2\xf2\xea\xa5\xd9z\f\x1b\xfag\x04>\x1c\x17\x86\xffS\x13\xbeH\xad\x84ڟU\xeb\xee \x9eϪ%\xf3C\xb0\x14)i\xa6\\ŢTE>\xf2ԭ)\xac5m\xa6\x1b\x17\x1e\xb5]\x15\x12\x88E\x8e\x04\x9e\xa6\x11`\xfa]\xfa\xf3\xab\xb4H\xc8\xfc2U\x12\xe9\xb7\xd0G\xce\xe1\xe9\x8b\xd5\xe9q\xe5\xa5]\xe9j\xde!\x9f㝒\x12\x9bZWu?$\xec\xab\xe7h\x8e4\xa8r\xc9E\xbb\xfd\xcd\xc3V\f\xd9\x06\x91g[tch\x81V\xc9\xff\xac9\xca\xfa\x8b-\xd7\xea\v\x92\xf4\x97ۛo\x13̭~qF\x8e\x02\xe2\x1c\x13\xde\xdd*1\xdfRS8\v\xa7>\x0eH{`7\x82$w4\x17#\xb9\x88\xab\x13\x00\x85J\xa5\x8b\x064\xf7g@\xd6\x19\x9d\a\xc2?\xe0\x8a\x01\x03\x01B\x83^\xfc\xf4H\xdb\"7i\x8fZz\xac\xb4\xd1\x0e\xaf,\b\xd0{\xa3G\xdai\u05ca;\xb8\xd8!o\x19kq\xc5\xe3\xfa\x8eX=\xef>k\xed<^\x8c\xc1\xe7\xee\xe8\x8cKv\x10:\xba=P=\x8d\xdf\x13\xe0\xfa\x84\xddd\n\x14tu(Z1>\xba\f(\x04\xd2\x0f\x16\xbcS\x83\xf7a\x9c\r>e}\x9e\x9d\xde\"Ɩ/\x9e\xdf\x12uo\xbd\\\x0fb\xc7#a\x85_3\xc1UN\xb0\xe3\xf0\x9a\xea\x9c\v\xafO\xe9ӅHPY\xac\xa8\x1b\x89\xc7.\x866\xc8\xfd\t\xa7C\x18\x1c0\xcb\xfbR\xdd\x15^\xa4\x12\xb4\x13ԹDmHA\u007fGv\xbc\xe7\x84\xe7!\x8f\x05-\xa5T\xb5\xde8T\xfdPԉ\xd6_\xf2<\xc84\x9c\xee\x1b\xae\xf8I\x8e-\x93JS\xf2\x88\xfa\xc7\xeda\xe9B\x83q\x06\n#\x15#\fmk\f.\f\xcd \x86\xf6\xf8㓩\xdf\x103\xaeΧ\xd7ϙ&χ\xdd\x06\xc0\x85k\xe3n@\x1c\xa4\xf8\x15w\xd1s\xf9t:2\x82\rC\x16\x050s\a\x1f\x8dI;\x0e\xd3z\u007f\x89\x9a\xe4Y\x90\xb8\xe5\xbf\xcdp\x00_#\x9f7νP\x8c%Ϯ\x06\x9d\xc9\x1eH\x13a\xdb\x1c\x9fP\xc0\x1dmN\xd6n\xed}p\xab@|\x1c\x1aE\x1f?'\xca\x16\xf0>\xc5\xf9\xc5\xfav\a\x9cW\xb9#\x82ڙ>=]D\x03\xb6m\x16\x14D\xef\xc56\x12\x0f\x8b\xf0\xe9̟\xa6\x88\xbd\xd1\x0ev\xf7W\b\x99O7\x14Uh\xd3-\x9b\xe4Lt\xa04{\x83\xa7SQ\xafBB\x12\x922\x92\xd2\xfbh\xed\xd3\xd4SH\x9f^rK\x91\xa4\xb9qv\x14\xe3\xf6\xf9\xa9m\xfc\xe3\x0fO\"\x0em#\xad\x06E\xbd\xfb*\x06|'\xfc\xff\u05fc\x9fl\xacl\xd1s\xed\xe2\xed\xcdYo\xcfwd}\x94\xefAK\xaa]\xe9ޯ#\xea]>li\xf9\xc9apq\xeaq\xc4\x10/k\x1e\xf3\x01\xe93}#\xf1%U\u009c<\x06\x8c\xa7\x81\x99\ue0ef\x8f\u007fey\r\xacӵ\x98`\x9f\f\x86\xf2\xa8\xcb\xd2N\x04ڹ\x90c\xf5\x94\xe3\xa0\x11\f\n\xffP\xf4oQ\xf3G\xe2\xe1hi\xff\x93ӛ\xfd[\x8aˢ\xfb\x89)}\xe8\xd4R\a\x87w\xb7\xaa\xdd\xca\x1e\x86`%\x90\x9d\xd4\xdd\xf1\x8fL\xaf\xf2UI\xff\xabQz\xad\x9c\xcdh\x96g\xf0\xf9\xcb\x04\xba\xbb\xd6O\xbd\x1c\xb2\xf8\x9f\x00\x00\x00\xff\xff\x80\xb6\xf7)\x9e\x1b\x00\x00"), diff --git a/pkg/apis/velero/v1/backupstoragelocation_types.go b/pkg/apis/velero/v1/backupstoragelocation_types.go index 9b77d37b02..bb32ca31a5 100644 --- a/pkg/apis/velero/v1/backupstoragelocation_types.go +++ b/pkg/apis/velero/v1/backupstoragelocation_types.go @@ -70,14 +70,14 @@ type BackupStorageLocationStatus struct { AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"` } +// +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="Phase",type="string",JSONPath=".status.phase",description="Backup Storage Location status such as Available/Unavailable" - -// +genclient -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" // BackupStorageLocation is a location where Velero stores backup objects type BackupStorageLocation struct { @@ -88,9 +88,8 @@ type BackupStorageLocation struct { Status BackupStorageLocationStatus `json:"status,omitempty"` } -// +kubebuilder:object:root=true - // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root=true // BackupStorageLocationList contains a list of BackupStorageLocation type BackupStorageLocationList struct { diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index ac5635c2d1..65076bfb05 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -908,8 +908,6 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger.Fatal("Problem starting manager", err) } - s.logger.Info("Server started successfully") - s.logger.Info("Waiting for all controllers to shut down gracefully") return nil diff --git a/pkg/cmd/util/output/backup_storage_location_printer.go b/pkg/cmd/util/output/backup_storage_location_printer.go index 851bc7b737..8eed52f057 100644 --- a/pkg/cmd/util/output/backup_storage_location_printer.go +++ b/pkg/cmd/util/output/backup_storage_location_printer.go @@ -30,7 +30,7 @@ var ( {Name: "Name", Type: "string", Format: "name"}, {Name: "Provider"}, {Name: "Bucket/Prefix"}, - {Name: "Status"}, + {Name: "Phase"}, {Name: "Access Mode"}, } ) From 6f72f6bf7052ab58baf2b3405a779acb1c5245bc Mon Sep 17 00:00:00 2001 From: Carlisia Date: Wed, 17 Jun 2020 15:03:00 -0700 Subject: [PATCH 25/34] More code reviews Signed-off-by: Carlisia --- design/backup-resource-list.md | 2 +- hack/update-all.sh | 2 -- .../velero/v1/backupstoragelocation_types.go | 2 +- pkg/apis/velero/v1/groupversion_info.go | 2 +- pkg/cmd/cli/backuplocation/create.go | 2 +- pkg/cmd/cli/restic/server.go | 2 -- pkg/controller/backup_sync_controller.go | 1 + pkg/controller/backup_sync_controller_test.go | 3 --- .../pod_volume_backup_controller.go | 8 +++--- .../pod_volume_restore_controller.go | 12 ++++----- pkg/controller/suite_test.go | 1 - pkg/restic/common.go | 26 +++++++++++++++---- pkg/restic/common_test.go | 3 +-- pkg/restic/repository_manager.go | 8 +++--- site/docs/master/code-standards.md | 3 +-- 15 files changed, 42 insertions(+), 35 deletions(-) diff --git a/design/backup-resource-list.md b/design/backup-resource-list.md index ad71e08502..309679a425 100644 --- a/design/backup-resource-list.md +++ b/design/backup-resource-list.md @@ -60,7 +60,7 @@ This property will be moved to the [Backup request struct](https://github.com/he In order to record the API group, version and kind for the resource, this key will be constructed from the object's `schema.GroupVersionKind` in the format `{group}/{version}/{kind}` (e.g. `apps/v1/Deployment`). The `backedUpItems` map is kept as a flat structure internally for quick lookup. -When the backup is ready to upload, `backedUpItems` will be converted to a nested structure representing the metadata file above, grouped by `schema.SchemeGroupVersionKind`. +When the backup is ready to upload, `backedUpItems` will be converted to a nested structure representing the metadata file above, grouped by `schema.GroupVersionKind`. After converting to the right format, it can be passed to the `persistBackup` function to persist the file in object storage. ### Changes to DownloadRequest CRD and processing diff --git a/hack/update-all.sh b/hack/update-all.sh index b5fd7c2b6f..714ba36b07 100755 --- a/hack/update-all.sh +++ b/hack/update-all.sh @@ -24,5 +24,3 @@ for f in ${HACK_DIR}/update-*.sh; do fi $f done - - diff --git a/pkg/apis/velero/v1/backupstoragelocation_types.go b/pkg/apis/velero/v1/backupstoragelocation_types.go index bb32ca31a5..02beabb158 100644 --- a/pkg/apis/velero/v1/backupstoragelocation_types.go +++ b/pkg/apis/velero/v1/backupstoragelocation_types.go @@ -1,5 +1,5 @@ /* -Copyright the Velero contributors. +Copyright 2017, 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. diff --git a/pkg/apis/velero/v1/groupversion_info.go b/pkg/apis/velero/v1/groupversion_info.go index 84737a0236..ab5b20433b 100644 --- a/pkg/apis/velero/v1/groupversion_info.go +++ b/pkg/apis/velero/v1/groupversion_info.go @@ -1,5 +1,5 @@ /* -Copyright the Velero contributors. +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. diff --git a/pkg/cmd/cli/backuplocation/create.go b/pkg/cmd/cli/backuplocation/create.go index 53ea54f080..633fd5593f 100644 --- a/pkg/cmd/cli/backuplocation/create.go +++ b/pkg/cmd/cli/backuplocation/create.go @@ -155,7 +155,7 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { } if err := client.Create(context.Background(), backupStorageLocation, &kbclient.CreateOptions{}); err != nil { - return err + return errors.WithStack(err) } fmt.Printf("Backup storage location %q configured successfully.\n", backupStorageLocation.Name) diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/restic/server.go index 63dc624377..5bab7b1337 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/restic/server.go @@ -157,8 +157,6 @@ func newResticServer(logger logrus.FieldLogger, factory client.Factory) (*restic return nil, err } - // +kubebuilder:scaffold:builder - s := &resticServer{ kubeClient: kubeClient, veleroClient: veleroClient, diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index ec7086e377..85112a35bc 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -305,6 +305,7 @@ func (c *backupSyncController) run() { c.deleteOrphanedBackups(location.Name, backupStoreBackups, log) + // update the location's last-synced time field statusPatch := client.MergeFrom(location.DeepCopyObject()) location.Status.LastSyncedTime = &metav1.Time{Time: time.Now().UTC()} if err := c.kbclient.Status().Patch(context.Background(), &location, statusPatch); err != nil { diff --git a/pkg/controller/backup_sync_controller_test.go b/pkg/controller/backup_sync_controller_test.go index 804040e033..8ff93736c2 100644 --- a/pkg/controller/backup_sync_controller_test.go +++ b/pkg/controller/backup_sync_controller_test.go @@ -108,7 +108,6 @@ func defaultLocationsListWithLongerLocationName(namespace string) []*velerov1api } func TestBackupSyncControllerRun(t *testing.T) { - type cloudBackupData struct { backup *velerov1api.Backup podVolumeBackups []*velerov1api.PodVolumeBackup @@ -471,7 +470,6 @@ func TestBackupSyncControllerRun(t *testing.T) { } func TestDeleteOrphanedBackups(t *testing.T) { - baseBuilder := func(name string) *builder.BackupBuilder { return builder.ForBackup("ns-1", name).ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "default")) } @@ -615,7 +613,6 @@ func TestDeleteOrphanedBackups(t *testing.T) { } func TestStorageLabelsInDeleteOrphanedBackups(t *testing.T) { - longLabelName := "the-really-long-location-name-that-is-much-more-than-63-characters" tests := []struct { name string diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 22600bf050..754fb2c41f 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -229,11 +229,11 @@ func (c *podVolumeBackupController) processBackup(req *velerov1api.PodVolumeBack ) // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic - location, err := restic.GetCACert(c.kbClient, req.Namespace, req.Spec.BackupStorageLocation) + caCert, err := restic.GetCACert(c.kbClient, req.Namespace, req.Spec.BackupStorageLocation) if err != nil { log.WithError(err).Error("Error getting caCert") } - caCert := location.Spec.ObjectStorage.CACert + var caCertFile string if caCert != nil { caCertFile, err = restic.TempCACertFile(caCert, req.Spec.BackupStorageLocation, c.fileSystem) @@ -249,12 +249,12 @@ func (c *podVolumeBackupController) processBackup(req *velerov1api.PodVolumeBack // set resticCmd.Env appropriately (currently for Azure and S3 based backuplocations) var env []string if strings.HasPrefix(req.Spec.RepoIdentifier, "azure") { - if env, err = restic.AzureCmdEnv(location); err != nil { + if env, err = restic.AzureCmdEnv(c.kbClient, req.Namespace, req.Spec.BackupStorageLocation); err != nil { return c.fail(req, errors.Wrap(err, "error setting restic cmd env").Error(), log) } resticCmd.Env = env } else if strings.HasPrefix(req.Spec.RepoIdentifier, "s3") { - if env, err = restic.S3CmdEnv(location); err != nil { + if env, err = restic.S3CmdEnv(c.kbClient, req.Namespace, req.Spec.BackupStorageLocation); err != nil { return c.fail(req, errors.Wrap(err, "error setting restic cmd env").Error(), log) } resticCmd.Env = env diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index b280a32d42..a2b995e8f3 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -296,11 +296,11 @@ func (c *podVolumeRestoreController) processRestore(req *velerov1api.PodVolumeRe defer os.Remove(credsFile) // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic - location, err := restic.GetCACert(c.kbClient, req.Namespace, req.Spec.BackupStorageLocation) + caCert, err := restic.GetCACert(c.kbClient, req.Namespace, req.Spec.BackupStorageLocation) if err != nil { log.WithError(err).Error("Error getting caCert") } - caCert := location.Spec.ObjectStorage.CACert + var caCertFile string if caCert != nil { caCertFile, err = restic.TempCACertFile(caCert, req.Spec.BackupStorageLocation, c.fileSystem) @@ -312,7 +312,7 @@ func (c *podVolumeRestoreController) processRestore(req *velerov1api.PodVolumeRe } // execute the restore process - if err := c.restorePodVolume(req, location, credsFile, caCertFile, volumeDir, log); err != nil { + if err := c.restorePodVolume(req, credsFile, caCertFile, volumeDir, log); err != nil { log.WithError(err).Error("Error restoring volume") return c.failRestore(req, errors.Wrap(err, "error restoring volume").Error(), log) } @@ -331,7 +331,7 @@ func (c *podVolumeRestoreController) processRestore(req *velerov1api.PodVolumeRe return nil } -func (c *podVolumeRestoreController) restorePodVolume(req *velerov1api.PodVolumeRestore, location *velerov1api.BackupStorageLocation, credsFile, caCertFile, volumeDir string, log logrus.FieldLogger) error { +func (c *podVolumeRestoreController) restorePodVolume(req *velerov1api.PodVolumeRestore, credsFile, caCertFile, volumeDir string, log logrus.FieldLogger) error { // 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)) @@ -350,13 +350,13 @@ func (c *podVolumeRestoreController) restorePodVolume(req *velerov1api.PodVolume // Running restic command might need additional provider specific environment variables. Based on the provider, we // set resticCmd.Env appropriately (currently for Azure and S3 based backuplocations) if strings.HasPrefix(req.Spec.RepoIdentifier, "azure") { - env, err := restic.AzureCmdEnv(location) + env, err := restic.AzureCmdEnv(c.kbClient, req.Namespace, req.Spec.BackupStorageLocation) if err != nil { return c.failRestore(req, errors.Wrap(err, "error setting restic cmd env").Error(), log) } resticCmd.Env = env } else if strings.HasPrefix(req.Spec.RepoIdentifier, "s3") { - env, err := restic.S3CmdEnv(location) + env, err := restic.S3CmdEnv(c.kbClient, req.Namespace, req.Spec.BackupStorageLocation) if err != nil { return c.failRestore(req, errors.Wrap(err, "error setting restic cmd env").Error(), log) } diff --git a/pkg/controller/suite_test.go b/pkg/controller/suite_test.go index 01e9747230..8fe4412c40 100644 --- a/pkg/controller/suite_test.go +++ b/pkg/controller/suite_test.go @@ -28,7 +28,6 @@ import ( "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client" - // +kubebuilder:scaffold:imports ) func newFakeClient(t *testing.T, initObjs ...runtime.Object) client.Client { diff --git a/pkg/restic/common.go b/pkg/restic/common.go index 0a58220273..d7e99b9936 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -288,11 +288,11 @@ func TempCACertFile(caCert []byte, bsl string, fs filesystem.Interface) (string, return name, nil } -func GetCACert(client client.Client, namespace, name string) (*velerov1api.BackupStorageLocation, error) { +func GetCACert(client client.Client, namespace, backupLocation string) ([]byte, error) { location := &velerov1api.BackupStorageLocation{} if err := client.Get(context.Background(), kbclient.ObjectKey{ Namespace: namespace, - Name: name, + Name: backupLocation, }, location); err != nil { return nil, err } @@ -301,7 +301,7 @@ func GetCACert(client client.Client, namespace, name string) (*velerov1api.Backu return nil, nil } - return location, nil + return location.Spec.ObjectStorage.CACert, nil } // NewPodVolumeRestoreListOptions creates a ListOptions with a label selector configured to @@ -316,7 +316,15 @@ func NewPodVolumeRestoreListOptions(name string) metav1.ListOptions { // should be used when running a restic command for an Azure backend. This list is // the current environment, plus the Azure-specific variables restic needs, namely // a storage account name and key. -func AzureCmdEnv(loc *velerov1api.BackupStorageLocation) ([]string, error) { +func AzureCmdEnv(client client.Client, namespace, backupLocation string) ([]string, error) { + loc := &velerov1api.BackupStorageLocation{} + if err := client.Get(context.Background(), kbclient.ObjectKey{ + Namespace: namespace, + Name: backupLocation, + }, loc); err != nil { + return nil, err + } + azureVars, err := getAzureResticEnvVars(loc.Spec.Config) if err != nil { return nil, errors.Wrap(err, "error getting azure restic env vars") @@ -334,7 +342,15 @@ func AzureCmdEnv(loc *velerov1api.BackupStorageLocation) ([]string, error) { // should be used when running a restic command for an S3 backend. This list is // the current environment, plus the AWS-specific variables restic needs, namely // a credential profile. -func S3CmdEnv(loc *velerov1api.BackupStorageLocation) ([]string, error) { +func S3CmdEnv(client client.Client, namespace, backupLocation string) ([]string, error) { + loc := &velerov1api.BackupStorageLocation{} + if err := client.Get(context.Background(), kbclient.ObjectKey{ + Namespace: namespace, + Name: backupLocation, + }, loc); err != nil { + return nil, err + } + awsVars, err := getS3ResticEnvVars(loc.Spec.Config) if err != nil { return nil, errors.Wrap(err, "error getting aws restic env vars") diff --git a/pkg/restic/common_test.go b/pkg/restic/common_test.go index 7ce835a00b..b4dec49299 100644 --- a/pkg/restic/common_test.go +++ b/pkg/restic/common_test.go @@ -404,10 +404,9 @@ func TestTempCACertFile(t *testing.T) { fakeClient.Create(context.Background(), bsl) // expect temp file to be created with cacert value - location, err := GetCACert(fakeClient, bsl.Namespace, bsl.Name) + caCert, err := GetCACert(fakeClient, bsl.Namespace, bsl.Name) require.NoError(t, err) - caCert := location.Spec.ObjectStorage.CACert fileName, err := TempCACertFile(caCert, "default", fs) require.NoError(t, err) diff --git a/pkg/restic/repository_manager.go b/pkg/restic/repository_manager.go index edb404d53e..08b23ffd9b 100644 --- a/pkg/restic/repository_manager.go +++ b/pkg/restic/repository_manager.go @@ -245,11 +245,11 @@ func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error { cmd.PasswordFile = file // if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic - location, err := GetCACert(rm.kbClient, rm.namespace, backupLocation) + caCert, err := GetCACert(rm.kbClient, rm.namespace, backupLocation) if err != nil { return err } - caCert := location.Spec.ObjectStorage.CACert + var caCertFile string if caCert != nil { caCertFile, err = TempCACertFile(caCert, backupLocation, rm.fileSystem) @@ -262,13 +262,13 @@ func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error { cmd.CACertFile = caCertFile if strings.HasPrefix(cmd.RepoIdentifier, "azure") { - env, err := AzureCmdEnv(location) + env, err := AzureCmdEnv(rm.kbClient, rm.namespace, backupLocation) if err != nil { return err } cmd.Env = env } else if strings.HasPrefix(cmd.RepoIdentifier, "s3") { - env, err := S3CmdEnv(location) + env, err := S3CmdEnv(rm.kbClient, rm.namespace, backupLocation) if err != nil { return err } diff --git a/site/docs/master/code-standards.md b/site/docs/master/code-standards.md index 261c5945ff..49abc29417 100644 --- a/site/docs/master/code-standards.md +++ b/site/docs/master/code-standards.md @@ -54,8 +54,7 @@ Example: corev1client "k8s.io/client-go/kubernetes/typed/core/v1" corev1listers "k8s.io/client-go/listers/core/v1" - veleroapiv1 "github.com/vmware-tanzu/velero/api/v1" // for the new kubebuilder apis - velerov1api ""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" ) From e8c830fd784cf7f5d6c0f2a6359fb995142138ff Mon Sep 17 00:00:00 2001 From: Carlisia Date: Wed, 17 Jun 2020 16:57:00 -0700 Subject: [PATCH 26/34] Clean up Signed-off-by: Carlisia --- pkg/controller/backup_controller.go | 8 +-- pkg/controller/backup_controller_test.go | 8 +-- pkg/controller/backup_deletion_controller.go | 8 +-- pkg/controller/backup_sync_controller.go | 10 ++-- pkg/controller/download_request_controller.go | 8 +-- pkg/controller/gc_controller.go | 8 +-- .../restic_repository_controller.go | 55 +++++++++---------- pkg/controller/restore_controller.go | 8 +-- 8 files changed, 56 insertions(+), 57 deletions(-) diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 741e88310b..4c410655d7 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -68,7 +68,7 @@ type backupController struct { backupper pkgbackup.Backupper lister velerov1listers.BackupLister client velerov1client.BackupsGetter - kbclient client.Client + kbClient client.Client clock clock.Clock backupLogLevel logrus.Level newPluginManager func(logrus.FieldLogger) clientmgmt.Manager @@ -94,7 +94,7 @@ func NewBackupController( backupLogLevel logrus.Level, newPluginManager func(logrus.FieldLogger) clientmgmt.Manager, backupTracker BackupTracker, - kbclient client.Client, + kbClient client.Client, defaultBackupLocation string, defaultVolumesToRestic bool, defaultBackupTTL time.Duration, @@ -115,7 +115,7 @@ func NewBackupController( backupLogLevel: backupLogLevel, newPluginManager: newPluginManager, backupTracker: backupTracker, - kbclient: kbclient, + kbClient: kbClient, defaultBackupLocation: defaultBackupLocation, defaultVolumesToRestic: defaultVolumesToRestic, defaultBackupTTL: defaultBackupTTL, @@ -375,7 +375,7 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg // validate the storage location, and store the BackupStorageLocation API obj on the request storageLocation := &velerov1api.BackupStorageLocation{} - if err := c.kbclient.Get(context.Background(), client.ObjectKey{ + if err := c.kbClient.Get(context.Background(), client.ObjectKey{ Namespace: request.Namespace, Name: request.Spec.StorageLocation, }, storageLocation); err != nil { diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index dd83ce24b1..80a0515d7a 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -193,7 +193,7 @@ func TestProcessBackupValidationFailures(t *testing.T) { discoveryHelper: discoveryHelper, client: clientset.VeleroV1(), lister: sharedInformers.Velero().V1().Backups().Lister(), - kbclient: fakeClient, + kbClient: fakeClient, snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), defaultBackupLocation: defaultBackupLocation.Name, clock: &clock.RealClock{}, @@ -260,7 +260,7 @@ func TestBackupLocationLabel(t *testing.T) { discoveryHelper: discoveryHelper, client: clientset.VeleroV1(), lister: sharedInformers.Velero().V1().Backups().Lister(), - kbclient: fakeClient, + kbClient: fakeClient, snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), defaultBackupLocation: test.backupLocation.Name, clock: &clock.RealClock{}, @@ -322,7 +322,7 @@ func TestDefaultBackupTTL(t *testing.T) { c := &backupController{ genericController: newGenericController("backup-test", logger), discoveryHelper: discoveryHelper, - kbclient: fakeClient, + kbClient: fakeClient, snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), defaultBackupTTL: defaultBackupTTL.Duration, clock: clock.NewFakeClock(now), @@ -812,7 +812,7 @@ func TestProcessBackupCompletions(t *testing.T) { discoveryHelper: discoveryHelper, client: clientset.VeleroV1(), lister: sharedInformers.Velero().V1().Backups().Lister(), - kbclient: fakeClient, + kbClient: fakeClient, snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), defaultBackupLocation: defaultBackupLocation.Name, defaultVolumesToRestic: test.defaultVolumesToRestic, diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 837e37d5f7..bee84abb0e 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -66,7 +66,7 @@ type backupDeletionController struct { backupTracker BackupTracker resticMgr restic.RepositoryManager podvolumeBackupLister velerov1listers.PodVolumeBackupLister - kbclient client.Client + kbClient client.Client snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister csiSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister csiSnapshotContentLister snapshotv1beta1listers.VolumeSnapshotContentLister @@ -89,7 +89,7 @@ func NewBackupDeletionController( backupTracker BackupTracker, resticMgr restic.RepositoryManager, podvolumeBackupLister velerov1listers.PodVolumeBackupLister, - kbclient client.Client, + kbClient client.Client, snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, csiSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister, csiSnapshotContentLister snapshotv1beta1listers.VolumeSnapshotContentLister, @@ -107,7 +107,7 @@ func NewBackupDeletionController( backupTracker: backupTracker, resticMgr: resticMgr, podvolumeBackupLister: podvolumeBackupLister, - kbclient: kbclient, + kbClient: kbClient, snapshotLocationLister: snapshotLocationLister, csiSnapshotLister: csiSnapshotLister, csiSnapshotContentLister: csiSnapshotContentLister, @@ -217,7 +217,7 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR // Don't allow deleting backups in read-only storage locations location := &velerov1api.BackupStorageLocation{} - if err := c.kbclient.Get(context.Background(), client.ObjectKey{ + if err := c.kbClient.Get(context.Background(), client.ObjectKey{ Namespace: backup.Namespace, Name: backup.Spec.StorageLocation, }, location); err != nil { diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index 85112a35bc..de05f21455 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -44,7 +44,7 @@ type backupSyncController struct { *genericController backupClient velerov1client.BackupsGetter - kbclient client.Client + kbClient client.Client podVolumeBackupClient velerov1client.PodVolumeBackupsGetter backupLister velerov1listers.BackupLister csiSnapshotClient *snapshotterClientSet.Clientset @@ -58,7 +58,7 @@ type backupSyncController struct { func NewBackupSyncController( backupClient velerov1client.BackupsGetter, - kbclient client.Client, + kbClient client.Client, podVolumeBackupClient velerov1client.PodVolumeBackupsGetter, backupLister velerov1listers.BackupLister, syncPeriod time.Duration, @@ -77,7 +77,7 @@ func NewBackupSyncController( c := &backupSyncController{ genericController: newGenericController("backup-sync", logger), backupClient: backupClient, - kbclient: kbclient, + kbClient: kbClient, podVolumeBackupClient: podVolumeBackupClient, namespace: namespace, defaultBackupLocation: defaultBackupLocation, @@ -123,7 +123,7 @@ func (c *backupSyncController) run() { c.logger.Debug("Checking for existing backup storage locations to sync into cluster") locationList := &velerov1api.BackupStorageLocationList{} - if err := c.kbclient.List(context.Background(), locationList, &client.ListOptions{ + if err := c.kbClient.List(context.Background(), locationList, &client.ListOptions{ Namespace: c.namespace, }); err != nil { c.logger.WithError(errors.WithStack(err)).Error("Error getting backup storage locations from lister") @@ -308,7 +308,7 @@ func (c *backupSyncController) run() { // update the location's last-synced time field statusPatch := client.MergeFrom(location.DeepCopyObject()) 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.Status().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/download_request_controller.go b/pkg/controller/download_request_controller.go index 9538563dde..51125c92f7 100644 --- a/pkg/controller/download_request_controller.go +++ b/pkg/controller/download_request_controller.go @@ -48,7 +48,7 @@ type downloadRequestController struct { downloadRequestLister velerov1listers.DownloadRequestLister restoreLister velerov1listers.RestoreLister clock clock.Clock - kbclient client.Client + kbClient client.Client backupLister velerov1listers.BackupLister newPluginManager func(logrus.FieldLogger) clientmgmt.Manager newBackupStore func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) @@ -59,7 +59,7 @@ func NewDownloadRequestController( downloadRequestClient velerov1client.DownloadRequestsGetter, downloadRequestInformer velerov1informers.DownloadRequestInformer, restoreLister velerov1listers.RestoreLister, - kbclient client.Client, + kbClient client.Client, backupLister velerov1listers.BackupLister, newPluginManager func(logrus.FieldLogger) clientmgmt.Manager, logger logrus.FieldLogger, @@ -69,7 +69,7 @@ func NewDownloadRequestController( downloadRequestClient: downloadRequestClient, downloadRequestLister: downloadRequestInformer.Lister(), restoreLister: restoreLister, - kbclient: kbclient, + kbClient: kbClient, backupLister: backupLister, // use variables to refer to these functions so they can be @@ -162,7 +162,7 @@ func (c *downloadRequestController) generatePreSignedURL(downloadRequest *velero } backupLocation := &velerov1api.BackupStorageLocation{} - if err := c.kbclient.Get(context.Background(), client.ObjectKey{ + if err := c.kbClient.Get(context.Background(), client.ObjectKey{ Namespace: backup.Namespace, Name: backup.Spec.StorageLocation, }, backupLocation); err != nil { diff --git a/pkg/controller/gc_controller.go b/pkg/controller/gc_controller.go index 64315cca3c..488589aa64 100644 --- a/pkg/controller/gc_controller.go +++ b/pkg/controller/gc_controller.go @@ -48,7 +48,7 @@ type gcController struct { backupLister velerov1listers.BackupLister deleteBackupRequestLister velerov1listers.DeleteBackupRequestLister deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter - kbclient client.Client + kbClient client.Client clock clock.Clock } @@ -59,7 +59,7 @@ func NewGCController( backupInformer velerov1informers.BackupInformer, deleteBackupRequestLister velerov1listers.DeleteBackupRequestLister, deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter, - kbclient client.Client, + kbClient client.Client, ) Interface { c := &gcController{ genericController: newGenericController("gc-controller", logger), @@ -67,7 +67,7 @@ func NewGCController( backupLister: backupInformer.Lister(), deleteBackupRequestLister: deleteBackupRequestLister, deleteBackupRequestClient: deleteBackupRequestClient, - kbclient: kbclient, + kbClient: kbClient, } c.syncHandler = c.processQueueItem @@ -134,7 +134,7 @@ func (c *gcController) processQueueItem(key string) error { log.Info("Backup has expired") loc := &velerov1api.BackupStorageLocation{} - if err := c.kbclient.Get(context.Background(), client.ObjectKey{ + if err := c.kbClient.Get(context.Background(), client.ObjectKey{ Namespace: ns, Name: backup.Spec.StorageLocation, }, loc); err != nil { diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index 17e266cf2b..3baf1df572 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -32,7 +32,6 @@ import ( "k8s.io/apimachinery/pkg/util/clock" "k8s.io/client-go/tools/cache" - v1 "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" velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" @@ -47,7 +46,7 @@ type resticRepositoryController struct { resticRepositoryClient velerov1client.ResticRepositoriesGetter resticRepositoryLister velerov1listers.ResticRepositoryLister - kbclient client.Client + kbClient client.Client repositoryManager restic.RepositoryManager defaultMaintenanceFrequency time.Duration @@ -59,7 +58,7 @@ func NewResticRepositoryController( logger logrus.FieldLogger, resticRepositoryInformer velerov1informers.ResticRepositoryInformer, resticRepositoryClient velerov1client.ResticRepositoriesGetter, - kbclient client.Client, + kbClient client.Client, repositoryManager restic.RepositoryManager, defaultMaintenanceFrequency time.Duration, ) Interface { @@ -67,7 +66,7 @@ func NewResticRepositoryController( genericController: newGenericController("restic-repository", logger), resticRepositoryClient: resticRepositoryClient, resticRepositoryLister: resticRepositoryInformer.Lister(), - kbclient: kbclient, + kbClient: kbClient, repositoryManager: repositoryManager, defaultMaintenanceFrequency: defaultMaintenanceFrequency, @@ -133,7 +132,7 @@ func (c *resticRepositoryController) processQueueItem(key string) error { // Don't mutate the shared cache reqCopy := req.DeepCopy() - if req.Status.Phase == "" || req.Status.Phase == v1.ResticRepositoryPhaseNew { + if req.Status.Phase == "" || req.Status.Phase == velerov1api.ResticRepositoryPhaseNew { return c.initializeRepo(reqCopy, log) } @@ -146,21 +145,21 @@ func (c *resticRepositoryController) processQueueItem(key string) error { } switch req.Status.Phase { - case v1.ResticRepositoryPhaseReady: + case velerov1api.ResticRepositoryPhaseReady: return c.runMaintenanceIfDue(reqCopy, log) - case v1.ResticRepositoryPhaseNotReady: + case velerov1api.ResticRepositoryPhaseNotReady: return c.checkNotReadyRepo(reqCopy, log) } return nil } -func (c *resticRepositoryController) initializeRepo(req *v1.ResticRepository, log logrus.FieldLogger) error { +func (c *resticRepositoryController) initializeRepo(req *velerov1api.ResticRepository, log logrus.FieldLogger) 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 := c.kbClient.Get(context.Background(), client.ObjectKey{ Namespace: req.Namespace, Name: req.Spec.BackupStorageLocation, }, loc); err != nil { @@ -169,9 +168,9 @@ func (c *resticRepositoryController) initializeRepo(req *v1.ResticRepository, lo repoIdentifier, err := restic.GetRepoIdentifier(loc, req.Spec.VolumeNamespace) if err != nil { - return c.patchResticRepository(req, func(r *v1.ResticRepository) { + return c.patchResticRepository(req, func(r *velerov1api.ResticRepository) { r.Status.Message = err.Error() - r.Status.Phase = v1.ResticRepositoryPhaseNotReady + r.Status.Phase = velerov1api.ResticRepositoryPhaseNotReady if r.Spec.MaintenanceFrequency.Duration <= 0 { r.Spec.MaintenanceFrequency = metav1.Duration{Duration: c.defaultMaintenanceFrequency} @@ -180,7 +179,7 @@ func (c *resticRepositoryController) initializeRepo(req *v1.ResticRepository, lo } // defaulting - if the patch fails, return an error so the item is returned to the queue - if err := c.patchResticRepository(req, func(r *v1.ResticRepository) { + if err := c.patchResticRepository(req, func(r *velerov1api.ResticRepository) { r.Spec.ResticIdentifier = repoIdentifier if r.Spec.MaintenanceFrequency.Duration <= 0 { @@ -194,8 +193,8 @@ func (c *resticRepositoryController) initializeRepo(req *v1.ResticRepository, lo return c.patchResticRepository(req, repoNotReady(err.Error())) } - return c.patchResticRepository(req, func(req *v1.ResticRepository) { - req.Status.Phase = v1.ResticRepositoryPhaseReady + return c.patchResticRepository(req, func(req *velerov1api.ResticRepository) { + req.Status.Phase = velerov1api.ResticRepositoryPhaseReady req.Status.LastMaintenanceTime = &metav1.Time{Time: time.Now()} }) } @@ -203,7 +202,7 @@ func (c *resticRepositoryController) initializeRepo(req *v1.ResticRepository, lo // 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 *v1.ResticRepository, repoManager restic.RepositoryManager) error { +func ensureRepo(repo *velerov1api.ResticRepository, 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. @@ -219,7 +218,7 @@ func ensureRepo(repo *v1.ResticRepository, repoManager restic.RepositoryManager) return nil } -func (c *resticRepositoryController) runMaintenanceIfDue(req *v1.ResticRepository, log logrus.FieldLogger) error { +func (c *resticRepositoryController) runMaintenanceIfDue(req *velerov1api.ResticRepository, log logrus.FieldLogger) error { log.Debug("resticRepositoryController.runMaintenanceIfDue") now := c.clock.Now() @@ -236,23 +235,23 @@ func (c *resticRepositoryController) runMaintenanceIfDue(req *v1.ResticRepositor log.Debug("Pruning repo") if err := c.repositoryManager.PruneRepo(req); err != nil { log.WithError(err).Warn("error pruning repository") - if patchErr := c.patchResticRepository(req, func(r *v1.ResticRepository) { + if patchErr := c.patchResticRepository(req, func(r *velerov1api.ResticRepository) { r.Status.Message = err.Error() }); patchErr != nil { return patchErr } } - return c.patchResticRepository(req, func(req *v1.ResticRepository) { + return c.patchResticRepository(req, func(req *velerov1api.ResticRepository) { req.Status.LastMaintenanceTime = &metav1.Time{Time: now} }) } -func dueForMaintenance(req *v1.ResticRepository, now time.Time) bool { +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 *v1.ResticRepository, log logrus.FieldLogger) error { +func (c *resticRepositoryController) checkNotReadyRepo(req *velerov1api.ResticRepository, log logrus.FieldLogger) error { // no identifier: can't possibly be ready, so just return if req.Spec.ResticIdentifier == "" { return nil @@ -269,16 +268,16 @@ func (c *resticRepositoryController) checkNotReadyRepo(req *v1.ResticRepository, return c.patchResticRepository(req, repoReady()) } -func repoNotReady(msg string) func(*v1.ResticRepository) { - return func(r *v1.ResticRepository) { - r.Status.Phase = v1.ResticRepositoryPhaseNotReady +func repoNotReady(msg string) func(*velerov1api.ResticRepository) { + return func(r *velerov1api.ResticRepository) { + r.Status.Phase = velerov1api.ResticRepositoryPhaseNotReady r.Status.Message = msg } } -func repoReady() func(*v1.ResticRepository) { - return func(r *v1.ResticRepository) { - r.Status.Phase = v1.ResticRepositoryPhaseReady +func repoReady() func(*velerov1api.ResticRepository) { + return func(r *velerov1api.ResticRepository) { + r.Status.Phase = velerov1api.ResticRepositoryPhaseReady r.Status.Message = "" } } @@ -286,7 +285,7 @@ func repoReady() func(*v1.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 *v1.ResticRepository, mutate func(*v1.ResticRepository)) error { +func (c *resticRepositoryController) patchResticRepository(req *velerov1api.ResticRepository, mutate func(*velerov1api.ResticRepository)) error { // Record original json oldData, err := json.Marshal(req) if err != nil { @@ -312,7 +311,7 @@ func (c *resticRepositoryController) patchResticRepository(req *v1.ResticReposit } // patch, and if successful, update req - var patched *v1.ResticRepository + var patched *velerov1api.ResticRepository if patched, err = c.resticRepositoryClient.ResticRepositories(req.Namespace).Patch(req.Name, types.MergePatchType, patchBytes); err != nil { return errors.Wrap(err, "error patching ResticRepository") } diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index 06c29f93b4..756a49eec6 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -83,7 +83,7 @@ type restoreController struct { restorer pkgrestore.Restorer backupLister velerov1listers.BackupLister restoreLister velerov1listers.RestoreLister - kbclient client.Client + kbClient client.Client snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister restoreLogLevel logrus.Level defaultBackupLocation string @@ -101,7 +101,7 @@ func NewRestoreController( podVolumeBackupClient velerov1client.PodVolumeBackupsGetter, restorer pkgrestore.Restorer, backupLister velerov1listers.BackupLister, - kbclient client.Client, + kbClient client.Client, snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, logger logrus.FieldLogger, restoreLogLevel logrus.Level, @@ -118,7 +118,7 @@ func NewRestoreController( restorer: restorer, backupLister: backupLister, restoreLister: restoreInformer.Lister(), - kbclient: kbclient, + kbClient: kbClient, snapshotLocationLister: snapshotLocationLister, restoreLogLevel: restoreLogLevel, defaultBackupLocation: defaultBackupLocation, @@ -400,7 +400,7 @@ func (c *restoreController) fetchBackupInfo(backupName string, pluginManager cli } location := &velerov1api.BackupStorageLocation{} - if err := c.kbclient.Get(context.Background(), client.ObjectKey{ + if err := c.kbClient.Get(context.Background(), client.ObjectKey{ Namespace: c.namespace, Name: backup.Spec.StorageLocation, }, location); err != nil { From 7d51d027fe19a46824f57daaca1504bed511bbd0 Mon Sep 17 00:00:00 2001 From: Carlisia Date: Wed, 17 Jun 2020 18:01:43 -0700 Subject: [PATCH 27/34] Manager registers ALL controllers for restic too Signed-off-by: Carlisia --- .../managercontroller/managercontroller.go | 55 +++++++++++++++++++ pkg/cmd/cli/restic/server.go | 33 +++-------- pkg/cmd/server/server.go | 35 ++---------- 3 files changed, 68 insertions(+), 55 deletions(-) create mode 100644 internal/util/managercontroller/managercontroller.go diff --git a/internal/util/managercontroller/managercontroller.go b/internal/util/managercontroller/managercontroller.go new file mode 100644 index 0000000000..44387d339a --- /dev/null +++ b/internal/util/managercontroller/managercontroller.go @@ -0,0 +1,55 @@ +/* +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 managercontroller + +import ( + "context" + + "sigs.k8s.io/controller-runtime/pkg/manager" + + "github.com/vmware-tanzu/velero/pkg/controller" +) + +// Runnable will turn a "regular" runnable component (such as a controller) +// into a controller-runtime Runnable +func Runnable(p controller.Interface, numWorkers int) manager.Runnable { + return manager.RunnableFunc(func(stop <-chan struct{}) error { + ctx, cancel := contextForChannel(stop) + defer cancel() + + return p.Run(ctx, numWorkers) + }) +} + +// contextForChannel derives a child context from a parent channel. +// +// The derived context's Done channel is closed when the returned cancel function +// is called or when the parent channel is closed, whichever happens first. +// +// Note the caller must *always* call the CancelFunc, otherwise resources may be leaked. +func contextForChannel(parentCh <-chan struct{}) (context.Context, context.CancelFunc) { + ctx, cancel := context.WithCancel(context.Background()) + + go func() { + select { + case <-parentCh: + cancel() + case <-ctx.Done(): + } + }() + return ctx, cancel +} diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/restic/server.go index 5bab7b1337..01e4111c65 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/restic/server.go @@ -20,8 +20,8 @@ import ( "fmt" "os" "strings" - "sync" + "github.com/vmware-tanzu/velero/internal/util/managercontroller" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/pkg/errors" @@ -183,8 +183,6 @@ func (s *resticServer) run() { s.logger.Info("Starting controllers") - var wg sync.WaitGroup - backupController := controller.NewPodVolumeBackupController( s.logger, s.veleroInformerFactory.Velero().V1().PodVolumeBackups(), @@ -196,11 +194,6 @@ func (s *resticServer) run() { s.mgr.GetClient(), os.Getenv("NODE_NAME"), ) - wg.Add(1) - go func() { - defer wg.Done() - backupController.Run(s.ctx, 1) - }() restoreController := controller.NewPodVolumeRestoreController( s.logger, @@ -213,32 +206,24 @@ func (s *resticServer) run() { s.mgr.GetClient(), os.Getenv("NODE_NAME"), ) - wg.Add(1) - go func() { - defer wg.Done() - restoreController.Run(s.ctx, 1) - }() go s.veleroInformerFactory.Start(s.ctx.Done()) go s.kubeInformerFactory.Start(s.ctx.Done()) go s.podInformer.Run(s.ctx.Done()) go s.secretInformer.Run(s.ctx.Done()) - s.logger.Info("Controllers started successfully") + // Adding the controllers to the manager will register them as a (runtime-controller) runnable, + // so the manager will ensure the cache is started and ready before all controller are started + s.mgr.Add(managercontroller.Runnable(backupController, 1)) + s.mgr.Add(managercontroller.Runnable(restoreController, 1)) - wg.Add(1) - go func() { - defer wg.Done() - // +kubebuilder:scaffold:builder - if err := s.mgr.Start(ctrl.SetupSignalHandler()); err != nil { - s.logger.Fatal("Problem starting manager", err) - } - }() + s.logger.Info("Controllers starting...") - <-s.ctx.Done() + if err := s.mgr.Start(ctrl.SetupSignalHandler()); err != nil { + s.logger.Fatal("Problem starting manager", err) + } s.logger.Info("Waiting for all controllers to shut down gracefully") - wg.Wait() } // validatePodVolumesHostPath validates that the pod volumes path contains a diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 65076bfb05..49e660b58c 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -56,6 +56,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/cmd" "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" "github.com/vmware-tanzu/velero/pkg/cmd/util/signals" + "github.com/vmware-tanzu/velero/pkg/controller" velerodiscovery "github.com/vmware-tanzu/velero/pkg/discovery" "github.com/vmware-tanzu/velero/pkg/features" @@ -74,6 +75,7 @@ import ( kbclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/manager" + "github.com/vmware-tanzu/velero/internal/util/managercontroller" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" ) @@ -897,9 +899,9 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string for i := range controllers { controllerRunInfo := controllers[i] - // Adding the controllers to the manager will register them as a runnable, + // Adding the controllers to the manager will register them as a (runtime-controller) runnable, // so the manager will ensure the cache is started and ready before all controller are started - s.mgr.Add(controllerRunnable(controllerRunInfo.controller, controllerRunInfo.numWorkers)) + s.mgr.Add(managercontroller.Runnable(controllerRunInfo.controller, controllerRunInfo.numWorkers)) } s.logger.Info("Server starting...") @@ -913,35 +915,6 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string return nil } -// controllerRunnable will turn an existing controller into a Runnable -func controllerRunnable(p controller.Interface, numWorkers int) manager.Runnable { - return manager.RunnableFunc(func(stop <-chan struct{}) error { - ctx, cancel := contextForChannel(stop) - defer cancel() - - return p.Run(ctx, numWorkers) - }) -} - -// contextForChannel derives a child context from a parent channel. -// -// The derived context's Done channel is closed when the returned cancel function -// is called or when the parent channel is closed, whichever happens first. -// -// Note the caller must *always* call the CancelFunc, otherwise resources may be leaked. -func contextForChannel(parentCh <-chan struct{}) (context.Context, context.CancelFunc) { - ctx, cancel := context.WithCancel(context.Background()) - - go func() { - select { - case <-parentCh: - cancel() - case <-ctx.Done(): - } - }() - return ctx, cancel -} - func (s *server) runProfiler() { mux := http.NewServeMux() mux.HandleFunc("/debug/pprof/", pprof.Index) From 1dcfd0478aa038e1c4b7cb7be3a1778b9c5a820d Mon Sep 17 00:00:00 2001 From: Carlisia Date: Fri, 19 Jun 2020 06:23:27 -0700 Subject: [PATCH 28/34] More code reviews Signed-off-by: Carlisia --- pkg/cmd/cli/restic/server.go | 2 - pkg/cmd/server/server.go | 2 - .../backup_deletion_controller_test.go | 175 +++++++++--------- pkg/controller/gc_controller_test.go | 22 +-- pkg/controller/restore_controller_test.go | 98 +++++----- site/docs/v1.3.1/code-standards.md | 3 +- site/docs/v1.3.2/code-standards.md | 2 +- site/docs/v1.4/code-standards.md | 2 +- 8 files changed, 148 insertions(+), 158 deletions(-) diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/restic/server.go index 01e4111c65..d75cbbcc64 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/restic/server.go @@ -222,8 +222,6 @@ func (s *resticServer) run() { if err := s.mgr.Start(ctrl.SetupSignalHandler()); err != nil { s.logger.Fatal("Problem starting manager", err) } - - s.logger.Info("Waiting for all controllers to shut down gracefully") } // validatePodVolumesHostPath validates that the pod volumes path contains a diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 49e660b58c..aef1cf6747 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -910,8 +910,6 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger.Fatal("Problem starting manager", err) } - s.logger.Info("Waiting for all controllers to shut down gracefully") - return nil } diff --git a/pkg/controller/backup_deletion_controller_test.go b/pkg/controller/backup_deletion_controller_test.go index 7a0c75e0c8..7b12b325a7 100644 --- a/pkg/controller/backup_deletion_controller_test.go +++ b/pkg/controller/backup_deletion_controller_test.go @@ -37,7 +37,6 @@ import ( core "k8s.io/client-go/testing" "sigs.k8s.io/controller-runtime/pkg/client" - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/builder" @@ -87,21 +86,21 @@ func TestBackupDeletionControllerProcessQueueItem(t *testing.T) { req := pkgbackup.NewDeleteBackupRequest("foo", "uid") req.Namespace = "foo" req.Name = "foo-abcde" - req.Status.Phase = velerov1.DeleteBackupRequestPhaseProcessed + req.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed err = controller.processQueueItem("foo/bar") assert.NoError(t, err) // Invoke processRequestFunc - for _, phase := range []velerov1.DeleteBackupRequestPhase{"", velerov1.DeleteBackupRequestPhaseNew, velerov1.DeleteBackupRequestPhaseInProgress} { + 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 *velerov1.DeleteBackupRequest + var actual *velerov1api.DeleteBackupRequest var called bool - controller.processRequestFunc = func(r *velerov1.DeleteBackupRequest) error { + controller.processRequestFunc = func(r *velerov1api.DeleteBackupRequest) error { called = true actual = r return errorToReturn @@ -129,7 +128,7 @@ type backupDeletionControllerTestData struct { volumeSnapshotter *velerotest.FakeVolumeSnapshotter backupStore *persistencemocks.BackupStore controller *backupDeletionController - req *velerov1.DeleteBackupRequest + req *velerov1api.DeleteBackupRequest } func setupBackupDeletionControllerTest(t *testing.T, objects ...runtime.Object) *backupDeletionControllerTestData { @@ -193,7 +192,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { expectedActions := []core.Action{ core.NewPatchAction( - velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), + velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), td.req.Namespace, td.req.Name, types.MergePatchType, @@ -213,15 +212,15 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { require.NoError(t, td.sharedInformers.Velero().V1().DeleteBackupRequests().Informer().GetStore().Add(td.req)) - existing := &velerov1.DeleteBackupRequest{ + existing := &velerov1api.DeleteBackupRequest{ ObjectMeta: metav1.ObjectMeta{ Namespace: td.req.Namespace, Name: "bar", Labels: map[string]string{ - velerov1.BackupNameLabel: td.req.Spec.BackupName, + velerov1api.BackupNameLabel: td.req.Spec.BackupName, }, }, - Spec: velerov1.DeleteBackupRequestSpec{ + Spec: velerov1api.DeleteBackupRequestSpec{ BackupName: td.req.Spec.BackupName, }, } @@ -230,15 +229,15 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { require.NoError(t, err) require.NoError(t, td.sharedInformers.Velero().V1().DeleteBackupRequests().Informer().GetStore().Add( - &velerov1.DeleteBackupRequest{ + &velerov1api.DeleteBackupRequest{ ObjectMeta: metav1.ObjectMeta{ Namespace: td.req.Namespace, Name: "bar-2", Labels: map[string]string{ - velerov1.BackupNameLabel: "some-other-backup", + velerov1api.BackupNameLabel: "some-other-backup", }, }, - Spec: velerov1.DeleteBackupRequestSpec{ + Spec: velerov1api.DeleteBackupRequestSpec{ BackupName: "some-other-backup", }, }, @@ -247,7 +246,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { assert.NoError(t, td.controller.processRequest(td.req)) expectedDeleteAction := core.NewDeleteAction( - velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), + velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), td.req.Namespace, "bar", ) @@ -269,7 +268,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { expectedActions := []core.Action{ core.NewPatchAction( - velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), + velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), td.req.Namespace, td.req.Name, types.MergePatchType, @@ -281,7 +280,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("patching to InProgress fails", func(t *testing.T) { - backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result() + backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").StorageLocation("default").Result() location := builder.ForBackupStorageLocation("velero", "default").Result() td := setupBackupDeletionControllerTest(t, location, backup) @@ -295,12 +294,12 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { expectedActions := []core.Action{ core.NewGetAction( - velerov1.SchemeGroupVersion.WithResource("backups"), + velerov1api.SchemeGroupVersion.WithResource("backups"), backup.Namespace, backup.Name, ), core.NewPatchAction( - velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), + velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), td.req.Namespace, td.req.Name, types.MergePatchType, @@ -311,7 +310,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("patching backup to Deleting fails", func(t *testing.T) { - backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result() + backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").StorageLocation("default").Result() location := builder.ForBackupStorageLocation("velero", "default").Result() td := setupBackupDeletionControllerTest(t, location, backup) @@ -328,19 +327,19 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { expectedActions := []core.Action{ core.NewGetAction( - velerov1.SchemeGroupVersion.WithResource("backups"), + velerov1api.SchemeGroupVersion.WithResource("backups"), backup.Namespace, backup.Name, ), core.NewPatchAction( - velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), + velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), td.req.Namespace, td.req.Name, types.MergePatchType, []byte(`{"status":{"phase":"InProgress"}}`), ), core.NewPatchAction( - velerov1.SchemeGroupVersion.WithResource("backups"), + velerov1api.SchemeGroupVersion.WithResource("backups"), backup.Namespace, backup.Name, types.MergePatchType, @@ -358,12 +357,12 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { expectedActions := []core.Action{ core.NewGetAction( - velerov1.SchemeGroupVersion.WithResource("backups"), + velerov1api.SchemeGroupVersion.WithResource("backups"), td.req.Namespace, td.req.Spec.BackupName, ), core.NewPatchAction( - velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), + velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), td.req.Namespace, td.req.Name, types.MergePatchType, @@ -375,7 +374,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("unable to find backup storage location", func(t *testing.T) { - backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result() + backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").StorageLocation("default").Result() td := setupBackupDeletionControllerTest(t, backup) @@ -384,12 +383,12 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { expectedActions := []core.Action{ core.NewGetAction( - velerov1.SchemeGroupVersion.WithResource("backups"), + velerov1api.SchemeGroupVersion.WithResource("backups"), td.req.Namespace, td.req.Spec.BackupName, ), core.NewPatchAction( - velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), + velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), td.req.Namespace, td.req.Name, types.MergePatchType, @@ -401,7 +400,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("backup storage location is in read-only mode", func(t *testing.T) { - backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result() + backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").StorageLocation("default").Result() location := builder.ForBackupStorageLocation("velero", "default").AccessMode(velerov1api.BackupStorageLocationAccessModeReadOnly).Result() td := setupBackupDeletionControllerTest(t, location, backup) @@ -411,12 +410,12 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { expectedActions := []core.Action{ core.NewGetAction( - velerov1.SchemeGroupVersion.WithResource("backups"), + velerov1api.SchemeGroupVersion.WithResource("backups"), td.req.Namespace, td.req.Spec.BackupName, ), core.NewPatchAction( - velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), + velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), td.req.Namespace, td.req.Name, types.MergePatchType, @@ -428,13 +427,13 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("full delete, no errors", func(t *testing.T) { - backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").Result() + backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").Result() backup.UID = "uid" backup.Spec.StorageLocation = "primary" - restore1 := builder.ForRestore("velero", "restore-1").Phase(velerov1.RestorePhaseCompleted).Backup("foo").Result() - restore2 := builder.ForRestore("velero", "restore-2").Phase(velerov1.RestorePhaseCompleted).Backup("foo").Result() - restore3 := builder.ForRestore("velero", "restore-3").Phase(velerov1.RestorePhaseCompleted).Backup("some-other-backup").Result() + 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) @@ -459,12 +458,12 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { require.NoError(t, td.fakeClient.Create(context.Background(), location)) - snapshotLocation := &velerov1.VolumeSnapshotLocation{ + snapshotLocation := &velerov1api.VolumeSnapshotLocation{ ObjectMeta: metav1.ObjectMeta{ Namespace: backup.Namespace, Name: "vsl-1", }, - Spec: velerov1.VolumeSnapshotLocationSpec{ + Spec: velerov1api.VolumeSnapshotLocationSpec{ Provider: "provider-1", }, } @@ -514,55 +513,55 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { expectedActions := []core.Action{ core.NewPatchAction( - velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), + 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( - velerov1.SchemeGroupVersion.WithResource("backups"), + velerov1api.SchemeGroupVersion.WithResource("backups"), td.req.Namespace, td.req.Spec.BackupName, ), core.NewPatchAction( - velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), + velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), td.req.Namespace, td.req.Name, types.MergePatchType, []byte(`{"metadata":{"labels":{"velero.io/backup-uid":"uid"}}}`), ), core.NewPatchAction( - velerov1.SchemeGroupVersion.WithResource("backups"), + velerov1api.SchemeGroupVersion.WithResource("backups"), td.req.Namespace, td.req.Spec.BackupName, types.MergePatchType, []byte(`{"status":{"phase":"Deleting"}}`), ), core.NewDeleteAction( - velerov1.SchemeGroupVersion.WithResource("restores"), + velerov1api.SchemeGroupVersion.WithResource("restores"), td.req.Namespace, "restore-1", ), core.NewDeleteAction( - velerov1.SchemeGroupVersion.WithResource("restores"), + velerov1api.SchemeGroupVersion.WithResource("restores"), td.req.Namespace, "restore-2", ), core.NewDeleteAction( - velerov1.SchemeGroupVersion.WithResource("backups"), + velerov1api.SchemeGroupVersion.WithResource("backups"), td.req.Namespace, td.req.Spec.BackupName, ), core.NewPatchAction( - velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), + velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), td.req.Namespace, td.req.Name, types.MergePatchType, []byte(`{"status":{"phase":"Processed"}}`), ), core.NewDeleteCollectionAction( - velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), + velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), td.req.Namespace, pkgbackup.NewDeleteBackupRequestListOptions(td.req.Spec.BackupName, "uid"), ), @@ -584,15 +583,15 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { backup.Spec.StorageLocation = "primary" restore1 := builder.ForRestore("velero", "restore-1"). - Phase(velerov1.RestorePhaseCompleted). + Phase(velerov1api.RestorePhaseCompleted). Backup("the-really-long-backup-name-that-is-much-more-than-63-characters"). Result() restore2 := builder.ForRestore("velero", "restore-2"). - Phase(velerov1.RestorePhaseCompleted). + Phase(velerov1api.RestorePhaseCompleted). Backup("the-really-long-backup-name-that-is-much-more-than-63-characters"). Result() restore3 := builder.ForRestore("velero", "restore-3"). - Phase(velerov1.RestorePhaseCompleted). + Phase(velerov1api.RestorePhaseCompleted). Backup("some-other-backup"). Result() @@ -620,12 +619,12 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { } require.NoError(t, td.fakeClient.Create(context.Background(), location)) - snapshotLocation := &velerov1.VolumeSnapshotLocation{ + snapshotLocation := &velerov1api.VolumeSnapshotLocation{ ObjectMeta: metav1.ObjectMeta{ Namespace: backup.Namespace, Name: "vsl-1", }, - Spec: velerov1.VolumeSnapshotLocationSpec{ + Spec: velerov1api.VolumeSnapshotLocationSpec{ Provider: "provider-1", }, } @@ -673,55 +672,55 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { expectedActions := []core.Action{ core.NewPatchAction( - velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), + 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( - velerov1.SchemeGroupVersion.WithResource("backups"), + velerov1api.SchemeGroupVersion.WithResource("backups"), td.req.Namespace, td.req.Spec.BackupName, ), core.NewPatchAction( - velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), + velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), td.req.Namespace, td.req.Name, types.MergePatchType, []byte(`{"metadata":{"labels":{"velero.io/backup-uid":"uid"}}}`), ), core.NewPatchAction( - velerov1.SchemeGroupVersion.WithResource("backups"), + velerov1api.SchemeGroupVersion.WithResource("backups"), td.req.Namespace, td.req.Spec.BackupName, types.MergePatchType, []byte(`{"status":{"phase":"Deleting"}}`), ), core.NewDeleteAction( - velerov1.SchemeGroupVersion.WithResource("restores"), + velerov1api.SchemeGroupVersion.WithResource("restores"), td.req.Namespace, "restore-1", ), core.NewDeleteAction( - velerov1.SchemeGroupVersion.WithResource("restores"), + velerov1api.SchemeGroupVersion.WithResource("restores"), td.req.Namespace, "restore-2", ), core.NewDeleteAction( - velerov1.SchemeGroupVersion.WithResource("backups"), + velerov1api.SchemeGroupVersion.WithResource("backups"), td.req.Namespace, td.req.Spec.BackupName, ), core.NewPatchAction( - velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), + velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), td.req.Namespace, td.req.Name, types.MergePatchType, []byte(`{"status":{"phase":"Processed"}}`), ), core.NewDeleteCollectionAction( - velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), + velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), td.req.Namespace, pkgbackup.NewDeleteBackupRequestListOptions(td.req.Spec.BackupName, "uid"), ), @@ -744,7 +743,7 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) { tests := []struct { name string - requests []*velerov1.DeleteBackupRequest + requests []*velerov1api.DeleteBackupRequest expectedDeletions []string }{ { @@ -752,14 +751,14 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) { }, { name: "older than max age, phase = '', don't delete", - requests: []*velerov1.DeleteBackupRequest{ + requests: []*velerov1api.DeleteBackupRequest{ { ObjectMeta: metav1.ObjectMeta{ Namespace: "ns", Name: "name", CreationTimestamp: metav1.Time{Time: expired1}, }, - Status: velerov1.DeleteBackupRequestStatus{ + Status: velerov1api.DeleteBackupRequestStatus{ Phase: "", }, }, @@ -767,45 +766,45 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) { }, { name: "older than max age, phase = New, don't delete", - requests: []*velerov1.DeleteBackupRequest{ + requests: []*velerov1api.DeleteBackupRequest{ { ObjectMeta: metav1.ObjectMeta{ Namespace: "ns", Name: "name", CreationTimestamp: metav1.Time{Time: expired1}, }, - Status: velerov1.DeleteBackupRequestStatus{ - Phase: velerov1.DeleteBackupRequestPhaseNew, + Status: velerov1api.DeleteBackupRequestStatus{ + Phase: velerov1api.DeleteBackupRequestPhaseNew, }, }, }, }, { name: "older than max age, phase = InProcess, don't delete", - requests: []*velerov1.DeleteBackupRequest{ + requests: []*velerov1api.DeleteBackupRequest{ { ObjectMeta: metav1.ObjectMeta{ Namespace: "ns", Name: "name", CreationTimestamp: metav1.Time{Time: expired1}, }, - Status: velerov1.DeleteBackupRequestStatus{ - Phase: velerov1.DeleteBackupRequestPhaseInProgress, + Status: velerov1api.DeleteBackupRequestStatus{ + Phase: velerov1api.DeleteBackupRequestPhaseInProgress, }, }, }, }, { name: "some expired, some not", - requests: []*velerov1.DeleteBackupRequest{ + requests: []*velerov1api.DeleteBackupRequest{ { ObjectMeta: metav1.ObjectMeta{ Namespace: "ns", Name: "unexpired-1", CreationTimestamp: metav1.Time{Time: unexpired1}, }, - Status: velerov1.DeleteBackupRequestStatus{ - Phase: velerov1.DeleteBackupRequestPhaseProcessed, + Status: velerov1api.DeleteBackupRequestStatus{ + Phase: velerov1api.DeleteBackupRequestPhaseProcessed, }, }, { @@ -814,8 +813,8 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) { Name: "expired-1", CreationTimestamp: metav1.Time{Time: expired1}, }, - Status: velerov1.DeleteBackupRequestStatus{ - Phase: velerov1.DeleteBackupRequestPhaseProcessed, + Status: velerov1api.DeleteBackupRequestStatus{ + Phase: velerov1api.DeleteBackupRequestPhaseProcessed, }, }, { @@ -824,8 +823,8 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) { Name: "unexpired-2", CreationTimestamp: metav1.Time{Time: unexpired2}, }, - Status: velerov1.DeleteBackupRequestStatus{ - Phase: velerov1.DeleteBackupRequestPhaseProcessed, + Status: velerov1api.DeleteBackupRequestStatus{ + Phase: velerov1api.DeleteBackupRequestPhaseProcessed, }, }, { @@ -834,8 +833,8 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) { Name: "expired-2", CreationTimestamp: metav1.Time{Time: expired2}, }, - Status: velerov1.DeleteBackupRequestStatus{ - Phase: velerov1.DeleteBackupRequestPhaseProcessed, + Status: velerov1api.DeleteBackupRequestStatus{ + Phase: velerov1api.DeleteBackupRequestPhaseProcessed, }, }, }, @@ -880,7 +879,7 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) { expectedActions := []core.Action{} for _, name := range test.expectedDeletions { - expectedActions = append(expectedActions, core.NewDeleteAction(velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), "ns", name)) + expectedActions = append(expectedActions, core.NewDeleteAction(velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), "ns", name)) } velerotest.CompareActions(t, expectedActions, client.Actions()) @@ -983,7 +982,7 @@ func TestDeleteCSIVolumeSnapshots(t *testing.T) { Name: "vs1", Namespace: "ns1", Labels: map[string]string{ - velerov1.BackupNameLabel: "backup1", + velerov1api.BackupNameLabel: "backup1", }, }, Status: &snapshotv1beta1api.VolumeSnapshotStatus{ @@ -1005,7 +1004,7 @@ func TestDeleteCSIVolumeSnapshots(t *testing.T) { Name: "vs2", Namespace: "ns1", Labels: map[string]string{ - velerov1.BackupNameLabel: "backup1", + velerov1api.BackupNameLabel: "backup1", }, }, Status: &snapshotv1beta1api.VolumeSnapshotStatus{ @@ -1027,7 +1026,7 @@ func TestDeleteCSIVolumeSnapshots(t *testing.T) { Name: "vs1", Namespace: "ns2", Labels: map[string]string{ - velerov1.BackupNameLabel: "backup1", + velerov1api.BackupNameLabel: "backup1", }, }, Status: &snapshotv1beta1api.VolumeSnapshotStatus{ @@ -1049,7 +1048,7 @@ func TestDeleteCSIVolumeSnapshots(t *testing.T) { Name: "vs2", Namespace: "ns2", Labels: map[string]string{ - velerov1.BackupNameLabel: "backup1", + velerov1api.BackupNameLabel: "backup1", }, }, Status: &snapshotv1beta1api.VolumeSnapshotStatus{ @@ -1063,7 +1062,7 @@ func TestDeleteCSIVolumeSnapshots(t *testing.T) { Name: "ns1NilStatusVS", Namespace: "ns2", Labels: map[string]string{ - velerov1.BackupNameLabel: "backup2", + velerov1api.BackupNameLabel: "backup2", }, }, Status: nil, @@ -1075,7 +1074,7 @@ func TestDeleteCSIVolumeSnapshots(t *testing.T) { Name: "ns1NilBoundVSCVS", Namespace: "ns2", Labels: map[string]string{ - velerov1.BackupNameLabel: "backup3", + velerov1api.BackupNameLabel: "backup3", }, }, Status: &snapshotv1beta1api.VolumeSnapshotStatus{ @@ -1090,7 +1089,7 @@ func TestDeleteCSIVolumeSnapshots(t *testing.T) { Name: "ns1NonExistentVSCVS", Namespace: "ns2", Labels: map[string]string{ - velerov1.BackupNameLabel: "backup3", + velerov1api.BackupNameLabel: "backup3", }, }, Status: &snapshotv1beta1api.VolumeSnapshotStatus{ @@ -1153,7 +1152,7 @@ func TestDeleteCSIVolumeSnapshotContents(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "retainVSC", Labels: map[string]string{ - velerov1.BackupNameLabel: "backup1", + velerov1api.BackupNameLabel: "backup1", }, }, Spec: snapshotv1beta1api.VolumeSnapshotContentSpec{ @@ -1164,7 +1163,7 @@ func TestDeleteCSIVolumeSnapshotContents(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "deleteVSC", Labels: map[string]string{ - velerov1.BackupNameLabel: "backup2", + velerov1api.BackupNameLabel: "backup2", }, }, Spec: snapshotv1beta1api.VolumeSnapshotContentSpec{ @@ -1176,7 +1175,7 @@ func TestDeleteCSIVolumeSnapshotContents(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "nothingVSC", Labels: map[string]string{ - velerov1.BackupNameLabel: "backup3", + velerov1api.BackupNameLabel: "backup3", }, }, Spec: snapshotv1beta1api.VolumeSnapshotContentSpec{}, diff --git a/pkg/controller/gc_controller_test.go b/pkg/controller/gc_controller_test.go index 6259396bda..7663b8951f 100644 --- a/pkg/controller/gc_controller_test.go +++ b/pkg/controller/gc_controller_test.go @@ -23,8 +23,6 @@ import ( "testing" "time" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -36,7 +34,7 @@ import ( kbclient "sigs.k8s.io/controller-runtime/pkg/client" - api "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" @@ -68,7 +66,7 @@ func TestGCControllerEnqueueAllBackups(t *testing.T) { var expected []string for i := 0; i < 3; i++ { - backup := builder.ForBackup(api.DefaultNamespace, fmt.Sprintf("backup-%d", i)).Result() + 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)) } @@ -158,8 +156,8 @@ func TestGCControllerProcessQueueItem(t *testing.T) { tests := []struct { name string - backup *api.Backup - deleteBackupRequests []*api.DeleteBackupRequest + backup *velerov1api.Backup + deleteBackupRequests []*velerov1api.DeleteBackupRequest backupLocation *velerov1api.BackupStorageLocation expectDeletion bool createDeleteBackupRequestError bool @@ -196,14 +194,14 @@ func TestGCControllerProcessQueueItem(t *testing.T) { name: "expired backup with a pending deletion request is not deleted", backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Result(), backupLocation: defaultBackupLocation, - deleteBackupRequests: []*api.DeleteBackupRequest{ + deleteBackupRequests: []*velerov1api.DeleteBackupRequest{ { ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1api.DefaultNamespace, Name: "foo", Labels: map[string]string{ - api.BackupNameLabel: "backup-1", - api.BackupUIDLabel: "", + velerov1api.BackupNameLabel: "backup-1", + velerov1api.BackupUIDLabel: "", }, }, Status: velerov1api.DeleteBackupRequestStatus{ @@ -217,14 +215,14 @@ func TestGCControllerProcessQueueItem(t *testing.T) { name: "expired backup with only processed deletion requests is deleted", backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Result(), backupLocation: defaultBackupLocation, - deleteBackupRequests: []*api.DeleteBackupRequest{ + deleteBackupRequests: []*velerov1api.DeleteBackupRequest{ { ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1api.DefaultNamespace, Name: "foo", Labels: map[string]string{ - api.BackupNameLabel: "backup-1", - api.BackupUIDLabel: "", + velerov1api.BackupNameLabel: "backup-1", + velerov1api.BackupUIDLabel: "", }, }, Status: velerov1api.DeleteBackupRequestStatus{ diff --git a/pkg/controller/restore_controller_test.go b/pkg/controller/restore_controller_test.go index a7d8cd6929..2a3ebd043b 100644 --- a/pkg/controller/restore_controller_test.go +++ b/pkg/controller/restore_controller_test.go @@ -24,8 +24,6 @@ import ( "testing" "time" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" @@ -37,7 +35,7 @@ import ( core "k8s.io/client-go/testing" "k8s.io/client-go/tools/cache" - api "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" @@ -60,17 +58,17 @@ func TestFetchBackupInfo(t *testing.T) { name string backupName string informerLocations []*velerov1api.BackupStorageLocation - informerBackups []*api.Backup - backupStoreBackup *api.Backup + informerBackups []*velerov1api.Backup + backupStoreBackup *velerov1api.Backup backupStoreError error - expectedRes *api.Backup + expectedRes *velerov1api.Backup expectedErr bool }{ { name: "lister has backup", backupName: "backup-1", informerLocations: []*velerov1api.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result()}, - informerBackups: []*api.Backup{defaultBackup().StorageLocation("default").Result()}, + informerBackups: []*velerov1api.Backup{defaultBackup().StorageLocation("default").Result()}, expectedRes: defaultBackup().StorageLocation("default").Result(), }, { @@ -78,7 +76,7 @@ func TestFetchBackupInfo(t *testing.T) { backupName: "backup-1", backupStoreBackup: defaultBackup().StorageLocation("default").Result(), informerLocations: []*velerov1api.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result()}, - informerBackups: []*api.Backup{defaultBackup().StorageLocation("default").Result()}, + informerBackups: []*velerov1api.Backup{defaultBackup().StorageLocation("default").Result()}, expectedRes: defaultBackup().StorageLocation("default").Result(), }, { @@ -107,7 +105,7 @@ func TestFetchBackupInfo(t *testing.T) { defer backupStore.AssertExpectations(t) c := NewRestoreController( - api.DefaultNamespace, + velerov1api.DefaultNamespace, sharedInformers.Velero().V1().Restores(), client.VeleroV1(), client.VeleroV1(), @@ -162,7 +160,7 @@ func TestProcessQueueItemSkips(t *testing.T) { tests := []struct { name string restoreKey string - restore *api.Restore + restore *velerov1api.Restore expectError bool }{ { @@ -177,17 +175,17 @@ func TestProcessQueueItemSkips(t *testing.T) { { name: "restore with phase InProgress does not get processed", restoreKey: "foo/bar", - restore: builder.ForRestore("foo", "bar").Phase(api.RestorePhaseInProgress).Result(), + restore: builder.ForRestore("foo", "bar").Phase(velerov1api.RestorePhaseInProgress).Result(), }, { name: "restore with phase Completed does not get processed", restoreKey: "foo/bar", - restore: builder.ForRestore("foo", "bar").Phase(api.RestorePhaseCompleted).Result(), + restore: builder.ForRestore("foo", "bar").Phase(velerov1api.RestorePhaseCompleted).Result(), }, { name: "restore with phase FailedValidation does not get processed", restoreKey: "foo/bar", - restore: builder.ForRestore("foo", "bar").Phase(api.RestorePhaseFailedValidation).Result(), + restore: builder.ForRestore("foo", "bar").Phase(velerov1api.RestorePhaseFailedValidation).Result(), }, } @@ -203,7 +201,7 @@ func TestProcessQueueItemSkips(t *testing.T) { ) c := NewRestoreController( - api.DefaultNamespace, + velerov1api.DefaultNamespace, sharedInformers.Velero().V1().Restores(), client.VeleroV1(), client.VeleroV1(), @@ -238,14 +236,14 @@ func TestProcessQueueItem(t *testing.T) { name string restoreKey string location *velerov1api.BackupStorageLocation - restore *api.Restore - backup *api.Backup + restore *velerov1api.Restore + backup *velerov1api.Backup restorerError error expectedErr bool expectedPhase string expectedValidationErrors []string expectedRestoreErrors int - expectedRestorerCall *api.Restore + expectedRestorerCall *velerov1api.Restore backupStoreGetBackupMetadataErr error backupStoreGetBackupContentsErr error putRestoreLogErr error @@ -257,7 +255,7 @@ func TestProcessQueueItem(t *testing.T) { restore: NewRestore("foo", "bar", "backup-1", "another-1", "*", velerov1api.RestorePhaseNew).ExcludedNamespaces("another-1").Result(), backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, - expectedPhase: string(api.RestorePhaseFailedValidation), + expectedPhase: string(velerov1api.RestorePhaseFailedValidation), expectedValidationErrors: []string{"Invalid included/excluded namespace lists: excludes list cannot contain an item in the includes list: another-1"}, }, { @@ -266,37 +264,37 @@ func TestProcessQueueItem(t *testing.T) { restore: NewRestore("foo", "bar", "backup-1", "*", "a-resource", velerov1api.RestorePhaseNew).ExcludedResources("a-resource").Result(), backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, - expectedPhase: string(api.RestorePhaseFailedValidation), + expectedPhase: string(velerov1api.RestorePhaseFailedValidation), expectedValidationErrors: []string{"Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: a-resource"}, }, { name: "new restore with empty backup and schedule names fails validation", restore: NewRestore("foo", "bar", "", "ns-1", "", velerov1api.RestorePhaseNew).Result(), expectedErr: false, - expectedPhase: string(api.RestorePhaseFailedValidation), + 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 backup and schedule names provided fails validation", restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseNew).Schedule("sched-1").Result(), expectedErr: false, - expectedPhase: string(api.RestorePhaseFailedValidation), + expectedPhase: string(velerov1api.RestorePhaseFailedValidation), expectedValidationErrors: []string{"Either a backup or schedule must be specified as a source for the restore, but not both"}, }, { name: "valid restore with schedule name gets executed", location: defaultStorageLocation, restore: NewRestore("foo", "bar", "", "ns-1", "", velerov1api.RestorePhaseNew).Schedule("sched-1").Result(), - backup: defaultBackup().StorageLocation("default").ObjectMeta(builder.WithLabels(api.ScheduleNameLabel, "sched-1")).Phase(api.BackupPhaseCompleted).Result(), + backup: defaultBackup().StorageLocation("default").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "sched-1")).Phase(velerov1api.BackupPhaseCompleted).Result(), expectedErr: false, - expectedPhase: string(api.RestorePhaseInProgress), + expectedPhase: string(velerov1api.RestorePhaseInProgress), expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseInProgress).Schedule("sched-1").Result(), }, { name: "restore with non-existent backup name fails", restore: NewRestore("foo", "bar", "backup-1", "ns-1", "*", velerov1api.RestorePhaseNew).Result(), expectedErr: false, - expectedPhase: string(api.RestorePhaseFailedValidation), + expectedPhase: string(velerov1api.RestorePhaseFailedValidation), expectedValidationErrors: []string{"Error retrieving backup: backup.velero.io \"backup-1\" not found"}, backupStoreGetBackupMetadataErr: errors.New("no backup here"), }, @@ -307,8 +305,8 @@ func TestProcessQueueItem(t *testing.T) { backup: defaultBackup().StorageLocation("default").Result(), restorerError: errors.New("blarg"), expectedErr: false, - expectedPhase: string(api.RestorePhaseInProgress), - expectedFinalPhase: string(api.RestorePhasePartiallyFailed), + expectedPhase: string(velerov1api.RestorePhaseInProgress), + expectedFinalPhase: string(velerov1api.RestorePhasePartiallyFailed), expectedRestoreErrors: 1, expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseInProgress).Result(), }, @@ -318,7 +316,7 @@ func TestProcessQueueItem(t *testing.T) { restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseNew).Result(), backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, - expectedPhase: string(api.RestorePhaseInProgress), + expectedPhase: string(velerov1api.RestorePhaseInProgress), expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseInProgress).Result(), }, { @@ -327,7 +325,7 @@ func TestProcessQueueItem(t *testing.T) { restore: NewRestore("foo", "bar", "backup-1", "ns-1", "nodes", velerov1api.RestorePhaseNew).Result(), backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, - expectedPhase: string(api.RestorePhaseFailedValidation), + expectedPhase: string(velerov1api.RestorePhaseFailedValidation), expectedValidationErrors: []string{ "nodes are non-restorable resources", "Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: nodes", @@ -339,7 +337,7 @@ func TestProcessQueueItem(t *testing.T) { restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events", velerov1api.RestorePhaseNew).Result(), backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, - expectedPhase: string(api.RestorePhaseFailedValidation), + expectedPhase: string(velerov1api.RestorePhaseFailedValidation), expectedValidationErrors: []string{ "events are non-restorable resources", "Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: events", @@ -351,7 +349,7 @@ func TestProcessQueueItem(t *testing.T) { restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events.events.k8s.io", velerov1api.RestorePhaseNew).Result(), backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, - expectedPhase: string(api.RestorePhaseFailedValidation), + expectedPhase: string(velerov1api.RestorePhaseFailedValidation), expectedValidationErrors: []string{ "events.events.k8s.io are non-restorable resources", "Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: events.events.k8s.io", @@ -363,7 +361,7 @@ func TestProcessQueueItem(t *testing.T) { restore: NewRestore("foo", "bar", "backup-1", "ns-1", "backups.velero.io", velerov1api.RestorePhaseNew).Result(), backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, - expectedPhase: string(api.RestorePhaseFailedValidation), + expectedPhase: string(velerov1api.RestorePhaseFailedValidation), expectedValidationErrors: []string{ "backups.velero.io are non-restorable resources", "Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: backups.velero.io", @@ -375,7 +373,7 @@ func TestProcessQueueItem(t *testing.T) { restore: NewRestore("foo", "bar", "backup-1", "ns-1", "restores.velero.io", velerov1api.RestorePhaseNew).Result(), backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, - expectedPhase: string(api.RestorePhaseFailedValidation), + expectedPhase: string(velerov1api.RestorePhaseFailedValidation), expectedValidationErrors: []string{ "restores.velero.io are non-restorable resources", "Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: restores.velero.io", @@ -384,9 +382,9 @@ func TestProcessQueueItem(t *testing.T) { { name: "backup download error results in failed restore", location: defaultStorageLocation, - restore: NewRestore(api.DefaultNamespace, "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseNew).Result(), - expectedPhase: string(api.RestorePhaseInProgress), - expectedFinalPhase: string(api.RestorePhaseFailed), + restore: NewRestore(velerov1api.DefaultNamespace, "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseNew).Result(), + expectedPhase: string(velerov1api.RestorePhaseInProgress), + expectedFinalPhase: string(velerov1api.RestorePhaseFailed), backupStoreGetBackupContentsErr: errors.New("Couldn't download backup"), backup: defaultBackup().StorageLocation("default").Result(), }, @@ -410,7 +408,7 @@ func TestProcessQueueItem(t *testing.T) { defer backupStore.AssertExpectations(t) c := NewRestoreController( - api.DefaultNamespace, + velerov1api.DefaultNamespace, sharedInformers.Velero().V1().Restores(), client.VeleroV1(), client.VeleroV1(), @@ -635,7 +633,7 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) { ) c := NewRestoreController( - api.DefaultNamespace, + velerov1api.DefaultNamespace, sharedInformers.Velero().V1().Restores(), client.VeleroV1(), client.VeleroV1(), @@ -651,7 +649,7 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) { formatFlag, ).(*restoreController) - restore := &api.Restore{ + restore := &velerov1api.Restore{ ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1api.DefaultNamespace, Name: "restore-1", @@ -664,8 +662,8 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) { // no backups created from the schedule: fail validation require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add( defaultBackup(). - ObjectMeta(builder.WithLabels(api.ScheduleNameLabel, "non-matching-schedule")). - Phase(api.BackupPhaseCompleted). + ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "non-matching-schedule")). + Phase(velerov1api.BackupPhaseCompleted). Result(), )) @@ -678,9 +676,9 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) { defaultBackup(). ObjectMeta( builder.WithName("backup-2"), - builder.WithLabels(api.ScheduleNameLabel, "schedule-1"), + builder.WithLabels(velerov1api.ScheduleNameLabel, "schedule-1"), ). - Phase(api.BackupPhaseInProgress). + Phase(velerov1api.BackupPhaseInProgress). Result(), )) @@ -695,9 +693,9 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) { defaultBackup(). ObjectMeta( builder.WithName("foo"), - builder.WithLabels(api.ScheduleNameLabel, "schedule-1"), + builder.WithLabels(velerov1api.ScheduleNameLabel, "schedule-1"), ). - Phase(api.BackupPhaseCompleted). + Phase(velerov1api.BackupPhaseCompleted). StartTimestamp(now). Result(), )) @@ -705,9 +703,9 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) { defaultBackup(). ObjectMeta( builder.WithName("foo"), - builder.WithLabels(api.ScheduleNameLabel, "schedule-1"), + builder.WithLabels(velerov1api.ScheduleNameLabel, "schedule-1"), ). - Phase(api.BackupPhaseCompleted). + Phase(velerov1api.BackupPhaseCompleted). StartTimestamp(now.Add(time.Second)). Result(), )) @@ -718,7 +716,7 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) { } func TestBackupXorScheduleProvided(t *testing.T) { - r := &api.Restore{} + r := &velerov1api.Restore{} assert.False(t, backupXorScheduleProvided(r)) r.Spec.BackupName = "backup-1" @@ -735,7 +733,7 @@ func TestBackupXorScheduleProvided(t *testing.T) { } func TestMostRecentCompletedBackup(t *testing.T) { - backups := []*api.Backup{ + backups := []*velerov1api.Backup{ { ObjectMeta: metav1.ObjectMeta{ Name: "a", @@ -782,7 +780,7 @@ func TestMostRecentCompletedBackup(t *testing.T) { now := time.Now() - backups = append(backups, &api.Backup{ + backups = append(backups, &velerov1api.Backup{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", }, @@ -792,7 +790,7 @@ func TestMostRecentCompletedBackup(t *testing.T) { }, }) - expected := &api.Backup{ + expected := &velerov1api.Backup{ ObjectMeta: metav1.ObjectMeta{ Name: "bar", }, diff --git a/site/docs/v1.3.1/code-standards.md b/site/docs/v1.3.1/code-standards.md index d1b83a29ec..586a865fa6 100644 --- a/site/docs/v1.3.1/code-standards.md +++ b/site/docs/v1.3.1/code-standards.md @@ -43,8 +43,7 @@ Example: 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" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" ) diff --git a/site/docs/v1.3.2/code-standards.md b/site/docs/v1.3.2/code-standards.md index e2a409c440..4323756bfc 100644 --- a/site/docs/v1.3.2/code-standards.md +++ b/site/docs/v1.3.2/code-standards.md @@ -44,7 +44,7 @@ Example: 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" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" ) diff --git a/site/docs/v1.4/code-standards.md b/site/docs/v1.4/code-standards.md index 2dc87f671e..00a1795462 100644 --- a/site/docs/v1.4/code-standards.md +++ b/site/docs/v1.4/code-standards.md @@ -52,7 +52,7 @@ Example: 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" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" ) From d63fb4fe8b1200e0faa2c78173cc75063e6b6296 Mon Sep 17 00:00:00 2001 From: Carlisia Date: Fri, 19 Jun 2020 07:01:31 -0700 Subject: [PATCH 29/34] Add deprecation warning/todo Signed-off-by: Carlisia --- pkg/cmd/cli/restic/server.go | 5 +++++ pkg/cmd/server/server.go | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/pkg/cmd/cli/restic/server.go b/pkg/cmd/cli/restic/server.go index d75cbbcc64..d72fa242e6 100644 --- a/pkg/cmd/cli/restic/server.go +++ b/pkg/cmd/cli/restic/server.go @@ -212,6 +212,11 @@ func (s *resticServer) run() { go s.podInformer.Run(s.ctx.Done()) go s.secretInformer.Run(s.ctx.Done()) + // 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 + // cache for them manually. + // Adding the controllers to the manager will register them as a (runtime-controller) runnable, // so the manager will ensure the cache is started and ready before all controller are started s.mgr.Add(managercontroller.Runnable(backupController, 1)) diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index aef1cf6747..fcf734b4df 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -897,6 +897,10 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger.WithField("informer", informer).Info("Informer cache synced") } + // 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 + // cache for them manually. for i := range controllers { controllerRunInfo := controllers[i] // Adding the controllers to the manager will register them as a (runtime-controller) runnable, From e9fb8ddea783d8ecd0409fcb7a11ebb211e22fed Mon Sep 17 00:00:00 2001 From: Carlisia Date: Fri, 19 Jun 2020 07:06:58 -0700 Subject: [PATCH 30/34] Add documentation Signed-off-by: Carlisia --- pkg/cmd/server/server.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index fcf734b4df..e0f9abf789 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -363,6 +363,8 @@ func (s *server) run() error { return err } + // Fetching from the server directly since at this point + // the cache has not yet started bsl := &velerov1api.BackupStorageLocation{} if err := s.mgr.GetAPIReader().Get(context.Background(), kbclient.ObjectKey{ Namespace: s.namespace, From ff7e5469a4cc3522dbdb46823e4ffa5e83205d89 Mon Sep 17 00:00:00 2001 From: Carlisia Date: Fri, 19 Jun 2020 15:02:37 -0700 Subject: [PATCH 31/34] Add helpful comments Signed-off-by: Carlisia --- internal/util/managercontroller/managercontroller.go | 2 ++ pkg/apis/velero/v1/backupstoragelocation_types.go | 4 ++++ pkg/cmd/server/server.go | 2 ++ 3 files changed, 8 insertions(+) diff --git a/internal/util/managercontroller/managercontroller.go b/internal/util/managercontroller/managercontroller.go index 44387d339a..2c08b30eb7 100644 --- a/internal/util/managercontroller/managercontroller.go +++ b/internal/util/managercontroller/managercontroller.go @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +// TODO(2.0) After converting all controllers to runttime-controller, +// the functions in this file will no longer be needed and should be removed. package managercontroller import ( diff --git a/pkg/apis/velero/v1/backupstoragelocation_types.go b/pkg/apis/velero/v1/backupstoragelocation_types.go index 02beabb158..675c1852d6 100644 --- a/pkg/apis/velero/v1/backupstoragelocation_types.go +++ b/pkg/apis/velero/v1/backupstoragelocation_types.go @@ -70,6 +70,8 @@ type BackupStorageLocationStatus struct { AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"` } +// TODO(2.0) After converting all resources to use the runttime-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 @@ -88,6 +90,8 @@ type BackupStorageLocation struct { Status BackupStorageLocationStatus `json:"status,omitempty"` } +// TODO(2.0) After converting all resources to use the runttime-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 diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index e0f9abf789..fb867cb431 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -469,6 +469,8 @@ func (s *server) validateBackupStorageLocations() error { pluginManager := clientmgmt.NewManager(s.logger, s.logLevel, s.pluginRegistry) defer pluginManager.CleanupClients() + // Fetching from the server directly since at this point + // the cache has not yet started locations := &velerov1api.BackupStorageLocationList{} if err := s.mgr.GetAPIReader().List(context.Background(), locations, &kbclient.ListOptions{ Namespace: s.namespace, From 4732d4fb6259343ea35312970266b979d942ea3a Mon Sep 17 00:00:00 2001 From: Carlisia Date: Wed, 24 Jun 2020 07:41:49 -0700 Subject: [PATCH 32/34] Address code review Signed-off-by: Carlisia --- pkg/controller/backup_controller.go | 8 ++++---- pkg/controller/backup_controller_test.go | 8 +++----- pkg/restic/common.go | 7 +++---- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 4c410655d7..7038cfad73 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -59,7 +59,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/logging" "github.com/vmware-tanzu/velero/pkg/volume" - "sigs.k8s.io/controller-runtime/pkg/client" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" ) type backupController struct { @@ -68,7 +68,7 @@ type backupController struct { backupper pkgbackup.Backupper lister velerov1listers.BackupLister client velerov1client.BackupsGetter - kbClient client.Client + kbClient kbclient.Client clock clock.Clock backupLogLevel logrus.Level newPluginManager func(logrus.FieldLogger) clientmgmt.Manager @@ -94,7 +94,7 @@ func NewBackupController( backupLogLevel logrus.Level, newPluginManager func(logrus.FieldLogger) clientmgmt.Manager, backupTracker BackupTracker, - kbClient client.Client, + kbClient kbclient.Client, defaultBackupLocation string, defaultVolumesToRestic bool, defaultBackupTTL time.Duration, @@ -375,7 +375,7 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg // validate the storage location, and store the BackupStorageLocation API obj on the request storageLocation := &velerov1api.BackupStorageLocation{} - if err := c.kbClient.Get(context.Background(), client.ObjectKey{ + if err := c.kbClient.Get(context.Background(), kbclient.ObjectKey{ Namespace: request.Namespace, Name: request.Spec.StorageLocation, }, storageLocation); err != nil { diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 80a0515d7a..354a568cc1 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -22,8 +22,6 @@ import ( "fmt" "io" - "sigs.k8s.io/controller-runtime/pkg/client" - "sort" "strings" "testing" @@ -36,8 +34,8 @@ import ( "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/clock" - "k8s.io/apimachinery/pkg/version" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" @@ -181,7 +179,7 @@ func TestProcessBackupValidationFailures(t *testing.T) { discoveryHelper, err := discovery.NewHelper(apiServer.DiscoveryClient, logger) require.NoError(t, err) - var fakeClient client.Client + var fakeClient kbclient.Client if test.backupLocation != nil { fakeClient = newFakeClient(t, test.backupLocation) } else { @@ -782,7 +780,7 @@ func TestProcessBackupCompletions(t *testing.T) { backupper = new(fakeBackupper) ) - var fakeClient client.Client + var fakeClient kbclient.Client // add the test's backup storage location if it's different than the default if test.backupLocation != nil && test.backupLocation != defaultBackupLocation { fakeClient = newFakeClient(t, test.backupLocation) diff --git a/pkg/restic/common.go b/pkg/restic/common.go index d7e99b9936..fc8f20ca0b 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -23,7 +23,6 @@ import ( "strings" "time" - "sigs.k8s.io/controller-runtime/pkg/client" kbclient "sigs.k8s.io/controller-runtime/pkg/client" "github.com/pkg/errors" @@ -288,7 +287,7 @@ func TempCACertFile(caCert []byte, bsl string, fs filesystem.Interface) (string, return name, nil } -func GetCACert(client client.Client, namespace, backupLocation string) ([]byte, error) { +func GetCACert(client kbclient.Client, namespace, backupLocation string) ([]byte, error) { location := &velerov1api.BackupStorageLocation{} if err := client.Get(context.Background(), kbclient.ObjectKey{ Namespace: namespace, @@ -316,7 +315,7 @@ func NewPodVolumeRestoreListOptions(name string) metav1.ListOptions { // should be used when running a restic command for an Azure backend. This list is // the current environment, plus the Azure-specific variables restic needs, namely // a storage account name and key. -func AzureCmdEnv(client client.Client, namespace, backupLocation string) ([]string, error) { +func AzureCmdEnv(client kbclient.Client, namespace, backupLocation string) ([]string, error) { loc := &velerov1api.BackupStorageLocation{} if err := client.Get(context.Background(), kbclient.ObjectKey{ Namespace: namespace, @@ -342,7 +341,7 @@ func AzureCmdEnv(client client.Client, namespace, backupLocation string) ([]stri // should be used when running a restic command for an S3 backend. This list is // the current environment, plus the AWS-specific variables restic needs, namely // a credential profile. -func S3CmdEnv(client client.Client, namespace, backupLocation string) ([]string, error) { +func S3CmdEnv(client kbclient.Client, namespace, backupLocation string) ([]string, error) { loc := &velerov1api.BackupStorageLocation{} if err := client.Get(context.Background(), kbclient.ObjectKey{ Namespace: namespace, From 8160dee98cd43d8039931881db0732f25c943ecf Mon Sep 17 00:00:00 2001 From: Carlisia Date: Wed, 24 Jun 2020 08:06:41 -0700 Subject: [PATCH 33/34] More idiomatic Runnable Signed-off-by: Carlisia --- .../managercontroller/managercontroller.go | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/internal/util/managercontroller/managercontroller.go b/internal/util/managercontroller/managercontroller.go index 2c08b30eb7..5a009d7fa1 100644 --- a/internal/util/managercontroller/managercontroller.go +++ b/internal/util/managercontroller/managercontroller.go @@ -29,29 +29,26 @@ import ( // Runnable will turn a "regular" runnable component (such as a controller) // into a controller-runtime Runnable func Runnable(p controller.Interface, numWorkers int) manager.Runnable { - return manager.RunnableFunc(func(stop <-chan struct{}) error { - ctx, cancel := contextForChannel(stop) + f := func(stop <-chan struct{}) error { + + // Create a cancel context for handling the stop signal. + ctx, cancel := context.WithCancel(context.Background()) defer cancel() + // If a signal is received on the stop channel, cancel the + // context. This will propagate the cancel into the p.Run + // function below. + go func() { + select { + case <-stop: + cancel() + case <-ctx.Done(): + } + }() + + // This is a blocking call that either completes + // or is cancellable on receiving a stop signal. return p.Run(ctx, numWorkers) - }) -} - -// contextForChannel derives a child context from a parent channel. -// -// The derived context's Done channel is closed when the returned cancel function -// is called or when the parent channel is closed, whichever happens first. -// -// Note the caller must *always* call the CancelFunc, otherwise resources may be leaked. -func contextForChannel(parentCh <-chan struct{}) (context.Context, context.CancelFunc) { - ctx, cancel := context.WithCancel(context.Background()) - - go func() { - select { - case <-parentCh: - cancel() - case <-ctx.Done(): - } - }() - return ctx, cancel + } + return manager.RunnableFunc(f) } From 3261b32b1988890dcce18e283cb382d76dd37d20 Mon Sep 17 00:00:00 2001 From: Carlisia Date: Wed, 24 Jun 2020 08:42:31 -0700 Subject: [PATCH 34/34] Clean up imports Signed-off-by: Carlisia --- .../download_request_controller_test.go | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/pkg/controller/download_request_controller_test.go b/pkg/controller/download_request_controller_test.go index 4ae18e01c6..c17bd20fb8 100644 --- a/pkg/controller/download_request_controller_test.go +++ b/pkg/controller/download_request_controller_test.go @@ -29,7 +29,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" - v1 "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" @@ -87,8 +86,8 @@ func newDownloadRequestTestHarness(t *testing.T, fakeClient client.Client) *down } } -func newDownloadRequest(phase velerov1api.DownloadRequestPhase, targetKind velerov1api.DownloadTargetKind, targetName string) *v1.DownloadRequest { - return &v1.DownloadRequest{ +func newDownloadRequest(phase velerov1api.DownloadRequestPhase, targetKind velerov1api.DownloadTargetKind, targetName string) *velerov1api.DownloadRequest { + return &velerov1api.DownloadRequest{ ObjectMeta: metav1.ObjectMeta{ Name: "a-download-request", Namespace: velerov1api.DefaultNamespace, @@ -124,16 +123,16 @@ func newBackupLocation(name, provider, bucket string) *velerov1api.BackupStorage func TestProcessDownloadRequest(t *testing.T) { - defaultBackup := func() *v1.Backup { - return builder.ForBackup(v1.DefaultNamespace, "a-backup").StorageLocation("a-location").Result() + defaultBackup := func() *velerov1api.Backup { + return builder.ForBackup(velerov1api.DefaultNamespace, "a-backup").StorageLocation("a-location").Result() } tests := []struct { name string key string - downloadRequest *v1.DownloadRequest - backup *v1.Backup - restore *v1.Restore + downloadRequest *velerov1api.DownloadRequest + backup *velerov1api.Backup + restore *velerov1api.Restore backupLocation *velerov1api.BackupStorageLocation expired bool expectedErr string @@ -154,14 +153,14 @@ func TestProcessDownloadRequest(t *testing.T) { { name: "backup contents request for nonexistent backup returns an error", downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindBackupContents, "a-backup"), - backup: builder.ForBackup(v1.DefaultNamespace, "non-matching-backup").StorageLocation("a-location").Result(), + backup: builder.ForBackup(velerov1api.DefaultNamespace, "non-matching-backup").StorageLocation("a-location").Result(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectedErr: "backup.velero.io \"a-backup\" not found", }, { name: "restore log request for nonexistent restore returns an error", downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindRestoreLog, "a-backup-20170912150214"), - restore: builder.ForRestore(v1.DefaultNamespace, "non-matching-restore").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(), + restore: builder.ForRestore(velerov1api.DefaultNamespace, "non-matching-restore").Phase(velerov1api.RestorePhaseCompleted).Backup("a-backup").Result(), backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectedErr: "error getting Restore: restore.velero.io \"a-backup-20170912150214\" not found", @@ -182,7 +181,7 @@ func TestProcessDownloadRequest(t *testing.T) { }, { name: "backup contents request with phase 'New' gets a url", - downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, velerov1api.DownloadTargetKindBackupContents, "a-backup"), + downloadRequest: newDownloadRequest(velerov1api.DownloadRequestPhaseNew, velerov1api.DownloadTargetKindBackupContents, "a-backup"), backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, @@ -196,7 +195,7 @@ func TestProcessDownloadRequest(t *testing.T) { }, { name: "backup log request with phase 'New' gets a url", - downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, velerov1api.DownloadTargetKindBackupLog, "a-backup"), + downloadRequest: newDownloadRequest(velerov1api.DownloadRequestPhaseNew, velerov1api.DownloadTargetKindBackupLog, "a-backup"), backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, @@ -204,15 +203,15 @@ func TestProcessDownloadRequest(t *testing.T) { { name: "restore log request with phase '' gets a url", downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindRestoreLog, "a-backup-20170912150214"), - restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(), + restore: builder.ForRestore(velerov1api.DefaultNamespace, "a-backup-20170912150214").Phase(velerov1api.RestorePhaseCompleted).Backup("a-backup").Result(), backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, }, { name: "restore log request with phase 'New' gets a url", - downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, velerov1api.DownloadTargetKindRestoreLog, "a-backup-20170912150214"), - restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(), + downloadRequest: newDownloadRequest(velerov1api.DownloadRequestPhaseNew, velerov1api.DownloadTargetKindRestoreLog, "a-backup-20170912150214"), + restore: builder.ForRestore(velerov1api.DefaultNamespace, "a-backup-20170912150214").Phase(velerov1api.RestorePhaseCompleted).Backup("a-backup").Result(), backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, @@ -220,27 +219,27 @@ func TestProcessDownloadRequest(t *testing.T) { { name: "restore results request with phase '' gets a url", downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindRestoreResults, "a-backup-20170912150214"), - restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(), + restore: builder.ForRestore(velerov1api.DefaultNamespace, "a-backup-20170912150214").Phase(velerov1api.RestorePhaseCompleted).Backup("a-backup").Result(), backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, }, { name: "restore results request with phase 'New' gets a url", - downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, velerov1api.DownloadTargetKindRestoreResults, "a-backup-20170912150214"), - restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(), + downloadRequest: newDownloadRequest(velerov1api.DownloadRequestPhaseNew, velerov1api.DownloadTargetKindRestoreResults, "a-backup-20170912150214"), + restore: builder.ForRestore(velerov1api.DefaultNamespace, "a-backup-20170912150214").Phase(velerov1api.RestorePhaseCompleted).Backup("a-backup").Result(), backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, }, { name: "request with phase 'Processed' is not deleted if not expired", - downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseProcessed, velerov1api.DownloadTargetKindBackupLog, "a-backup-20170912150214"), + downloadRequest: newDownloadRequest(velerov1api.DownloadRequestPhaseProcessed, velerov1api.DownloadTargetKindBackupLog, "a-backup-20170912150214"), backup: defaultBackup(), }, { name: "request with phase 'Processed' is deleted if expired", - downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseProcessed, velerov1api.DownloadTargetKindBackupLog, "a-backup-20170912150214"), + downloadRequest: newDownloadRequest(velerov1api.DownloadRequestPhaseProcessed, velerov1api.DownloadTargetKindBackupLog, "a-backup-20170912150214"), backup: defaultBackup(), expired: true, }, @@ -307,7 +306,7 @@ func TestProcessDownloadRequest(t *testing.T) { output, err := harness.client.VeleroV1().DownloadRequests(tc.downloadRequest.Namespace).Get(tc.downloadRequest.Name, metav1.GetOptions{}) require.NoError(t, err) - assert.Equal(t, string(v1.DownloadRequestPhaseProcessed), string(output.Status.Phase)) + assert.Equal(t, string(velerov1api.DownloadRequestPhaseProcessed), string(output.Status.Phase)) assert.Equal(t, "a-url", output.Status.DownloadURL) assert.True(t, velerotest.TimesAreEqual(harness.controller.clock.Now().Add(signedURLTTL), output.Status.Expiration.Time), "expiration does not match") }