diff --git a/.github/workflows/pr-codespell.yml b/.github/workflows/pr-codespell.yml index 1fff7e920..ddc2acd4e 100644 --- a/.github/workflows/pr-codespell.yml +++ b/.github/workflows/pr-codespell.yml @@ -14,7 +14,7 @@ jobs: uses: codespell-project/actions-codespell@master with: # ignore the config/.../crd.go file as it's generated binary data that is edited elswhere. - skip: .git,*.png,*.jpg,*.woff,*.ttf,*.gif,*.ico,./config/crd/v1beta1/crds/crds.go,./config/crd/v1/crds/crds.go,./go.sum,./LICENSE + skip: .git,*.png,*.jpg,*.woff,*.ttf,*.gif,*.ico,./config/crd/v1beta1/crds/crds.go,./config/crd/v1/crds/crds.go,,./config/crd/v2alpha1/crds/crds.go,./go.sum,./LICENSE ignore_words_list: iam,aks,ist,bridget,ue,shouldnot,atleast check_filenames: true check_hidden: true diff --git a/Dockerfile b/Dockerfile index c628ef524..7f600d0f2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -68,7 +68,7 @@ RUN mkdir -p /output/usr/bin && \ # Velero image packing section FROM gcr.io/distroless/base-nossl-debian11:nonroot -LABEL maintainer="Nolan Brubaker " +LABEL maintainer="Xun Jiang " COPY --from=velero-builder /output / diff --git a/Makefile b/Makefile index 6aac273f5..f9229ffc9 100644 --- a/Makefile +++ b/Makefile @@ -229,6 +229,10 @@ endif update: @$(MAKE) shell CMD="-c 'hack/update-all.sh'" +# update-crd is for development purpose only, it is faster than update, so is a shortcut when you want to generate CRD changes only +update-crd: + @$(MAKE) shell CMD="-c 'hack/update-3generated-crd-code.sh'" + build-dirs: @mkdir -p _output/bin/$(GOOS)/$(GOARCH) @mkdir -p .go/src/$(PKG) .go/pkg .go/bin .go/std/$(GOOS)/$(GOARCH) .go/go-build .go/golangci-lint diff --git a/Tiltfile b/Tiltfile index bc7438384..a1e13558b 100644 --- a/Tiltfile +++ b/Tiltfile @@ -12,6 +12,8 @@ k8s_yaml([ 'config/crd/v1/bases/velero.io_schedules.yaml', 'config/crd/v1/bases/velero.io_serverstatusrequests.yaml', 'config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml', + 'config/crd/v2alpha1/bases/velero.io_datauploads.yaml', + 'config/crd/v2alpha1/bases/velero.io_datadownloads.yaml', ]) # default values @@ -60,7 +62,7 @@ RUN wget --output-document /restart.sh --quiet https://raw.githubusercontent.com additional_docker_helper_commands = """ # Install delve to allow debugging -RUN go get github.com/go-delve/delve/cmd/dlv +RUN go install github.com/go-delve/delve/cmd/dlv@latest RUN wget -qO- https://dl.k8s.io/v1.25.2/kubernetes-client-linux-amd64.tar.gz | tar xvz RUN wget -qO- https://get.docker.com | sh diff --git a/changelogs/unreleased/6151-tkaovila b/changelogs/unreleased/6151-tkaovila new file mode 100644 index 000000000..08050fd5e --- /dev/null +++ b/changelogs/unreleased/6151-tkaovila @@ -0,0 +1 @@ +Make GetPluginConfig accessible from other packages. \ No newline at end of file diff --git a/changelogs/unreleased/6176-Lyndon-Li b/changelogs/unreleased/6176-Lyndon-Li new file mode 100644 index 000000000..d9840b919 --- /dev/null +++ b/changelogs/unreleased/6176-Lyndon-Li @@ -0,0 +1 @@ +Add data mover CRD under v2alpha1, include DataUpload CRD and DataDownload CRD \ No newline at end of file diff --git a/changelogs/unreleased/6208-blackpiglet b/changelogs/unreleased/6208-blackpiglet new file mode 100644 index 000000000..2829fcaa5 --- /dev/null +++ b/changelogs/unreleased/6208-blackpiglet @@ -0,0 +1 @@ +Enable errcheck linter and resolve found issues \ No newline at end of file diff --git a/changelogs/unreleased/6232-kaovilai b/changelogs/unreleased/6232-kaovilai new file mode 100644 index 000000000..b3d112460 --- /dev/null +++ b/changelogs/unreleased/6232-kaovilai @@ -0,0 +1 @@ +log volumes to backup to help debug why `IsPodRunning` is called. \ No newline at end of file diff --git a/changelogs/unreleased/6248-Lyndon-Li b/changelogs/unreleased/6248-Lyndon-Li new file mode 100644 index 000000000..e3776a9cb --- /dev/null +++ b/changelogs/unreleased/6248-Lyndon-Li @@ -0,0 +1 @@ +Bump up Kopia to v0.13 \ No newline at end of file diff --git a/changelogs/unreleased/6268-Lyndon-Li b/changelogs/unreleased/6268-Lyndon-Li new file mode 100644 index 000000000..2aa27069a --- /dev/null +++ b/changelogs/unreleased/6268-Lyndon-Li @@ -0,0 +1 @@ +Fix issue #5123, Kopia repository supports self-cert CA for S3 compatible storage. \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_backuprepositories.yaml b/config/crd/v1/bases/velero.io_backuprepositories.yaml index fa7e5596e..6ab28487c 100644 --- a/config/crd/v1/bases/velero.io_backuprepositories.yaml +++ b/config/crd/v1/bases/velero.io_backuprepositories.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: backuprepositories.velero.io spec: group: velero.io @@ -96,9 +94,3 @@ spec: served: true storage: true subresources: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/v1/bases/velero.io_backups.yaml b/config/crd/v1/bases/velero.io_backups.yaml index ae6b4aa6f..5314b862c 100644 --- a/config/crd/v1/bases/velero.io_backups.yaml +++ b/config/crd/v1/bases/velero.io_backups.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: backups.velero.io spec: group: velero.io @@ -42,6 +40,11 @@ spec: CSI VolumeSnapshot status turns to ReadyToUse during creation, before returning error as timeout. The default value is 10 minute. type: string + datamover: + description: DataMover specifies the data mover to be used by the + backup. If DataMover is "" or "velero", the built-in data mover + will be used. + type: string defaultVolumesToFsBackup: description: DefaultVolumesToFsBackup specifies whether pod volume file system backup should be used for all volumes by default. @@ -175,6 +178,7 @@ spec: contains only "value". The requirements are ANDed. type: object type: object + x-kubernetes-map-type: atomic name: description: Name is the name of this hook. type: string @@ -360,6 +364,7 @@ spec: are ANDed. type: object type: object + x-kubernetes-map-type: atomic metadata: properties: labels: @@ -420,6 +425,7 @@ spec: are ANDed. type: object type: object + x-kubernetes-map-type: atomic nullable: true type: array orderedResources: @@ -452,6 +458,12 @@ spec: - kind - name type: object + x-kubernetes-map-type: atomic + snapshotMoveData: + description: SnapshotMoveData specifies whether snapshot data should + be moved + nullable: true + type: boolean snapshotVolumes: description: SnapshotVolumes specifies whether to take snapshots of any PV's referenced in the set of objects included in the Backup. @@ -591,9 +603,3 @@ spec: type: object served: true storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/v1/bases/velero.io_backupstoragelocations.yaml b/config/crd/v1/bases/velero.io_backupstoragelocations.yaml index ac2362c2b..b1d4b2d49 100644 --- a/config/crd/v1/bases/velero.io_backupstoragelocations.yaml +++ b/config/crd/v1/bases/velero.io_backupstoragelocations.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: backupstoragelocations.velero.io spec: group: velero.io @@ -92,6 +90,7 @@ spec: required: - key type: object + x-kubernetes-map-type: atomic default: description: Default indicates this location is the default backup storage location. @@ -173,9 +172,3 @@ spec: served: true storage: true subresources: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/v1/bases/velero.io_deletebackuprequests.yaml b/config/crd/v1/bases/velero.io_deletebackuprequests.yaml index c86088593..b4139758e 100644 --- a/config/crd/v1/bases/velero.io_deletebackuprequests.yaml +++ b/config/crd/v1/bases/velero.io_deletebackuprequests.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: deletebackuprequests.velero.io spec: group: velero.io @@ -73,9 +71,3 @@ spec: served: true storage: true subresources: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/v1/bases/velero.io_downloadrequests.yaml b/config/crd/v1/bases/velero.io_downloadrequests.yaml index 9602c927b..f4daaec53 100644 --- a/config/crd/v1/bases/velero.io_downloadrequests.yaml +++ b/config/crd/v1/bases/velero.io_downloadrequests.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: downloadrequests.velero.io spec: group: velero.io @@ -90,9 +88,3 @@ spec: type: object served: true storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/v1/bases/velero.io_podvolumebackups.yaml b/config/crd/v1/bases/velero.io_podvolumebackups.yaml index 6af4ce294..aa582b1d2 100644 --- a/config/crd/v1/bases/velero.io_podvolumebackups.yaml +++ b/config/crd/v1/bases/velero.io_podvolumebackups.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: podvolumebackups.velero.io spec: group: velero.io @@ -117,6 +115,7 @@ spec: description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' type: string type: object + x-kubernetes-map-type: atomic repoIdentifier: description: RepoIdentifier is the backup repository identifier. type: string @@ -199,9 +198,3 @@ spec: served: true storage: true subresources: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/v1/bases/velero.io_podvolumerestores.yaml b/config/crd/v1/bases/velero.io_podvolumerestores.yaml index 0a2acd64b..6ea3eca97 100644 --- a/config/crd/v1/bases/velero.io_podvolumerestores.yaml +++ b/config/crd/v1/bases/velero.io_podvolumerestores.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: podvolumerestores.velero.io spec: group: velero.io @@ -110,6 +108,7 @@ spec: description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' type: string type: object + x-kubernetes-map-type: atomic repoIdentifier: description: RepoIdentifier is the backup repository identifier. type: string @@ -184,9 +183,3 @@ spec: served: true storage: true subresources: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/v1/bases/velero.io_restores.yaml b/config/crd/v1/bases/velero.io_restores.yaml index 5b95c2741..8084a3bcd 100644 --- a/config/crd/v1/bases/velero.io_restores.yaml +++ b/config/crd/v1/bases/velero.io_restores.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: restores.velero.io spec: group: velero.io @@ -147,6 +145,7 @@ spec: contains only "value". The requirements are ANDed. type: object type: object + x-kubernetes-map-type: atomic name: description: Name is the name of this hook. type: string @@ -203,6 +202,7 @@ spec: to be added to a pod during its restore. items: type: object + x-kubernetes-preserve-unknown-fields: true type: array x-kubernetes-preserve-unknown-fields: true timeout: @@ -289,6 +289,7 @@ spec: are ANDed. type: object type: object + x-kubernetes-map-type: atomic namespaceMapping: additionalProperties: type: string @@ -350,6 +351,7 @@ spec: are ANDed. type: object type: object + x-kubernetes-map-type: atomic nullable: true type: array preserveNodePorts: @@ -474,9 +476,3 @@ spec: type: object served: true storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/v1/bases/velero.io_schedules.yaml b/config/crd/v1/bases/velero.io_schedules.yaml index 1bc08a8a9..a334ee6ce 100644 --- a/config/crd/v1/bases/velero.io_schedules.yaml +++ b/config/crd/v1/bases/velero.io_schedules.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: schedules.velero.io spec: group: velero.io @@ -72,6 +70,11 @@ spec: for CSI VolumeSnapshot status turns to ReadyToUse during creation, before returning error as timeout. The default value is 10 minute. type: string + datamover: + description: DataMover specifies the data mover to be used by + the backup. If DataMover is "" or "velero", the built-in data + mover will be used. + type: string defaultVolumesToFsBackup: description: DefaultVolumesToFsBackup specifies whether pod volume file system backup should be used for all volumes by default. @@ -206,6 +209,7 @@ spec: requirements are ANDed. type: object type: object + x-kubernetes-map-type: atomic name: description: Name is the name of this hook. type: string @@ -396,6 +400,7 @@ spec: "value". The requirements are ANDed. type: object type: object + x-kubernetes-map-type: atomic metadata: properties: labels: @@ -456,6 +461,7 @@ spec: only "value". The requirements are ANDed. type: object type: object + x-kubernetes-map-type: atomic nullable: true type: array orderedResources: @@ -488,6 +494,12 @@ spec: - kind - name type: object + x-kubernetes-map-type: atomic + snapshotMoveData: + description: SnapshotMoveData specifies whether snapshot data + should be moved + nullable: true + type: boolean snapshotVolumes: description: SnapshotVolumes specifies whether to take snapshots of any PV's referenced in the set of objects included in the @@ -545,9 +557,3 @@ spec: served: true storage: true subresources: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/v1/bases/velero.io_serverstatusrequests.yaml b/config/crd/v1/bases/velero.io_serverstatusrequests.yaml index 5c05af450..93467a4a4 100644 --- a/config/crd/v1/bases/velero.io_serverstatusrequests.yaml +++ b/config/crd/v1/bases/velero.io_serverstatusrequests.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: serverstatusrequests.velero.io spec: group: velero.io @@ -77,9 +75,3 @@ spec: type: object served: true storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml b/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml index 3db023bff..79be91067 100644 --- a/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml +++ b/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: volumesnapshotlocations.velero.io spec: group: velero.io @@ -63,6 +61,7 @@ spec: required: - key type: object + x-kubernetes-map-type: atomic provider: description: Provider is the provider of the volume storage. type: string @@ -84,9 +83,3 @@ spec: type: object served: true storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 56a2f14a6..fc60e2201 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -29,17 +29,17 @@ import ( ) var rawCRDs = [][]byte{ - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WAo\xdc6\x13\xbd\xebW\f\xf2\x1dr\xf9\xa4M\xd0C\v\xddR\xb7\x05\x82&\x86a\a\xbe\x14=P\xe4\xec.c\x8ad\xc9\xe1\xa6ۢ\xff\xbd\x18R\xf2j%\xd9\x1b\a\xa8n\"\x87of\xde\xcc\x1bQU]ו\xf0\xfa\x1eC\xd4ζ \xbc\xc6?\t-\xbf\xc5\xe6\xe1\x87\xd8h\xb79\xbc\xad\x1e\xb4U-\\\xa5H\xae\xbf\xc5\xe8R\x90\xf8\x13n\xb5դ\x9d\xadz$\xa1\x04\x89\xb6\x02\x10\xd6:\x12\xbc\x1c\xf9\x15@:K\xc1\x19\x83\xa1ޡm\x1eR\x87]\xd2Fa\xc8\xe0\xa3\xebÛ\xe6\xfb\xe6M\x05 \x03\xe6\xe3\x9ft\x8f\x91D\xef[\xb0ɘ\n\xc0\x8a\x1e[\xe8\x84|H>\xa0wQ\x93\v\x1acs@\x83\xc15\xdaUѣd\xb7\xbb\xe0\x92o\xe1\xb4QN\x0f!\x95t~\xcc@\xb7#\xd01o\x19\x1d\xe9\xd7\xd5\xed\x0f:R6\xf1&\x05a\xd6\x02\xc9\xdbQ\xdb]2\",\f\xd8A\x94\xcec\v\xd7\x1c\x8b\x17\x12U\x050P\x90c\xabA(\x95I\x15\xe6&hK\x18\xae\x9cI\xfdHf\r\x9f\xa3\xb37\x82\xf6-4#\xed͂\xb2l;\x12\xf6n\x87\xc3;\x1dٹ\x12\x84K0f\xae9\xc5\xfa\xe9\xe8\xf1\f\xe5D\x04L\xf6\nb\xa4\xa0\xed\xae:\x19\x1f\xde\x16*\xe4\x1e{\xd1\x0e\xb6Σ}w\xf3\xfe\xfe\xbb\xbb\xb3e\x00\x1f\x9c\xc7@z,Oy&}9Y\x05P\x18eОr\u05fcf\xc0b\x05\x8a\x1b\x12#\xd0\x1eGNQ\r1\x80\xdb\x02\xedu\x84\x80>`D[Z\xf4\f\x18\xd8HXp\xddg\x94\xd4\xc0\x1d\x06\x86\x81\xb8w\xc9(\xee\xe3\x03\x06\x82\x80\xd2\xed\xac\xfe\xeb\x11;\x02\xb9\xec\xd4\b¡GNO\xae\xa1\x15\x06\x0e\xc2$\xfc?\b\xab\xa0\x17G\b\xc8^ \xd9\t^6\x89\r|t\x01AۭkaO\xe4c\xbb\xd9\xec4\x8dz\x94\xae\xef\x93\xd5t\xdcdi\xe9.\x91\vq\xa3\xf0\x80f\x13\xf5\xae\x16A\xee5\xa1\xa4\x14p#\xbc\xaes\xe86k\xb2\xe9\xd5\xff\u00a0\xe0\xf8\xfa,\xd6E-˓\xc5\xf2L\x05X-\xa0#\x88\xe1h\xc9\xe2D4/1;\xb7?\xdf}\x82\xd1u.Ɯ\xfd\xcc\xfb\xe9`<\x95\x80\t\xd3v\x8b\xa1\x14q\x1b\\\x9f1\xd1*ﴥ\xfc\"\x8dF;\xa7?\xa6\xae\xd7\xc4u\xff#a$\xaeU\x03WyHA\x87\x90<\xabA5\xf0\xde\u0095\xe8\xd1\\\x89\x88\xffy\x01\x98\xe9X3\xb1_W\x82\xe9|\x9d\x1b\x17\xd6&\x1b\xe3\b|\xa2^\xf3\xb1v\xe7Qr\xf9\x98A>\xaa\xb7Zfm\xc0\xd6\x05\x10\v\xfb\xe6\fz]\xba\xfc\x94\xe1wG.\x88\x1d~p\x05sn\xb4\x1a\xdb\xec\xcc\x18\x1cO\x96\"c\\7\\`\x03\xd0^\xd0D\xbf$\xb4}\x1c\x03\xab\xf90\x92-S\bhi\x80\xc97\x88o\x1e\x99FD\x9a\x8c\v\xbe\xcd]\xe8\x80\x0f\xcb\x13c`\f\x06\xc4\v\xd3\xf9\xf2E̿\xba\xb9hk\x93e\xebB/\xa8\\\x17k\x06ZX\xf0\xb5\\t\x06[\xa0\x90\x96\xdb\xcf\xcdQ\x8cQ\xec.e\xf7\xb1X\x95\xcb\xc5p\x04D\xe7\x12=A=\xed\x97Q\xc0\x85r\\\x88\xd4\xefE\xbc\x14\xe7\r۬5\xc4\xec{\xf5\\\bO\xcd\xcck\xfc\xb2\xb2z\x8bB-u\\õ\xa3\xf5\xad'3\\U\xc5b1\xf2=LM\xea\x1c\x8b\x90\xa7+\xa9{\xbcW\xb6\xf0\xf7?\xd5IXBJ\xf4\x84\xeaz\xfe\a6\xcc\xf7\xf1\x87*\xbfJg\xcb\x0fPl\xe1\xb7߫\xe2\n\xd5\xfd\xf8\x93ċ\xff\x06\x00\x00\xff\xff\xc8p\x98۸\x0e\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec}\xcds\xdc:r\xf8]\x7fE\x97~\a\xefniF\xcf\xf5;$\xa5\x9bW\xb6\x93\xa9\xf7⧲\xb4\xdeC\x92\x03\x86\xec\x99\xc1\x13\bp\x01p\xe4\xd9T\xfe\xf7\x14\xbe\xf8\t\x92\xe0x\xf4\xe2M\x99\x17[\x1c\xa0\xd1\xe8n\xf4\x17\x1a\xe0\xd5j\xb5\xba\"%\xfd\x82RQ\xc1\uf014\x14\xbfj\xe4\xe6/\xb5~\xfeg\xb5\xa6\xe2\xf6\xf8\xf6\xea\x99\xf2\xfc\x0e\xee+\xa5E\xf1\x19\x95\xa8d\x86\xefqG9\xd5T\xf0\xab\x025ɉ&wW\x00\x84s\xa1\x89y\xad̟\x00\x99\xe0Z\n\xc6P\xae\xf6\xc8\xd7\xcf\xd5\x16\xb7\x15e9J\v<\f}\xfci\xfdO럮\x002\x89\xb6\xfb\x13-PiR\x94w\xc0+Ʈ\x008)\xf0\x0e\xb6${\xaeJ\xb5>\"C)\xd6T\\\xa9\x1233\xd6^\x8a\xaa\xbc\x83\xe6\a\xd7\xc5\xe3\xe1\xe6\xf0g\xdb۾`T\xe9\x9f[/\x7f\xa1J\xdb\x1fJVI\xc2\xea\x91\xec;E\xf9\xbebD\x86\xb7W\x00*\x13%\xde\xc1'3DI2̯\x00\xfct\xec\x90+\x8f\xf0\U0006d0d0\x1d\xb0 \x0e\x17\x00Q\"\x7f\xf7\xb0\xf9\xf2\xff\x1f;\xaf\x01rT\x99\xa4\xa5\xb6Dq\x88\x01U@\xe0\x8b\x9d\x16HO~\xd0\a\xa2Ab)Q!\xd7\n\xf4\x01!#\xa5\xae$\x82\xd8\xc1\xcf\xd5\x16%G\x8d\xaa\x06\r\x90\xb1Ji\x94\xa04\xd1\bD\x03\x81RP\xae\x81rд@\xf8û\x87\r\x88\xedo\x98i\x05\x84\xe7@\x94\x12\x19%\x1as8\nV\x15\xe8\xfa\xfeq]C-\xa5(Qj\x1a\xe8잖T\xb5\xde\xf6\xa6\xf7\xc6P\xc0\xb5\x82܈\x13\xbaix*b\xee\x89f\xe6\xa3\x0fT5ӵ\x12\xd2\x01\f\xa6\x11\xe1\x1e\xf95<\xa24`@\x1dD\xc5r#\x85G\x94\x86`\x99\xd8s\xfa\xf7\x1a\xb6\x02-젌h\xf4\x02\xd0<\x94k\x94\x9c08\x12V\xe1\x8d%IAN ь\x02\x15o\xc1\xb3M\xd4\x1a\xfeMH\x04\xcaw\xe2\x0e\x0eZ\x97\xea\xee\xf6vOuXM\x99(\x8a\x8aS}\xba\xb5\v\x83n+-\xa4\xba\xcd\xf1\x88\xecV\xd1\xfd\x8a\xc8\xec@5f\x86\x91\xb7\xa4\xa4+\x8b:\xb7+j]\xe4\xff/\b\x80z\xd3\xc1U\x9f\x8c0*-)߷~\xb0R?\xc1\x01\xb3\x00\x9c|\xb9\xaen\x16\r\xa1\xcd+C\x9d\xcf\x1f\x1e\x9fڲGU\x9f\xfa\x96\xee-\x81lX`\bF\xf9\x0e\xa5c\xe2N\x8a\xc2\xc2D\x9e;鳢\xcb(\xf2>\xf9U\xb5-\xa86|\xff[\x85\xca\b\xb9XýU1\xb0E\xa8\xca\xdcH\xe6\x1a6\x1c\xeeI\x81\xec\x9e(|u\x06\x18J\xab\x95!l\x1a\v\xdaڱ\xdf\xd8Q\xad\xf5C\xd0e#\xfcr\n\xe1\xb1Ĭ\xb3`L/\xba\xa3\x99]\x16\xb0\x13\xb2\xd1\x17N]\xad; \xe3K\xd6<\x99\xa2\x8f\x9c\x94\xea \xb4ѿ\xa2\xd2\xfd\x16=\x84\xee\x1f7\xbd\x0e\x01\x19\x8f\x9aU+\x95\xc2ܬ\xb3\x17B\xb5Ao\x00\x13\f \xf8b5L\x80g5M\xa5@W\x92\xdbU\xfa\x19I~z\x12\x7fQ\bye\x855؊\x1b\xd8\xe2NH\x8c\xc0\x95h\xfa\x9b\xc6(\xa5!\x8c\xb2(\x89J\xaf\xe1逆\x8c\xa4b\xda\xcb=U\xf0\xf6'((\xaf4\xae\a\xd0F\x18\xec\x88b\xc1\xb8\x19\xa8'\xf1Q9V͐\xef\xfdH\xb7\x16\x11_\x0e\xa8\x0f(\xa1\x14A\x05Gf\xb9\xa3\fA\x9d\x94\xc6\xc2s<(\xbe\xad\xa7\xbe\x15\n\xc6<\b\x05\xdbS\xc0y8Ocoɖ\xe1\x1dhY\r\x87sd\xd8\n\xc1\x90\xf4\x95p\x9f\x0e\x9fQi\x9a\xcdP\xe1\xbaO\x06\xd7+B\x04\xe9\x7f\xb0s\x8b\xd0a\xdbȚ&\xcf\b$P\xc3\x18\a\xc6ZD\xecP\x00\xfe\x83\xc3{\xa3\xb92\xa3O\x86\u0602\xd7\\\x14\x99Ֆ\\\x00\x13|\x8fҍf\xac\xc2\ve\xcc\f/\xb1\x10G\xcc\xc1(\f\x89\xcch>\xd8UF\x97\f\xe9\f`dyT\x06(W\x1aI\xbe\xbe\xbe$\x83\xf0kƪ\x1c\xf3{\xe7\n<\x1a'&\x0f>\xdd@\x1f\xf4\x18\xf5a\xb2\xb3\xb7#\x8cf\xd6\x03\xf1\xce\xc6\xca\xfaI1f5\xe6\xe4T\xa2s\xd5\f\xe3<\x86\x8d\x9d\xf0*\f6;P\xa8M\x93\xeb?]\xdf\x18~F\x80vG펡\x80H\xac)\x10_\xff\x11\x90X\x94\xfa4\xe4\x1e\xd5XD\b6\xa9&\x12YG\xa4$\xa7\x11\xc6\xd5\xfe\xe6y\xac\x1b\xeb\xdec\x1e\x0f\xcd~g\xf6\xf5\xc7]\xc8\xc0\bD\xaa\xbeW\x06.f\x99\xb2\xc1\x14\xa1ܰʄ/\x1dN)\x1b\x12D\xa6chf<&\xca\x1d<\xeb\xed7\x8c\xf9^\xe8\xb2T\x92\xc7D\xb7\x96\x18/\x92&N\"Q\xdf\xe0;&\xcaA\x88\xe79B\xfc\xabi\xd3xܐ\xd9\xf8\x1c\xb6x G*\xa4\x9fz\xe3\a\xe0W\xcc*\x1d]\xcbDCNw;\x94\x06Ny \n\x95\v\xba\xc6\t2\xeeDBK9D\x7f\xecͣa\xa4\x91T;\xf31ԍ#зh\xe11\x88\x1a?\xcfZΜ\x1ei^\x11f\x8d(ᙛ\x0f\xa9\xf1\x8a\x19\xe3\t&\x0fpv&:`n8\xd1q\xca\x05G\x10\x12\n\x13\x89\f\x9bƌ\x8c{Ʀ\xbd%\xc6\xcf\x10NDe\xc5P\xf9\xa1\x9cc\xd7耛Q\xd05G\\\x14\xcb\xc8\x16\x19(d\x98i!\xe3\xe4\x98c\xb2{R\xf4\xda\b\x15#\x1a\xae\x1b=4\x13\x9b\x00\t6\xb48\xd0\xec\xe0\xdc4#A\x16\x0e\xe4\x02\x95]\xe5\xa4,Y\xc4\x024\xcf$\xe7\xfd S\v\xbdyf\x96|\x1f^l\xf17O\x82nl\x9e\x19-٥l-\x0e\xa0\xc5\xe4\xb4\xffo\x126\xa8\xfd3\x84v3\xe8zY\xa15$\xa5\xa8\xac\xc3d=\x97\x1b\xa0:\xbc\x9d\x83h\x82\x9cf\xfc\x7f`\xc6,\x97\xf8M\xbf\xe7E%~\x92+s\x10\rW\xea\xe1\xff\x01\x99b\x8dţ\xb7\x15\xc9\f\xf9\xa5\xdd\xeb\x06\xe8\xaefH~\x03;\xca4\xca\x1eg\xbei\xbd\\\x82\x18)\xf6\xce<\x05\xd1\xd9\xe1\xc3W\xe3y\xa9f\xab#\x91.\xfd\xce\xce\x7f\r\xfe|\xd70\xcf\xc0\x05\x9b\x1a\xa5\x12\v\x97r}\xb2\xd4l\xdeX\x8f\xeaݧ\xf7\x98O\x91\a\xd2$o0\x91w=d\xdbC{\xa7t\x9f\xa2Rv{\x86\v\xber\xa9\x97\xd8H\x8e؉ \x85\xecpd\x88Z=\xe8H\xae'\xfe<\x19K\xe2\xfa\xbb\xddEF2\xcc\xc3\xee\x82\xdd\x13#\x1a\xf74\x83\x02\xe5~\xcap\xb4\x9f\xd2\xe8\xf74\x14\x12\xb5\xae{\x16JX\x9ai\x0f\x8fW\xdd\xd1\xe4w\xf7Y\x99\x95\x9b\xd0*0{\xb6\xe9\xc8V\xd8x\xd3\xf9\x19Y\x13k\xfd\x8fY\xea\x92<\xb7U\f\x84=,\xd0\xf8\vx1\xb4\xfd\x0e1g!\vb7'\xfe˘9+\xd0\xff\r%\xa12a\r\xbf\xb3E\t\f;}}\x16\xab=\x8c\x19\x81*0\xfc=\x126\xdcd\x8dLN\x18݂\xcc\x19r\xb1\x1bx,7\xf0r\x10\xca\xd9T\xbb)2\v\x92*\xb8~\xc6\xd3\xf5\xcd@\x0f\\o\xf8\xb53\xf0\x8b\xd5M\xed-\b\xceNpm\xfb^\x7f\x8b\x13\x94(\x89I\xcdl\xf1G\xaa\xablb\xc9\xe0\t\x98\x8euŃqs\xa7\xb0N\x92\xc3R\xa8\xc8>\xee\b*\x0fBi\x97Y츥K\xb2X\xe0d\xc8g\xaf\x80\xec\\͉\x90\xa1\x9a\xc0\xa8\xbd^\xc2\xd5pMMkX\x97\xfb\xf7\x191\a\xd4\x04V\xd7\xcd\nv\xfa\xf4\xda\xed=\xd8AHf\x9d\x8bY\xb8\xa5\x14\x19*5-\"\t\xdaz&IX'\b\x89\v`\xdcV\xfdtR2<\xe9\x0e\xa9!\xd2BW\xfe\xc3\xd7V\xf6\xd2,~\xf3\xf7\x9c\xf0-\xc5\v\xec\x9a-\nүIIB\xf1\xde\xf5\f\xcb\xc4\x03r\xa1\x81\xdcWv\xa9\xa7{\x90^\x90\xbe\a3]P\xbe\xb1\x03\xc0ۋ\x9b\xf5ZI\xe29\x8e\xfb}\xe8\xdb\x10\xbd~aWo\xaaG$l\xe6^b\x87s\xc3<\xb7q\x14\x13Ar\xa1\xdb\xe9\x04\x03\xb7\x14\xf9\x1b\x05;*\x95n#\x9a*\x14\xd5\xcc\xeao\x9e\xa5\x91\x13\xff \xe5Y\x81ӯ\xaeg+\x91u\x10/\xa1\xb2g\xb4\b\"\xf6\xd8M!\x04\xba\x03\xaa\x01y&*n\xd3/f\xa9\xdb!\x1c\v\x9c\x82N&Y\x9a\x820\x0f\xf2\xaaH#\xc0\xcaJ\x1d\xe5\x93y\x9av\xf3\x8f\x84\xc6v\x92\x87\xcfB\xb6\xe9\xb1\x02\xa8\xd8\xd3a[\xa8\x84j\x97h\x15\xe4+-\xaa\x02HaH\x9f\x1a\xf6\xec\\\xfdT\x87\xe3u\x15\x95\x85k͈\x16fQ\x95\fu\xea\x8at\xf5Rf\x99(\x9acm\x98\xbd\x14\b\x0e\x04v\x84\xb2\x91\xb2\x95\u1cc8\xb6Kb\r\xaf,.\x17D\xa4\r\xbe\xb2\xa4HH\xc4&:\x8b\xd3ں\x94\xe9\xae\xe2\x83\xc44\xf7l.)\x1dܳRR#K\xe2\xd2\x1e\x9a\x171\xc2O?\\\xb4\xc1\xf3\xc3E\x9by~\xb8h\xa3\xcf\x0f\x17m\xfe\xf9\xe1\xa2\xf9燋\x16\x9e\x1f.\xda\x0f\x17m\xaaٔ\xb6\x9e\xc3ȝ\xf5\x1a\xf9q\x16\x8b\x84\xed\xe9)\x14'\xe0\xfbj\n_\xaf\x9dZa\xb9\x89\xf7\x8a\xd4\xe3'\xd7x\xab\x96)\xa9K.\xcd\x02\t\xe2펮\xcc\x14a~C\xdd{\x18\xf4\xac\xba\xf7\xcdd\xe7\vս{\f\xfb^\xf7\x85\xaa\xde\xc3\xfc\x97U\xbd\xdf\xf8\x92\x8b\x02IH\xb3\xbb=\xf5|l\xc8\xdeh\x03\xc0\xbfs\x1d\xed\xa0\xce\xeb<ƿz\xd5\xfc\b\xf3\x13\vܯ\xfft\xfd\xfdQz1mG\xa99 SdR\xe1Ъ\x89+\xdbEZ݂\xb8\xefS8\x97Jcj\xe5\xfb\x14\xbd\x86Z\xa6E\xb0\xefu1k,~-\xbd\xadH;\x9f\xb8\x89t\x99;\xa1\x18\x99\x8fM\x02\xa8\x13\xcf\x0eRpQ)\x9f70\xd0\xdf\xd9\xf4\x85\xdfҴ\xe5\\\x89\n\xf6-\x1cD\x15\xa9\xbc\x9e\xa0\xddL\x1d\xdex\xf5\x9d\xdfkFM\x8eo\xd7\xdd_\xb4\xf0\xb5x\xf0B\xf5!\x82\xe7\xcb\x01\xb9\xdd%\xe7\xfbva}Xp\xfe\xf8t_\x90@H\xe0\x94\x8d\x19\xac\xfa\x8cy\xc74\xfdZ\xba$\xd1b\xcb?\x9d\xe0H\xab\xd6;\xbbF\xaf[\x837\xe2\x04.ݔN?\x8a\x90^\x857]6\xb7\xa4\xf6\xae_Y7\nt\xbe\xe2.%75S]wFM]b=\xf57o\xbd\xa7T͝U+7[r\x9cX!\u05ed}\x9b\x06\xb9\xa0..\x898\xf35p\x8b+\xdf|\xa5\xd9\xe4<\x92\xeb\xdd\"\x95l\x93\x80G\xabܦ\xea\xd7f\xf2\xde\xc3ڶ\xf4\xaa\xb5Iж\xa2m\xbeV\xedr\x15闈\xb2\xc7U\xcdl\xbd\xd9l\x14>\x8d\xdflEْ:\xb2Y\x8a\x9dY3Vׄ\x8d\x8c\xbb\xb4R\xac[\t6\x024\xa5>l\xa4\xfek\x04\xe2dUXj\xd5\xd7\b\xec\x19\xb3;)%\x13?\xc6/\xf9\x80Y\xfb\xc6~/\x89:wbBv\xdcŹ\x10\xe5\xd7^s\xc3\xcb\xe05M\xbb\x9f1ϓ\xea\xc3r\xf7\xb3\xa8\x98\xa6%\xb3\x1b\x86G\x9aG\xa3p}\xc0S}e\xc3o\xc2\x1e\xa4ܞ,\xa4_?\xd7\xe2\xb9\xee9\xd1D\xc1\v2\x06$&\\\x83\x99g\ue79aL\xac\xd0\xe8|\xb3\xe0\xfce\x14\xfe:\x9b\x1b'\xc1\xf6\xachlOE\x1f\xb00P\u00ad\x16\v\xe2\xafi\a\xd1\xf9\xb2\xf6\xdd\xdf*\x94'\x10G\x94\x8d\xc70sR\xc9-4e\xa2\x9b\xb0\xf4\xbd\xfep\xb7#\xf5\x1c\xe7f\xc1\xc1;\xeeLX\x14l\x0fG\vǬyV\xf3ڨ7\x13\a\x8c4\x8d\xa7VE\xdd;\xf2\xfb\x9c\xef\x99z\xcc\xe7uC\x87\xe5\xc1ì\xd9~\x95\x00\xe2\xfc\x10b\x02d걝\xb4-\xee\xd9c:\xaf\x15J\xcc\x05\x13\xc9^T\xda1\x9c\xd78~\xb3\xe0\xd8͂\xa0bYX\x91L\xa6\x94\xe35\xaf\x12\\\xbcbx\xf1\x1a\x01\xc6y!\xc6\f\xc8ޱ\x99\x94\x031I\xe5\x1b\xc9;\x98)\xe5\x17\xf3\x9b\x8c\xd3\a]\x12\x0e\xb8$l?\xcea\x9ap\x90e\xd9\x01\x96\x04\x1a\xbeR\xf0\xf1J\xe1\xc7k\x04 \xaf\x1b\x82\xcc\x06!\xb3\x923\xf9\xf3\xd9\xe9u!s\x94\x93\xbb\x11\xa9\xa26)d\xbdx\xa1;f/7\x1fno3\xad:\xaei,\xa5\\\x9f/\xcf\xe0g\xca\xfdN\xa7\x11\xaa\x96\x1d\xefl\x914\x8eE\xae\xf1\xe0\xf2\x05{\xb9P\xd6ޜ.Mø\xe3d\x9d\xac\xeeE\x86;\xc1\x98xY\x18^\x93\x92\xfe\x8b\xbd\x15x>\xcb\xf2\xeeac\x9b\x06I\xb1\xb7\t\xd75<5\xd2[4\x16\xb0\x99\xce\xd8\n\xde\xec:\x10#\xb5p\xf5\x9fVZk\vL\xc7\xee7ruyFsM\xbbVg/\xf8\xb5\x8d\x9c\a`O\xc1\x84\xdb\xdb\xdd1\x98\x02\x95\"\xfbp\xb3\xef\x8bq\xc0\xf6\xc8\xd1F\xe4\x91\xd9\xf8\x9cesl\xa2{\xaf\xad\xdb\\!\x99\xae\x88\x1f \x14\xa5\xb5Z\xbd\x89)`&\xf6\xee\xde}\x1a\xbe:\x12<Ӆ4\xf9ZR\x99\xe2\xc9~\xa8\x1b\x1a\xda\xd8}Qˈ\xe6+1\xc8\xe8\x9e\x1a7\xd00iO\xe4\x96\xecq\x95\t\xc6\xd0\xea\xda!^\xaf\xb9X\xfd\xe1\x94\xcfH\xd4\xec\xd4>\xb6\xdb\xfa$\xbc㶻\x16\x8e\xb8\xfaL\xfb\xd1\x10M%6_\xe1\x19 $\xec\xc0\x8b\xd2c\x9al\xf8\x83\x14{\x89j\xb8\xaaV\xf0WB5\xe5\xfb\x8fB>\xb0jOy\xe3o,j\xfc@\xa4\xa6\x84\xb1\x93\xc3'\x86(\xe5\x84ѿǸ\xd3\xfeq\x1eP\xadn#\xbf%\xa01\xf6\xc3{4\xa6v4܈\v\x82\xa7\xeb\x9c,\xf8fM\x1e\x9br'\xbb\xf6X\xd9VT\xba\xa3\xfc\x1a\xe5\x19\x11\xe40\xe6\x1a>\t\x8da{\x94va\x1as\x81J\xafp\xb7\x13R\xbb\xb4\xf9j\x05t\xe7×X\x9e\x94Pf\xcb;\xdcW~\x80\xea\xa6\u00adYo63!\xadڰ\x17\xb2\x16\xe4\xe4\x92h$\xcbLt\x8c\xb7J\x13\x16\xd1\xc8\xdfTPl\xe3D\xb3^0\xffKJnq\xd3n_'\xa0j{l\xc19\xca\xd9#\xa0\xce\x1aEm3\u0603\x81\xc8\xe1ER\xad\x8d\x05h\u05ff\x806:\x9f1PF\v\x8e\xdc\xea=e\x8b\xec\xef\xc6[،\xef\xc7u\x13\x12u\xe31g\xc3ON\x18\xb6l-\tF\xa6\xe5λP\x15\xfa\x1aVf\a\xc2\xf7F\xa8\xa4\xa8\xf6\x87 \x97#\xb6|\x04n^\x19\xa4\xa0\xb4\x1aB\x85\xda\x03]I\xde\xdaϨ\x8f55\xe8\x92\xecy\x14S\xbf\xbf\x1a\xbe4w\xeb\xef\xe9^\xed\xa4(V\x9e\x17\xb6d\xe0\xc6\xef1H*L\xc0\xae\x0fQ\x92\x83\xfb\x84\x8f\xbf\x10\u05caAY\"\a\xa2<>\t\xf7\x1fL\xb3u*\xa5\xa8\x89ԩq\xd0c\xa7\xf1L\bd!\xc7\xf1}\xf4{(\xee\x1e\x88\xfb\xfe7\xffn@Q\x1e>r\xe7vh\x9c((\x13\x19I\xb4ɵh5\xc8 \xa6\xe9D0]\xf4\x7f\xdf\xe0\xe5X\xdb\xc4\x0f)^\xf0\x97^\xf3\xdeY\x1f\xfb-\xa7\xba\x89\xf7\\#\xf4\xf8\x03ݹ\x02\x95\xcc`\xfd\xc7\xff\xf53<\xc7$/\xebͤ\x83e}\xa7\xdaS\x9a\xf9r\xd3\x03C\xe3\xf9(Į\xef\xf6f\x91\x93~\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\x7f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\x7fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\x7f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\x7f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\x7f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5\xe9\xa5x\x0e\xff\xfeϤMה\x13\x1b\x8f\xe2\x87\xfeǵw)d\x94/d\xf1'\xa7:&}\x1d\x84\xbf\xfdc\x92\xaeB\xf1\\>i\xd1\xe4\x7f\x03\x00\x00\xff\xff\x1d\r\x93\v\x97\x1c\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96Ms\xe36\x0f\xc7\xef\xfa\x14\x98}\x0e{y$\xefN\x0f\xed\xe8\xd6\xcd\xee!\xd36\xe3I2\xb9tz\xa0I\xd8\xe2F\"Y\x00t\xeav\xfa\xdd;$%\xbf\xc8v6=\x947\x91 \xf0\xe7\x0f\x04Ī\xae\xebJ\x05\xfb\x84\xc4ֻ\x16T\xb0\xf8\x87\xa0K_\xdc<\xff\xc0\x8d\xf5\x8b\xed\xc7\xea\xd9:\xd3\xc2Md\xf1\xc3=\xb2\x8f\xa4\xf13\xae\xad\xb3b\xbd\xab\x06\x14e\x94\xa8\xb6\x02P\xceyQi\x9a\xd3'\x80\xf6N\xc8\xf7=R\xbdA\xd7<\xc7\x15\xae\xa2\xed\rRv>\x85\xde~h\xbeo>T\x00\x9a0o\x7f\xb4\x03\xb2\xa8!\xb4\xe0b\xdfW\x00N\r\u0602\xc1\x1e\x05WJ?\xc7@\xf8{D\x16n\xb6\xd8#\xf9\xc6\xfa\x8a\x03\xea\x14xC>\x86\x16\x0e\ve\xff(\xaa\x1c\xe8sv\xf5)\xbb\xba/\xae\xf2joY~\xbaf\xf1\xb3\x1d\xadB\x1fI\xf5\x97\x05e\x03\xb6n\x13{E\x17M*\x00\xd6>`\vwIVP\x1aM\x050\xf2\xc82kP\xc6dª_\x92u\x82t\xe3\xfb8Ldk0Țl\x90L\xf0\xb1\xc3|D\xf0k\x90\x0e\xa1\x84\x03\xf1\xb0\xc2Q\x81\xc9\xfb\x00\xbe\xb2wK%]\vM\xe2\xd5\x14\xd3$d4(\xa8?ͧe\x97\x04\xb3\x90u\x9bk\x12X\x94D\x9eD\xe4\xb8\xd6;\xa0#\xbe\xa7\x02\xb2}\x13:ŧ\xd1\x1f\xf2µ\xc8\xc5f\xfb\xb1\x90\xd6\x1d\x0e\xaa\x1dm}@\xf7\xe3\xf2\xf6黇\x93i8\xd5z!\xb5`\x19Ԥ4\x81+\xd4\xc0;\x04O0x\x9a\xa8r\xb3w\x1a\xc8\a$\xb1\xd3\xd5*㨪\x8efg\x12\xde'\x95\xc5\nL*'\xe4\fm\xbc\x04hƃ\x15\x98\x96\x810\x102\xbaR`'\x8e!\x19)\a~\xf5\x15\xb54\xf0\x80\x94\xdc\x00w>\xf6&U\xe1\x16I\x80P\xfb\x8d\xb3\x7f\xee}s:g\n\xda+9\xe4g\x1a\xf9\xd29\xd5\xc3V\xf5\x11\xff\x0f\xca\x19\x18\xd4\x0e\bS\x14\x88\xee\xc8_6\xe1\x06~I\x98\xac[\xfb\x16:\x91\xc0\xedb\xb1\xb12u\x13\xed\x87!:+\xbbEn\fv\x15\xc5\x13/\fn\xb1_\xb0\xddԊtg\x05\xb5D\u0085\n\xb6\xce\xd2]\xee(\xcd`\xfeGc\xff\xe1\xf7'Z\xcf.H\x19\xb9\xd0_\xc9@*\xf3\x92\xf6\xb2\xb5\x9c\xe2\x00:M%:\xf7_\x1e\x1ea\n\x9d\x931\xa7\x9f\xb9\x1f6\xf2!\x05\t\x98uk\xa4\x92\xc45\xf9!\xfbDg\x82\xb7N\xf2\x87\xee-\xba9~\x8e\xab\xc1\nOW2媁\x9b\xdcbSQ\xc7`\x94\xa0i\xe0\xd6\xc1\x8d\x1a\xb0\xbfQ\x8c\xffy\x02\x12i\xae\x13ط\xa5\xe0\xf8\xef07.Ԏ\x16\xa6\xf6}%_\x17\x8a\xf6!\xa0N\x19L\x10\xd3n\xbb\xb6:\x97\a\xac=\xc1Kgu7\x15\xed\x8c\xee\xbe\xc0\x9b\x93\x85\xcb\x05\x9dơM\xceW\xae\x1e\x1er\xee,\xe1\xec\x16\xd6p\xd6s_璛\xe1\xbf$S:\xf1\xc8FG\"trԟեMoe\x81D\x9e\xcefg\xa2\xbed\xa3\xfc\x04P\xd61(\xb7\x1b7\x82tJ\xe0\x05)\x95\x81\xf61\xf5\x194`\xe2\x19\xbf\x11\xcb\xf1\xbf$\x90\xd7\xc8ܜ\xd9Y\xc1ႦW\xb2\x93Fz^\xa8U\x8f-\bE\xbc\x92YE\xa4v\xb3\xb5\xfc\xcf\xfa\x06\x82e\xb2\xb9\x94\x83\xfd\x7f\xfa\x9bIȸ]\x1c\xce#\xd5p\x87/\x17foݒ\xfc\x86\x90\xe7W>-.\v\xbd\xfdc\xe0\r\x94.^ʳIN\xfd\xce\x1cQd\xf1\xa46\xc7\\9\xae\xf6\xfd\xbb\x85\xbf\xfe\xae\x0e\xf7Zi\x8dA\xd0\xdc\xcd_i\xefޝ<\xb7\xf2\xa7\xf6\xae\xbc\x8c\xb8\x85_\x7f\xabJ(4O\xd3\xeb)M\xfe\x13\x00\x00\xff\xff--\nM\xde\n\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WO\x93ܦ\x13\xbdϧ\xe8\xf2\xef\xe0_\xaa,\x8d]9$5\xb7d\xed\xc3V\x1cǵ\xeb\xec%\x95\x03\x83z$\xb2\b\b\xdd\xccz\xf3\xe9S\r\xd2\xfc\xd1hfLJp\x134\xcd\xe3\xf1\xfa\x81\x16UU-T0\x0f\x18\xc9x\xb7\x02\x15\f~et\xf2E\xf5\xe3\x8fT\x1b\xbfܾ[<\x1a\u05ec\xe0&\x11\xfb\xfe\x0eɧ\xa8\xf1=n\x8c3l\xbc[\xf4ȪQ\xacV\v\x00\xe5\x9cg%\xdd$\x9f\x00\xda;\x8e\xdeZ\x8cU\x8b\xae~Lk\\'c\x1b\x8c9\xf9\xb8\xf4\xf6m\xfdC\xfdv\x01\xa0#\xe6\xe9_L\x8fĪ\x0f+p\xc9\xda\x05\x80S=\xae\xa0\xf1O\xcez\xd5D\xfc;!1\xd5[\xb4\x18}m\xfc\x82\x02jY\xb4\x8d>\x85\x15\xec\a\xca\xdc\x01P\xd9\xcc\xfb!\xcd]I\x93G\xac!\xfeen\xf4\xa3\x19\"\x82MQ\xd9S\x10y\x90\x8ck\x93U\xf1dx\x01@\xda\a\\\xc1'\x81\x11\x94\xc6f\x010\xec=ê\x86\xddmߕT\xba\xc3^\x15\xbc\x00>\xa0\xfb\xe9\xf3\xed\xc3\xf7\xf7G\xdd\x00\r\x92\x8e&pfp\x82\x19\f\x81\x82\x01\x01\xb0߁\x02\xe5@E6\x1b\xa5\x196\xd1\xf7\xb0V\xfa1\x85]V\x00\xbf\xfe\v5\x03\xb1\x8f\xaa\xc57@Iw\xa0$_\t\x05\xeb[\xd8\x18\x8b\xf5nR\x88>`d3\xb2\\ځ\xb8\x0ez'\xc0_\xcb\xdeJ\x144\xa2*$\xe0\x0eG~\xb0\x19\xe8\x00\xbf\x01\xee\fA\xc4\x10\x91\xd0\x15\x9d\x1d%\x06\tRn\xd8A\r\xf7\x18%\rP\xe7\x93mD\x8c[\x8c\f\x11\xb5o\x9d\xf9g\x97\x9b\x84!Y\xd4*\x1e\xe5\xb0o\xc61F\xa7,l\x95M\xf8\x06\x94k\xa0W\xcf\x101\xf3\x94\xdcA\xbe\x1cB5\xfc\xea#\x82q\x1b\xbf\x82\x8e9\xd0j\xb9l\r\x8fE\xa5}\xdf'g\xf8y\x99\xebì\x13\xfbH\xcb\x06\xb7h\x97d\xdaJE\xdd\x19F\xcd)\xe2R\x05Se\xe8.\x17V\xdd7\xff\x8bC\x19\xd2\xeb#\xac\xfc,2#\x8eƵ\a\x03Y\xf3\x17N@T_\x04S\xa6\x96]쉖.a\xe7\xee\xc3\xfd\x17\x18\x97·1e\xbf(g7\x91\xf6G \x84\x19\xb7\xc1X\x0e1+Or\xa2k\x827\x8e\xf3\x87\xb6\x06ݔ~J\xeb\xde0\x8db\x96\xb3\xaa\xe1&;\r\xac\x11Rh\x14cSí\x83\x1bգ\xbdQ\x84\xff\xf9\x01\b\xd3T\t\xb1\xd7\x1d\xc1\xa1IN\x83\vk\a\x03\xa3\x93\x9d9\xafI\xa9\xdf\a\xd4rzB\xa0\xcc4\x1b\xa3si\xc0\xc6GP\xfb\xca\x1f\b\xac\x8f2\xcfWn\x06\xa7b\x8b<\xed\x9d`\xf9\x92\x83d\xf9\xa7N\x1d\x1b\xcd\xff\xb1nk\xf1\n\x1a\x80\x14\xf7\xf8\xae>\xc9x\x1e\x03̪w\x16\xc9(b\xa1Ax\x15+\x10\x93:\xc4t\xba\xb44t\xa9\x9f_\xa0\x82\x9f3揾\xbd8~\xe3\x1d\x8b\xdc/\x06=x\x9bz\xbcw*P\xe7_\x88\xbde\xec\x7f\v\x18\xcbUz1t\xbc\x91w\xb7ԅ\xc0dϮ{\x87\xe2\xf7x~\xa7C\xc0UY\xae\xc04D^\xb5ћ\xfb\xdbo\xa1\xf0L\xf8\xc5C:S\xb6c\xcb\xd7\xf3\xcb\x1a\x94\v~ԠL)w\x16\x82<{\xa2CF\xda\xdb\xe7\x93\xe1n6#\xc0Sgt\x97'f\x01\x8b3\x13ym\xb2\xcf};|\xa9{\x13q\xa6\x88\xaa\\\\3\xdd\x02\xfe\xa4\xfb\x8c[\x9d[\xa0\x1a\x1c\xe4*\xc7cʼn\xbe\xc1\xf3r\xfcH\xb5N1\xa2\xe3!K~\x03L'\\kz\xa3S\xfc~\xf7\xf1\x05\xe7{\xbf\x8f̯\\e\\A\x13\"VdZy\xb9Șx_\xf6\xa4S2J;~I\x1d\x135{\xa2\xf85\x98R0/@\xfc\xb0\v,\x06\x8d\xae\\\xbeӷbN\x88\x94\x1f6ZM\x9fT\xd2\xd6\b\rZdl`\xfd\\n\x9agb\xecOqo|\xec\x15\xaf@.\xe5\x8a͌\x8c\xe4=\xaf\xd6\x16W\xc01\x9dS\xd9\xec\xc6C\xa7h\xa6\f\x8f\xf6\xfcYb愱+Ƌʀ\xb3\xf7A\x05\x9f\xf0i\xa6\xf7s\xf4\x1a\x89\xf0\xb4\x8c\xce\xeed\xb6\bN:I^N\xcd\x01KÃ|\xe8ٗ\x8c\xd2\x1a\x03c\xf3i\xfa\x97\xf3\xea\xd5\xd1oK\xfe\xd4\xde5\xa6\xfc\xa0\xc1\x1f\x7f.JVl\x1eƿ\x11\xe9\xfc7\x00\x00\xff\xff:@\xbd\xf3\x1a\x0e\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Z\xdds\x1b\xb7\x11\x7f\xe7_\xb1\xe3<\xa8\x991\x8f\x89\xdbi;|s\xa4\xa6\xa36\x915\x96\xad\x17\x8f\x1f\xc0\xc3\xf2\x0e\xd1\x1d\x80\x028\xcal&\xff{g\xf1q\xbc\x0f\x90\x944ur/6\xf1\xb1\xf8a\xbfw\xa1\xc5r\xb9\\0-\xee\xd1X\xa1\xe4\x1a\x98\x16\xf8š\xa4_\xb6x\xf8\xbb-\x84Z\xed\xbe_<\b\xc9\xd7p\xd9Y\xa7\xda\xf7hUgJ\xbc\u00ad\x90\xc2\t%\x17-:ƙc\xeb\x05\x00\x93R9FÖ~\x02\x94J:\xa3\x9a\x06ͲBY\xf38\xfa\x01\xed\xe4\xfa\x8b\x99\xdb\x1e\xd1~[aޞ\xc3\xf4\xee\xfb\xe0@\xcb\x1a[\xb6\x8e+\x95F\xf9\xf6\xf6\xfa\xfe\xcfw\xa3a\x00m\x94F\xe3Dr\xe8\xe1\x1bı\xc1(\x8cY}A\x04\xc3*\xe0\x14\xc0\xd0\x06\xab\bc\xc8#\x86 \x0eaIu\rZ\x94nȒ\xf4\xa9-0\tj\xf3\v\x96\xae\x80;4D&\t\xa6Tr\x87Ɓ\xc1RUR\xfc\xb7\xa7mI\xd7\xe8І9\x8cq\xe5\xf0y\xd7/Y\x03;\xd6t\xf8\x1a\x98\xe4в=\x18\xa4S\xa0\x93\x03z~\x89-\xe0ge\x10\x84ܪ5\xd4\xcei\xbb^\xad*\xe1R\xfc.U\xdbvR\xb8\xfdʇb\xb1\xe9\x9c2v\xc5q\x87\xcdʊj\xc9LY\v\x87\xa5\xeb\f\xae\x98\x16K\x0f]\xfa\x18^\xb4\xfc\x1b\x13#\xbe\xbd\x18a\x9d)F\xf8|x=!\x01\n\xb0 ,\xb0\xb85\xdc\xe2\xc0\xe8\xe4 \xdf\xff\xe3\xee\x03\xa4\xa3\xbd0\xa6\xdc\xf7|?l\xb4\a\x11\x10Ä\xdcbt0[\xa3ZO\x13%\xd7JH\xe7\x7f\x94\x8d@9e\xbf\xed6\xadp$\xf7\xffth\x1dɪ\x80K\x9fԐ\xc3\xec4i./\xe0Z\xc2%k\xb1\xb9d\x16\xbf\xba\x00\x88\xd3vI\x8c}\x9a\b\x86\xf9\xd8tq\xe0\xda`\"%MG\xe45Ʉ\xee4\x96$=b \xed\x14[\x11=\x14\xb9s6]^\x8c\b\xe7\r\x97\xbe\xacw\x9a.\x82\\p\x99\xecI\xd8\xe4\xc0\xa7&\x87\x19VΈ\x024S/\xdb\xef\x19F.\x1b\x1dl1\xa3pD\f\xf4I\xc5\xf1\xcc=n\x14\xc7\x1cl\xda\n\xaefA[)\xe3#\x7f\xd4I9?\x85>%\x9f\x05L+~\x06W<\x91\x81\xc1-\x1a\x94%&\xc7u*\x9d\xc9 \x1b&\x1as\x8cǕ\x02Nx\xf5,ⷷ\xd7ɓ'&F\xecn~\xee\x19\xfeз\x15\xd8p\x1f\xe8Ο}q\xbd\r\x87y\x9f\xe6\x140\xd0\x02Cb\xda\a\t\x10\xd2:d\x1c\xd46K\x91\xca' \xc37\x18w\xbc\x0e\x1e,\xba\xcaCh!\xde\x03#\xdf)8\xfc\xeb\xee\xdd\xcd\xea\x9f9\xd6\xf7\xb7\x00V\x96h}^\xee\xb0E\xe9^\xf7\xa5\x02G+\frJ\xfc\xb1h\x99\x14[\xb4\xae\x88g\xa0\xb1\x9f\xde|\xces\x0f\xe0Ge\x00\xbf\xb0V7\xf8\x1aD\xe0x\uf593\xd2\b\x1b\xd8\xd1S\x84G\xe1j1\r\xa6=\aH\xbd\xe2\xb5\x1f\xfdu\x1d{@P\xf1\xba\x1dB#\x1ep\r\xaf|Zs\x80\xf9+\xd9\xceo\xaf\x8eP\xfdS0\xedW\xb4\xe8U\x00\xd7\xc7\xe1\xa1\xd1\x1d@\x06\xcb3\xa2\xaa\xf0\x90UM?\x1fT\xc8U\x7f\v\xca\x10\a\xa4\x1a\x90\xf0\x84Iz\xc1Q\"\x9f\x81\xfe\xf4\xe6\xf3Q\xc4c~\x81\x90\x1c\xbf\xc0\x1b\x10\xb1\xd8Ҋ\x7f[\xc0\a\xaf\x1d{\xe9\xd8\x17:\xa9\xac\x95\xc5c\x9cU\xb2ه\xf6\x881\tʄyp\xcdL\ueffa*\x13C;C\x88\xf6\xcb\xd8\xf6[2\xc9\xe9\xffVXG\xe3/\xe2`'\x9ed\xbe\x1f\xaf\xaf~\x1f\x05\xefċl\xf5H\x02\x1etd\xd8\xe58\x93\x98\xbd\x1f-N\xa9c&c\xed\xd7<+3t\xacʤb\xc3\xf6䩄\xed$\aƭ\x18VY`\x06\x81A\xcb4I\xee\x01\xf7\xcb\x10\xe25\x13\x14\x9f)\x04\xf7}\x0e`Z7\"\x1b\x8ac \x8fIh\xe4\x04\x15ڬ\xb2\xc7\ue795ð\xafsF\n\x1f\aK\x93\f\xcet\x96\\\x9d\xb3\xd4Q\xbfi\x8e\x16e\xd7Ρ,\xe1Ai\xc12\xe3\x06\xad\x13ef\xe2\xd5<\xd38!\xac\xc0\xcb3<\x88-\xe8L\xf1\x12E\x112\xbd\xbe\x80\xf1]\xc7\\\x85p\xbc<8\n\x91*t\xca[\xc7\x10\x97\xf9Rr\xb2\x86J\xabɐV|1ed\xa6\xf3\x98&G\x9d\xd1!\xd2y}\xed\x1b\xdeϨ\xb0C#?\xf24\xf8S\x97\xda\xfbTL\xbc\xb4\xc6.\x15\xe5\xe9㧕\xd3⽜\xef\xf0\xed,ã\xba\x8b\x96\xacw\xd0\xf6\x8fg\xe4\x8ad\x18\x90\v;}\x04#j\xc8}\x12M9\xfe\x96\x89\x069\xa4\xb7\x9d\xe9\x9e\f\xd5!\x95\rn\xc9\xdd\a\xd3K\xa5i\x84\xd7'\xaa5\x82\xf5}\xa2\v{\x82fg\x91\xfb\x9eF\x86\t\xf3\xe4u\xabL\xcb\\\xe8k.\xb3De\xd74l\xd3\xe0\x1a\x9c\xe9\xe6\xd3',\xb1EkYu\xce\x14\x7f\x0e\xabB\xc5\x1e\xb7\x00ۨ\xce\xf5%\xfb\xc8=^بS\xcf\xeb\x1ad\x8b\xe1\xb1:3*VlLڛ\xc6\xef\x19:\x82Ã\xa0G\xb5\xc1|\xd0\x7f\x89O\x00\xf0\x0fZ\xe7\x10Қ\x9c\x81\xf5\xde뤅\xc1\t\xa7|\x83\x8f\x99\xd1\xd9C\xdcp\xf22\x99Lf\xeeGo\rϺ\x7f<\xe8\x1c\v\xe22\xa8U\x93\x8cY9ր\xec\xda\r\x1a\xe2\xc3f\xefЎ\xddy\xae?\xe3\xeb\xba\x03\x1b\a\xfb\x93\xfc\x02\xa5X\xaa\x96L\xfa>*Y\x97S\xc0\x85\xd5\r\xdbg\b\xa7\x8b\xf8܍\x8c\x8b\\\xc0A\x9f\x93Qk4~\xea\xb9}%\x8f\xe9J\xc9#\x95F\xb2g!\xdd_\xffr\"\xd3\x13\xd2a5\t\x0eq\x9e\xd8\xf9\x03\x9d\xf2uN8\x91\xc4Xɴ\xad\x95\xbb\xbe:\xa3\x05w\xfd\xc2d\r\xb3\xe79\xec\xa9EUȉ\xaa\xf7-\xcf2\xd5\xf1\x13\xf09\xa8\xa3\xc5g\xa2P||\xceŠ;\xd4̐\xa5\xfb7\x81\xcb\xe9\xa3\xd5k\xb0\xc27:)\xf3\f\xa9hhCX\nN\x94Z)\x83\x19\x97\t\xf3\xb02\n\"c\xf8\xbfg\xfc\xc8\xea\xc9l\xd0#\xe7\x03ڱY>\x1c\xe96\xfdC\xd0\x1a~\xfdmqHlXI\xc5\x13\xf2\x9b\xe9\x1fYĔ3\xfdՄ\xffY*\x19*\t\xbb\x86O\x9f\x17\xe9\xd9\xf2>\xfd1\x04\r\xfe/\x00\x00\xff\xff\xb0\xddǼ\x99\"\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\x1b\xb9\x11\xbe\xf3Wti\x0f\xcaV\x99\xc3]'\x95\xa4x\xb3\xa5lJɮ\xac2e]\\>\x80\x83\xe6\f\x963\x00\x02`H3[\xfb\xdfS\x8d\a9/\x92\xa2*Z\xcf\xc5\x16\xd0h|\xf8\xd0/4'\xd3\xe9t´xBc\x85\x92s`Z\xe0W\x87\x92\xfe\xb2\xd9\xfa\xef6\x13j\xb6\xf9q\xb2\x16\x92\xcfᦱN\xd5\x1fѪ\xc6\xe4x\x8b+!\x85\x13JNjt\x8c3\xc7\xe6\x13\x00&\xa5r\x8c\x86-\xfd\t\x90+錪*4\xd3\x02e\xb6n\x96\xb8lD\xc5\xd1x\xe5i\xeb\xcd\x0f\xd9߲\x1f&\x00\xb9A\xbf\xfcQ\xd4h\x1d\xab\xf5\x1cdSU\x13\x00\xc9j\x9c\x83V|\xa3\xaa\xa6F\x83\xd6)\x836\xdb`\x85FeBM\xacƜv-\x8cj\xf4\x1c\x0e\x13aqD\x14N\xf3\xa0\xf8\x93\xd7\xf31\xe8\xf1S\x95\xb0\xeeߣ\xd3?\v뼈\xae\x1aê\x11\x1c~\xd6\nY4\x153\xc3\xf9\t\x80͕\xc69\xdc\x13\x14\xcdr\xe4\x13\x80H\x80\x876\x05ƹ\xa7\x94U\x0fFH\x87\xe6\x86T$*\xa7\xc0\xd1\xe6Fh\xe7)\xdb\xeb\x01\xb5\x02W\"m\xe9\xe9fB\nY\xf8\xa1\x00\x01\x9c\x82%BD½2\x80_\xad\x92\x0f̕sȈ\xb8L+\x9eɤ3\xca\x04\xce\xef{\xa3nG\xe7\xb0\xce\bY\x1cC\xf6\x7f\x06\xd5\xc1\xf3\xa0\xf83\x91<\x96\xe8e\x12\x9aFW\x8aq4\xb4y\xc9$\xaf\x10\xc8r\xc1\x19&\xed\n\xcd\x11\x14i\xd9\xe3Nw\x91|J\xfaZ3\x97\xb0s\t\x15A\xb6\xb3\xfdS{\xe8ܾ\x0f\x8a\xc7\x05\x10\x8d\x1a\xacc\xae\xb1`\x9b\xbc\x04f\xe1\x1e\xb7\xb3;\xf9`Ta\xd0\xda\x11\x18^<\xd3%\xb3]\x1c\v?\xf1\xba8V\xca\xd4\xcc\xcdAH\xf7\u05ff\x1c\xc7\x16\x17eN9V\xbd\xdf9\xb4\x1d\xa4\x8f\xfdဖ\x9c\xad\x88\xd7\xffM\xe0.\tҭ\x92]^\xdf\xf7F\xc7\xc0\xb6\x94\xa6@\x9c\r\x82hG뻢\xab\x8f3\x17\x06\xc2\xf4\xe6\xc7\x10\xca\xf2\x12k6\x8f\x92J\xa3|\xf7p\xf7\xf4\xe7Eg\x18@\x1b\xa5\xd18\x91\xa2k\xf8ZY\xa55\n]f\xafIa\x90\x02N\xe9\x04mp\x8a0\x86\xa3f5\xff\xce\xc4\xfck\xaf;X\aN\x17>\x9f\xebN\xdc\x00%;\x10\x16X\\\x1aNq :\x85\xec\x8f\xffX\xdc\xcf\xfe9\xc6\xfc\xfe\x14\xc0\xf2\x1c\xad\xf5\xf9\x1ak\x94\xee\xcd>gs\xb4\xc2 \xa7\xc2\x05\xb3\x9aI\xb1B벸\a\x1a\xfb\xf9\xed\x97q\xf6\x00~R\x06\xf0+\xabu\x85o@\x04\xc6\xf7\xe1/ٌ\xb0\x81\x8e\xbdF\xd8\nW\x8a~\xd2\xda3@\xd6\x15\x8f\xbd\xf5\xc7ul\x8d\xa0\xe2q\x1b\x84J\xacq\x0eW\xbe\x12<\xc0\xfc\x8d\x1c\xeb\xf7\xab#Z\xff\x14\x1c芄\xae\x02\xb8}\xbek{\xe4\x01\xa4+\x99\x03gDQ\xe0\xa1\x10\xed\x7f>xSH\xfc\x1e\x94!\x06\xa4j\xa9\xf0\x8a\xe9\xf6B\x00\xfd\xf9헣\x88\xbb|\x81\x90\x1c\xbf\xc2[\x102p\xa3\x15\xff>\x83Go\x1d;\xe9\xd8W\xda)/\x95\xc5c\xcc*Y\xedB\xb5\xbfA\xb0\xaaF\xd8bUMC\xbd\xc1a\xcbv\xc4B\xba8\xb27\x06\x9a\x19w\xd2ZS\x95\xf1\xf8\xe1\xf6\xc3< #\x83*|\xbc\xa3\xec\xb4\x12T5P\xb9\x10r\x9e\xb7\xc6A\xd2L\x9fm\x82\xf98\x05y\xc9d\x81\xe1\xbc\b\xab\x86\xb2Pv\xfd\x12?\x1e\xa6\xfe\xf4\x8d\x94\x00\xfd\xc0\xf1͒\xe83\x0f\xe7+\xd5g\x1c\xae\xfd\xd6:y\xb8u\xb3D#ѡ?\x1fW\xb9\xa5\xa3娝\x9d\xa9\r\x9a\x8d\xc0\xedl\xab\xccZ\xc8bJ\xa69\r6`g\xfe\xc9<\xfb\xce\xff\xf3\xe2\xb3\xf8\xd7\xf5s\x0f\xd4y\xf4\xbf\xe6\xa9h\x1f;{ѡR\xad\xf8\xfc2q5\xacTN0\x11\f\xe0\f\a\xb1\x994\xf22\x8a\xf6\x13*E?B\xaf\x11oE\xe3!\xf6R\xbb\xa2\x874\x95\xbd]\x84\xd3\xf1\xf7^OF+>\xe9\x93\xd6v\xc9\xde\xe4\xc1\xa1\xfa\x13][\xed\xcdv\x9a\x9c\xed\xd3\f\x9fʾ\x83v\xc9c9t\xed\"\xef!f\xbb\xd4ˣ\aˋ\x9f˹\xa2\xc7@\xf7W\x8b\xd36p3\\\xe1{S\x86G\x9f\x105\xfa7hh8n\x99M\x9b\x8c\xdd7\xb4\xf4\x85\xa5>O\x92:\xe4\xbeT\xa7\x97Ċ\x89\n9\xec\x7f7\xf1\xcdq\xeb\x9b4\xd7c\x95iR\xd4X\xe4>n\x8c\x80\x1e\xaeK}O\xce\x1cNI\xc5@B6UŖ\x15\xce\xc1\x99f8}½j\xb4\x96\x15\xe7\xfc\xeb\x97 \x15^\xf1q\t\xb0\xa5j\xdc\xfe\x19\x1f\x1d-Rqm\xa3\x15\\\xd6J(\x99=\a\xe5\x81d\xc6,n\xef\xf2\xa7M\x0eN\x84\xb2{\u070e\x8c\x0e\xfa\xd0\xedɛdB#s?y븈\x80\xb8\xd19\x0e\xa2\x18\x94\xaaJ֭\x1c%\xa5\xa6^\xa2!\"|\xf3;1\x92\x02\xc7X_Ŀ\xa7\x0eL\x1e4\xa4X\x18T\xc5\x17bΤo\x13\x92\xfd:\x05\\X]\xb1݈\xdet\x12_2\x91\xf9\x92\x1f\x1d,&y!\xb9\xbf\x9f\xbb\xb4\x9f\xb3o\xee\x8f\x17tc?\x15\x8c\xddB\xbb\xefߛ\xdf\xff\xaa\xf1:;\x9c(\xe2\xacc\xc6=7\xec-:\xc2\xe7\"\x9eW=\x1e\xefڡk\x18\xa8\xba\xdb\xfc\x911j\x94\xa8\xc1\xa0G\xce[\xbac/\xb4=\xd2,\xf7\x9d\xfe9\xfc\xf6\xfb\xe4\x90\xeeXNU;\xf2\xfb\xfeOڱVI\xbfP\xfb?s%\xc3O\xcav\x0e\x9f\xbfL 6M\x9f\xd2\xcf\xce4\xf8\xbf\x00\x00\x00\xff\xffe\xe5\xd5&\b \x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xdc\xa0@\xad\xd6\\]\x99\x123Zl\xa7UU\xde@\xfb\x87\x9f\x136\xe2\x91x\xf2\xd3\xdd\x1b\xc1\x8d\xfdK\xf7\xed_\xb9\xb1\xee\x9fRT\x9a\x89v1\xf7\xd2p\xb9\xab\x04\xd3\xcd\xeb+\x00\x93\xa9\x12o\xe0\x81\x96)Y\x86\xf9\x15@\xc0\xc9-\xbb\n\xbb>|\xf6 \xb2=\x16\xcc\xef\a@\x95(o\x1f\xef\xbf\xff\xebs\xef5@\x8e&Ӽ\xb4\x8e2ao\xc0\r0\xf8\xeep\xa3\r8&\x80\xdd3\v\x1aK\x8d\x06\xa55`\xf7\b\xac,\x05\xcf\x1c\x11\x1b\x88\x00j\xdb\xcc2\xb0ժh\xa1mX\xf6R\x95`\x150\xb0L\xef\xd0\xc2_\xaa\rj\x89\x16\rd\xa22\x16\xf5\xba\x81UjU\xa2\xb6\xbc&\xac\x7f:r\xd4y{\x82\xcbGB\u05cf\x82\x9c\x04\b\xfd\x96\x03\xc90\x0f\x14\xa2\xdd\xda=7-j\xa7\xe8\x04\x94\x98\x04\xb5\xf9;fv\rϨ\t\f\x98\xbd\xaaDNrw@M\xc4\xc9\xd4N\xf2\xffn`\x1bB\x94\x16\x15\xccb\xe0w\xfbpiQK&\xe0\xc0D\x85\xd7\xc0d\x0e\x05;\x82FZ\x05*ف熘5\xfcͱGn\xd5\r\xec\xad-\xcdͧO;n\xeb\U000d3a62\xa8$\xb7\xc7O\xee(\xf0Me\x956\x9fr<\xa0\xf8d\xf8n\xc5t\xb6\xe7\x163[i\xfc\xc4J\xber[\x97\xee\f\xad\x8b\xfc\x9f\x1a\xb6}\xec\xed\xd5\x1eI\xf2\x8c\xd5\\\xee:\x7f81\x9f\xe0\x00\t\xbc\x97%?\xd5c\xd1\x12\x9a^\x11u\x9e\xbe<\x7f\xeb\xca\x197\xa7\xd4wt\xef\b_\xcb\x02\"\x18\x97[Ԟ\x89N\xda\b&ʼT\\Z\xf7#\x13\x1c\xe5)\xf9M\xb5)\xb8%\xbe\xffZ\xa1!\x81Vk\xb8sJ\x056\bU\x993\x8b\xf9\x1a\xee%ܱ\x02\xc5\x1d3xq\x06\x10\xa5͊\b\x9bƂ\xae><\x1d\xec\xa9\xd6\xf9\xa3V^#\xfc\n\xa7\xff\xb9Ĭwbh\x1a߆c\x0e[\xa5{ʁ\xa6\xac{@㇖\x1e\x7f\xfaI\x83\x9d\xfes\xb2\x95\xffh\x06\x92\xfc\xd0&*\xc9\x7f\xadЩ8\x7fbq\xa0R\x06 \xa1ޟ\x13\x8b\xf5\xe0\xff\x11\x9a҃?2Q\xe5\x987\xdav\x80\xcbɎ\xbf\f&8sĸ$\xf9'\xf5Oۖ\xed\xbf\xa4N#;f\x1a\x81$\x90K\x0f\x0f\xb8t\xc8F)M\x0f\xb7XD67\x89\x1d8;\xc76\x02o\xc0\xea\nG(ôf\xc7\x11\xc2Զ9\x95.\xcd\xf8\xa0\x10\x04ϰk(\t\xea\xb6}f\x14\xef@\xb1\x84\xf7Q\xcb\xd9>\xff?\t[[\x923\x84\xf6~0\xf5}\x85\xd6\x05U\xe4\xec\xdfo\x01\x8b\xd2\x1e\xaf\x81\xdb\xfa\xed\x1cD&Dg\xfd?0c\x96K\xfc\xfd\xe9\xccw\x95\xf8I\xae\xccA$\xae4\xcb\xff\x01\x99\xe2\x8c\xc5s\xb0\x15\xc9\f\xf9kw\xd65\xf0mÐ\xfc\x1a\xb6\\X\xd4'\x9cy\xd3yy\x0fb\xa4\xd8;z\nf\xb3\xfd\x97\x1f\xe4٘6\x01\x95H\x97\xd3\xc9\xde%\xaec\x84\xbea\x9e\x81\v.|\xe5\x1a\v\x1f\x16\x7fs\xd4l߸x\xe2\xf6\xe1'̧\xc8\x03i\x927@\xe4\xf6d\xb3ݥ\x83\x9f\x9f\x8aFp}\x9a\x98\xc9'<\xae\x81\xc1\v\x1e\xbd\xc7\xc2$\x10s\x98u\xfen4z\x1a\x12\xc7e^\xbc{\x8cG\a&\xa4Rfg\xa7\x8a\x82\x7f^0\xe2\xeeǞ\x1e\x01iO!\xc0\xf5\x94\xa4\x17\x8e\x10.\xf0N'\x1e\xb8\xb4X\xad\x8b摃tER?5\xed\xcf@\xb3a['}\xe8\x18\xfb\xd1x\x16\xd1)\xd8\xf32\x11Q\x97=4\xe8NK\x9d\x18\xfb\xce\x04ϛ\x85\xbc\xdc\xdf\xcbqo\xb8\xff<({/\xaf}Df\x9c\x94\xfc\xa4\xd0<(\xeb\xde\\\x84\x9c~\xe3g\x10\xd3Ot\xc7Kz\xb5Mt\xe8f\xd8\x12\x84\xdb?\xf7>\x91Ұ\x87\x1b\xb8\x97\x14\xb8\x04z\xb8|\xa9_n\xda>\xf4\x9f\xa22.\x85&\x95\\9S\xb9\x8e\xad䉝\bR\xe9\x1eG\x86[k\x16\xf5\v&\x82\xfdF\x96\xc4\xcf\xf7\x19`\xc12\xcc\xebh\xd3\xe5-\x99\xc5\x1dϠ@\xbd\x9b2\x1cݧ$\xfd\x9e\xb6\x85D\xad럅\x12\x96f\xda\xeb'\xa8\xee|~3+:\xb9\t\xa3jf\xcf\x0e\x1dIW\x8e\x0f\x9d\xc7șX\xe7\x7f\xccR\x97幫-1\xf1\xb8@\xe3/\xe0\xc5\xd0\xf6\xfb\x8dy\vY\xb0\x92\xce\xef\xff\x90\x99s\x02\xfd\xbfP2\xae\x13\xce\xf0\xad+\x13\t\xec\xcd\r\x89\xb1\xee2\xb4\x027@\xfc=01L\x84G\x90S\xa4[PxC\xae\xb6\x03\x8f\xe5\x1a^\xf7\xcax\x9b\xba\xe5(b)\x9b\xfe\xc3\r|x\xc1\xe3\x87\xeb\x81\x1e\xf8p/?x\x03\xbfX\xdd4ނ\x92\xe2\b\x1f\xdc\xdc\x0foq\x82\x12%1i\x98\x8c\xa6\xb9ۧ'\x16\xddTw\x9b\xe3\x0en\xeeԮ\x93\xe4\xb0T\xc6\xfe9\x9e\xb0\x1b\xd9\xcfc=\xa3\xef\x9bF\xf2^\xb3>{\xc8a5J\x95<\xb9\xadE\x1d\x92x^\xd1\xd6\x11\xc0\x1bc\xa3\xb9$]\x93\xa0cMb\x95\b<#\x15\xbe䑲\xc5%^#\xd1e\xa1\xbf\xfd\xe5G'\xc7H'\x94~w\x11yo\xaf6SE\xc1N\x8b|I[\xbd\xf33k\x99\x0e\x80<\xf7\xf5\xaer\xe72\xddݫeȕ\xf7^\xb9\xdds\t\xac>\xfe\xa8\x83@1(ռ&\xf2Ϟ\x19\xd8 \xca&7\xfe{\xb0\xd7\x05\x97\xf7n\x01\xf8\xfc\xee\xf6\x1dZr\x9d\xc5Κ\xd4\rC\x9b\x17\xce⤺F*\x87\xd7=j\xecI\xc50\xe1M\x1ec\"H\xa9l7\xaf@pK\x95\x7f4\xb0\xe5\xda\xd8\xeeFS\x05\xae2\xa9ⰐÄ\xdd7^\xa0\xaa\xec\x19<\xf8\xd2\xce\xee\xd5g\v\xf6\x83\x17U\x01\xacPU\x82q\xf7\x0f\xd9\x17^4E\xd4\xc0\x81W\xc6mSOr\x19\x16\xab\x88K\xa5@\x9b\xca\xe2\rnI\x1deJ\x1a\x9e\xa3\xae\x8b\xfc\x9e\xb3\\\xd1\xc1\xdd2.\xaaX\xf9&\xf6,\rS\xe5\x17\xadϊR\xbf\xfa\x99\x9d\xac\xe1^\xbd\xf6\t\x94L\x82=; \xf0-p\v(3\xe2\vj\xaf\xb2\xdd\x12\x81\x18\x8e4\xc9b\x99\xa6\xe0\xe9AY\x15i\x04X\xb9\x93\xcd\xe5dR\xac;\xfcg\xc6\xc5%\xd8F\x92w\xfe\xd1\xf8\xa5\x9d\xfd\x9b\x1c\x8dF\xa9\xa4\x9b\xb0\r\xc2\x13\xb2\xfcX\x9f\x0ff-\x85\xaaN\x06\x14\xe8Jv5\xe2\x05Nƒ\xf8.\xec\xe2=\x037.y\x02cO\xf2\xf9\xdcv\xbd\x1d\x02qQo\x87\x16h\f\xdd9\xa9\x99\xfb\x1e\x002\x95\xb5\xe3\xec\xf6\xdeH\xcd\x02\xcfg\x83\x14\xa0b\xee\x93^d>\x83\x1f\xed[\x97F\xca\xe0Q얻.I\x9c\xedNH\xcf\x12\xfdX\xb5\xed\n+\x97\x14\xd4\a\\U\xf2E\xaaW\xb9r1\xa5\x99\xcd\xd67\x8b\x9f\xad8~K\xa5\xd1\x17\xaft\x11\xa8\xed\xef\x05\x94B2\x9b\x17\x05\xc6SR0\xa7\x86|\x17\xebȟ\xb3\xbb\x98Z\x7fbr\xa89\xde\xf9\xf6\xd3\xd4֦\xfb\xf8\xac\x8e\xff\xf0\xbaG\xbbG]\xf7\xb5\xae\\\voL\xad\xb6\xa5\xc9\xd6\x15nz\x9dH~jo\xca\xf7\xe0\x9dt?\xc5}eY\tqM\x82\xcd*a}'\xaa\xae\"B\x94\xd4\x03\xb4QJ ;\xed\x8aM)\xa2ϕ\xce\xfb\xed`M\xe9\xba\xee\aS\xf5\"\x11\f=/}\xd3g\xb7.ۯ\x81\xbb\xecO\xbd\xd3\x7fx\xa7XBy{\xa6\xa8=\xdd?7E\xaf\xa1\xd8t)\xd6\xca`\x18\x17\x1a+\x7f_\xe4\xb3X|-\xc39\x18\xf5\x17\xfb\x14\x8cL9\xe9\fp\x9a\x9b\xa2>Wcf<\xa6\\\xe8\x14\x86\x8c\x12A\xbc\xcd\xdcIT5`㲎ᴅFgn\xe03\xecU\x15鮚\xa0\xceL\xad}\xbc\xc2\x1e\xf2\xc9h\xd9\xe1\xf3\xba\xff\x8fU\xa1\xde\xee\x92'\x11\xec^\xf7M*\xc49\x172\xe7\a\x9eWL\xf4\x0eYG,Z\xe9\x01\xa5Ar\x11+\xb5\x91X\xd5\xf3{b\x04_K\x9fr_\xac\x8e\xa6=\xba\xb4\xb2\xfc\xd9\xc5\xf8~\xb1}\xc4H-\xcd>\xa7w\x1d\xa6\x97ۧ\xeb\xe3K\x8a\xec\xa7%\xf4Q\xa0\xf3\xa5\xf5\x14g|\xa6\x8c~F\xf1<\xb1q\xea\xcd9\xf6\x94\xf2\xf8YE\xf1\xd9ޢ\xc4Rx\xbf\xc8=\rrA\x01<\x898\xf3\xc5\xee\xc5%\xeePR\x9e\xc4#\xb9\xb0\x1d)YO\x02\x1e-gO\x15\xaa\xa7I\x1e)b\xa7\x97\xa7'A\xbb\xd2\xf5|Q\xfa\xfdZ\xcf\xde#\n\x18W5\xb3\x85\xe57E\t\t\xa5\xe3%\x05\xe3Y\x8a\x9dY\x1cn\x8a\xbf#\xeb.-\t\xf7K\xbe#@S\n\xc1#\x85\xde\x11\x88\x93\xe5\xdf\xd4\xf2\xee\b\xec\x19\xb3;)%\x13\x7f6\x81\xc5\xdfXYr\xb9\x1br>U>&ecP\x1d\xee\xae\xd9\x13\x8e\xae\xffߋ\x9cbK\xfaO.#QV\x9dW\xe3Ҫ5\xdc\xca\xe3\x00\xae\xeb\xf7\x8eF\x15\xfdorh[\xaf\\\x88\xee\x87'\x0el\x17T\xf8\x84\xcb\xc4c}\x1a8\xe63G\x99\xa2t\xcfߝ\v\xaa\xbe\x9e\f\xeff\xea\xa6\xfd\xe7\x98\xeb\xcc\xed\xfeL\xff\xb9\xa8\x84\xe5e\xf4\x10\x97Z\x1d\xb8\xcb\xfb\xed\xf1\xd8\xd0\xf3\xef\xca}\xf2\xb19:H_\x9f\x9a\xf3\xb5>\t\x05X\xecT\xbc\xa2\x10\xc0\xcc\x10\xfd\xcc\x7f\xf5\x98\xa9\x95\xfb\x90\x898Y\xcbC\xf8:\xf2ڝ\xc1X\f.\xeb\xef\xf1\n\x02㾜4\x91\x1cǨu\x99\xf6p\xbd3\xee\xde\xfdZ\xa1>\x82:\xb8Bopyfz\xaa\xbd\xa60\x14\x91պ+(@\xff\xad\xed\x89\xe7\xdfj\f\xb8\x95\xde\x06G\xc1\x9e\xec\xd1\xc1!\xa5\xd5F;\xa4\x9f)\x90\x19\x19\x1a\x85*U3;.\x0f\x93\xa6&\xb5!\xf9\xb2\xb1\xcf\xf2\xe8g\xd6\xef\xb8H\x04t~\f4\x012\xb5\xc18\xad(1\xdbP|\xa9Xh.\x1aJv\x03\xd3\x1a\x86/\xd1(\xbc\xa0AxAT\xb4,.J&SJ#\xf0E\xa2\xa3\v\xc6G\x97\x88\x90\u038b\x91f@\x9e4\xf8\xa6\xb4\xee&\xd5ђ\xab0)u\xb3\xf9\xd2\xedtKnB+nB}gn\xa7\t-\xb7\xcbZm\x13hx\xa1\xe8\xe9B\xf1\xd3%\"\xa8\xcb\xc6P\xb3QԬ\xe4L\xfe}v\x19\xa0.\x18?\xa8\x1c\x1f\x95\xb6s\x0e\xff\xe3\xe9\xf8H\x91\xae\x13\x04)\x91\x83\xac\x87\xc6j\x01\xe4\xcb\a?\xfe<\xa4\xe2\xf5\xb4\xb0\xfe\xe3\xf79|\x9e\x9a\x81ӈ\x90KZ\xc7g\x11\x9eR>\xee\x7fe\x99\xb8\x1fw\xd4\xfa\xbd\xc2\xcd\xe0\x1a\x017}\x88F\xefε\xf16!n\xea\xa9ijl\xcf\xe4\x8e\xc4G\xabj\xb7\xafEpLS\x8f\xe5C*w\xe5[\xe9N\xaa\xa9\x93\u05f6Ҳ\x93{\t\xe9\xec\xbc\xdd\xee\x14\xd0i\x12N\xf8\x99\xbam\x96kuƭ\xff\xea!&31ogd\xf2\b\xfd#\xfbg\xcd\x14f\x8e2\x9b\xee\xe0\xf3\x8d\xda|\xa2\xaf~\x8a\x18Q|\x1b\rx\x0e\xbe\xcd\xe4t|[\xafW\x1c[_j\t\xf2\xe3~\xf6;\x90ë\xf4sh\xe1g\x8e\x1d<\x87_d\xe7\x8b\xd8\x1d\xb2\r(\xc9\xc1tU\xddAN\x03j\xb7m\x19-L\xcf˜\v\xbaz\x83\xdf\xe6M\xbb\x85ɗ\xfe\xfdz\xc1\x87ƍ\xf9\x92\xe2\x0f\x7f?\x19~\xd2\x0eM\x9eq\v1\xf8\xb0\x11\xe2\xfc3\xdf֗Wo\x04\xfe\xcb`\xc4o\xdc\xd6\xfcʴ\xe4r7\x87\xfc/aX$\x1c\b\x10\"\x01A\x04\x89&DX\x14\x10ԛ\x1c\xb9\x9f\xb5\t\x12\xde\x10\x12D\xcd\xc9\xe0\xa5\x13\xe4\xbcC\xe4\xb0Rxӆ\xd2,ː\x94\xff\xc3\xe9\xd5\xec\x1f>\xb8\x1f\xf5\xdd\xeb\xeeg\xa6\xa4\xb7\x90\xe6\x06\xfe\xf3\xbf\xaej\x84\xbe\xd7W\xac\xd3\xcb\xff\v\x00\x00\xff\xffNKg\x8f\xc7^\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec=Ms\xdc:rw\xfd\x8a.\xe5\xf0\x92\x94f\xbc\xae\x1c\x92\xd2͑\xfd\x92\xa9ul\x95\xa5\xf5%\xc9\x01C\xf6h\xf0D\x02\\\x00\x1cy\xb2\xb5\xff=\x85\x06\xc0\xaf!Hp$彷\x11.\xb68@\xa3\xd1\xdd\xe8\x0f\xa0\x01\\\xacV\xab\vV\xf1\xef\xa84\x97\xe2\x1aX\xc5\xf1\x87Aa\xff\xd2\xeb\xc7\x7f\xd1k.\xdf\x1d\xde_\x15\\\x9b?\xf6>\x7f\xe6\xda\xd0OUQ+Vt\xfa\xa3\xaf\x9a\x8b\x87\xba`\xaa\xfd~\x01\xa03Y\xe15|\xb1]U,\xc3\xfc\x02\xc0\x0f\x8c\xba^\x01\xcbs\"\x15+n\x15\x17\x06Ս,\xea2\x90h\x059\xeaL\xf1\xca\x10)\xee\f3\xb5\x06\xb9\x03\xb3\xc7n?\xb6\xfc\xa2\xa5\xb8ef\x7f\rkM\xf5\xd6՞\xe9\xf0\xab#\x91\x03\xe0?\x99\xa3\xc5M\x1b\xc5\xc5\xc3Xo\x1f\xe0FI\x01\xf8\xa3R\xa8-ʐ\x13g\xc5\x03<\xedQ\x80\x91\xa0jA\xa8\xfc+\xcb\x1e\xebj\x04\x91\n\xb3\xf5\x00O\x8fI\xff\xe3\x1c.\xf7{\x84\x82i\x03\x86\x97\b\xccw\bOL\x13\x0e;\xa9\xc0칞\xa7\x89\x05\xd2\xc3֡\xf3y\xf8\xd9!\x943\x83\x1e\x9d\x0e\xa8 \xd5\xeb\x13\x89\xec\xc1\xfc\xf0\x80\t\xc0\x88D\x15\xab5\tG\xdb\xfa\xb6\xfb\xc9\x01\xd8JY \x13\x17m\xa5\xc3{'{\xd9\x1eKv\xed+\xcb\nŇ\xdb\xcd\xf7\x7f\xba\xeb}\x86\x81,yJ\x01\xd7\xc0\xe0;M\fP~\n\x83\xd93\x03\n-\xe7Q\x18[\xa3R\xb8\n\xd4\xcd\x1b\x90\x00RA\x85\x8a˜g\x81+\xd4X\xefe]\xe4\xb0EˠuӠR\xb2Bex\x98z\xaetTM\xe7\xeb\x00\xe3\x9f\xec\xa0\\-'\x89\xa8I\xf8\xfc\x84\xc2\xdc\xd3\xc1\xcd\x0f\xae[\xfc\x89I=\xc0`+1\x01r\xfb\vff\rw\xa8,\x98\x80u&\xc5\x01\x95\xa5@&\x1f\x04\xff\x9f\x06\xb6\xb6RoH\x18\rz}\xd0\x16\x9a\xc0\x82\x15p`E\x8dW\xc0D\x0e%;\x82B\xdb\vԢ\x03\x8f\xaa\xe85\xfc\x87T\b\\\xec\xe45썩\xf4\xf5\xbbw\x0f\xdc\x04\x15\x9bɲ\xac\x057\xc7w\xa4-\xf9\xb66R\xe9w9\x1e\xb0x\xa7\xf9Ê\xa9l\xcf\rf\xa6V\xf8\x8eU|E\xa8\vR\xb3\xeb2\xff\xbb\xc0Q\xfdS\x0fד\xf9\xe6\n)\xc2\t\x0eX\x8d\xe8\x04\xc65u\xa3h\tm?Y\xea|\xfbtw\xdf\x15&\xae\x87\xd4'\xbaw$\xace\x81%\x18\x17;\xf43z\xa7dI0Q\xe4\x95\xe4\xc2\xd0\x1fY\xc1Q\fɯ\xebmɍ\xe5\xfb\x9fk\xd4\xc6\xf2j\r7dw\xac\x1c֕\x9d\x81\xf9\x1a6\x02nX\x89\xc5\r\xd3\xf8\xea\f\xb0\x94\xd6+K\xd84\x16tM氲\xa3Z\xe7\x87`\xde\"\xfc\ns\xfc\xae¬7el;\xbe\xe3\x19M\fҞ\x8d\n\x18hPW\xc6g-\xfdBjj\xf8u\x80\x87\xd3e\xa1W\xd4\xd6~\x98=q\xb85cV\xae\x1c4\xabS\x84\x1crwL\vv(\xe1\xa1\xcc`\xd2\xd7z\xa9\xf6\xed\x04&xU\xb7\x8e\xe0x\xc2U\xfa\t\xcbʪ\x8d\x19\x14\xef}5\x8b\xa2\xa5O\u07b8S\xc1\xf0\a5+\xbdv\x85\x13\xe5F\xdd\xed\xd1\xf2\xed\xc0s\xaf\xbdN\xb8\n\x93\x9c\xb5%\xd3\xfcN\xb0J辰6N\xd6f\xac\xd6`\x007w\x9bA\xa3\x0e\xe7-VdÉ\xd1F\xc2\x13㧜v\xc5\xca\xe5\xcd\xdd\x06\xbe[\x97\b\x03Lp\x96\x1cL\xad\x04\xa9\xe3o\xc8\xf2\xe3\xbd\xfc\x93F\xc8k\xd2J\xc1._E\x00oqg'\xbdB\v\xc36@\xa5\xec\x1cЄ\x9a\xac͚\x1c\x8e\x1cw\xac.\x8cWr\\\xc3\xfb?@\xc9Em\xf0\x94\xef0\xcd{G$\x02\xe7F\xa3\xef\xe5\xcf\xda12\x81\xa4\x1f#MG\xa6T%s8P\xbd\x18Uy\x81\xa0\x8f\xda`\t[\x0f\xa5\xb1\xd5\xc4\x15\xd2\aE\xe1\xc1h\xd8\x1e\x03\xee\xe3\xe3\xb6^8\xdb\x16x\rF\xd5\xe3\xddNM\xdd1\xda|Cmx\x96@\x99\xcb!i\\\xcb\x11\xc2(\xfa!B\x94\x01\x05\xac\x91g\x8f\xd6\xd1\xf4\x14\xb2\xdeBQt\x88;O\x15\x80\xff\x12\xf0\xd1\x1a\xb8̚\x9dko\xce8\x16dB\x85\x84B\x8a\aT\xaeG\xeb*<\xf1\xa2\xa0)\x8d\xa5<\xf4\x9c\xacn\xb1\xb6Eaa\x8d$\xecjkv\xd6`e?*#\\h\x83,__\xbe\x16\xf3\xf0GV\xd49\xe67E\xad\r\xaa;\x1b\xf4\xe4!\x1a\x1c\xd5+\x03&~\x9a\x04\xe0\x1d\x8e\x82gh\xf9\x90\xb9J+\x8a\xadbDj}\x8fc\x85.ԳL\xf5\x98\xb6N\x85c\xef\x1a6;\xd0hl\x95\xcb\x7f\xbc\x8c\xa9\r+\x01\xfd\xde\xfb\xfdh`\n\x1bj\xf4tH\x04b\xa3Y\xb0\xac\xccq\\\x8e\xb8\xc12B\xc4Y\x95\xb3\x80\xbdL)v\x9c`n\x13Þ\xcf\xde\x18\x88\x01\x83E\xa8\xf6+\xb1x\xd8\xff\xffG&\x9f\xc5VMK:\x8c\v\xcb\u0382k\xd3\xe3\xe60\x04h0\xb3Ѣ\xa5\xa9uӹp0\xadr\xeb0\xef\xb7L\xb3sfBL\xf4\x1bI\xf3\xe2\xbcg1\xa1\xfa\x1d\x12l/\xe5c\n\x91\xfe\xdd\xd6kCC\xc8hu\x11\xb6\xb8g\a.\x95\x1e\xae/\xe0\x0f\xccj\x13\xd5\x13\xcc@\xcew;T\x16\x16-\x895+hSĚv\x8c\xa1\xa3\x80\xa2\x15\x06\xe3j\x99n\x99GԈ\r\x85\x02\x90(T ĭ\xdfJ\xd6=\xe7\a\x9e\u05ec C\xcfD\xe6\xc6\xc7\x1a\xfcb\xeeɌ@\x9c\xe0\xef܉0\n˥^\\)\x05\xda\xc0\xad\x94*\xe6y\xbar\n&N\x86-\xa3p0\x16\x84\xb5E\xd5\x05j\x8f\x8as`[\xbds\xd5r\xca-\xc9\x14l\x8b\x05h,03R\xc5ɓ\"\x04\xae\xa4\xea\xcf\beG4i?b\x9aU\xa2m\xb1!՞g{\xe7nZ)#X\x90KԤ1XU\x15\x11+ԖY\xc9\xf0\x9d\xcd)\x8d\xb6$\xa8\x8f!ܘ\"iK\xa2\x0enˌ6\xeeS\xbd\x11\x9b7\xa2\xf7\xd0\x14\xcf\x12\xf6\xcdI\xf3\x97\x17vKn\x8e\x9a\x9c>\U000bab80\x9b\xf05\x05j\xcf\x0f\xd4\x7fc\x8c;o\xb6l\x86\xad_|\xb6\xbc\b\xd7\x1a4\xfeF\x98F\xc6\xea\xce۪E\f\xfb\xdcmy\x05|\xd70,\xbf\x82\x1d/\f\x92/5\x87h\xc7љ\xe5\xdcK\x12(\xd5\xf6\xdaR2\x93\xed?5\v\xb9\t-\x06\xb4\x1a\x02p~y\x88a\x88\a\t \xa1q*h\x0f\x82+,\xdd\xde\xc6=͏\xf6\vy\x80\x1f\xbe|\xc4|\x8ed\x90.\xa9'\x83\xfa0\xf0t\xba(\xd0\x00\x93@v\x06EnZ\x13\xe3\xb9\x1d\xac+`\xf0\x88G\xe7Y\x8d.\x0f\x8d\x15\xcbZրTH\xdbi\xa4F\x1e\xf1H\xa0\xfc\xfeX\x12\xbc%\xa2\xe2\xca#\x1eS\xab\x0e\x88j\xf1\xf3+\xf3\x8e\xba\xf6\x03\x8d\"e*\xb5\xa5!\xaa\x9f;`d\xda`a\x99R\n%P\xfc\xcca7\f\xebm\n?\xe2\xf1'\xed\xd8gg͞W\v(`\x156-\xc9\xc8]\xb3\x1b\xfa\x9d\x15\bgf\x87\xb8\x12,\xabC\xda\xe0hJu\xdaX(\x06B\xc8\x06B\xa4}\x8a/\xbd\xe4@\xe2k\x84J/\x11,%\xb9\x15\xaf\x110\xbdVȴ4hZ\x92\x8a\x91t\xa0\xf05B\xa7%\xc1\xd3\"\x0f0\xfd\xc0\xe0k\x1d\x14|\x85 \xea\xec0j\x11\xe9R\x0f\x02.\x0e\xa6\x12\xc67s\xf0\xef\xc4\xe3J\x00\x19=\xf07\x1eP%@<9\xe87\x1bR\xa5̃a\xd0\xf5\xecc{\xc9iI\x8b\xf6\xc6SS\x8aҶ\xad\xe7\x8f\xe3%\x1e\xc3K\xdc\xd4N\xc1>\xf1\xb8\xdd\xf2cv\x89t>3ؚ\xec:\xf18ݢp\xeb̀k\x12\xe2\xd4\xf1\xb9\xe9\x90kz9mxl\xee\fw\"A\xc2f\xab<{\x9bD\xaa\x1c\xd5\xec\x8e\xd3\x12ќ\x15\xcaAL\xd4\xef\x7f\xb0\xd7\x12\xee\x1b\xb5\xb5\xba\xbbY1\xee\xc8\xe6V\x8f\f\xfeȅ\xdfG\xb7B\xd8\xf1/z[b\xad\xf3\x13\xdfqi=N\x7fQ\xb7\xdbK\xd3X1E;\xecۣK\xd7\xd1k\xf8IJ}\xd3C\x04$\xf5\xbbg\x1avR\x95\xcc\xc0e\xb3I\xf9\xceu`\xff\xbe\\\x03\xfc,\x9bĎέ]\x11\xa8\x9a\x97Uq\xb4\xd1\x0f\\v\xc1\a\xa7\xe9#ų\x87\x89_\x01\xa7i\xf7gET\x8c\xfc\x14M\x94\x9b\x99\xe1\xda_1\xee\xefPN\x98\xe2w\xfd\x16cW\xdc\xfb\xab\xa4\x03\xec\tEn\xe5\xf3\xf6;\xc5I\x8dn\xf0\xe2죠\xb0\xc65\xb8\xf53\x022vG=\xbcL\x1a\x9b6R\xb1\a\xfc,\xdd3\x02)\xd4\xea\xb7\xe8\xbd$ὐ\x90\xd4\xea\x05+\xa6\xb9\xfc؆\x00\xdb\\\xf7\x93\x9b\xcd-\xb6\xb1y;#\x8b\xc6\x14\t\x83\xbb\xbf\xff\xec\x06dx\x89돵Kt\xb0JF\xa3\xa5t\x18\xa8k\xb4\x8dOŽ|\xa2\xab\xc1\xbbw\xfdw^SAʭ\xa7\xecųFs\xe8ݦ\x1fH\x97\"\xec\xdf\xc7[v\x1c\x86\x0e\x13\xa72\x99\xe4.\n\x8bi-3N>\x06-\xf6RJ\xfb\xeb]j;\xe5+N(\x8bZ\xe3\xd7'\x81\xea[\x98\xa8z#b\x97\xf9\xf7H\xf8\xa7\x93\x86ы\xfc\x8d$\xcffP}̣\x15\x9e@\xda=|\x10V\xad\xb9n\x9e\xbb8%\xdd\xcc\xfc\x8f\xcf\xfdq\xb5\xbc\x1a\x7fab\xd5\xed\x05qp\x9di;\xacc\x9b\xf1\xb8K\xf9\xf4\xc8v\xb2m\x93\x94]\x90\xf0-\xf6\xfc\xf9\xcdo\xfed\xaa\xba\xae+\xe5\xe9\t9\x90\xb3-(O\xf8\xa7\xa0M_\xa1y\xfe.4\xe4V\xc7\xcf\xd53ٮ\x85u\f\xe2\xfaG\f.\xb2\xc6\x1fpG\x96\x84\x9c\xadz\x14\xd5)Qm\x05\xa0\xacu\xa2\xd2uH\x9f\x00\xdaYag\fr\xbdG\xdb<\xc7-n#\x99\x0e9\x1b\x1f]\x1f?5\xdf6\x9f*\x00͘տP\x8fAT\xef[\xb0ј\n\xc0\xaa\x1e[\b\xc8II\x94\xc4\xc0\xf8G\xc4 \xa19\xa2Av\r\xb9*x\xd4\xc9\xf1\x9e]\xf4-\x9c\x1f\x8a\xfe\x00\xaa\x04\xb4ɦ6\xd9\xd4c1\x95_\r\x05\xf9\xe9\x96\xc4\xcf4Hy\x13Y\x99e@Y \x1c\x1c\xcb\xfd\xd9i\r!py!\xbb\x8fF\xf1\xa2r\x05\x10\xb4\xf3\xd8B\xd6\xf5JcW\x01\fLe[\xf5\xc0\xc5\xf1s1\xa7\x0fث\xe2\x04\xc0y\xb4\xdf?\xdc=}\xb3\xb9\xba\x06\xe80h&/\x99\xef\x85Ȁ\x02(\x18P\x808PZc\b\xa0#3Z\x81\x82\x12\xc8\xee\x1c\xf79G\xaf\xa6\x01\xd4\xd6E\x019 d\x05\xd9*\x03Ge\"~\r\xcavЫ\x130&/\x10텽,\x12\x1a\xf8\xc51f2[8\x88\xf8ЮV{\x92\xb1\xeb\xb4\xeb\xfbhIN\xab\xdc@\xb4\x8d\xe28\xac:<\xa2Y\x05\xda\u05ca\xf5\x81\x04\xb5Dƕ\xf2Tg\xe86w^\xd3w_\xf1Ч\xe1\xe3\x15V9\xa5\xca\n\xc2d\xf7\x17\x0f\xb9!\xfe!\x03\xa9\x1dJ}\x14\xd5\x12ř\xe8t\x95\xd8y\xfcq\xf3\x05F\xd79\x19S\xf63\xefg\xc5pNA\"\x8c\xec\x0e\xb9$qǮ\xcf6\xd1vޑ-ե\r\xa1\x9d\xd2\x1f\xe2\xb6'\tc\xed\xa6\\5\xb0Σ\b\xb6\b\xd1wJ\xb0k\xe0\xce\xc2Z\xf5h\xd6*\xe0\xff\x9e\x80\xc4t\xa8\x13\xb1\xefK\xc1\xe5\x14\x9d\n\x17\xd6.\x1e\xc61w#_\vݽ\xf1\xa8S\x06\x13\x89I\x9bv\xa4s{\xc0\xce1\xa8%\x95\xe6]H\xb2ƿ\xc42L\x92\x82f2_R\x7f\xbe\x8dfy\x9c䗃\n8\xbd\x9c`zH2S\xff\x86v\xa8O\xda`1Q\xa6\t\xbe\r%\x1d\xb4\xb1\x9f\xfb\xac\xe1\x1e_\x16n\x1fإɚ\xe7\xfa\xf5\xb9Q\x1bP\xfe7{\xb2\xb3p\xa7\x91\x15\xa9\xfc\x0f\xbb\x1c\xd5\x17\x03z0\x04\x1c\xadM};\x9b\x90\x19\xc8t\x92\xcfdH\xb0_@\xb3\x88\xe7\xce\xee\\\xde\x04Tr\xac\xa4\xf4\x13\x0e\xc9\x1e\xfc\x14\\\v\x06o纜\xf9\xf0z\x17\xa1\xe5\xe4?\xe9\x7fSN\xe3\x86\x18\x17}\xd7\x19\xd5\xe2C\xf2\xb8\xc4\xf8r\x7f\r(\xa31jk\xb0\x05\xe18\xd7.\xba\x8aY\x9d\xa6U3\x96\xday\x9fz\xa3\x80f\n\xa9O^\x0ehou\x03\xbc\xa8锿\xf2\f\xdb\xd3-\xd5\xf5\xebr8o\xa9R\xba-\xa4\xd9]\v-p\xf6.R\x16\xb3WJzq\xf3\x98\x11\xb2\xb9\x94\x1dg\xc6Uk\x8c\x8b\xc8<\x86\x9b\x10\x16\x93=\xbb\xcc滋\xf0\x828V\xfb1\xe0\xf3\xe8M\x9b\x9a\x17\xec\xee\xa7+\xee\x87\x0fW\xbbj\xfe\xd4\xcevT6t\xf8\xf5\xb7\xaaX\xc5\xeei\\0\xd3\xe5\xdf\x01\x00\x00\xff\xff\xfb\xb1p\x12\x1b\f\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WO\x8f۶\x13\xbd\xfbS\f\x92\xc3^\"9\xc1\xef\xf0+t)\x82M\x0fA\xf3g\x11o\xf7R\xf4@\x93#\x8b]\x8aTgHm\xddO_\f)\xad\xbd\xb6\x9cl\x8aV\x17C\x149|\xf3\u07bc!\xbd\xaa\xaaj\xa5\x06{\x87\xc46\xf8\x06\xd4`\xf1ψ^\u07b8\xbe\xff\x81k\x1b\xd6\xe3\x9bս\xf5\xa6\x81\xeb\xc41\xf4_\x90C\"\x8dﰵ\xdeF\x1b\xfc\xaaǨ\x8c\x8a\xaaY\x01(\xefCT2\xcc\xf2\n\xa0\x83\x8f\x14\x9cC\xaav\xe8\xeb\xfb\xb4\xc5m\xb2\xce \xe5\xe0\xf3\xd6\xe3\xeb\xfa\xff\xf5\xeb\x15\x80&\xcc\xcbom\x8f\x1cU?4\xe0\x93s+\x00\xafzl`\f.\xf5\xc8^\r܅\xe8\x82.\x9b\xd5#:\xa4P۰\xe2\x01\xb5콣\x90\x86\x06\x0e\x1fJ\x88\tW\xc9\xe9.G\xdbL\xd1>L\xd1\xf2\x04g9\xfe\xfc\x95I\x1f,\xc7\xe8\xcdK\x9a,\xcaWO\xb0ƽT\x11G\xb2~w\xf4!\x1b\xe1+\n\x88\aJ!\x94\xa5%\x8b\x03\xd12$\xec|\xf9is\v\xf3\xd6Y\x8cS\xf63\uf1c5|\x90@\b\xb3\xbeE*\"\xb6\x14\xfa\x1c\x13\xbd\x19\x82\xf51\xbfhgџ\xd2\xcfi\xdb\xdb(\xba\xff\x91\x90\xa3hU\xc3u\xeeB\xb0EH\x83Q\x11M\r\xef=\\\xab\x1eݵb\xfc\xcf\x05\x10\xa6\xb9\x12b\x9f'\xc1q\x03=\x9d\\X;6\xd8\xd4\xde.\xe8\xb5\xec\xe4̀\xfa\x89\x81$\x8am\xed\xe4\xec6\xd0\t\xafj\xf6\xf9r\xbc\xfa\xc9\xf4e\x83C\xe9\xfe\xadݝ\x8e\x02(c\xf2١\xdc\xcdŵ_!l!\xef뼓\x14j\x1bH\x10\x8d\xd6 Us\x9e\x13\x92DS\xc2\x16\x9d\xe1\xfa,\xe4\x05\xces*\x84F4V\xee\x1c\xe8S$\x8f\x13\xf3᧬/\x94\x1f\x02\xe4ң~\xea\xb1>\xa27\xb9\xa9\x9f\xa1\t\xb9\x86\x19\r<\xd8\xd8\x15s\xb8\xe3C\xeay*\xc8s\x8f\xfb\xa5\xe1\x13\xec\xb7\x1d\xca\xcc\xd2N\x11\x185a\x14\x1c\x8cN\xcc+ά\x01>&\xce\xf6R\x8b\x11AZ\x845\xf3\xea{ܟ\x13\r\xdf\x12w:\xef\xbf\r\xf9J\xce\xc5\x190a\x8b\x84>.Z\\\xee\x1e\xe41bv\xb9\t\x9a\xc5\xe0\x1a\x87\xc8\xeb0\"\x8d\x16\x1f\xd6\x0f\x81\xee\xad\xdfUBxU\n\x81\xd7\xf9ް~\x99\x7f.\xa4|\xfb\xf9\xdd\xe7\x06\xde\x1a\x03!vH\xa2Z\x9b\xdc\\hG\xa7ݫ\xdcq_A\xb2\xe6ǫ\x7f\xc2K\x18\x8as\x9e\xc1\xcd&W\xff^N\xee\fJ(\xda\x14U\x02\x81\xf4M\x11\xbb\x9f\xd4,\xfda\xa9\x10gL\xdb\x10\x1c\xaa\xf3ғ\xeek\t\xcd9\xa4Jv\xf8\x1e\x9b\xcd\xce\xfd\x86\xc9n\xa6ibx\xc9j^6\x17B\xb9\x97\xe4[\x8a\xda\xe1%\xa3/p\xbc\x9cJ\xf5\xb8\xc1\xb3ZtT1\xf1\xf77\xe9\xbcl\x9a\xb9\x9d\x1a\xb5N$\x05=\xc5\\\xb8\xd0\xfc;\x8dz\xe8\x14/\xb8\xed\x19\xa8od\xe5,\x83\xb3-\xea\xbdvX\x02Bh\x17\xaa\xe9\xbb ˃>\xf5K\xa5\xf5vT֩\xadÅo\xbfxu\xf1\xebE\xf1\x17\xf5<\x1bd\xb9\xb5\x98\x06\"\xa5\x12{\xaa\xb2i䠾\xd2\xd2\\\xd0|:\xfd\xdb\xf1\xe2œ\x7f\x0e\xf9U\a_\xceDn\xe0\xd7\xdfV%*\x9a\xbb\xf9\xa2/\x83\x7f\a\x00\x00\xff\xff\xe4\xf3S\x85\xb2\r\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VAo\xe46\x0f\xbdϯ \xf6;\xec\xe5\xb3g\xb7\xbd\x14\xbem\xd3\x16\b\x9a\x04A\x12\xe4Nۜ\x19mdI\x95\xa8I\xa7E\xff{A\xc9\xcexl'\xb3Y\xa0\xbaY\xa2\x1e\xc9G>ZEQ\xacЩG\xf2AYS\x01:E\x7f2\x19\xf9\n\xe5\xd3O\xa1Tv\xbd\xff\xbczR\xa6\xad\xe0\"\x06\xb6\xdd\x1d\x05\x1b}C\xbf\xd0F\x19\xc5ʚUG\x8c-2V+\x004\xc62\xcav\x90O\x80\xc6\x1a\xf6Vk\xf2ŖL\xf9\x14k\xaa\xa3\xd2-\xf9\x04>\xb8\xde\x7f*?\xffP~Z\x01\x18쨂\x1a\x9b\xa7\xe8<9\x1b\x14[\xaf(\x94{\xd2\xe4m\xa9\xec*8j\x04}\xebmt\x15\x1c\x0f\xf2\xed\xdes\x8e\xfa\xe7\x04t7\x00\x1dґV\x81\x7f_<\xbeR\x81\x93\x89\xd3ѣ^\n$\x1d\ae\xb6Q\xa3\x9f\x19\x88\x83\xd0XG\x15\xdcH,\x0e\x1bjW\x00}\xa6)\xb6\x02\xb0m\x13w\xa8o\xbd2L\xfe\xc2\xea\xd8\r\x9c\x15\xf05Xs\x8b\xbc\xab\xa0\x1c\xd8-\x1bO\x89\xd8\a\xd5Q`\xec\\\xb2\x1d\b\xfb\xb2\xa5\xfe\x9b\x0f\xe2\xbcE\xa69\x980W\x1ec}88:A9\x12\x01\xa3\xb3\x8c\x18\xd8+\xb3]\x1d\x8d\xf7\x9f3\x15͎:\xacz[\xeb\xc8|\xb9\xbd|\xfc\xf1\xfed\x1b\xc0y\xebȳ\x1aʓר\xfdF\xbb\x00-\x85\xc6+ǩ9>\n`\xb6\x82V\xfa\x8e\x02\xf0\x8e\x06N\xa9\xedc\x00\xbb\x01ީ\x00\x9e\x9c\xa7@&w\xe2\t0\x88\x11\x1a\xb0\xf5Wj\xb8\x84{\xf2\x02\x03ag\xa3n\xa5]\xf7\xe4\x19<5vk\xd4_/\xd8\x01\xd8&\xa7\x1a\x99\xfa\x1e9\xaeTC\x83\x1a\xf6\xa8#\xfd\x1fд\xd0\xe1\x01<\x89\x17\x88f\x84\x97LB\t\xd7\xd6\x13(\xb3\xb1\x15\xec\x98]\xa8\xd6\xeb\xad\xe2Av\x8d\xed\xbah\x14\x1f\xd6IA\xaa\x8el}X\xb7\xb4'\xbd\x0ej[\xa0ov\x8a\xa9\xe1\xe8i\x8dN\x15)t\x93\xa4Wv\xed\xff|/\xd4\xf0\xf1$\xd6Y-\xf3Jby\xa3\x02\xa2\x16P\x01\xb0\xbf\x9a\xb38\x12-[\xc2\xceݯ\xf7\x0f0\xb8NŘ\xb2\x9fx?^\f\xc7\x12\ba\xcal\xc8\xe7\"n\xbc\xed\x12&\x99\xd6Ye8}4Z\x91\x99\xd2\x1fb\xdd)\x96\xba\xff\x11)\xb0Ԫ\x84\x8b4\x8b\xa0&\x88N\xd4Жpi\xe0\x02;\xd2\x17\x18\xe8?/\x800\x1d\n!\xf6\xdbJ0\x1e\xa3S\xe3\xcc\xda\xe8`\x18\x81\xaf\xd4k:\xd6\xee\x1d5R>aP\xae\xaa\x8dj\x926`c=\xe0̾<\x81^\x96\xae\xac<\xfc\xee\xd9z\xdcҕ͘S\xa3\xc5\xd8&w\x86\xe0d\xb2d\x19Ӳ\xe1\f\x1b\x80w\xc8#\xfd2*\xf32\x06\x16\xf3y\xa3\b\xa9\x10(r6h\x1a\xfa-u\x94i\x0egr\xba^\xb8\")\xed\xec3\xd8\r\x93\x19\x83\xf6\xb1.dR\x13\xf8h\xde\x15\xec\xe90?\x13\xe6݉1(\xd3J\x1b\xf4\xd3T\x9c\f\xd4K]ɴ\xe0O\xff\x9b\xe3E&vsw\x05\xfdy\xf3\xf6_7\x7f~E\x88\xa0\x05ܒ\x1d͞\xaaRoN\xc0A\xc9\r\x93\xaft\t\x99\x05yP\xb2*oI\xf3\x83\xeb\xe2\x87s\xa8\xfe\x05{\xe3\vδ\xf9\xa9\xf5\xf2g\xa6\r\xfeP\xf2JQ^\x8f\x84\xef4\x13\x87\x8aS\x15\u07be\"Dg\xb2\x84[\xf2\xc9\x0eQ\xd2\f\xf2W\x84x\xacqȵG\xf8\xf4\xd6AȎPP\x87\v!\xb2\x04\xf1\xee~\xfb\xe5\xdf\x1e:\xaf\t\xc9Ag\x8a\x95\x06\xe7\xee\x10#L\x13J\xbeഈ\xf2T&\xe6H\rQP*\xd0 \x8c&\xe6\b$\xa3\xa5\xa9\x14\x10\xb9'?U;P\x02\f\xe8\x1a4!\x19\xaf\xb4\x01E\xb4\xa1\x06\b5\x84\x92R2a\b\x13İ\x02\xc8\x1f\xde\xddo\x89\xdc\xfd\x06\x99ф\x8a\x9cP\xadeƨ\x81\x9c\x9c$\xaf\np}\xff\xb8\xa9\xa1\x96J\x96\xa0\f\vtvOKxZo{\xd3{m)\xe0Z\x91\xdcJ\r\xb8ix*B\xee\x89f\xe7c\x8eL7\xd3E9\xea\x00&\xb6\x11\x15\x1e\xf9\ry\x00e\xc1\x10}\x94\x15ϭ\xb0\x9d@Y\x82e\xf2 \xd8?jؚ\x18\x89\x83rj\xc0\v@\xf30a@\t\xcaɉ\xf2\nn\x90$\x05=\x13\x05v\x14R\x89\x16\x93\x96r\x88\xfe؛G\xc3H+\xa98\xf31ԭ#зh᱈Z\x0f\x17-g\xceN,\xaf(G#JE\xe6\xe6Ck\xbcb\xc6x\x82\xc9\x03\x9c\x9d\x89\x0e\x98[Nt\xc2\x11)\xc0\xba\xa0\x85\x8d\xc1\x86McF\xc6=c\xd3\xdeQ\xebgH'\xa2\xaa\xe2\xa0\xfdPαkt\xc0\xcd(\xe8\x9a#.~\xe7t\a\x9ch\xe0\x90\x19\xa9\xe2\xe4\x98c\xb2{R\xf4\xda\b\x15#\x1a\xae\x1b\x104\x13\x9b\x00I0\xa8:\xb2\xec\xe8\xdc4+A\b\x87\xe4\x124\xaerZ\x96\x055\xd9\xf1\xc3W\xeby\xe9&\x97\x93H\x97~g\xe7\xbf\x06\x7f\xbek\x98g\xe0\x12\xdc\x14f\n\n\xb7\xd9\xfc\x88\xd4lޠG\xf5\xee\xd3\xfb\xd8nV\xf7I\x90\xbc\xc1D\xde\xf5\x90m\x0f\xed\x9d\xf2\xd4ixק\x8eo\\\x1a\xe1\x86P\xf2\x04g\xe7\xb1PA,s\xa8\x1dh$\xd2\x19\x12\a\xf3\x19(dOpF0>A1\xdb;U\x14\xdc\xf3\x04\xe7\x94f=\x02Z\x9c\x98\xf6\x89\x17KI\xfb\x02\t\x81\xfb\xd9\xe9\xc4#\x98l\n\xbah~r$]\x91\x84'\xd0\xfe\x82i\xd6lk%ꐱ\xaf\xb5c\x91]\x05GV&NԚ9\xdcJ\x90\xfb:\xdd\xf4\x85r\x96\xd7\x039\xb9ߊqo\xb8\xfb|\x92f+nȇ\xafL\xfb\x8c\xdf{\t\xfa\x934\xf8\xe6E\xc8\xe9\x10\xbf\x80\x98\xae#./\xe1Զ\xa5C;o\x95 \xdc\xeeٺ\b\xaff\x0f\xd3d+l\xdc\xe2\xe9\x81YH7ܴ}\xe8>E\xa511%\xa4X\xbb\xad\x97\xd8H\x8e؉ \xa5\xeapd\x88Z=\xe8\xc8^O\xfcy\xb4\x96\xc4\xf5wyUN3\xc8C^\x05\xb3\x81\xd4\xc0\x81e\xa4\x00u\x982\x1c\xed\xa7\xb4\xfa=\r\x85D\xad랅\x12\x96f\xda\xc3\xe3Uwt\xf3\xbb\xfb\xac\xed\xcaMh\x15\x98=\xdbt$\t8\xdet~Fhb\xd1\xff\x98\xa5.\xcds,Ӡ\xfc~\x81\xc6_\xc0\x8b\xa1\xedw\x889\vYPLN\xfc\xb75s(\xd0\xffCJ\xcaT\xc2\x1a~\x87\xe5\x18\x1c:}\xfd.V{\x18;\x02\xd3\xc4\xf2\xf7D\xf90\xbd\x1c\x99\x9c\xb4\xba\x05\xb83\xe4r?\xf0Xn\xc8\xf3QjgS1)2\v\x92i\xb2z\x82\xb3OƵ\xf5\xc0j+V\xce\xc0/V7\xb5\xb7 \x05?\x93\x15\xf6]}\x8b\x13\x94(\x89\x89;\xae\x9f\xea\xf2\x93uA˵\x97^#\v\x96\x8d\xf6\xc3r\x99T\x17\xdbƠ\xc1\x83\xb0\x1d\xeb\x1a\x11\xeb\x1eO\xcd6I~K\xa9#\x99\xef\x11T\xee\xa56nG\xb2\xe3\xce.\xd9\xfd\"N\xf6\xfc\xae\x17\xa1{W\xa5#U\xa8\xbf\xb0겷Qk\xb9\xad\xa75\xb3\xcb\x19\xf8\x9d4\a\xd4\x06d\xabf\xe5;=\xbcr9\v\x1c\x84f\xe8\x94\xcc\xc2-\x95\xcc@G\xb3\xc5͓\xa0\xe5g6\x17\xeb\x8dE\xea\x02\x1fW\xdc0\xbd\x99\x19\x9etG\xd6\x12ia\b\xf0\xe1kk\xd7\xd3*\r\xfb\xf7\x9c\xf0-ŋ\xe0Z/\nگ\xe2IB\xf1\xce\xf5\f\xcb\xc4\x03r!\x85:T\xa8\"\xd2=O/H߃y/\x98\xd8\xe2\x00\xe4\xed\xd5݁Z\xb9\xc6j9bO\x8f\xe4\xbeoC\xf4\xfa\x85\x18)\xe6\x88=\xa5\xc4\x1d\x7f\x05\x1d\xce\r\xf7ǭ\x83\x99\bRH\xd3ކ\xb0pK\x99\xbf\xd6dϔ6mDS\x85\"^+\x12{\x96F\\\xe2\x83R\x17\x05\\\xbf\xba\x9e\xad\r\xb0\xa3|\x0e\xb5P\xa3\xc5\x13\xb1\a\x93I@؞0C@d\xb2\x12\xb8mc\x97:\x0e\xe1X\xe0\x14t2\xc9\xd2\x14\x84}@TE\x1a\x01\xd6(uLL\xeeﴛ\x7f\xa4,\x96\x81\x1e>\v\xd9f\xc6J\xc6bO\x87m\xa1v\xac]\xd4VЯ\xac\xa8\nB\vK\xfa\xd4pi\xef*\xce:\x1c\xaf\xeb\xce\x10.\x9a\x11#\xed\xa2*9\x98\xd4\x15\xe9*\xcc\xec2\xd1,\x87\xda0{)\x90\x82P\xb2\xa7\x8c\x8f\x94\xbb\f\x9fE\xb4]\x12\xa3xeq\xbd\xe0#m\xf05\x92\"a\x037\xd1ɜ\xd6֥Jw\x15\xef\x15\xa4\xb9gs\x9b\xd9\xc1=+\x15\x93X\xa7we\x0f͋\x18\x15\xe7\x1f.\xda\xe0\xf9\xe1\xa2\xcd3ś\xdfP/\x1f\x06\xbd\xa8^~;\xd9\xf9J\xf5\xf2\x1eþ\xd7}\xa5j\xf90\xffe\xd5\xf27\xbeT\xa3\x00\x1a\xb6\xe7].>\x1f\x1b\xb27\xda\x00\xf0\xef\\\x7f;\xa8\x0f\xbb\x8c\xf1/^m?\xc2\xfc\xc4\xc2\xf8՟V\xdf\x1f\xa5\x17\xd3v\x94\x9a\x032E&\x15\x8e\xf9ڸ\xb2]\xdc\xd5-\xa4\xfb>\x85s\xa94\xa6V\xccO\xd1k\xa8eZ\x04\xfb^\x17\xb3\x81\xe2\xd7\xd2ۊ\xb4\x13\x9d\xdbH\x97\xb93\x9d\x91\xf9\xe0&\x80>\x8b쨤\x90\x95\xf6\xfb\x06\x16\xfa;ܾ\xf0\xa9P,\x03KT\xb0o\xc9QV\x91\x8a\xed\t\xda\xcd\xd4\xef\x8dW\xed\xf9\x1c5\x18zz\xbb\xe9\xfeb\xa4\xaf\xe1#\xcf\xcc\x1c#x>\x1fA`v]\x1c\xda\x05\xf9a\xc1\xf9\x03\xe7}A\"R\x11\xc1\xf8\x98\xc1\xaaO\xe5wLӯ\xa5\xdb$Zl\xf9\xa778Ҫ\xfc.\xae\xed\xeb\xd6\xee\x8d8\x81K\x93\xd9\xe9G\x18ҫ\xf7\xa6\xcb\xed\x96\xd4\xec\xf5+\xf2F\x81\xceW\xea\xa5\xecM\xcdT\xe5]P\x8b\x97X\x87\xfdͩ\xf7\x94j\xbb\x8bj\xecfK\x95\x13+\xeb\xba5s\xd3 \x17\xd4\xd3%\x11g\xbevnqŜ\xafP\x9b\x9cGr\x9d\\\xa4\x02n\x12\xf0hu\xdcT\xdd\xdb̾\xf7\xb0&.\xbd\xdam\x124V\xc2\xcd\u05f8]\xaf\x92\xfd\x1aQ\xf6\xb8\xaa\x99\xadS\x9b\x8d§\xf1\x9b\xadD[R\x7f6K\xb1\vk\xcd\xeaZ\xb2\x91q\x97V\x98u+\xc8F\x80\xa6ԕ\x8dԍ\x8d@\x9c\xac&K\xad\x16\x1b\x81=cv'\xa5d\xf2\xc7%Ub\xf1KTȬ5俗\xfc]J\x06\xa9:\xce\xe5\\@\xf3k\xaf\xb9\xe5|\U0003199d\u0558\x9f\xca\xccq\xb9\xb3ZTܰ\x92cz\xf1\xc4\xf2h\xccn\x8ep\xae/\x86\xf8M\xe2qMw\x99\t\xf9\xf5s-̛\x9e\xcbM5y\x06\xce\t\x8d\x89\xe2`晻\a(\x93k\xb0\x16\xc2.O\x7f兿.\xe8\xc6\xc9;\x9eH\x8de`\xcc\x11\n\ve\xfcړQU>\xedN:\xcf\x17\xdf\xfd\xbd\x02u&x=K\xed_̜\x87r\xcbR\xdbX((\n\xafm\xdc\xedS=7\xbbY\x9e\xe4\x9dp\x06/\n\xb6\x87#±\x1a\x82\u05fc\xb6\xca\xd0F\r#M\xe3\x1b\xb1\xb2\xee\x1d\xf9}\xceSM=L\xf4\xb2\x81\xc6\xf2Pc\xd6ȿH\xb8qy\xc01\x012\xf5pPZB|\xf60\xd0K\x05\x1es\xa1G\xb2ϕv\xd8\xe7%\x0e\xf9,8ܳ \x04Y\x16\x84$\x93)\xe5\x10ϋ\x84\"/\x18\x8c\xbcD8rY@2\x03\xb2w8'\xe5\xd8MR\xb1Gr\xbe3\xa5Xc>%9}\x9c&\xe1\x18MB\xb2r\x0eӄ\xe32ˎ\xc9$\xd0\xf0\x85B\x95\x17\nV^\"\\yـe6d\x99\x95\x9c\x99\x9f\x97\x1do\xb9x\xf3^\xaa\x1c\xd4d\xae#U4'\x85\xb2\x17_t\xc7\xec\xed\xfc\x87;\xe5l\xab\x8e+\x1b۰\xaeO\xbdg\xe4'&|\x1e\xd5\na\xcb\xeew\x120\x8d#\x12\xdf\xffo\xbc<\x7fۨ\xcb\xdah(\xa9\xc2\f\xeb\xee\xecJ+\xf4\x86|\xa0ٱ\a\xfd\x18\x8d+\xf6R\x15ԐU\x9d\xf2z\xe3\x80ۿW\x1bB>\xca:i߾IF\xb3\xa2\xe4g\x1b7D`\xae\xda .\x13\x88\xa8\xf0\x85\xf1\xef%gY\xc4ӊ^.\xe4\x1a\x0f\xae\x84\xc0+\x8f\xb2v껴\r\xe3\x8e\x16:e\xdd\xeb\x15\xf7\x92s\xf9\xbc0\x1c\xa7%\xfb\x0f\xbc\xa5y~\x0f\xe7\xdd\xfd\x16\x9b\x06I\xc1\u06dd\xeb\n\xa1\x1a\xe9\x1dX\x8b\xd9Lgl\xc5o\xf7\x1d\x88\x91J\xbb\xfaO\x94\xd6\xdab\xb3\xb1[\x97\\՟\xd54\xf7[\x87\xdd\x06\x85\x85\x8a3\x91X\xeba\x8eL\xe5\xeb\x92*svu\x0575\x0e\xe3\xfb8\xc1nN\xed\xb6\x8c\x9a\x97\xe1u\xbfQچ[\x7f1\x99w.\xbb\xa9\xd0>E/\xc1c\xfc(\xdf\xec!\xbe+\xe21\ue0ac\x91R\x91\xd7Ѣ\xa4\xab\xedbi\x7f\xb5\xed/\xf2\x04\uf8fbY\x1d\xf2<\xf4\x9aGʉ\x02Dw\xb9\xeb\xd4\xed\xa0x9\xe7e\xba(^\x1f\x14\x86\xf6\xd7w&\xceŷ\x8eL%\xdc\\\x1a\xe0\xea\xf8\xae\x8d]^\xf7_0\xb4\xaaU\x98wv|\xf0\x14\xb6\xae\xfa\x17\xdc\xfd\xe5\xfa5R\xdaHE\x0f\xf0\xb3tW/\xcfѠۺs\xef\xb6wyB\xcdbX\r\xb1P\xc0_\x02\xdd\x03֔\"\x0f\xae\xc1\xb5X.\xbc\xd5\xd7\x18>3\x99\xc7ǟ\xdd\x04\f+`\xf3\xber\xb9|\xab\xed4Xj\x86\x89\xb9N;\xfb\xdfc\xc4^\x10\xbcO\xb6ş\x16\xde\n\xb0\xdc\x19\xcb\xde\x16a\x7f\xea\\$\x1dH4'\xa2_\xe2\xbdZ\xfbK-&9\xd7#*\xa1cpZw\xe9\xe3\xce+\x1eW\xbe\ueb4bc\xce\xe4\xd8m\xe3x\xc3\xf6\xfc}\xe3\xee\"n\xffu\x01_4_)\xbc2\xd1_ҍW\f^t\xe5\xf8\xae\xae\v\xa9\xabN\xf4;cl\xa0\x1c\xd3\xdc\x11\xf4F\xfa\xd6\x06N\x1aʉ\xa8\x8a\x1d\xfa\xac1\x95Rw\xc1\x8a\x95\xc9R\x15\xe7\x80L0Α\x9a\t\x03\x87\xc1\xa6{l\xaew\xbe\xc6\xf9\x92\xb9\xd6}\xd3窫,\x03\xad\xf7\x15\xe7纾z\xc9\xc4c\xd6\xe5J\xa4\xf8H\x19\xbf\x88\x0e\xae\xe3\b\x11\xdc\xdcF\xf5h\x12\x9b}Q'\x88<,ށ)\xb0\x0f\x9e:XF\a\xcf\x02_k\xa5\r-\xe6nN\xbf\x1b\xf6\xc0\xcfZ\xa8\xbcU\x9dU_\xff\xfdLu\xc3\xe6\x98Oـs=\xd1\x05\xb5\xd0 'p\x02A\xa4\xc0\xaay\xbc\x8b\xd3}z\xa5\xdf'\x02\xb5\rŗ\xe5W%\x974\x0f\x06.D\x92\xfes\x1d\x8fh\xbe\xd5\t\xd4k=\x01\xb3\xbe\xd0=B\x84\xa1d\xba\xd0\xee\xd6\xfaF\xb0\x8e\x02M2\xfdQ]\x9bi\xd6\xd5\xf3\xc9J\xeb\xeea;\xd6sT\x82C\x83\x18\xff\x06\x1fN\xf8F%5\x9cY\xaa\x8a\x1a\xcelNAu\xd4Qdr\x8d\x82\xba\xfa4q\xad\xce\u07b8\x8c\x8d\x9c\a\x80Nj\xc2u\xfa\xee|Q\x01Z\xd3C\xb8j\xf9\xd9:`\a\x10\x80\x9b\x11\x91\xd9\xf8\xed\xdd\xe6\x027\xaf,R\xa4D\r\xa1C\x99\x86\xa9\x94h\xa5r\xea\xf3b\r\xba4{\x1a\xc5ԧ\xa2\xc3G\x0f\xdf\xf8\x8b\xd3\xd7{%\x8b\xb5\xe7\x05VW\xdc\xf8\xf4\x8ab\xd2\x06\xec\xe6\x18%9q\xdfT\xf27\x14\xa3\x18\x94%\bB\xb5\xc7'\xe1b\x89i\xb6N\xec\xa6jC\x95I\x8d\x83\x1e:\x8dgB \x84\x1c\xc7\xf7\xc1\xa7\x8f\xdc\x05\x1bw\xfe\x93b5\xe0\x1b\xa2\x99\b\xdf[t\xc9)'\n\xdaFF\nps-Z83\x88i:\x11L\x17\xfd\xdf7x9\xd56\xf1C\x8a\x17\xfc\xa5\u05fcw\x88\n?\xaeU7\xf1\x9ek\x84\x1e\x7f`{W˓Y\xac\xff\xf8\x7f~8\xea\x94\xe4e\xbd\x9et\xb0\xd0w\xaa=\xa5\x99Oi\xdds\xb0\x9e\x8f\x06\xe8\xfan\xaf\x179\xe9\xa7\xcb\xc2\xcekƜ\xe1S\xa0\u05c9\xc4N\x97E\x9b/\x16j^wv\xcf\x14??8\xb7\xc6\xfe\xe6\x9bEbM\x0f!\x12mF\xa6Qǟ\xb3\xd1f+\xd8\f8\x8e|-\xa8\x17\x80^)܌ځ\xc1KT\xa0ykm\xfb\x91\xfc\x9b\xff\r\x00\x00\xff\xff\x9a\xfbL\xe1\xa9x\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\xe3\xb8\x11\xbe\xebWt\xed\x1e|YQ3\x9bKJ\x97\x94FN\xaa\xa6\xe2\x89]#ǹ\xe4\xb0\x10Д\xb0\x06\x01\x06\x0fi\x94T\xfe{\xaa\x01\xf0!\x92\xb2\xe4\xa9$\x8b\x8bM\xb2\xd1\xe8\xfe\xfa\r\xcd\xe7\xf3\x19\xab\xe5\vZ'\x8d^\x02\xab%~\xf3\xa8\xe9\xc9\x15\xaf\xbfw\x854\x8b\xc3\xc7٫\xd4b\t\xeb༩\xbe\xa23\xc1r\xbc\xc7Rj\xe9\xa5ѳ\n=\x13̳\xe5\f\x80im<\xa3\u05ce\x1e\x01\xb8\xd1\xde\x1a\xa5\xd0\xcew\xa8\x8bװ\xc5m\x90J\xa0\x8d̛\xa3\x0f\x1f\x8a\x8f?\x17\x1ff\x00\x9aU\xb8\x84-㯡v\xdeX\xb6CexbY\x1cP\xa15\x8543W#\xa7\x13vքz\t݇\xc4!\x9f\x9e$\xff\x14\x99m\x12\xb3\x87\xcc,~W\xd2\xf9?_\xa6y\x90\xceG\xbaZ\x05\xcb\xd4%\xb1\"\x89\xdb\x1b\xeb\xff\xd2\x1d=\x87\xadS\xe9\x8bԻ\xa0\x98\xbd\xb0}\x06ษq\tqw\xcd8\x8a\x19@\x86&r\x9b\x03\x13\"\x82\xcdԓ\x95ڣ]\x1b\x15*ݞ%\xd0q+k\x1f\xc1L\xba@V\x06\x1am\xc0y\xe6\x83\x03\x17\xf8\x1e\x98\x83ՁIŶ\n\x17\x7fլ\xf9?\xf2\x03\xf8\xd5\x19\xfd\xc4\xfc~\tE\xdaU\xd4{暯\xc9FO\xbd7\xfeD\n8o\xa5\xdeM\x89\xf4\xc0\x9c\x7faJ\x8a(ɳ\xac\x10\xa4\x03\xbfGP\xccy\xf0\xf4\x82\x9e\x12B@\x10!4\b\xc1\x91\xb9|\x0e\xc0!q\x89\x18MK\xaaFg\x9d\x89M\xa2\xc0ˀK\x92\x9f\xded\xe9{l\x1b\xff.\xb8Ŗ\xa5\xf3\xac\xaa\xcf\xf8\xaevx\x89\xd9\x19\x14\xf7X\xb2\xa0|_U\xb2\x92\xea\xfb\xe5\xb9Z5\xf2B\xa4]g'ޟ\xbdK\xa7n\x8dQ\xc8\x12\x97Du\xf8\x98\xbc\x90\xef\xb1b\xcbLljԫ\xa7\xcf/\xbfۜ\xbd\x86)G\x1a\x04\x05\x19\x8e\xf5l\xb3G\x8b\xf0\x12\xe3/\xd9\xcde\xd5Z\x9e\x00f\xfb+r\xdf\x19\xb1\xb6\xa6F\xebe\x13,i\xf5rQ\xef\xed@\xa6;\x12;Q\x81\xa0$\x84ɏr\xbc\xa0Ț\x82)\xc1\xef\xa5\x03\x8b\xb5E\x87\xda\xf7\xe1m\x05+\x81\xe9,^\x01\x1b\xb4Ćb9(A\xb9\xeb\x80փEnvZ\xfe\xb3\xe5\xed\xc0\x9b\xec\xbc\x1e\x9d\x1f\xf0\x8c\xf1\xa9\x99\"W\r\xf8\x130-\xa0b'\xb0H\xa7@\xd0=~\x91\xc4\x15\xf0\x85\xfc]\xea\xd2,a\xef}햋\xc5N\xfa&\asSUAK\x7fZ\xc4t*\xb7\xc1\x1b\xeb\x16\x02\x0f\xa8\x16N\xee\xe6\xcc\xf2\xbd\xf4\xc8}\xb0\xb8`\xb5\x9cG\xd1uJ\x9a\x95\xf8\xd1\xe6\xac\xed\xee\xced\x1dEmZ1k\xbea\x01ʘ\xc9\v\xd2֤E\a4\xbd\"t\xbe\xfeq\xf3\f\xcd\xd1\xd1\x18C\xf4#\xee\xddFי\x80\x00\x93\xbaD\x9b\x8cXZSE\x9e\xa8Em\xa4\xf6\xf1\x81+\x89z\b\xbf\v\xdbJz\xb2\xfb?\x02:O\xb6*`\x1d\v\x13l\x11B\x1d㾀\xcf\x1a֬B\xb5f\x0e\xff\xe7\x06 \xa4ݜ\x80\xbd\xcd\x04\xfd\x9a:$N\xa8\xf5>4\xb5\xf0\x82\xbd&\xa3xS#?\x8b\x1f\x81NZ\xf2p\xcf<Ƹ\x18\xe0\x9aC\xfcr1m\xd6tp\xd3b\x9c\xa3s_\x8c\xc0ᗁȫ\x96\xf0L\xc6\x1am%],\x8bP\x1a;\xac\x18\xac\xcd\xc0\xfd\xd5d\xaab\xf4\ru\xa8Ƃ\xcc\xe1+2\xf1\xa8\xd5\xe9§\xbfY\xe9\xc7\a]0$\xad$\xe2\xe6\xa4\xf9\x13Zi\xc4\x15\xe5?\r\xc8[\b\xf6\xe6\betk\xedՉr\x90;i>ζ\xcdZ=}n2o\n\xa0\x1co\x19\xab\x02V9rM\t\x1f@HG\r\x80\x8bL\xc7`\xe9\xa0b\x83\xb0\x04oû\xd4\xe7F\x97r7V\xba\xdf\xd3\\\xf2\x98+\xac\aȭ\xe3I\x94\x9a\xc8;jk\x0eR\xa0\x9dS|\xc8R\xf2,I\xb0\xa9r\x95\x12\x95pcM/DYTŢ\xa0\xa8f\xea\x8a\r\xd7-a쀙\xd4Ƀ;\x061\xd9\xd8*\x97T\xedQ\x8b\xb6\x1b9\x93\xc6Ĭ\xe5P\xc0Q\xfa}J\x87j*\xee\xe0\xcdأ\xf5\x8a\xa7\xa9\xd7\x03ٟ\xf7H\x94\xa9\x80\"8\xe4\x16}\xf46T\xe4>\xe4J\x05\xc0\x97\xe0bB\x1d\xe6\x89f\xc5F\xad\xd9\xfd\x8a\xa71\xd0p\u0378\xb9\x85\xb9.\xf2\x1d\xb5\u038d\xc0\x16K\xb4\xa8\xfddR\xa7\x01\xc4j\xf4\x18\xf3\xba0\xdcQJ\xe7X{\xb70\a\xb4\a\x89\xc7\xc5\xd1\xd8W\xa9ws\x02|\x9e#h\x11NJŏ\xf1\xcf\x05\x95\x9f\x1f\xef\x1f\x97\xb0\x12\x02\x8cߣ%\xab\x95A5\x8e\xd6\xebo~\x8a5\xf6'\bR\xfc\xe1\xee{p1u\x8a\x9c\x1b\xb0\xd9D\xef?Q\xa3\x16\x85\"\x886\xc9*\xc6\x02UJ2v\x95\xad\x99r͔#Nu\x98\xfdE\x89\x89*\xc8TF}\xc5q2}#\xcc\x00\xbe\xcd;C\xcd+V\xcf\x135\xf3\xa6\x92|6\xd46\xb6\xc1W\"\xb2i\xbb\xa5\x16\x92S\xdbv\x1eI\xcd8\"κ\xf3\t\x18\x86\xfd\xfa\xa5\xfc1\rSR7W\xcf+\x12?\xf6i\xbb!.%\xb3\\\x11\x1dzj\xb7\x1ch\xa4\x8a\xc9\xec\x18\xe7\x98B\xb8њb\xd7\x1b`mb\xbcsÊ\xf0\xce|\xb2\r\xfc\x15'\x80\x1f\xa9\xf2)\x126\x18\xa7m$Kp\x18S\xf551\xe0zDp\xb6F{\x8b,\xeb\x15\x11\xb6E\x95\xc1z\x05۠\x85\xc2F\xa2\xe3\x1e5\xcd\x13\xb2\x14?\xfcf3\x93b\xce\xd3\b\x84\xe2+\x1e\xe4\xf8Nh\x8c\xee\xc3hG\x13\xf8m8\xd0\xc3/\xcdh\xbd\xb0\x99\xec\x97\t0J\xa9\xa8s\x9c\xc8\x13]\xc70\xbe\xbd\xfc\xb4y\xb8s\xb1\xe1G\xed\xa7\x9a\xc4#Z\x8c\xf3\x15\n\xea\xf9M\xbe\xc5\bΣ\x9dp\x80\xd6z\xd1栌\xde\r\x02'\xad|\xa7A\xfd\\r(cA\xa0\xa7Ҥw\xc0\xf7Lﰻ\xb3\xca\xf2\xbf-)\xb9\xcf\xc0g:\x0f\x91\xfa\x92{\xdcd\xd1g9\xd5ԏ\xee\x8b;\xe2\xe9\xbb\xe2F\xfaƲ\x17\x87\xa2+\xb8\x8f\xe8\x9b*M\xa0\xce}w\x7fܭ\xef\x1f\x86Ǘ\xd37 \xf1ޛ\xf37nA\xe0\xc8\\w\x87\xfe\xdb\xe1PQ\xb7z\xb5\x05\xfe\x92\xa8\xd2ec\xde\x02lk\x82\x7f+2\xef\xa6\x1c:\xff8\xf0\x1e\x19\xe3O\x1eך\f\xa2i,\u0083\xa5\xc1\xb3\xbbC\x8bIa\xaa\xb6\xdc~\x19\xb5\x1a\xfc2\xd3\xff6\xfe\xdd\xe6\x06\xbd&k\xed\xe8e\xaa\x97=\xbbf\x90\xfbo¶\xbdW^¿\xfe=\xfbO\x00\x00\x00\xff\xff\x80.\x12\xd3P\x1c\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96M\x93\xdb6\x0f\xc7\xef\xfe\x14\x98y\x0e\xb9<\x92\xb3\xed\xa5\xa3[\xb3\xc9a\xa7mƳ\x9bɝ&a\x8bY\x8ad\x01\xd0[\xb7\xd3\xef\xde!)\xf9E\xb67\xdbCy\x13\t\x02\x7f\xfe@\x80j\x9af\xa1\xa2\xfd\x8a\xc46\xf8\x0eT\xb4\xf8\x87\xa0\xcf_\xdc>\xffĭ\r\xcb\xdd\xdd\xe2\xd9z\xd3\xc1}b\t\xc3#rH\xa4\xf1#n\xac\xb7b\x83_\f(\xca(Q\xdd\x02@y\x1fD\xe5iΟ\x00:x\xa1\xe0\x1cR\xb3E\xdf>\xa75\xae\x93u\x06\xa98\x9fB\xef\u07b7w?\xb4\xef\x17\x00^\r\u0601A\x87\x82k\xa5\x9fS$\xfc=!\v\xb7;tH\xa1\xb5a\xc1\x11u\xf6\xbf\xa5\x90b\aDž\xba\x7f\x8c]u\x7f,\xae>\x14W\x8f\xd5UYu\x96\xe5\x97[\x16\xbf\xda\xd1*\xbaD\xca]\x17T\f\xd8\xfamr\x8a\xae\x9a,\x00X\x87\x88\x1d|β\xa2\xd2h\x16\x00㱋\xcc\x06\x941\x05\xa4r+\xb2^\x90\xee\x83K\xc3\x04\xb0\x01\x83\xac\xc9F)\xa0\xbe\xf4X\x8e\ba\x03\xd2#\xd4p \x01\xd68*0e\x1f\xc07\x0e~\xa5\xa4\xef\xa0ͼ\xdaj\x9a\x85\x8c\x06\x15\xf5\x87\xf9\xb4\xec\xb3`\x16\xb2~{K\x02\x8b\x92ē\x88\x12\xd7\x06\x0ft\xc2\xf7\\@\xb1oc\xaf\xf8<\xfaSY\xb8\x15\xb9\xda\xec\xee*i\xdd㠺\xd16D\xf4?\xaf\x1e\xbe\xfe\xf8t6\r\xe7Z\xaf\xa4\x16,\x83\x9a\x94fp\x95\x1a\x04\x8f\x10\b\x86@\x13Un\x0fN#\x85\x88$v\xbaZu\x9c\x14\xcf\xc9\xecL»\xac\xb2Z\x81\xc9U\x83\\\xa0\x8d\x97\x00\xcdx\xb0\n\xd32\x10FBF_\xeb\xe8\xcc1d#\xe5!\xac\xbf\xa1\x96\x16\x9e\x90\xb2\x1b\xe0>$gr\xb1\xed\x90\x04\bu\xd8z\xfb\xe7\xc17\xe7s\xe6\xa0N\xc91?\xd3(\x97\xce+\a;\xe5\x12\xfe\x1f\x9470\xa8=\x10\xe6(\x90\xfc\x89\xbfb\xc2-\xfc\x961Y\xbf\t\x1d\xf4\"\x91\xbb\xe5rkej\x1a:\fC\xf2V\xf6\xcbR\xffv\x9d$\x10/\r\xee\xd0-\xd9n\x1bE\xba\xb7\x82Z\x12\xe1RE\xdb\x14\xe9\xbe4\x8ev0\xff\xa3\xb1\xcd\xf0\xbb3\xad\x17\x17\xa4\x8eR\xe8\xafd \x97yM{\xddZOq\x04\x9d\xa72\x9d\xc7OO_`\n]\x921\xa7_\xb8\x1f7\xf21\x05\x19\x98\xf5\x1b\xa4\x9a\xc4\r\x85\xa1\xf8Dob\xb0^ʇv\x16\xfd\x1c?\xa7\xf5`\x85\xa7+\x99s\xd5\xc2}餹\xa8S4Jд\xf0\xe0\xe1^\r\xe8\xee\x15\xe3\x7f\x9e\x80L\x9a\x9b\f\xf6m)8}\x04\xe6ƕ\xda\xc9\xc2Ծo\xe4\xebJ\xd1>E\xd49\x83\x19b\xdem7V\x97\xf2\x80M x\xe9\xad\ue9e2\x9d\xd1=\x14x{\xb6p\xbd\xa0\xf38\xb6\xc9\xf9\xca\xcd\xc3Cɝ%\x9c\xdd\xc2\x06.z\xee\xeb\\J3\xfc\x97dj'\x1e\xd9\xe8D\x84^N\xfa\xb3\xba\xb6\xe9\xad,\x90(\xd0\xc5\xecLԧbT^ze=\x83\xf2\xfbq#H\xaf\x04^\x90r\x19\xe8\x90r\x9fA\x03&]\xf0\x1b\xb1\x9c\xbe%\x91\x82F\xe6\xf6\xc2\xce\n\x0eW4\xbd\x92\x9d<|rN\xad\x1dv \x94\xf0Ff\x15\x91\xda\xcf\xd6ʛ\xf5\x1d\x04\xabls-\a\x87w\xfa\xbbI(\xb8}\x1a.#5\xf0\x19_\xae\xcc>\xf8\x15\x85-!ϯ|^\\Uz\x87\x9f\x817P\xbaz)/&9\xf7;sB\x91%\x90ڞr\xe5\xb4>\xf4\xef\x0e\xfe\xfa{\xf1O\x00\x00\x00\xff\xff\x045\f\xc6i\n\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VOs۶\x13\xbd\xebS\xec\xcc\xef\x90_gB*i/\x1d\xddZ%\aO\xd3\xd4c\xa5\xbeC\xe0\x8aD\r\x02(v!\xc7\xfd\xf4\x9d\x05H\xfd\xa5d\xf9P\xde\b,\x16\x0fo\xdf>\xa0\xaa\xaa\x99\n\xe6\x11#\x19\xef\x16\xa0\x82\xc1\xef\x8cN\xfe\xa8~\xfa\x99j\xe3\xe7ۏ\xb3'\xe3\x9a\x05,\x13\xb1\xef\x1f\x90|\x8a\x1a?\xe1\xc68\xc3ƻY\x8f\xac\x1a\xc5j1\x03P\xceyV2L\xf2\v\xa0\xbd\xe3\xe8\xad\xc5X\xb5\xe8꧴\xc6u2\xb6\xc1\x98\x93\x8f[o?\xd4\x1f\x7f\xac?\xcc\x00\x9c\xeaq\x01\x8d\x7fv֫&\xe2\xdf\t\x89\xa9ޢ\xc5\xe8k\xe3g\x14PK\xee6\xfa\x14\x16\xb0\x9f(k\x87}\v\xe6OC\x9a\x87\x92&\xcfXC\xfc\xdb\xd4\xec\x173D\x04\x9b\xa2\xb2\xe7 \xf2$\x19\xd7&\xab\xe2\xd9\xf4\f\x80\xb4\x0f\xb8\x80\xaf\x02#(\x8d\xcd\f`8b\x86U\r\xa7\xdb~,\xa9t\x87\xbd*x\x01|@\xf7\xcb\xfd\xdd\xe3O\xab\xa3a\x80\x06IG\x138\x13u\x82\x19\f\x81\x82\x01\x01\xb0߁\x02\xe5@E6\x1b\xa5\x196\xd1\xf7\xb0V\xfa)\x85]V\x00\xbf\xfe\v5\x03\xb1\x8f\xaa\xc5\xf7@Iw\xa0$_\t\x05\xeb[\xd8\x18\x8b\xf5nQ\x88>`d3\xb2\\\xbe\x03\r\x1d\x8c\x9e\x00\x7f'g+QЈx\x90\x80;\x1c\xf9\xc1f\xa0\x03\xfc\x06\xb83\x04\x11CDBW\xe4t\x94\x18$H\xb9\xe1\x045\xac0J\x1a\xa0\xce'ۈ\xe6\xb6\x18\x19\"j\xdf:\xf3\xcf.7\tC\xb2\xa9U<\xcaa\xff\x19\xc7\x18\x9d\xb2\xb0U6\xe1{P\xae\x81^\xbd@\xc4\xccSr\a\xf9r\b\xd5\xf0\xbb\x8f\b\xc6m\xfc\x02:\xe6@\x8b\xf9\xbc5<\xf6\x8e\xf6}\x9f\x9c\xe1\x97yn\x03\xb3N\xec#\xcd\x1bܢ\x9d\x93i+\x15ug\x185\xa7\x88s\x15L\x95\xa1\xbb\xdc?u\xdf\xfc/\x0e\xddF\uf3b0\xf2\x8bȌ8\x1a\xd7\x1eLd\xcd_\xa9\x80\xa8\xbe\b\xa6,-\xa7\xd8\x13-C\xc2\xce\xc3\xe7\xd57\x18\xb7\xce\xc58e\xbf(g\xb7\x90\xf6%\x10\u008c\xdb`,E\xccʓ\x9c\xe8\x9a\xe0\x8d\xe3\xfc\xa3\xadAwJ?\xa5uo\x98F1K\xadjXfC\x815B\n\x8dblj\xb8s\xb0T=ڥ\"\xfc\xcf\v LS%\xc4\xdeV\x82C/<\r.\xac\x1dL\x8cNv\xa1^'\xad\xbe\n\xa8\xa5zB\xa0\xac4\x1b\xa3sk\xc0\xc6GP\xfb\xce\x1f\b\xac\x8f2Own\x06\xa7b\x8b|:z\x82\xe5[\x0e\x92\xed\x9f;ul4\xffǺ\xad\xc5+h\x00R\xdc\xe3\x87\xfa,\xe3e\f0\xa9\xdeI$\xa3\x88\x85\x06\xe1U\xac@L\xea\x10\xd3\xf9\xd6\xf2\xa1K\xfd\xf4\x06\x15\xfc\x9a1\x7f\xf1\xed\xd5\xf9\xa5w,r\xbf\x1a\xf4\xe8m\xeaq\xe5T\xa0ο\x12{\xc7\xd8\xff\x110\x96\x1b\xf3j\xe8x\xf1\xeen\xa9+\x81\xc9^\xdc\xf7\x01\xc5\xef\xf1\xf2I\x87\x80\x9b\xb2܀i\x88\xbc\xe9\xa0\xcb\xd5\xdd[(\xbc\x10~\xb5H\x17\xdav\xfc\xf2\xf5\xfc\xba\x06\xe5\x82\x1f5(Kʝ\x85 \xaf\x9b萑\xf6\xf6\xf9l\xb8\x9b\xcc\b\xf0\xdc\x19\xdd\xe5\x85Y\xc0\xe2\xccD^\x9b\xecso\x87/}o\"N4Q\x95\x9bkbX\xc0\x9f\r_p\xabK\x1bT\x83\x83\xdc\xe4x\xac8\xd1\x1bz\x8dDx\xdeF\x17O2\xd9\x04g\x83$/\xa7急\xe1A>\x8c\xfc\x1b\x00\x00\xff\xff\x02\x83F4\xa5\r\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\x1c\xb7\x11\xbe\xf3Wt\xc9\a\xc6U\x9aYKI\xa5R{\x93\xc98\xc5ĦX\xa2\xa4\x8b\xcb\a\xec\xa0w\x06\xe6\f\x80\x00\x98\xa56.\xff\xf7T\xe31;\x0f\xec.Ɋ\x9c\xb9\x90\x8bG\xe3C\xbf\xbbQ\x14\xc5\x05\xd3\xe23\x1a+\x94\\\x03\xd3\x02\xbf8\x94\xf4˖\x0f\x7f\xb3\xa5P\xabݛ\x8b\a!\xf9\x1a\xaez\xebT\xf7\x01\xad\xeaM\x85\u05f8\x15R8\xa1\xe4E\x87\x8eq\xe6\xd8\xfa\x02\x80I\xa9\x1c\xa3aK?\x01*%\x9dQm\x8b\xa6\xa8Q\x96\x0f\xfd\x067\xbdh9\x1aO<\x1d\xbd\xfb\xae|\xf3\xb6\xfc\xee\x02@\xb2\x0eנ\x15ߩ\xb6\xefpê\x87^\xdbr\x87-\x1aU\nua5VD\xbb6\xaa\xd7k8L\x84\xbd\xf1܀\xf9N\xf1Ϟ\xcc\xf7\x9e\x8c\x9fi\x85u\xff\xca\xcd\xfe(\xac\xf3+t\xdb\x1b\xd6.A\xf8I+dݷ\xcc,\xa6/\x00l\xa54\xae\xe1\x96`hV!\xbf\x00\x88W\xf4\xb0\n`\x9c{\xa6\xb1\xf6\xce\b\xe9\xd0\\\x11\x85Ĭ\x028\xda\xca\b\xed\x8a\x0e\xe1\xb1A\t\xae\x11\x16\xc2m\xe1\x91Y\x82c\x9c\xbfe\xfe`?Oۭc\x9d\x9e \xb82\xc8\x0e[\x03\x04\xce\x1c\xe6\x00\f\xfc\x04\xb5\x05\xd7 q\xde+\x16\x13R\xc8\xda\x0f\x05I\x80S\xb0A\x0f\x119\xf4:\x83LcUj\xc5K\x99\x88N`\xdd\xceF\xcf\xf1\x86\xd6\xff\xafQM\x00\xdd)\xfe\x02(\xcf:7,\x9e\x9c\xfay\xbc\x9e\x90\x00\x05X\x10\x16X\xdc\x1anq`tr\x90\x1f\xfe~\xff\x11\xd2\xd1^\x18s\xee{\xbe\x1f6ڃ\b\x88aBn1:\x98\xadQ\x9d\xa7\x89\x92k%\xa4\xf3?\xaaV\xa0\x9c\xb3\xdf\xf6\x9bN8\x92\xfb\xbf{\xb4\x8edU\u0095\xcf]\xc8a\xf6\x9a4\x97\x97p#\xe1\x8au\xd8^1\x8b_]\x00\xc4i[\x10c\x9f&\x82q\xda5_\x1c\xb86\x9aHI\xd3\x11y\xcd2\xa1{\x8d\x15I\x8f\x18H;\xc5VD\x0fE\xee\x9c͗\x97\x13\xc2yå/\xeb\x9d\xe6\x8b \x17\\f{\x1269\xf2\xa9\xc9a\x86\x95\v\xa2\x00\xed\xdc\xcb\x0e{Ƒ\xcbF\a[.(\x1c\x11\x03}Rq\xdb\a\xf5q\n\xaa\x86\xc9\x1a\xc3}\x11\xb6=E\xc7\xf2\xf2%v\xbcLIҗIM\xe6\x8e\xe3\xff\x16ܟx9\x9fA?\xe1r\xe3*\xe3\xe4\xe5\x1e\xfa\r\x1a\x89\x0e\xfd\xfd\xb8\xaa,]\xadB\xed\xecJ\xed\xd0\xec\x04>\xae\x1e\x95y\x10\xb2.H5\x8b\xa0\x03v\xe5\v\xe7\xd57\xfeϋ\xef\xe2k\xec\xa7^hR\xfb\x7f\xcd[\xd19v\xf5\xa2K\xa5\x1c\xf6\xe9q\xec\xf2>fV\xf3\xbdd\x16\x8f\x8d\xa8\x9aT\x9cD\x1f{Ę\x04e\xc2<\xb8f&\xf7_]\x95\x89\xa1\xbd!D\xfb\"v\xf7\n&9\xfdo\x85u4\xfe\"\x0e\xf6\xe2I\xe6\xfb\xe9\xe6\xfa\x8fQ\xf0^\xbc\xc8V\x8f$\xe0\xe1\xfbR\x1c`\x15\x1d\xd3EX͜\xeaD5[=퉜I\xe3>L\x16\xa7D3\x93\xdf\x0ek\x9e\x95G:Vg\x12\xb7q3\xf3Tzw\x92_\xd3\xc6\r\xab-0\x83\xc0\xa0c\x9a\xe4\xfc\x80\xfb\"$\x04\x9a\t\x8a\xe6\x14\xb0\x87\xae\b0\xad[\x91\r\xdc1\xecǔ5r\x82\xcarV\xdbcw\xcfJm\xdc\x05:#\x85O\xa3\xa5I\x06g\xfaP\xae\xc9\xd9\xf5\xa4;\xb5D\x8b\xb2\xef\x96P\nxPZ\xb0̸A\xeb\x16\xfaE\x13\xaf\x96y\xc9\ta\x05^\x9e\xe1AlXgJ\x9d(\x8a\x90\x17\x0e\xe5\x8e\xefQ\xe6\xea\x89\xe3\xc5\xc4Q\x88T\xcfS\x96;\x85X\xe4\v\xcf\xd9\x1a*\xc4fCZ\xf1\x8b9#3}\xca49风\x91.\xabq\xdf\x1e\x7fF=\x1e\xda\xfe\x91\xa7\xc1\xfb\xba\xf4\x18@\xa5\xc7K+\xf2JQV?\xe9\xe8\x9d\x11\xef\xd5r\x87o~\x19\x1e\xd5]td\xbd\xa3G\x82xF\xae\xa4\x86\x11\xb9\xb0\xd3\xc7;\xa2\x86ܧ\xdcT\x11l\x99h\x91Cz\t\x9a\xef\xc9P\x1dS\xd9\xe0\x96\x82C0\xbdT\xc8FxCZ\xdb X\xdfU\xba\xb4'h\xf6\x16\xb9\xef\x80d\x98\xb0Lu\xb7\xcat̅.h\x91%*\xfb\xb6e\x9b\x16\xd7\xe0L\xbf\x9c>a\x89\x1dZ\xcb\xeas\xa6\xf8SX\x15\xea\xfb\xb8\x05\xd8F\xf5n(\xf0'\xee\xf1\xd2F\x9dz^\x8f![:OՙQicc\x8a߶~\xcf\xd8\x11\x1c^\t=\xaa\r\xe6S\x84\x97\xf8\x04\x00\xff\xfcu\x0e!\xad\xc9\x19\xd8\xe0\xbdNZ\x18\x9cpʷ\xf8\x98\x19]<ۍ'\xaf\x92\xc9d\xe6~\xf0\xd6\xf0\xac\xfbǃα .\x83F\xb5ɘ\x95c-Ⱦ۠!>l\xf6\x0e\xedԝ\xe7\xba9\xbe\n<\xb0q\xb4?\xc9/P\x8a\x85mŤﺒu9\x05\\Xݲ}\x86p\xba\x88\xcf\xf4ȸ\xc8\x05\x1c\xf49\x19\xb5F㧞ۅ\U00098b95\x18\x9f\x83:Y|&\nŧ\xea\\\f\xbaG\xcd\fY\xba\x7fA\xb8\x9a?q\xbd\x06+|[\x942ϐ\x8a\x86\xa6\x85\xa5\xe0D\xa9\x952\x98q\x99\xb0\f+\x93 2\x85\xffGƏ\xac\x9e,\x06=r>\xa2\x1d[\xeb\xe3\x91~3<\x1b\xad\xe1\xb7\xdf/\xfe\x1b\x00\x00\xff\xffÊ\xc5\x01R\"\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y_\x93۶\x11\x7fק\xd8q\x1e\xae\x991\xa9\xd8\xedt:z\xb3\xef\x9aε\xc9Yc\x9d\xfd\x92\xc9\x03D\xacHD$\x80\x02\xa0tj&߽\xb3\x00A\xf1\x9f\xa4\xd3M.\xe1\x8b}\xc0b\xf1\xc3\x0f\xfb\x0f\xab$IfL\x8b\xafh\xacPr\x01L\v|r(\xe9/\x9bn\xffaS\xa1\xe6\xbbw\xb3\xad\x90|\x01\xb7\xb5u\xaa\xfa\x8cV\xd5&\xc3;\xdc\b)\x9cPrV\xa1c\x9c9\xb6\x98\x010)\x95c4l\xe9O\x80LIgTY\xa2Ir\x94\xe9\xb6^\xe3\xba\x16%G\xe3\x95ǭwߥ\xefާ\xdf\xcd\x00$\xabp\x01Z\xf1\x9d*\xeb\n\rZ\xa7\f\xdat\x87%\x1a\x95\n5\xb3\x1a3R\x9e\x1bU\xeb\x05\x1c'\xc2\xe2f\xe3\x00z\xa9\xf8W\xaf\xe7s\xd0\xe3\xa7Ja\xdd\x7f&\xa7\x7f\x10\xd6y\x11]ֆ\x95\x138\xfc\xac\x152\xafKf\xc6\xf33\x00\x9b)\x8d\vx (\x9ae\xc8g\x00\xcd9=\xb4\x04\x18\xe7\x9e9V.\x8d\x90\x0e\xcd-\xa9\x88\x8c%\xc0\xd1fFh\xe7\x99i\xf5\x80ڀ+\x90\xb6\xf4\xac2!\x85\xcc\xfdP\x80\x00N\xc1\x1a\xa1A½2\x80_\xac\x92K\xe6\x8a\x05\xa4D\\\xaa\x15Oe\xd4\xd9\xc8\x04\xce\x1f\x06\xa3\xee@\xe7\xb0\xce\b\x99\x9fB\xf6;\x83\xea\xe1Y*\xfeL$\x8f\x05z\x99\x88\xa6֥b\x1c\rm^0\xc9K\x042Pp\x86I\xbbAs\x02E\\\xf6x\xd0}$_\xa2\xbe\xce\xcc5\xec\\CE\x90\xedm\xff\xb5;tiߥ\xe2\xcd\x02h\x8c\x1a\xacc\xae\xb6`\xeb\xac\x00f\xe1\x01\xf7\xf3{\xb94*7h\xed\x04\f/\x9e\xea\x82\xd9>\x8e\x95\x9fx]\x1c\x1be*\xe6\x16 \xa4\xfb\xfb\xdfNck\x16\xa5N9V~<8\xb4=\xa4\x8f\xc3ဖ\x9c-o\xae\xffO\x81\xbb&HwJ\xf6y\xfd8\x18\x9d\x02\xdbQ\x1a\xe3m\x9a\x19\xf4\xa1\xf6QTh\x1d\xabtO뇼\xaf\x8f3\x17\x06\xc2\xf4\xee]\beY\x81\x15[4\x92J\xa3\xfc\xb0\xbc\xff\xfa\xd7Uo\x18@\x1b\xa5\xd18\x11\xa3k\xf8:ɣ3\n}foHa\x90\x02NY\x03mp\x8a0\x86\xbc\xc1\x10\x9cEX0\xa8\rZ\x94!\x8f\xf4\x14\x03\t1\tj\xfd\vf.\x85\x15\x1aR\x03\xb6Pu\xe9#\xd0\x0e\x8d\x03\x83\x99ʥ\xf8_\xabے\xefѦ%s\u0604\xf8\xe3\xe7c\xb0d%\xecXY\xe3[`\x92C\xc5\x0e`\x90v\x81Zv\xf4y\x11\x9b\u008fd!Bn\xd4\x02\n\xe7\xb4]\xcc\xe7\xb9p1if\xaa\xaaj)\xdca\xee\xf3\x9fX\xd7N\x19;\xe7\xb8\xc3rnE\x9e0\x93\x15\xc2a\xe6j\x83s\xa6E\xe2\xa1K\x9f8ӊ\x7fc\x9a4kozXGN\x17>\x9f\xeb\xce\xdc\x00%;\x10\x16X\xb34\x9c\xe2Ht\fٟ\xff\xb9z\x84\xb8\xb5\xbf\x8c!\xfb\x9e\xf7\xe3B{\xbc\x02\"L\xc8\r\x05]\xbačQ\x95\u05c9\x92k%\xa4\xf3\x7fd\xa5@9\xa4\xdf\xd6\xebJ8\xba\xf7\xff\xd6h\x1d\xddU\n\xb7\xbe\x92\xa0xYk\xb2\\\x9e½\x84[Vay\xcb,\xbe\xfa\x05\x10\xd36!b\x9fw\x05\xdd\"h(\x1cX\xebL\xc4\n\xe6\xc4}\r\xab\x92\x95ƌ\xae\x8f\x18\xa4\xa5b#2\xef\x1b\x14~\x80\x8d\xe4Ӟ\xeaiץoͲm\xadWN\x19\x96\xe3\x0f*\xe8\x1c\n\r\xb0}\x9cZ\x13\xc1\xc9N\xce\v\xca\xc1\x06ɑR\x802.\xde\x17h\xb0\xbbƠVV8e\x0e\xa48d\xcbt\xa4\xe1\xc4E\xf8#+~\xe1\x18\x14\xee\xbdC\x18ܠA\x99a\x8c\x10\xe7*\x99\x89St\x12\xfa\x18\xe2i\xea\xe1L\xf4\x9c\x04\xfcay\x1f#fd\xb8\x81\xee\xc6\xfb^\xa0\x87\xbe\x8d\xc0\x92\xfb\x84ry\xef\x9b\xfbM\xd8\xcc\xc7\x0e\xa7\x80\x81\x16\x18*\xd26\x18\x83\x90\xd6!\xe3\xa06\x93\x1a\xe9m\x00\xe4`\x06\x9b\x15oC\xa4hB\xd21\x84\x13\xf5\xc0(F\t\x0e\xff^}z\x98\xffk\x8a\xf9\xf6\x14\xc0\xb2\f\xad\xf5\xf9\x1a+\x94\xeem\x9b\xb39Za\x90S\xe1\x82iŤؠui\xb3\a\x1a\xfb\xd3\xfb\x9f\xa7\xd9\x03\xf8^\x19\xc0'V\xe9\x12߂\b\x8c\xb7\xe1/ڌ\xb0\x81\x8eV#\xec\x85+\xc40i\xb5\f\x90u5\xc7\xde\xfb\xe3:\xb6EP\xcdqk\x84Rlq\x01o|%x\x84\xf9+9\xd6ooNh\xfdKp\xa07$\xf4&\x80k\xf3]\xd7#\x8f ]\xc1\x1c8#\xf2\x1c\x8f\x85\xe8\xf0\xf3\xc1\x9bBⷠ\f1 UG\x85WL\xb7\x17\xe2\x11\xf2\x11\xe8\x9f\xde\xff|\x12q\x9f/\x10\x92\xe3\x13\xbc\a!\x037Z\xf1oSx\xf4\xd6q\x90\x8e=\xd1NY\xa1,\x9ebV\xc9\xf2\x10\xaa\xfd\x1d\x82U\x15\xc2\x1e\xcb2\t\xf5\x06\x87=;\x10\v\xf1\xe2\xc8\xde\x18hf\xdcYk\x8dU\xc6㧻O\x8b\x80\x8c\f*\xf7\xf1\x8e\xb2\xd3FP\xd5@\xe5B\xc8y\xde\x1aGI3~\xb6\x0e\xe6\xe3\x14d\x05\x939\x86\xf3\"lj\xcaB\xe9\xcdK\xfcx\x9c\xfa\xe37Q\x02\f\x03ǟ\x96D\x9fy8_\xa9>\xe3pݷ\xd6\xd9\xc3m\xeb5\x1a\x89\x0e\xfd\xf9\xb8\xca,\x1d-C\xed\xec\\\xed\xd0\xec\x04\xee\xe7{e\xb6B\xe6\t\x99f\x12l\xc0\xce\xfd\x93y\xfe\x8d\xff\xe7\xc5g\xf1\xaf\xeb\xe7\x1e\xa8\xf7\xe8\x7f\xcdS\xd1>v\xfe\xa2C\xc5Z\xf1\xf9y\xecf\xd5\x140õ\xe4\x16\xfbBdE|\x0441\xf6\x843\t\xaa8y\b\xcdL\x1e^ݔ\x89\xd0\xda\x10\xa2C\xd2\xf4\xb4\x12&9\xfd\xdf\n\xebh\xfcE\f\xd6\xe2Y\xee\xfb\xe5\xfe\xee\x8f1\xf0Z\xbc\xc8WO\x14\xba\xe1{J\x8e\xb0\x92\x8a\xe9$H3\xa7*\x91\r\xa4\xa9\xf6\xbb\xe7D\xfcF\xa0\xb9P\xc5}\xee\t\xc7*t\xa2\x8ale\xae*#\xadd\xda\x16\xca\xdd\xdf]\xc0\xb1j\x05#\x86\xe3u5\xc5c\xd45h\x02]\x87\xc7\xfb\xcb\xc3\xe9@\xd2\a\u0557\x8eȔ\x11\xb9O[\xad\xef\xfbW\x84d\x15\xeb6\xff\xba_Ŵ\x162\xbf\nk\xb7\x97v\x01藎hDy\xa1\x9b\xe7\x8a)\x9c\xbd\x1e\xdf\x18-ʺ\x1aCI`\xab\xb4`\x13\xe3tG#\xfb\xa4\x897\xe3\xba\xe6\f\x13\xc1\x00.pд\x9e&\xdeQ\x8d\xfd\x84\xbaҏ\xd0\xdb\xc5[\xd1t@\xbe֮\xe8\xd9MEr\x1fa2\xfd:\x1c\xc8h\xc5gCҺ.9\x98<:\xd4p\xa2o\xab\x83\xd9^K\xb4{\x9a\xf1\xc3\xda\xf7ۮyZ\x87\x1e_\xc3{\x88\xf0.v\xfe\xe8y\xf3\xe2\xc7u\xa6\xe8\xe9\xd0k\xcf]\xb0\x81\xdb\xf1\n\xdf\xc92\xbc\xf1\tQ\xa1\x7f\xb1\x86\xf6\xe4\x9eٸ\xc9\xd4}CG_X\xea\xb3*\xa9C\xee\v{zwl\x98(\x91C\xfb+\x8bo\xa5[\xdfҹ\x99\xaac\xa3\xa2\xda\"\xf7qc\x02\xf4x]\xec\x92r\xe60!\x15#\tY\x97%[\x97\xb8\x00g\xea\xf1\xf4\x19\xf7\xaa\xd0Z\x96_\xf2\xaf\x1f\x83Tx\xf37K\x80\xadU\xed\xdaG\x7f\xe3h\r\x157\xb6\xb1\x82\xeb\x1a\x0f\x05\xb3\x97\xa0,If\xca\xe2Z\x97?orp&\x94=\xe0~btԵ\xeeN\xdeF\x13\x9a\x98\xfb\xde[\xc7U\x044\x1b]\xe2\xa0\x11\x83B\x95Ѻ\x95\xa3\xa4TWk4D\x84o\x95GFb\xe0\x98\xea\xa2\xf8\xd7בɣ\x86\x18\v\x83\xaa\xe6=\x991雊d\xbfN\x01\x17V\x97\xec0\xa17\x9e\xc4\x17Xd\xbe\xe4GG\x8b\x89^H\xee\xef\xe7\xae\xed\xfe\xb4?\x05L\x97\x7fS?,L\xddB\xf7W\x82\xc1|\xfb\x1b\xc8\xeb\xecp\xa6䳎\x19\xf7ܰ\xb7\xea\t_\x8ax^\xf5t\xbc놮q\xa0\xeao\xf3GƨI\xa2F\x83\x1e9\xef\xe8n:\xa7ݑz\xdd\xfe.\xb0\x80_\x7f\x9b\xfd?\x00\x00\xff\xffg\b\x17r\xc1\x1f\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xdc\x00\x94GI忧\x1a\x00\xbfD\x90\x04e\xeb\xedlx\x13\x054П\xe8/p\xb5Z]\xb1\x92\x7fGm\xb8\x92\xb7\xc0J\x8e?,J\xfae\xd6/\xffi\xd6\\\xdd\x1c>]\xbdp\x99\xdf\xc2}e\xac*\x9eШJg\xf8\vn\xb9\xe4\x96+yU\xa0e9\xb3\xec\xf6\n\x80I\xa9,\xa3׆~\x02dJZ\xad\x84@\xbdڡ\\\xbfT\x1b\xdcT\\\xe4\xa8\x1d\xf0z\xe9ß֟\xfe}\xfd\xa7+\x00\xc9\n\xbc\x05\x8d\xc6*\x8df}@\x81Z\xad\xb9\xba2%f\x04s\xa7UU\xdeB\xfb\x87\x9f\x13\xd6\xf3{}\xf2\xd3\xdd\x1b\xc1\x8d\xfdK\xf7\xed_\xb9\xb1\xee\x9fRT\x9a\x89v1\xf7\xd2p\xb9\xab\x04\xd3\xcd\xeb+\x00\x93\xa9\x12o\xe1\v-S\xb2\f\xf3+\x80\xb0u\xb7\xec*\xec\xfa\xf0Ƀ\xc8\xf6X0\xbf\x1f\x00U\xa2\xbc{|\xf8\xfe\x1fϽ\xd7\x009\x9aL\xf3\xd2:\x02\x84\xbd\x017\xc0\xe0\xbbÍ6\xe0h\rv\xcf,h,5\x1a\x94ր\xdd#\xb0\xb2\x14\xc1\x01\x12x/K~\xaaǢ%4\xbd\"\xea<}~\xfe֕3nN\xa9\xef\xe8\xde\x11\xbe\x96\x05D0.\xb7\xa8=\x13\x9d\xb4\x11L\x94y\xa9\xb8\xb4\xeeG&8\xcaS\xf2\x9bjSpK|\xff\xbdBC\x02\xad\xd6p\xefl\al\x10\xaa2g\x16\xf35\x82S\xe9Ҍ\x0f\x06A\xf0\f\xbb\a\x85'\x8d?d\x98\x1e\xee\b~r\xaapC\xe6\xac\xc6\xf2Q\t\x9e\x1dgI\x13\x9bT\xab[P\xbeZ\x827\xb8g\a\xaet\x04%\xd2H\x1a\xfa\xd2\x1e\xa4\xad1Ud\xcb\x02\x90\xfc<\x84\xa3\xc4\xda+\xf52\xc7\xfb?Ә\xd6jC朷\x06\x95\xc0\xedp\x88n\x10\xf0\af\x95\x8dl\x13 \xaf\xdc\x01\xa24\x94\xca\xd8q\xbe\x8f\xdb\x1e\xf0\xe6`LhaJh\x06\x98\x05SYs\x8e\x10\xed\x99M%\x91\xf6Z\x10\xe7ڱZU~\xec\xe9\xf9֡x\x9c\"\xb0a\x06sPA\xea+\x81&\xac\x95;\xf6\xb7v\xe5z\x14t\x83\xbc\xf74\x04۠\x00\x83\x023\xab\xf4\x90\x92)\xf4\xf4O\x8a\xad\x1c\xa1c\xc4j\xf6ſEl\x02$\x90\x98\xbf\xeey\xb6\xf7N\x00ɦ\x83\x03\xb9B\xe3\f\a9\xaa\xc71$a\x8e\xf7a\x91)\xd3\xd1>3:u\n/fN\xda'\xc1ܶό\xe1\x1d\x18\x96\xf0>zr\xb6\xcf\xffO\xc2\xd6'\xc9\x19B\xfb0\x98\xfa\xbeB\xeb\x82*r\xf6\x1f\xb6\x80Ei\x8f\xd7\xc0m\xfdv\x0e\"\x13\xa2\xb3\xfe?1c\x96K\xfc\xc3\xe9\xccw\x95\xf8I\xae\xccA$\xae4\xcb\xff\x132\xc5\x1d\x16\xcf\xe1\xacHf\xc8_\xbb\xb3\xae\x81o\x1b\x86\xe4װ\xe5¢>\xe1̛\xf4\xe5=\x88\x91r\xde\xd1S0\x9b\xed?\xff \xcfƴy\xa6D\xba\x9cN\xf6.q\x1d#\xf4\x0f\xe6\x19\xb8\xe0\xc2W\xae\xb1\xf0a\xf17G\xcd\xf6\x8d\x8b'\xee\xbe\xfc\x82\xf9\x14y M\xf2\x06\x88ܝl\xb6\xbbt\xf0\xf3S\xd1\b\xaeO\x133\xf9\x84\xc750x\xc1\xa3\xf7X\x98\x04b\x0e\xb3\xceߍFOC\xe2\xb8̋w\x8f\xf1\xe8\xc0\x84T\xca\xec\xecTQ\xf0\xcf\vF\xdc\xfd\xd8\xd3# \xed)\x04\xb8\x9e\x92\xf4\xc2\x11\xc2\x05\xde\xe9\xc4\x03\x97\x16\xabm\xd1\u0a92/R\xbdʕ\x8b.\xcdl\xde\xfet\a\xe9\xe9\xaaw]ޞm\x89\xfeH+ԗ\xd7t\x99\xaa\x0f\xf4\vX\x99d\xb9Y\x14\xa1OI\xc1\x9c]\xf3\xed\xb4#\x7f\xce\xeebj\xfd\x89ɡ\xf8y\xef\xfb`S{\xac\x1e\xe2\xb3:\x0e\xc9\xeb\x1e\xed\x1eu\xdd`\xbbr\xbd\xc41;\xdd\xd6H[ߺi\xba\"\xf9\xa9\xdd3\xdf\fx҆\x15w\xbee%\xc45\t6\xab\x84\xf5-\xb1\xba\x8a\bQR3\xd2F)\x81\xec\xb4=7\xa5\x9a?W\xc3\xef\xf7\xa555\xf4\xba1MՋD0\xf4\xbc\xf4ݧ\xdd\x02q\xbf\x18\xef\xd2P\xf5N\xff\xe1-k\tu\xf6\x99\xea\xfat#\xdf\x14\xbd\x86bӥX+\x83a\\\xe8\xf0\xfc\xb9\xc8g\xb1\xf8Z\x06=\x18u@\xfb\x14\x8cL9iQp\x96\x9b\xc2HW\xecf\xe7K\xf47/\xe8k^\x10C-\x8b\xa2\x92ɔҿ|\x91X\xea\x82\xd1\xd4%\xe2\xa9\xf3\"\xaa\x19\x90'}\xc9)\x1d\xc7Ie\xbc\xe4\x9aMJ\x95m\xber<\xddI\x9c\xd0A\x9cP\r\x9a\xdbiB\xa7\xf0\xb2\x0e\xe1\x04\x1a^(ֺP\xb4u\x89x\xeb\xb2\x11\xd7l\xcc5+93\x7f/\xeb\xec=\xbb\xc8P\x97\xa3\xbf\xa8\x1c\x1f\x95\xb6s\x01\xc2\xe3\xe9\xf8H\t\xb0\x134)\x91\x83\xac\x87\xc6*\r\xe4\xfb\a\xbf\xff<\xa4\xe2պ\xb0\xfe\xe3\xf79|\x9e\x9a\x81ӈ\x90\v[\xc7s\x11\uf10c\x7fJK\xeb\xf9Ԧ\xeb\xa0\x0e\xfe\xafk{v\xec\x15\"\xf4=\x17h\f\xdb\xd5߇zE\x8d\xb0CI$\x9e\xfaZP\xdb:\x1e4\xb8i8!j\xb1\xccV,,\xe0O\xca&\xe9\x1bK\x18\xfaO\xd0\xd1\x10\xb6\x1b\xd5\x1b.-\xee\x06\xe9\xd6ж\xfe\x84̜~\xb2p@\x88_\xbbcC\xb0\xeci\xe0\xef\xc73\xdf\x1d\xe2\xbepg\xb9\xc6)\x0fA\xb9\x95\x17\xf5|\x94{f\xe6\xcc\xe5#\x8di\xeest\x94\xb2\xb1\x94O#{\x8a\xf7\x97\xaf\xe0\v\xbeF\xde\xfe\xea\x84\xde%@⪴\x82\a\xf9\xa8Վb\x92ȟ\xbf1n\xb9\xdc\xfd\xaa\xf4\xa3\xa8v\\6\xbd7\xcb\x06?2m9\x13\xe2\xe8\xf7\x13\x99{_+s\xe4\xbf\xf9\xd9#\x7fL1)\xe0<\x1b\r\xf8am0ťWtw\x9bb\xa3*\xdbՊ\x8f\xa6U\x98x&\xd7A[\xc3\x17e\xb1N\xd2\xf1>Pn`\x83Ʈp\xbbU\xda\xfa\xe0m\xb5\x02\xbe\r\x86:\x16d0.\x9c\xaf\xe1?\xb8H\x0eHSVnN>\xa5\x81\xc9#h\xa7\x15\xceI)\xd8\xd1\xf7&\xb2,\xab\xc8\x0e\xdc\x18\xcbb\aڛ\\[\xe7\xdc\x04i\x1e\xc9c\fZ\xbc\x9a\xf1ͽ̪ؠv\xdd\xdc\xf4\xb7'\x9d\xbb\xe8\xe1MP\xb4@\x01\xee:H\xe7\x9e\x19\x18R\xe7x<=e|\xdc\xff\xca2\xf10\xee\xa8\xf5;\x91\x9b\xc15\x02n\xfa\x10\x8dާ\xe5ƛ\x90\xb8\xa9\xa7\x12ϲ=\x93;\x12\x1f\xad\xaaݾ\x16\xc11K=\x96?\xa9ܗ\xedJ\xa7\xa9\xa6Nv\xdbJ\xcbN\xae&\xa4\xbf\xf3v\xbbS@\xa7I8\xe1g\xea\xb6\x15\xaf\xb5\x19w\xfe\x92FLfb\xde\xce\xc8\xe4\x11\xfaG\xf6Ϛ)\xcc\x1ce6\xdd\x1f\xe8\xdb\xc0\xf9\xc45\x80)bD\xf1m,\xe09\xf86\x93\xd3\xf1m\xbd^ql}\xa9%ȏ\xfb\xd9\xef@\x0eo\xd2ϡ\x85\x9f9\xa6x\x0e\xbf\xc8\xce\x17\xb1;d\x1bP\x92\x83\xe9\xaa\xc0\x83\x9c\x06\xd4n\xdb2Z\x98\x9e\x979\x17t\xf5\x06\xbf͛v\v\x93/\xfd\xf3z\xc1\x87ƍ\xf9\x9c\xe2\x0f\x7f?\x19~\xd2lM\x9eq\v1\xf8\xb0\x11\xe2\xfc+\xdf\xd6\xdf\xe8\xde\b\xfc\xb7\xc1\x88?\xb8i\xfa\x95i\xc9\xe5n\x0e\xf9\xdf°H8\x10 D\x02\x82\b\x12M\x88\xb0( \xa879\xf2\x19\xda&HxCH\x10=N\x06/\x9d \xe7\x1d\"\x87\x95\u009b\xff\v\x00\x00\xff\xff\x97{a\x8a9_\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec=Mw$)rw\xfd\n\x9e|\x18\xdbOU=m_\xfctk\xab{\xecz;ӭ\xd7\xd2\xf6\xc9\x17*3J\xc5(\x13r\x81,uy\xdf\xfew\xbf\b \xbf*\xc9$K\x92gfW\\\xba\x95\x05A\x10\x11\xc4\a\x04\xb0Z\xad.x%\xbe\x816B\xc9k\xc6+\x01\xdf-H\xfcˬ\x1f\xffì\x85zwx\x7f\xf1(d~\xcdnjcU\xf9\x15\x8c\xaau\x06\x1fa'\xa4\xb0Bɋ\x12,Ϲ\xe5\xd7\x17\x8cq)\x95\xe5\xf8\xd9\xe0\x9f\x8ceJZ\xad\x8a\x02\xf4\xea\x01\xe4\xfa\xb1\xde¶\x16E\x0e\x9a\x80\x87\xae\x0f?\xae\xdf\xff\xdb\xfa\xc7\v\xc6$/ᚙl\x0fy]\x80Y\x1f\xa0\x00\xad\xd6B]\x98\n2\x04\xfa\xa0U]]\xb3\xf6\a\xd7\xc8w落\xf3\xed\xe9S!\x8c\xfdS\xef\xf3\xcf\xc2X\xfa\xa9*j͋N\x7f\xf4\xd5\b\xf9P\x17\\\xb7\xdf/\x183\x99\xaa\xe0\x9a}Ʈ*\x9eA~\xc1\x98ǟ\xba^1\x9e\xe7D\x11^\xdcj!-\xe8\x1bU\xd4e\xa0Ċ\xe5`2-*K#\xbe\xb3\xdcֆ\xa9\x1d\xb3{\xe8\xf6\x83\xe5W\xa3\xe4-\xb7\xfbk\xb66To]\xed\xb9\t\xbf:\x129\x00\xfe\x93=\"n\xc6j!\x1f\xc6z\xfb\xc0n\xb4\x92\f\xbeW\x1a\f\xa2\xccrb\xa0|`O{\x90\xcc*\xa6kI\xa8\xfc'\xcf\x1e\xebj\x04\x91\n\xb2\xf5\x00O\x8fI\xff\xe3\x1c.\xf7{`\x057\x96YQ\x02\xe3\xbeC\xf6\xc4\r\xe1\xb0S\x9aٽ0\xf34A =l\x1d:?\x0f?;\x84rn\xc1\xa3\xd3\x01\x15\x84w\x9di \xb9\xbd\x17%\x18\xcb\xcb>\xcc\x0f\x0f\x90\x00\x8cHT\xf1ڐp\xb4\xado\xbb\x9f\x1c\x80\xadR\x05py\xd1V:\xbcw\xb2\x97\xed\xa1\xe4\u05fe\xb2\xaa@~\xb8\xdd|\xfb\xf7\xbb\xdeg6\x90%O)&\f\xe3\xec\x1bM\f\xa6\xfdLev\xcf-Ӏ\x9c\ai\xb1F\xa5a\x15\xa8\x9b7 \x19S\x9aU\xa0\x85\xcaE\x16\xb8B\x8d\xcd^\xd5Eζ\x80\fZ7\r*\xad*\xd0V\x84\xa9\xe7JG\xa3t\xbe\x0e0\xfe\x01\a\xe5j9I\x04C\xc2\xe7'\x14\xe4\x9e\x0en~\b\xd3\xe2OL\xea\x01fX\x89K\xa6\xb6\xbfBf\xd7\xec\x0e4\x82\tXgJ\x1e@#\x052\xf5 \xc5\xff6\xb0\rJ\xbd%a\xb4\xe0\xf5A[h\x02K^\xb0\x03/j\xb8b\\\xe6\xac\xe4G\xa6\x01{a\xb5\xec\xc0\xa3*f\xcd~Q\x1a\x98\x90;u\xcd\xf6\xd6V\xe6\xfaݻ\aa\x83&\xcdTY\xd6R\xd8\xe3;R\x8ab[[\xa5ͻ\x1c\x0eP\xbc3\xe2a\xc5u\xb6\x17\x162[kx\xc7+\xb1\"\xd4%i\xd3u\x99\xffS\xe0\xa8\xf9\xa1\x87\xeb\xc9|s\x85\x14\xe1\x04\aP#:\x81qM\xdd(ZB\xe3'\xa4\xce\xd7Ow\xf7]a\x12fH}\xa2{G\xc2Z\x16 \xc1\x84܁\x9f\xd1;\xadJ\x82\t2\xaf\x94\x90\x96\xfe\xc8\n\x01rH~SoKa\x91\xef\x7f\xa9\xc1X\xe4՚ݐyA9\xac+\x9c\x81\xf9\x9am$\xbb\xe1%\x147\xdc\xc0\xab3\x00)mVH\xd84\x16t-㰲\xa3Z\xe7\x87`\xde\"\xfc\ns\xfc\xae\x82\xac7e\xb0\x9d؉\x8c&\x06i\xcfF\x05\f4\xa8+㳖~!55\xfc:\xc0\xc3\xe9\xb2\xd0+\x18\xb4\x1fvO\x1cn\xcd\x18ʕ\x83\x86:E\xaa!wǴ`\x87\x12\x1e\xca\f&}\xad\x97j\xdfN`2\xaf\xea\xd6\x11\x1cO\xb8J?AY\xa1ژA\xf1\xdeWC\x14\x91>y\xe35\x05\xc3\x1fԬ\xf2ڕ\x9d(7\xean\x0fȷ\x83Ƚ\xf6:\xe1*\x9b\xe4,\x96̈;\xc9+\xb3W\x16m\x9c\xaa\xedX\xad\xc1\x00n\xee6\x83F\x1d\xce#VdÉ\xd1V\xb1'.N9\xed\n\xca\xe5\xcd݆}C\x97\b\x02L\xe6,9\xb3\xb5\x96\xa4\x8e\xbf\x02Ϗ\xf7\xea\xcf\x06X^\x93V\nv\xf9*\x02x\v;\x9c\xf4\x1a\x10\x066\x00\xadq\x0e\x18BM\xd5vM\x0eG\x0e;^\x17\xd6+9a\xd8\xfb\x1fY)dm\xe1\x94\xefl\x9a\xf7D$ny\xa9\x0e\xa0\x13h\xf8\x91[\xfe\v\xd6\x1d\x90\x0ea0\x02\xe2\xd9Od\xdc\x1e#\x03\xc5&['\xa9l\xb3\xeb@\x15\x86]^\xe2<\xbbt.\xf1啫[\x8b®\x84\xa4~\"0]\xefO\xa2(B\xff\xe7Q\xc3\x11\xd7\xf1\xd6ܫ\x9f\x8c\x13\xeb\x14\xe2D\x9a\x8e(\x98J\xe5\xec@\xf5b2&\n`\xe6h,\x94\x9eR\x1dυ\x88Kڱ(<\x18öǀ\xfb\xf8\xb8e]\x14|[\xc05\xb3\xba\x1e\xefvJ\x91\x8d\xd1\xe6+\x18+\xb2\x04\xca\\\x0eI\xe3Z\x8e\x10F\xd3\x0f\x11\xa2\f(\x80.\x0f\x7fD\xb7\xdbS\b}\xa7\xa2\xe8\x10w\x9e*\x8c\xfd\x8fd\x1f\xd1\xdcgh\x84\xaf\xbdq\x17P\x90C!\x15+\x94|\x00\xedzD\xc7)H\x98\x06\x94\xb8<\x02\x15-\xad\x86\x02]\x06\xb6\xab\xd1\b\xaf\x19j\x82\xa8\x8c\bi,\xf0|}\xf9Z̃\xefYQ\xe7\x90\xdf\x14\xb5\xb1\xa0\xef0\x04\xccC\b<\xaae\aL\xfc4\t\xc0\xbb_\x85\xc8\x00\xf9\x90\xb9J+\x8a4cDj=\xb1c\x05.\xf0E\xa6zL[\x17\xab\xa3*\fX\xacr\xf9\xaf\x971%\x8a\x12\xd0\xef\xbdߏa\\CC\x8d\x9eF\x8d@l\xf4,\x94\x95=\x8eˑ\xb0PF\x888\xabr\x16\xb0\x97k\xcdǔj\x18N\x13џ\xcf\xde\x18\x88\x01\x83e\xa8\xf6\x1b\xb1x\xd8\xff?\"\x93\xcfb\xab\xa1u,.$\xb2\xb3\x10\xc6\xf6\xb89\f\x88\x1a\xcc0vF\x9ab\xd0\"\xa4\x83\x89ʭü\xdf3\xcdΙ\t1\xd1o$͋\xf3\x9eDŽ\xea\x0fH\xb0\xbdR\x8f)D\xfao\xac\xd7\x06\xca,\xa3%U\xb6\x85=?\b\xa5\xcdp\xb5\x05\xbeCVۨ\x9e\xe0\x96\xe5b\xb7\x03\x8d\xb0h\x81\xb0YO\x9c\"\xd6t\x98\xc0:\n(Za0\xae\x96\xe9\xc8<\xa2Fl(\x14\x8eE\xa12B\x1c\xbdx\xb2\xee\xb98\x88\xbc\xe6\x05\x19z.37>\xde\xe0\x17sOf\x04\xe2\x04\x7f\xe7N\x84Q \x97zQ\xb6\x92\x80\xeeu\xa9t\xcc\xf3t\xe5\x14L\x9c\f[N\xc1q,$m\x8b\xae\v0\x1e\x15\xe7\xc0\xb6z\xe7\xaa\xe5\x94[\xa0*\xf8\x16\nf\xa0\x80\xcc*\x1d'O\x8a\x10\xb8\x92\xaa?#\x94\x1dѤ\xfd hV\x89\xb6\x05\x03̽\xc8\xf6\xce\xddD)#X,W`Hc\xf0\xaa*\"V\xa8-\xb3\x92\xe1;\x9bS\x1amIP\x1fC\xb81EҖD\x1dܖ\x19mܧz#6oD\xef\xa1)\x9f%웓\xe6//\xecHn\x01\x86\x9c>\U000bab98\xb0\xe1k\nԞ\x1fh\xfe\xce\x18w\xdel\xd9\f[\xbf\xf8ly\x11\xae5h\xfc\x9d0\x8d\x8c՝\xb7U\x8b\x18\xf6s\xb7\xe5\x15\x13\xbb\x86a\xf9\x15ۉ\xc2\x02\xf9Rs\x88v\x1c\x9dYν$\x81Rm/\x96\x92\xdbl\xff\xa9Y\xd6Nh1\xa0\xd5\x10\x80\xf3\xcbC\fC\f<\x9d.\n4\xc0$\x90\x9dA\x91\x9b\xd6\xc4xn?\xef\x8aq\xf6\bG\xe7Y\x8d.\x0f\x8d\x15d-o@j\xa0\xcdER#\x8fp$P~\xb70\t\xde\x12Qq\xe5\x11\x8e\xa9U\aDE\xfc\xfc>\x85\xa3.~\xa0Q\xa4L\xa5\xb64D\xf5s\x87Y\x956X\xb6L)\x85\x12(~\xe6\xb0\x1b\x86\xf5\xb6\xc8\x1f\xe1\xf8\x83q\xec\xc3Y\xb3\x17\xd5\x02\n\xa0¦%\x19\xb5k\xf6\x86\xbf\xf1B\xe4Mg4O\x16@\xdc\xc8+\xf6YY\xfc\xe7\xd3wa\x10E\x99\xb3\x8f\n\xccge\xe9˫\x92\xd8\r\xe2L\x02\xbb\xc64-\xa53\vH\x97E\xfd\xb78\x90\tE\x11m\xd8&\f\xdbH\x8c\xcf\x1c}\x96\xb0i\x0f\x019\x87VY\x1b\xda]\x96J\xaeܒ\x96\xefm\x01\xd0.^\x9eUJ\xf78u\xb5\x10\xe2(\x8a\x1e\xbd{\xb4V\ue5d3}\xf9\xa9\xa2\xa1*x\x06y\xd8e\xa3$\x00n\xe1Ad\xac\x04\xfd\x00\xacB\xbb\x91.T\v4\xb9+gHa\xbak\x11\x8a7\v#{\xdace\x85\xb3>\xb1f`sR\xf5Ȏ\xfft\xf5\xb4Q\x92y'\x7f(\x89\xfa\xdd\x14\xb5e\x96e!\xbfN}\x10\x87\xa4s?JN\x1bO\x7fE\xf3J\xe2\xfd\xb74kȅ6k\xf6\x81\x12\xf4\n\xe8\xb6\x0f\xab\x84\x9d\xae\x92@\"&\xc20\x94\x93\x03/\xd0}@\xe5-\x19\x14ΙP\xbb\x13\x0f*M\xc5<\xed\x95q6\xbf\xd9\x18\xbb|\x84\xa3ߜ\xedj\x89ˍ\x8c\xae\xda\xf7\v\xea\xfc\x13\xa5\xd5x-J\x16GvI\xbf]\x92c\xb6d\x8a\x9c\xe1\xbc-\x90\xea\x05U\xbf\xaf\x1e\xeb-h\t\x16̪\xe4\xd5\xca\xcf\x06\xab\xca\xe8\x1e\xa7+\x94F\xb7$\x8c\xc08=x<ظI6C\xf7\x7f\x8e\x02\xc9\xf3\xa1R&\x92i\x11A\xebV\x19\xeb\x16\x0f{\xae\xfa\xc8\xeabJ\xe4\xe8W\x1c\x19\xdfY\xd0\xccX\xa5Cb\x17\xaa\xec\xc1\xe2:J\x8d\x99\x97\x1b\xb7O\xe4W2\x1d`\fP/[\xed\xe2\xec\xc1\xa5۫\xc2\xff\xcf\xc3\xcc\xc8\xd1\"ؕV\x19\x98h6B[\x12\xad\xce\xccbo\xb3\xd0\xcb]\xe0\xb7KR\xeb)\xcbС,s㑴g\x04E\x9f\xbew֬Q\x85\xe1\xdf)\xa2|\x0e\x8e\x8cr\xbb˒\x0f\x93\f\x93ѽq\xad\xc3\x04\xf4\xc0\\\xb0\xa5\x1fjRH\xcb|n/\x92\xbf7\xa7\xa5\x14rC\x1d\xb1\xf7\xaf\xe6\xe8\xb0`\x06b\x19Ice\xc0\x0e߾eH\xf3!5\xf6e!UM\xd1>\x8f\x86\x1egOwA\xd29\xc5\xd0\x11\x97\xcav\x17z|O?\x18\xb6\x13\xda\xd8\x16\xe1\x05P\x85\x99\xc8z\x1a\x1d\xde\x19\xf1\xa9\xfc\xa4\xf5\xd9\xe1\xe9\x17\u05fa\xb3$\xb9WO>\xc1sIP\x1e\x88\xbf\xe7\a`bDŽe 3UKZ,Cu\x81\xdd,\x80\xe8\x98\xe8\x8cI\xa2\xcd\xec4\x96u\x99N\x90\x15I\xa7\x90\xb3+k\xdd&?q\x91\xb6\xb2\xc5\xcec\xab\x9dJ\xa2\x1c+\xfd\xccP\x9fM\xd9\xcd\xe4-\xf9wQ\xd6%\xe3%\xb2eI̹sy\x98!\xed\xd7\xf1\xfa\x89\v\xebOS\xb8M\xd9e\xda4SeU\x80\x85\x90a\x99)iD\x0e\x8d\xfb\xe0\xf9?\x9a\xaf\x1a+\x9c\xed\xb8(j\xbd@G/\xe6\xccҘϫ\xa7\x97\x0f\xe4\xd2\x11Y\x111\x13\x17\xec\x178\xdc\xf3\xf6\xa3\xd2\xcb\\\xe6[\r/\xef\x9aVZ(ʁ\x9d\xf1Nga\x92\xf7\xda\xf7N\xbd\xf0ry\x8c\xb9\xa7\xb3P\t\x937\xf7\xb4)o\xee\xe9\x9b{\xfa\xe6\x9e\x0eʛ{\xfa枾\xb9\xa7\xe3\xe5\xcd=\xed\x947\xf74\xd9~\xa4`\xb8\xa2\x95ۉ\nIX%\xa6o̡=ӗ\xcfR\xf2gA\x96dWo\xc6[\x8e\x9c\x05Zt\x86\xc4t\x8c^\x93n\x8dS2L&w\xa64\xc1\v\x7f\x81\xb36\x01\x81\xb3\xcf\xdal&\x01\xbc\xe0Y\x1b\x8f\xe9p\xed\xfc\x05O\xda\x04Z,?\x84q\xe5ӘJ\xe0aK\xc8\xe5\xa0\xe4\xb1nc^l\x0f\x8f\xd1:\xbfq\xd6\xfdI\xb6\xe6\xf9\"\xf3\xffr~'\"6'\xa7S#P\x85A\xb9\xfacp\xe2,\xdaG\xa9\xed\xfe\x17\x1b]KX\xa7x\xdd5\x03\xddT\xcb~\xca\xeb\x1fG\xb0ϑ\xe4\xd4\xf371\xe7<\xae\xdb:\xc4Խ\xf3\x1e\xbfoZZ(\xbfTޒ\xa5\x9f{ߌ4{\xc6\xc9wn\x8e2\xdbk%Um\xfc\n\x0f\xf6\xf0!sW\x01\x84\x8e\xcc\x12e\xf0\x9e\xedU\x1d9\xe31Cׄ\xcc\xdbx\xbe\xad\xcf\xe0\x00\xcb\x0f\xef\xd7\xfd_\xac\xf2ٷ\x11\xac\x9f\x84ݻ\xfb\x18x\x9e\xa3\xa3\xde9\xe2\x13&\xaf\xbf\x93e(x\x11\x88J3)\n'\x95\x01B߀~\xa9ܒ\xdf\xd9~\xcb\xfc\xc2Sz\x8e\xee\xd2\xcc\xdc&\x97r\xdeK~F>\xee\xb2\xc3R\xb3\xb9\xb7)H\xb3\x94\x8c\xdb\xf1\\\xda\x19\xa8K\xf2lS\xd7\x14\x13rj\xd33i\xd3\xc8\xc3\xe8&\xa5\xd4\xfc\xd9\xe4(45W\xf6u2d\x13\xf3b;ٮ\xb3 \xcf̆M&XZ\xe6kr\xbek'\x8bu\x9eZ\x13Y\xae㹫\xb3 \xc7r[S2V\x93pM\xceSm\xb2O\xe7wF\x9e\x95\x9d\xfa\xf2\xe7`^r\xddb:\xd74)\xc34imc\x1e\xe7\xa4\x1cҥ\x99\xa3IT]\x9a%\xdad\x80Nt\x9c\x94\x1bz\x9a\xf795\x94ٌ\xd0x\xb6\xe7\x14ر<Є\x1c\xcf\t\x90\xdd\xec\xcf\xc5n\xc0\xac4\xcdVX\x9a\xbb9~?Z(\xf3ֹ\xf8-d\xf6\xb9dR\xba\xe74\xa7\x04w_\x06MPZ\x82\x9f8\xe6\x88\xc7Ce瞟\xe1\x88G@nv\xac\xac\v+\xaa\xa2sA\x99\xddñ\xb9\xf2\xe7WE\a\u05f7G\x82\xf6\xe5k#\xf21\x90\xfd\x90\x82\x1b\xf6\x04E\x81\xff\x9eP!s\xd7\x01fj\x05h\xa5\xe2\x1b\x81\xfe\xaa#\x7f\x97\xe0\x95[\x16\xa3S\xfdd\x01K\x844}\x01֤)\x99v\x8f\x9dWO\xdf\xfeR\x83>2\xbas+\xf8AQ1kO{\xfa\xc9l0&\f\xca\xc7k1w)e_\x19\xc5gC\xa3\x02\xd8\a\xe9\f\xf3\x10W\x82\x85Z\xa7\r\xa7\xa6\x94-FO1\x10R5\x10\"\xedS\xbc\xef%\xc7\x1f_#\xb8z\x89\xf0*\xc9\x11y\x8d\x10뵂\xac\xa5a֒䍤㋯\x11l-\t\xb7\x16\xf9\x8c\xe9\xc7\x13_\xebX\xe2+\x84]g\a^\x8bH\x97z\xecpq\xf8\x950\xbe\x99c\x86'>Z\x02\xc8\xe8\xf1\xc2\xf1\x10,\x01\xe2ɱ\xc2\xd9 ,e\x1e\fôg\x1f\x12LNdZ\xb4\x9b\x9e\x9a\x84\x94\xb6\xd1=\x7f\xf8/\xf1\xd0_\xe26x\n\xf6\x89\x87\xfb\x96\x1f\xeaK\xa4\xf3\x99\xe1\xd9d\u05c9\x87\xf7\x16\x05hg\x86h\x93\x10\xa7\x0e\xebM\ai\xd3\vp\xc3Czg\xb8\x13\t\x12\x96Pe\xf9A\xbbgo\xc6(\x9d\x83\x9e\xdd\xd7Z\"γ\x82<\x88\xa3\xfa\xfd\x0fvt\u008d\xa8X\xab\xbbg\x16\xe3\xa8j\xee\x1d\xc9؟\x84\xf4\xbb\xf5(\xb8\x1d\x9f\xa4\xb7\xf1\xd6:L\xf1}\x9d\xd6K\xf5\x17\xab\xbb\x1d;\x03\x15״\x8f\xbf=\xba\xa4 \xb3f\x9fx\xb6oz\x88\x80\xa4~\xf7ܰ\x9d\xd2%\xb7\xec\xb2\xd9\n}\xe7:\xc0\xbf/\u05cc\xfd\xa4\x9a\xf4\x91νb\x11\xa8F\x94UqĈ\x89]v\xc1ti^\xd6ɂ\x88\xc5(\xd8\\\xf8\v\v\a\x97\n\xefTQ\xa8\xa73\x97.x%\xfe\x8b\xde1I[\x1b\xfbp\xbb\xa1\xeaA\xaa\xe8\r\x94&{\xae\x91\xb1-L+\xf4v\xe0\xe4zt\xa1\x8ed\xaf6\x7fN@\xa4W\x04\x82\x9f\xe1\xd5x\xa6P\x8b\xddn\x1c\x96k\x12,.\x8fL\xf9{\xe2\x85\xceW\x15\xd7\xd1M=\xe6\xe5\xc1\\\xf50\fv|n\x05kҬ\x9d\xbe\x8a\xd0-=\x9a\x87\a\x12h\xb3\xf7X\xf5\xb7щ\xd2\x1dz>\a\xa7\xe9\x83˳G\x96_\x01\xa7i\x97iET\x8c\xfc\x14M\xc7{\xf1\xd5C\xe3/\x91\xffE\x1d\xe0ct\x15\xb1\xffd\xc0\xa0\xc9H\x02]\x80:umz\x9b5\x17\xbf\xce\xfa\x052\xe2\x02*\xfe\xe2\xeb\x05\xe3\xf3-\xc6^i\xf0\xf7\x7f\a\xd8\x13\xb6\r\xa7\xec\xed7\n7\x1bu\xe9g\xb8\x0f&\xc3R\xe1\xe0\xaa\xd6\b\xc8\xd83\v/E-\xab4\x7f\x80\x9f\x95{\t#\x85Z\xfd\x16\xbd\xc7P\xbc3\x17\xb2\x89\xfd\\\x8b)s?\xb6!\xc0\xf6\x90\xc1\xc9u\xf4\x88홷\xed[[$\f\xee\xfe\xfeg7 +JX\x7f\xac]\x86\t\xea]\x03H\xe90P\xd7h\x1b\xd7N{\xf5D\xf7\xb9w\x9f\xab\xe8<\b\x04t\xa8\x81\xd2F\xcf\x1a͡\xf7 D ]\x8a\xb0\x7f\x1bo\xd9\xf1\xa1:L\x9cJ!S\xbb(,n\x8c\xca\x04\xb9]\xb4fNg\t^\xef&\xe2)\xf7yB\x7f\xd6\x06\xbe{\xd6.\x87\xce>\xb26¿\xe6Y\x9d\xe8;3.\x90p\x8f\xa0\xad\x10\xfey\xec\x1c\x9d\at\x8d\xf5\xdc\x13DX\xa79\a\xe6\tM\r\xc3\xf5\xd7w1\xd4\xc7\x0f\xf6\xac\xd8g8u\xe4W\xec\x93\xc4A\x9c\xdawwz\arZD\x1d{\x90lr\x88\x87\xa6\x15\x1d\x9d\x1a\xd1\x16}57\xa8>H\xec\xa4\xe7L\x9a*\xee\x98\xd4\x18[\xffY\xec\xdc\nw\x86c\xfa\x97\x93\x1aQ\xc55\xa9\xb4b\nktJ\x9d|4\xa0\x0f\xf4~H\x10\x12oû_\xeam{\x1b9\xfb\xeb\xdf.\xfe/\x00\x00\xff\xff\x80\xea<õr\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V\xcfo\xeb6\f\xbe\xe7\xaf \xb0û\xcc\xce\xebv\x19r\x1b\xba\x1d\x8am\x0fE\xf3л\"\xd3\tWY\xf2H*]\xf6\xd7\x0f\x92\xec&\xb1\x9d\xb5\x1b0\xdd\"\xf1\xc7Ǐ\xe4\xe7TU\xb52==#\v\x05\xbf\x01\xd3\x13\xfe\xa9\xe8\xd3/\xa9_~\x90\x9a\xc2\xfax\xb7z!\xdfl\xe0>\x8a\x86\xee\t%D\xb6\xf8\x13\xb6\xe4I)\xf8U\x87j\x1a\xa3f\xb3\x020\xde\a5\xe9Z\xd2O\x00\x1b\xbcrp\x0e\xb9ڣ\xaf_\xe2\x0ew\x91\\\x83\x9c\x83\x8f\xa9\x8f\x9f\xeb\xbb\xef\xea\xcf+\x00o:܀ \xa775\x1a\x85\U0004f222R\x1f\xd1!\x87\x9a\xc2Jz\xb4)\xfe\x9eC\xec7p~(\xfeC\xee\x82{\x9bCms\xa8\xa7\x12*\xbf:\x12\xfd\xe5\x96ů4X\xf5.\xb2qˀ\xb2\x81\x1c\x02\xeb\x97s\xd2\nD\xb8\xbc\x90\xdfGgx\xd1y\x05 6\xf4\xb8\x81\xec\xdb\x1b\x8b\xcd\n` $Ǫ\x06.\x8ew%\x9c=`gJ\x12\x80У\xff\xf1\xf1\xe1\xf9\xfb\xed\xd55@\x83b\x99zʹ.T\x06$``@\x01\x1a\xc0X\x8b\"`#3z\x85\x82\x12ȷ\x81\xbb\xdcɷ\xd0\x00f\x17\xa2\x82\x1e\x10\x9e3\xe5Ce\xf5\x9bIϡGV\x1a\xd9\x18\xdc\xceCvq;\xc1\xfa)\x95S\xac\xa0IӅ\x923\r\x94`30\x00\xa1\x05=\x90\x00c\xcf(\xe8u\x8a2\xf3ӂ\xf1\x10v\xbf\xa3\xd5z\xe0AR\xb3\xa2k\xd2P\x1e\x91\x15\x18m\xd8{\xfa\xeb-\xb6$BRRgt\x9c\x93\xf3!\xaf\xc8\xde88\x1a\x17\xf1[0\xbe\x81Μ\x801e\x81\xe8/\xe2e\x13\xa9\xe1\xb7\xc0\x98\xc9\xdc\xc0A\xb5\x97\xcdz\xbd'\x1d\x97ˆ\xae\x8b\x9e\xf4\xb4\xce{B\xbb\xa8\x81e\xdd\xe0\x11\xddZh_\x19\xb6\aR\xb4\x1a\x19צ\xa7*C\xf7y\xc1\xea\xae\xf9\x86\x87u\x94OWX\xf5\x94&K\x94\xc9\xef/\x1e\xf2B\xfcC\a\xd2:\x94\xf9(\xae\xa5\x8a3\xd1\xe9*\xb1\xf3\xf4\xf3\xf6+\x8c\xa9s3\xa6\xecg\xdeώrnA\"\x8c|\x8b\\\x9a\xd8r\xe8rL\xf4M\x1fȗ鲎\xd0O闸\xebHe\x9c\xddԫ\x1a\xee\xb3\xe2\xc0\x0e!\xf6\x8dQljx\xf0po:t\xf7F\xf0\x7fo@bZ\xaaD\xec\xc7Zp)\x96S\xe3\xc2\xda\xc5\xc3(s7\xfa\xb5\xb0\xdd\xdb\x1em\xea`\"1ySK6\xaf\a\xb4\x81\xc1,\xb9\xd4\x1fB\x92=\xfe%\x96AI\n\x9a\x89\xbe\xa4\xfd|\x1fͲ\x9c䗃\x11\x9c^N0=&\x9bi~G-ړuXB\x145\xc1\xf7\xa1\xa4\x83>v\xf3\x9c\x15|\xc1ׅ\xdbG\x0eIY\xb3\xae_\x9f\x1b\xb3\x01\xe5{\xb3'?+wZY\xb1\xca߰K\xa9\xbe\x10\xe8!\x10p\xf4>\xed\xedL!3\x90\xa9\x92\xcflH\xb1[@\xb3\x88\xe7\xc1\xb7!\x7f\xf0MJl\xb4\xec\x13\x0e\xcd\x1e\xf2\x14\\\v\x01o\xf7\xba\x9c\xb9x}\x88\xd0r\xf2\x97\xf4\xbf9'\xb9!\xc6\xc5\xdcUF\xb5\xf8\x902.1\xbe\xbc_\x03\xca\xe8\x9c\xd99܀r\x9c{\x17_\xc3lNө\x19G\xed+u(j\xba\xfe\xbd\x01\x9a9\xa4=y=\xa0\xbf\xb5\r\xf0j\xa6*\x7f\x95\x19v\xa7[\xae\xf7o\xff\x01\xe7+UFw\x03I\xbb+\xa5\x05\xce>D\xcab\xf7\xcaH/\xfe\xf3\x98\x11\xb2\xbd\xb4\x1d5\xe3j5\xc6?\"\xf3\x1anBXl\xf6\xec2\x87o.\xca\x13\rl\xf6c\xc1\x7f\a\x00\x00\xff\xff\xb1J-\xe7\xa6\v\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VA\x93\xdb6\x0f\xbd\xfbW`&\x87\xbdDr\xf2}\x97\x8e/\x9d̦\x87L\x93f'N\xf7N\x8b\x90\x8d\x9a\"U\x10\xd4\xc6\xfd\xf5\x1d\x90\xd2\xdak\xcb\xc9n\xa7\xd5\xc5c\n\x04\x1f\xde\xc3\x03UU\xd5\xc2\xf4t\x8f\x1c)\xf8\x15\x98\x9e\xf0\x9b\xa0\xd7\x7f\xb1\xde\xff\x14k\n\xcb\xe1\xedbOޮ\xe06E\t\xdd\x17\x8c!q\x83\xef\xb1%OB\xc1/:\x14c\x8d\x98\xd5\x02\xc0x\x1f\xc4\xe8rԿ\x00M\xf0\xc2\xc19\xe4j\x8b\xbeާ\rn\x129\x8b\x9c\x93OG\x0fo\xea\xb7\xff\xab\xdf,\x00\xbc\xe9p\x05Cp\xa9\xc3\xe8M\x1fwA\\hJ\xcez@\x87\x1cj\n\x8b\xd8c\xa3Gl9\xa4~\x05\xc7\x17%\xc5x|\x81~\x9f\xb3\xad\xc7l\x1f\xc7l9\xc0Q\x94_\xbf\x13\xf4\x91\xa2\xe4\xc0\xde%6\xee*\xb2\x1c\x13w\x81\xe5\xb7\xe3\xe9\x15\fѕ7\xe4\xb7\xc9\x19\xbe\xb6\x7f\x01\x10\x9b\xd0\xe3\n\xf2\xf6\xde4h\x17\x00#?9]5Q\xf3\xb6dlvؙr\x0e@\xe8ѿ\xbb\xfbp\xff\xff\xf5\x93e\x00\x8b\xb1a\xea%\xb3<_\"P\x04\x03\x13\x12x\xd8!#\xdcg>!J`\x8c#\xe8Ǥ\x00\x13\xfeX?.\xf6\x1czd\xa1\xa9\xf8\xf2\x9c\xf4\xd7\xc9\xea\x19\xae\x1b\x85^\xa2\xc0jca\x04\xd9\xe1T>ڱZ\b-Ȏ\"0\xf6\x8c\x11\xbd\x1c\x85<>\xa1\x05\xe3!l\xfe\xc0FjX#k\x1a\xd5&9\xab\xfd8 \v06a\xeb\xe9\xaf\xc7\xdc\x11$\xe4C\x9d\x11\x1c5?>\xe4\x05\xd9\x1b\a\x83q\t_\x83\xf1\x16:s\x00F=\x05\x92?ɗCb\r\x9f\x02#\x90o\xc3\nv\"}\\-\x97[\x92\xc9WM\xe8\xba\xe4I\x0e\xcbl\x11\xda$\t\x1c\x97\x16\at\xcbH\xdb\xcap\xb3#\xc1F\x12\xe3\xd2\xf4Te\xe8\xbe\xf8\xa0\xb3\xafxtb\xbcy\x82U\x0e\xdaEQ\x98\xfc\xf6\xe4E6\xc2w\x14P\x0f\x94F([K\x15G\xa2uI\xd9\xf9\xf2\xcb\xfa+LGg1\xce\xd9ϼ\x1f7ƣ\x04J\x18\xf9\x16\xb9\x88\xd8r\xe8rN\xf4\xb6\x0f\xe4%\xffi\x1c\xa1?\xa7?\xa6MG\xa2\xba\xff\x990\x8ajU\xc3m\x1e6\xb0AH\xbd5\x82\xb6\x86\x0f\x1enM\x87\xee\xd6D\xfc\xcf\x05P\xa6c\xa5\xc4>O\x82\xd39y\x1e\\X;5\xd88ޮ\xe85\xef\xe4u\x8f\xcd\x13\x03i\x16jitv\x1b\xf8\x8cW3\xf9|>_\xfd$|\xde\xe0P\x86|K\xdb\xf3U\x00cm\xbe\"\x8c\xbb\xbb\xba\xf7;\x84\xcd\xd4}\x9bO\xd2Fm\x03+\xa2\x81,r5\xd59\"I<\x16L\xe8l\xac/R^\xe1<\x97\xc2hUc\xe3.\x81>E\xf2\x18\x98\xef8C\xbeP~L\x90[\x8f\xbbq\xc6zAo\xf3P\xbf@\x13r\x0fG\xb4\xf0@\xb2+\xe6p\xa7\x97\xd4\xf3T\xd0g\x8f\x87\xb9\xe53\xec_w\xa8\x91e\x9c\"Dl\x18EqDtj^uf\r\xf0)\xc5l/3\x9b\x11tD\x90\x9dv\xef\xf1pI4\xfcH\xdc\xf1\xbe\xff1\xe4\x1b\xbd\x17'\xc0\x8c-2z\x99\xb5\xb8~b\xb0G\xc1\xecr\x1b\x9a\xa8\x06o\xb0\x97\xb8\f\x03\xf2@\xf8\xb0|\b\xbc'\xbf\xad\x94\xf0\xaa4B\\\xe6\xef\x86\xe5\xab\xfcs\xa5䯟\xdf\x7f^\xc1;k!\xc8\x0eYUk\x93\x9b\x1a\xed\xe4\xb6{\x9d'\xeekHd\x7f\xbe\xf9'\xbc\x84\xbe8\xe7\x19ܬs\xf7\x1f\xf4\xe6Π\x94\xa2uQ%0\xe8\xdcT\xb1\xbbQ\xcd2\x1f\xe6\x1aq´\t\xc1\xa1\xb9l=\x9d\xbe\xc4h/!Uz\xc2Kl\x06\xf0\xad:\nUu\xa6\xafJ\xb4\x91\xd0Qs\x16=\xf9\xfc\a\x96\xbc\x1b\xc3t<(\aӶ\xa9m\xcaWL\xfe\xa61[\xbc6\x16f\x14\x99/\xbcz<\xe0Y\x03]\x8c\xa4\xf8\U000917b7\x8d\x91\x9bq\xac7\x89\xb5\xfdǜ3\x9f?\xff\xceX\xefw&\xcex\xf3\x19\xa8\xeft\xe7$\x83\xa3\x16\x9bC\xe3\xb0$\x84\xd0\xce\xf4ދ \xeb\x83>us\x8d\xf8n0\xe4\xcc\xc6\xe1̻߽\xb9\xfa\xf6\xaa\xf8\xb3z^,F\xfdƱ+\x10N%\xf7\xd8e\xe3\xca\xdf\x01\x00\x00\xff\xff\xec\xa0\xe0\xa1k\r\x00\x00"), } var CRDs = crds() diff --git a/config/crd/v2alpha1/bases/velero.io_datadownloads.yaml b/config/crd/v2alpha1/bases/velero.io_datadownloads.yaml new file mode 100644 index 000000000..20d604153 --- /dev/null +++ b/config/crd/v2alpha1/bases/velero.io_datadownloads.yaml @@ -0,0 +1,169 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.12.0 + name: datadownloads.velero.io +spec: + group: velero.io + names: + kind: DataDownload + listKind: DataDownloadList + plural: datadownloads + singular: datadownload + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: DataDownload status such as New/InProgress + jsonPath: .status.phase + name: Status + type: string + - description: Time duration since this DataDownload was started + jsonPath: .status.startTimestamp + name: Started + type: date + - description: Completed bytes + format: int64 + jsonPath: .status.progress.bytesDone + name: Bytes Done + type: integer + - description: Total bytes + format: int64 + jsonPath: .status.progress.totalBytes + name: Total Bytes + type: integer + - description: Name of the Backup Storage Location where the backup data is stored + jsonPath: .spec.backupStorageLocation + name: Storage Location + type: string + - description: Time duration since this DataDownload was created + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2alpha1 + schema: + 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: DataDownloadSpec is the specification for a DataDownload. + properties: + backupStorageLocation: + description: BackupStorageLocation is the name of the backup storage + location where the backup repository is stored. + type: string + cancel: + description: Cancel indicates request to cancel the ongoing DataDownload. + It can be set when the DataDownload is in InProgress phase + type: boolean + dataMoverConfig: + additionalProperties: + type: string + description: DataMoverConfig is for data-mover-specific configuration + fields. + type: object + datamover: + description: DataMover specifies the data mover to be used by the + backup. If DataMover is "" or "velero", the built-in data mover + will be used. + type: string + operationTimeout: + description: OperationTimeout specifies the time used to wait internal + operations, before returning error as timeout. + type: string + snapshotID: + description: SnapshotID is the ID of the Velero backup snapshot to + be restored from. + type: string + sourceNamespace: + description: SourceNamespace is the original namespace where the volume + is backed up from. It may be different from SourcePVC's namespace + if namespace is remapped during restore. + type: string + targetVolume: + description: TargetVolume is the information of the target PVC and + PV. + properties: + namespace: + description: Namespace is the target namespace + type: string + pv: + description: PV is the name of the target PV that is created by + Velero restore + type: string + pvc: + description: PVC is the name of the target PVC that is created + by Velero restore + type: string + required: + - namespace + - pv + - pvc + type: object + required: + - backupStorageLocation + - operationTimeout + - snapshotID + - sourceNamespace + - targetVolume + type: object + status: + description: DataDownloadStatus is the current status of a DataDownload. + 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 DataDownload's status. + type: string + phase: + description: Phase is the current state of the DataDownload. + enum: + - New + - Accepted + - Prepared + - InProgress + - Canceling + - Canceled + - 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 + served: true + storage: true + subresources: {} diff --git a/config/crd/v2alpha1/bases/velero.io_datauploads.yaml b/config/crd/v2alpha1/bases/velero.io_datauploads.yaml new file mode 100644 index 000000000..ea354f1d4 --- /dev/null +++ b/config/crd/v2alpha1/bases/velero.io_datauploads.yaml @@ -0,0 +1,193 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.12.0 + name: datauploads.velero.io +spec: + group: velero.io + names: + kind: DataUpload + listKind: DataUploadList + plural: datauploads + singular: dataupload + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: DataUpload status such as New/InProgress + jsonPath: .status.phase + name: Status + type: string + - description: Time duration since this DataUpload was started + jsonPath: .status.startTimestamp + name: Started + type: date + - description: Completed bytes + format: int64 + jsonPath: .status.progress.bytesDone + name: Bytes Done + type: integer + - description: Total bytes + format: int64 + jsonPath: .status.progress.totalBytes + name: Total Bytes + type: integer + - description: Name of the Backup Storage Location where this backup should be + stored + jsonPath: .spec.backupStorageLocation + name: Storage Location + type: string + - description: Time duration since this DataUpload was created + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2alpha1 + schema: + 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: DataUploadSpec is the specification for a DataUpload. + properties: + backupStorageLocation: + description: BackupStorageLocation is the name of the backup storage + location where the backup repository is stored. + type: string + cancel: + description: Cancel indicates request to cancel the ongoing DataUpload. + It can be set when the DataUpload is in InProgress phase + type: boolean + csiSnapshot: + description: If SnapshotType is CSI, CSISnapshot provides the information + of the CSI snapshot. + nullable: true + properties: + snapshotClass: + description: StorageClass is the name of the snapshot class that + the volume snapshot is created with + type: string + storageClass: + description: StorageClass is the name of the storage class of + the PVC that the volume snapshot is created from + type: string + volumeSnapshot: + description: VolumeSnapshot is the name of the volume snapshot + to be backed up + type: string + required: + - storageClass + - volumeSnapshot + type: object + dataMoverConfig: + additionalProperties: + type: string + description: DataMoverConfig is for data-mover-specific configuration + fields. + nullable: true + type: object + datamover: + description: DataMover specifies the data mover to be used by the + backup. If DataMover is "" or "velero", the built-in data mover + will be used. + type: string + operationTimeout: + description: OperationTimeout specifies the time used to wait internal + operations, before returning error as timeout. + type: string + snapshotType: + description: SnapshotType is the type of the snapshot to be backed + up. + type: string + sourceNamespace: + description: SourceNamespace is the original namespace where the volume + is backed up from. It is the same namespace for SourcePVC and CSI + namespaced objects. + type: string + sourcePVC: + description: SourcePVC is the name of the PVC which the snapshot is + taken for. + type: string + required: + - backupStorageLocation + - operationTimeout + - snapshotType + - sourceNamespace + - sourcePVC + type: object + status: + description: DataUploadStatus is the current status of a DataUpload. + 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 + dataMoverResult: + additionalProperties: + type: string + description: DataMoverResult stores data-mover-specific information + as a result of the DataUpload. + nullable: true + type: object + message: + description: Message is a message about the DataUpload's status. + type: string + path: + description: Path is the full path of the snapshot volume being backed + up. + type: string + phase: + description: Phase is the current state of the DataUpload. + enum: + - New + - Accepted + - Prepared + - InProgress + - Canceling + - Canceled + - 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 in the + backup repository. + 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 + served: true + storage: true + subresources: {} diff --git a/config/crd/v2alpha1/crds/crds.go b/config/crd/v2alpha1/crds/crds.go new file mode 100644 index 000000000..00f9ec3fd --- /dev/null +++ b/config/crd/v2alpha1/crds/crds.go @@ -0,0 +1,60 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by crds_generate.go; DO NOT EDIT. + +package crds + +import ( + "bytes" + "compress/gzip" + "io" + + apiextinstall "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install" + apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/client-go/kubernetes/scheme" +) + +var rawCRDs = [][]byte{ + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcX\xcdr\xe4\xb8\r\xbe\xfb)P\x93\x83/#yg\x93J\xa5\xfa6\xd3N\xaa\\ٙt\xad\xa7\xfaNI\x90\x9a;\x14\xc9\xf0\xa7\x1d'\x95wO\x81?jI\xcdvۻ\x99\xd5M$\b|\x04\xc0\x0f \xab\xaa\xbaa\x9a\xef\xd1X\xae\xe4\x06\x98\xe6\xf8/\x87\x92\xfel\xfd\xed/\xb6\xe6\xea\xee\xf8\xe1\xe6\x1b\x97\xdd\x06\xb6\xde:5\xfe\x8cVy\xd3\xe2=\xf6\\rǕ\xbc\x19ѱ\x8e9\xb6\xb9\x01`R*\xc7h\xd8\xd2/@\xab\xa43J\b4Հ\xb2\xfe\xe6\x1bl<\x17\x1d\x9a\xa0<\x9b>\xfeP\x7f\xf8\xb1\xfe\xe1\x06@\xb2\x117@\xfa:\xf5$\x85b\x9d\xad\x8f(Ш\x9a\xab\x1b\xab\xb1%ŃQ^o\xe04\x11\x17&\xa3\x11\xf0=s\xec>\xe9\bÂ[\xf7\xf7\xb3\xa9\x9f\xb8uaZ\vo\x98X\xd9\x0e3\x96\xcb\xc1\vf\x96s7\x00\xb6U\x1a7\xf0\x85Lk\xd6\"\x8d\xa5=\x05(\x15\xb0\xae\v^bbg\xb8th\xb6J\xf81{\xa7\x82\x0emk\xb8v\xc1\vsX`\x1dsނ\xf5\xed\x01\x98\x85/\xf8t\xf7 wF\r\x06m\x84\x05\xf0\x8bUr\xc7\xdca\x03u\x14\xaf\xf5\x81YL\xb3ѕ\x8fa\"\r\xb9g\xc2k\x9d\xe1r(!\xf8\xcaG\x84Λ\x10B\xdaw\x8b\xe0\x0e\xdc.\xa1=1K\xf0\x8c\xc3\xee\"\x900O\xea\xacc\xa3^#\x9a-\x8d\x90:\xe6\xb0\x04h\xabF-\xd0a\aͳü\x8d^\x99\x91\xb9\rp\xe9\xfe\xfc\xa7˾HΪ\xc3\xd2{%\x97\x8e\xf9D\xa30\x1b\x8eH(J\x03\x9a\xa2w\x94c\xe2\xb7\x00q\xa4\xe0\xd3l}D\x12\xf5\xceǯB\xa1\x94\x03Ճ; |b\xed7\xaf\xe1\xd1)\xc3\x06\x84\x9fT\x1b\xc3\xf7t@\x83A\xa2\x89\x12\x94\xbd\xc0)v\xca\x14C\xa7\xb1\xad\xa3lR\x96u\xad\xe2\xb74\xf4\x7fϭ\xd6 +\xe6V\xa6\x9a:Hp%\xcb\t\xf6q\xc0rr\xc5\xe9\xe3\x8fL\xe8\x03\xfb\x10\xcfv{\xc0\x91m\x92\xbc\xd2(?\xee\x1e\xf6\x7f|\\\f\x03h\xa34\x1a\xc73\xc5\xc4oƞ\xb3QX\xee\xfb\x96\x14F)\xe8\x886ц\xa0$\xa2\xc0.a\x88\xe1\xe4\x16\fj\x83\x16\xa5\x9b{7\x7f\xaa\a&A5\xbf`\xebjxDCj\xc0\x1e\x94\x17\x1d\xb1\xed\x11\x8d\x03\x83\xad\x1a$\xff\xf7\xa4ۂS\xc1\xa8`\x0e\x13ߝ\xbe@L\x92\t82\xe1\xf1=0\xd9\xc1Ȟ\xc1 Y\x01/g\xfa\x82\x88\xad\xe1\xb32\b\\\xf6j\x03\a\xe7\xb4\xdd\xdc\xdd\r\xdc\xe5\xaaѪq\xf4\x92\xbb\xe7\xbbP\x00x\xe3\x9d2\xf6\xae\xc3#\x8a;ˇ\x8a\x99\xf6\xc0\x1d\xb6\xce\x1b\xbcc\x9aW\x01\xba\f\x95\xa3\x1e\xbb?\x98Tg\xec\xed\x02\xebY\x8e\xc5/\x10\xfe\v\x11 ֧\xc4gii\xdc\xc5\xc9\xd14D\xde\xf9\xf9\xaf\x8f_!\x9b\x0e\xc1X{?\xf8\xfd\xb4ОB@\x0e\xe3\xb2G\x13\x83\xd8\x1b5\x06\x9d(;\xad\xb8t\xe1\xa7\x15\x1c\xe5\xda\xfd\xd67#w\x14\xf7\x7fz\xb4\x8ebU\xc36\x94Rh\x10\xbc\xa6\xfc\xedjx\x90\xb0e#\x8a-\xb3\xf8\xdd\x03@\x9e\xb6\x159\xf6u!\x98w\x01k\xe1\xe8\xb5\xd9D.\xe3\x17\xe25'\x84G\x8d-\x85\x8e\xbcG\xcbx\xcf\x13\xbd\xf5\xca\x00[\xc8\xd6\v\x95\xe5#K_\x91\xe2\xd6B+L\x9fJk209c\xe3ĵ6J\x9e)\x05\x10\x17\xf9٠V\x96;e\x9eO,]\x9fi\xb8\x10\x00\xfaZ&[\x14Wv\xb2\rB\xc0eG\x9e\xc4)\xef\x88\"\xa2\x82\x80I\xc9Aѹ\xb8\xec\xe0\xf8=8ZE\x89j\xd1ўdX\xbe un\x81K8\xb5/0oS\xd6;k\x94\x12\xc8ּG\xb9\xf5Y\x1d\xa9\x81\x92=\x1f\xce\xf78\xef\xb4.\x05\xfe\x8a\xfb\ni83I\xbb\xa0\x9c#$\xd5H\xe3UNH\"ޞ\x0f\xa9\xb6\x15\x8c\xf6\x1cEg/\xc5\xf2\xec|\xe4\r\a+W\xc29\xa1\xcc\xc7#\x95\x97P\xec\x83\x02\n,\xf1\x88\r]\x14M\x16\x10\xc6\x14\xacᡟi\xe4\x16\u07bd\x03e\xe0]\xec\xb4߽\x8f\xe9\xea\xb9p\x15\x973\x1b\x05\x8dO\\\x88l\xf7MYLћ\xaa\xbb\xf2\xee\x8a\x03\xfe\xb1\x12_\xf9\xc1Q\xdb\x11\xf6\xee\x14<1\xee\xa6rW\xc0<\x99\xb6\xef\xa1\xc1\x9e(֠\xf3F\xd2I@c\x88rlP\xa9\xbc{Ӧ\xacd\xda\x1e\x94{\xb8\xbf\xb2\x9d\xc7I0\xb3\xcb\xc3}\xe6\x96}\x88\xc2D1I\x12\x9c*\x05\x94\xa0G\x0e\t\xc5\xe8mhC\x05\x9c\xee5\xd7 /\xa53ne\xf8\xc0\xa9\xad\x90\xd3̉\xf2\x8et\x0f*%\"\xb7a\x7f\u0601\xd7\x118Q\fU\xd7\x06\xa1\xe3}\x8f\x06\xa5\x8b\xf55\x1a\xde\xed\xb7\xb7\xf6d\xa4\xa4\xb3\x9fa\b\x1d\xd6ȴƎ\xdaQ\x8alrԛ\\\xe4\x98\x19\xd0\xed\xc36\xae\xf8\xe7\xebL4;\x87*7\xdd\x1d\xa8\x10\xa4\xe8F\x8d\xb0\xdbo\xa9\x03+lc\xb7?Gx\xb9\xcaAjx/D\xf0\f\xe5Y\xfc\x12\x9e\x97\x1c{\x85N\x01\xf4\xf1\x15\x96w\xfbR!\x9d\xdc\x01\xee\xc0\x1cI\xa4{\x014\xcfE\x9d\x90\xcfG\n\xe7\xaf\xc3۾\n\xf0\xf6E\xc4\xdb5\xe4\vx\x9b\xe7\xdf\f\x99\x8a77\u061d\xa3\xae^\x88\\\x05\xfaX\x1cl__\xa2ʖ\xabrw\xb5\x92YS\xfcj\xfaD\x96\xeb\x89%Ӭf\xe7G\xf2Umh\xb8\x9e\xbf\xb6\x11\x8d\xef1)\xec\xad7\x81\x86\xd2+\r\xdd\xca~U+\xda\xc6\xf7\x8d\xf9U\xf6Z\xfbv\xbe\"\xdc\xf7L7\xabw,'T\xbcO\xe7G\x94R\xffv\xd2\x17\x97\x06z$u\xd8\x01\x1eQ\x02\xb5ڌ\v\xec\xb2N[\xc3W\xea\xc6\xc3\xc5\xe7v}E\n\xfeN\x8aB٥\x9e\xa9\x00\xfa|]~L\xa1\xebNE*\xce$\xa4\x17\x825\x027\xe0\x8c\xbf\xd4?\x16\x0fʈֲ\xe1\x1aQ\x7f\x8eR\U0006a616\x00k\xa8\xa9X\xf7\xb4\xb76\xc5\xfeME#\xf4\xbdW@\xecH\xa6\x94g\x13ϼܒ\xa3\xf4c\x89\r\xbe\xe0Sa\xf4cۢ.1T\x05;\x83\x9a\x99\xe2\xd4\xd9K\xe4|2^0Jd\x95\xe7\x8a:\xa7\xa7\xbe\xc2\xdc\xdfB\x02\xbe\xc9\xd3\t\xdf5g\xe7\x1b\xc9A\x89|\x80\xc2k\x9c\xf4c\x83\x86<\x1e\xde\xfb\xb2\xeb33\x15\x92\x9e\xc9n\x11\xb2\x93\x86\xa9\x0f\v\xaa\xe8\xf4Pe\x88\x97\xa6ܙv\xdcj\xc1J\x85-\xefd\xd12\x9c\x922\x1f\xf4\x89R\xdf\xda#L\xaf\xa3\xe5\xc2Wz\xe2,Ea\xfeX\xb9\x9a\x9f^=\xbf\x8f\x85\x17.Q\xcbW\xe8km\xecB\xf8\x1a\xa9\xa6\a\xf0\x12\xa5\xce\xd9\xf1\x9c\v\x97f~O\x1a,:\xeal0 \xeff\xba\xd3S\xc6|\xc47\xd3\x03\xdd\x06\xfe\xf3ߛ\xff\x05\x00\x00\xff\xff\xd8T?\xb3K\x1a\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcY\xcbsۼ\x11\xbf\xfb\xaf\xd8I\x0f\xb9X\xf4\x97\xaf\x9dNG\xb7Dng4\xfd\x92j\"\xd7w\x88\\\x91\x88A\x80\xc5C\xae\xdb\xe9\xff\xdeY<(> \xd1N\x93ꐉ\x01\xecb\x9f\xbf\xdd\x05W\xab\xd5\r\xeb\xf8#jÕ\\\x03\xeb8\xfeӢ\xa4\xbfL\xf1\xf4'Spuw\xfap\xf3\xc4e\xb5\x86\x8d3V\xb5_\xd1(\xa7K\xbc\xc7#\x97\xdcr%oZ\xb4\xacb\x96\xado\x00\x98\x94\xca2Z6\xf4'@\xa9\xa4\xd5J\bԫ\x1ae\xf1\xe4\x0exp\\T\xa8=\xf3t\xf5\xe9\x97\xe2ï\xc5/7\x00\x92\xb5\xb8\x06\xe2\xe7:\xa1Xe\x8a\x13\nԪ\xe0\xea\xc6tX\x12\xdbZ+\u05ed\xe1\xbc\x11\xc8\xe2\x95A\xdc{f\xd9\xdf=\a\xbf(\xb8\xb1\x7f\x9dl\xfcƍ\xf5\x9b\x9dp\x9a\x89ѭ~\xddpY;\xc1\xf4p\xe7\x06\xc0\x94\xaa\xc35|\xa1+;V\"\xadEM\xbc\b+`U\xe5m\xc3\xc4NsiQo\x94pm\xb2\xc9\n*4\xa5\xe6\x9d\xf5\xba\x9f\x05\x02c\x99u\x06\x8c+\x1b`\x06\xbe\xe0\xf3\xddV\ued2a5\x9a \x12\xc07\xa3\xe4\x8e\xd9f\rE8^t\r3\x18w\x83\xf9\xf6~#.\xd9\x17\x92\xd6X\xcde\x9d\xbb\xff\x81\xb7\b\x95\xd3\xdem\xa4s\x89`\x1bn\x86\x82=3C\xc2i\x8b\xd5E1\xfc>13\x96\xb5\xddT\x9e\x01i\x10\xa8b\x16s\xe2lT\xdb\t\xb4X\xc1\xe1\xc5bR\xe2\xa8t\xcb\xec\x1a\xb8\xb4\x7f\xfc\xc3eKDS\x15\x9e\xf4^ɱY>\xd1*\f\x96\x83$\xe4\xa1\x1au\xd66\xca2\xf1\xbf\bb\x89\xc1\xa7\x01}\x90$\xf0\x1d\xae/\x8aB\xe1\x06\xea\b\xb6A\xf8\xc4\xca'\xd7\xc1\xde*\xcdj\x84\xdfT\x19\x9c\xf7ܠ\x8e\xce;\x84#\xa6QNTpH\x1a\x03\x18\xabt\u058b\x1d\x96E\xa0\x8a|\x13ۉ+\xc7w\xfe\xe0 +5\xb2l\x90%\x94)\xfc\t\xaed>\xd2>֘\x8f\xb2\xb0}\xfa\x95\x89\xaea\x1fBz\x97\r\xb6l\x1dϫ\x0e\xe5\xc7\xdd\xf6\xf1\xf7\xfb\xd12@\xa7U\x87\xda\xf2\x84/\xe17\x00\xce\xc1*\x8c\xb5~O\f\xc3)\xa8\b1\xd1x\xffE\xb4\xc0*\xca\x10\xfc\xca\rh\xec4\x1a\x94vh\xdb\xf4SG`\x12\xd4\xe1\x1b\x96\xb6\x80=jb\x93<\\*yBmAc\xa9j\xc9\xff\xd5\xf36`\x95\xbfT0\x8b\x11\xf0\xce?\x8fN\x92\t81\xe1\xf0\x16\x98\xac\xa0e/\xa0\x91n\x01'\a\xfc\xfc\x11S\xc0g\xa5\x11\xb8<\xaa54\xd6vf}wWs\x9b\nF\xa9\xda\xd6In_\xee<\xf6\xf3\x83\xb3J\x9b\xbb\nO(\xee\f\xafWL\x97\r\xb7XZ\xa7\xf1\x8eu|\xe5E\x97\xbeh\x14m\xf5;\x1dK\x8cy?\x92u\x16a\xe1\xe7\xd1\xfe\x8a\a\b\xf4\x81\x1b`\x914hq64-\x91u\xbe\xfey\xff\x00\xe9j\uf329\xf5\xbd\xddτ\xe6\xec\x022\x18\x97G\xd4\xc1\x89G\xadZ\xcf\x13e\xd5).\xad\xff\xa3\x14\x1c\xe5\xd4\xfc\xc6\x1dZn\xc9\xef\xffph,\xf9\xaa\x80\x8d\xaf\xa2p@p\x1d\xc5oU\xc0V\u0086\xb5(6\xcc\xe0Ow\x00YڬȰ\xafs\xc1\xb0\x01\x98\x1e\x0eV\x1bl\xa4\x1a~\xc1_g8\xd8wX\x92\xe3\xc8vDď<\xa2\xdcQi`\x83\x93ň]>]\xe9\x97\x05\xb7顉<\x9fr4I,9\x80䄷\xe1\xe4\x8c)\x80\x98\x82tO\xa3\xb1S\x86[\xa5_\x88q\xc0\xe7b\xc6\xe1\x82\xf1\xe9W2Y\xa2X\xd0d\xe3\x0f\x01\x97\x15\xd9\x11\xfb\x98#x\b\f\xbcLJ֊r\xe2\x92y\xc3ok\x89\x86BԠ%\x8d\xa4'\x1e\x8097\xc0%\x9c\xbb\x17\x18v)S\xad\x0eJ\tdS\xbc+\r\xdfK֙F\xd9\x05ݶGH'\x1f^:\xa4\xcb7\xfb\xed-\xfd\x93\xd6).N\xbc\x8a\x00L\xc9Cu|\x0e\xb2\x10\x80\x96\x0em\xf6[0\x91|n\x04\xe9\x84`\a\x81k\xb0\xda\xcd\x15\xbb\x1c\x86\xf4Kl7\x82\x99쁉\x821\x00\xfd\xf1\\\xf4%~P\xfa\x13\xb6aS\xa4\xe9\rN出\xd1\x01\x11\xef\xeb.\xab\x13\x8d<\xf2\xc8\xeb\xf9\xdd\xc3\xd9\xe8Z\x8a\\UmV3\x06W\x92ũD\x90$\xab\x96\xd6W\xa9~P\x97t\xe4ulC3\x97\x1e9\x8aʼ9\xd9\x17\xec\xe1\x85X\x80\xb0^\x89T\xec\"R\x11=x\x061 \x9c\xf1\xa3\x11mf\x14\b%\xa5 Ds!ҽo\xaaJ\xe4ܾSWn\t\xc3\xff69>\xb1\x83\xa5\x01\xc2\xebn\x15<3n\xfb\xd65\a\xe0\x89\x97\xb9\x85\x03\x1e\xa9]\xd2h\x9d\x96T\xd9Pkj \x8cg\xa9\\\x06گ(e\x06efA\xa1iE\xf2Z\xd0\xff\xa7\x98=L\xf4\x8c2\xae{\x9b\x84\xbe\x83\xed\x1f'\x96\x84\x1c\x9fNr*\xcdkNc\x81\xecw\xcemK\x00\x87\x8c\xa4q\xec\xf4p\xe5\xf1\xb6\xa0F!\xf5p\x04\x80gv\x94\xa1\xe1r\x02p\x1a;6\xfbm\x86gOQ\xc5\xfc\xcad\xe7\xa25v\x8f\x9bWفD\xc9\xe05-?7\xbcl\xc6~\x9b\x8d\b^\x16\xf6\x84\xbeE}\x83\x98y\xa0^\xe5\x1b\xd6əi\x96M\xb6\x87\xf1:\xdd\x1a\xbb>\xbb\xbb{ܼ\xaa\xa9\xf7\xaf\x1e\xafk\xeb\xc3\x13W\xb4r\xe9\xb4Fi\xd3\xc3\x17M\xb8\xdf\xd1ؗ\xe1\xc9h\xf8(\xb0\xd4\f\xcf)\xfc䬫\x01ڰԠ\xfb\x87\x89\xf4,\x95k\x87\xcf\xec\x02\xa5\x9f\xe4\x89\x1bV\x80'\x94@S\vゐ۳4Ŕ&\x9fN=\x97\x88b\xe1\r2ͬQ\xbc\xf4\"\xf0@\xc1\xe9G\xd2\xf7\xe6\nO\x0f\xa2\x94~\x19#\xcc#:\xbdw\xd1 \xba\xca2}Um\xcc&g\xdf+|E\xe3D\xa6@\xfc\xc4^!\\\x19\xa6-\x93\xed\x15\xae\xcf\b\xcc\x00\x03\x1d\x98D\x98\xb862}\x7f\x03Ѣ1\xac^\xc2\xf1\xcf\xe1Tx\xe9\x88$\xc0\x0eTGǢ\xbd71\xd9\xde\x04\xa3\x1d\xb3͂\x04;f\x9b\x94\xd6G'\x84\xa7\x99պ\xd8\xfa\x1e\x90b\xf8G\x95\x1a\xf5\u0093D\xea\x12+n:\xc1^2\x8c\x93\"\xc3\x1c\x1fdK\xc2\xd5TZ\xe7\xf1p}\xba\xef\xbf>\xe4\xa7\xc5\xdc'\x84\x9c\x0f\x86\x1f\x03&\xfb\xfdW\x85\x9fs\xc3\x158J\x99\xbc\xbd\x7fe\xfb\xbb\xbdOY\xc7+\x94\x96:z\xed\xcbϸ\x97\x92W\a\x9a\xc1\x1b\xd9\xdbڿ\xd17\xa9%\x89G\x87\x17\xfa\x81\xf85,\xd7\r\xec)\xc5\tX\xfcC\xf0f\xfa\xbd\xe2\xb6\xff\xfc\xc1l|\x8d.\x1b&kJ\bI%ŗ\xa4\x1c\xe3Y\x81\x1f\x95\xf3\xb1\xf8\xff\xcfJ\x9e\r\x97٢\x97\xbc\x1a\xf0\x8eo\x10\xc3\x15w\xe8_\xff\xd7\xf0\xef\xff\xdc\xfc7\x00\x00\xff\xffgK\fV\xa3\x1e\x00\x00"), +} + +var CRDs = crds() + +func crds() []*apiextv1.CustomResourceDefinition { + apiextinstall.Install(scheme.Scheme) + decode := scheme.Codecs.UniversalDeserializer().Decode + var objs []*apiextv1.CustomResourceDefinition + for _, crd := range rawCRDs { + gzr, err := gzip.NewReader(bytes.NewReader(crd)) + if err != nil { + panic(err) + } + bytes, err := io.ReadAll(gzr) + if err != nil { + panic(err) + } + gzr.Close() + + obj, _, err := decode(bytes, nil, nil) + if err != nil { + panic(err) + } + objs = append(objs, obj.(*apiextv1.CustomResourceDefinition)) + } + return objs +} diff --git a/config/crd/v2alpha1/crds/doc.go b/config/crd/v2alpha1/crds/doc.go new file mode 100644 index 000000000..9eed410f6 --- /dev/null +++ b/config/crd/v2alpha1/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/v1/main.go diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index f42432cbc..8d235284e 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -1,9 +1,7 @@ - --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - creationTimestamp: null name: velero-perms rules: - apiGroups: @@ -84,6 +82,46 @@ rules: - get - patch - update +- apiGroups: + - velero.io + resources: + - datadownloads + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - velero.io + resources: + - datadownloads/status + verbs: + - get + - patch + - update +- apiGroups: + - velero.io + resources: + - datauploads + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - velero.io + resources: + - datauploads/status + verbs: + - get + - patch + - update - apiGroups: - velero.io resources: @@ -184,12 +222,6 @@ rules: - get - patch - update -- apiGroups: - - velero.io - resources: - - restorestoragelocations - verbs: - - get - apiGroups: - velero.io resources: diff --git a/design/CLI/PoC/base/deployment.yaml b/design/CLI/PoC/base/deployment.yaml index 36cf4d462..20f8ff4e0 100644 --- a/design/CLI/PoC/base/deployment.yaml +++ b/design/CLI/PoC/base/deployment.yaml @@ -57,7 +57,7 @@ spec: - emptyDir: {} name: scratch --- -apiVersion: rbac.authorization.k8s.io/v1beta1 +apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: labels: diff --git a/go.mod b/go.mod index 4a20b8913..27e9f8afc 100644 --- a/go.mod +++ b/go.mod @@ -3,45 +3,46 @@ module github.com/vmware-tanzu/velero go 1.18 require ( - cloud.google.com/go/storage v1.21.0 + cloud.google.com/go/storage v1.30.1 github.com/Azure/azure-pipeline-go v0.2.3 github.com/Azure/azure-sdk-for-go v67.2.0+incompatible - github.com/Azure/azure-storage-blob-go v0.14.0 + github.com/Azure/azure-storage-blob-go v0.15.0 github.com/Azure/go-autorest/autorest v0.11.27 github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 github.com/Azure/go-autorest/autorest/to v0.3.0 - github.com/aws/aws-sdk-go v1.43.31 + github.com/aws/aws-sdk-go v1.44.253 github.com/bombsimon/logrusr/v3 v3.0.0 github.com/evanphx/json-patch v5.6.0+incompatible - github.com/fatih/color v1.13.0 + github.com/fatih/color v1.15.0 github.com/gobwas/glob v0.2.3 - github.com/golang/protobuf v1.5.2 - github.com/google/go-cmp v0.5.8 + github.com/golang/protobuf v1.5.3 + github.com/google/go-cmp v0.5.9 github.com/google/uuid v1.3.0 github.com/hashicorp/go-hclog v0.14.1 github.com/hashicorp/go-plugin v1.4.3 github.com/joho/godotenv v1.3.0 - github.com/kopia/kopia v0.10.7 + github.com/kopia/kopia v0.13.0 github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.20.1 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.12.2 + github.com/prometheus/client_golang v1.15.0 github.com/robfig/cron v1.1.0 - github.com/sirupsen/logrus v1.8.1 + github.com/sirupsen/logrus v1.9.0 github.com/spf13/afero v1.6.0 github.com/spf13/cobra v1.4.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.2 github.com/vmware-tanzu/crash-diagnostics v0.3.7 - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 - golang.org/x/net v0.7.0 - golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/text v0.7.0 - google.golang.org/api v0.74.0 - google.golang.org/grpc v1.45.0 - google.golang.org/protobuf v1.28.0 + go.uber.org/zap v1.24.0 + golang.org/x/mod v0.10.0 + golang.org/x/net v0.9.0 + golang.org/x/oauth2 v0.7.0 + golang.org/x/sync v0.1.0 + golang.org/x/text v0.9.0 + google.golang.org/api v0.120.0 + google.golang.org/grpc v1.54.0 + google.golang.org/protobuf v1.30.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.25.6 k8s.io/apiextensions-apiserver v0.24.2 @@ -56,9 +57,10 @@ require ( ) require ( - cloud.google.com/go v0.100.2 // indirect - cloud.google.com/go/compute v1.5.0 // indirect - cloud.google.com/go/iam v0.1.1 // indirect + cloud.google.com/go v0.110.0 // indirect + cloud.google.com/go/compute v1.19.0 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/iam v0.13.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3 // indirect github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0 // indirect @@ -70,44 +72,47 @@ require ( github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chmduquesne/rollinghash v4.0.0+incompatible // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/edsrzf/mmap-go v1.1.0 // indirect github.com/emicklei/go-restful/v3 v3.8.0 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.2.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/swag v0.21.1 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.4.1 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/gnostic v0.6.9 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/googleapis/gax-go/v2 v2.2.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/google/s2a-go v0.1.2 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect + github.com/googleapis/gax-go/v2 v2.8.0 // indirect github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.15.1 // indirect - github.com/klauspost/cpuid/v2 v2.0.12 // indirect + github.com/klauspost/compress v1.16.5 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/klauspost/pgzip v1.2.5 // indirect - github.com/kr/pretty v0.3.0 // indirect + github.com/klauspost/reedsolomon v1.11.7 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-ieproxy v0.0.1 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/minio/md5-simd v1.1.2 // indirect - github.com/minio/minio-go/v7 v7.0.23 // indirect + github.com/minio/minio-go/v7 v7.0.52 // indirect github.com/minio/sha256-simd v1.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.0.0 // indirect @@ -120,29 +125,31 @@ require ( github.com/oklog/run v1.0.0 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.34.0 // indirect - github.com/prometheus/procfs v0.7.3 // indirect - github.com/rs/xid v1.3.0 // indirect - github.com/stretchr/objx v0.4.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect + github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/rs/xid v1.4.0 // indirect + github.com/stretchr/objx v0.5.0 // indirect github.com/vladimirvivien/gexe v0.1.1 // indirect github.com/zeebo/blake3 v0.2.3 // indirect - go.opencensus.io v0.23.0 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/otel v1.14.0 // indirect + go.opentelemetry.io/otel/trace v1.14.0 // indirect go.starlark.net v0.0.0-20201006213952-227f4aabceb5 // indirect go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.8.0 // indirect - go.uber.org/zap v1.21.0 // indirect - golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect - golang.org/x/exp v0.0.0-20210916165020-5cb4fee858ee // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/term v0.5.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.8.0 // indirect + golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/term v0.7.0 // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb // indirect + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.66.2 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/component-base v0.24.2 // indirect diff --git a/go.sum b/go.sum index f02cd7ac0..7eb1e5c06 100644 --- a/go.sum +++ b/go.sum @@ -19,33 +19,24 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= -cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.2.0/go.mod h1:xlogom/6gr8RJGBe7nT2eGsQYAFUbbv8dbC29qE3Xmw= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0 h1:b1zWmYuuHz7gO9kDcM/EpHGr06UgsYNRpNJzI2kFiLM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ= +cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v0.1.1 h1:4CapQyNFjiksks1/x7jsvsygFPhihslYk5GptIrlX68= -cloud.google.com/go/iam v0.1.1/go.mod h1:CKqrcnI/suGpybEHxZ7BMehL0oA4LpdyJdUlTl9jVMw= +cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -55,8 +46,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.21.0 h1:HwnT2u2D309SFDHQII6m18HlrCi3jAXhUMTLOWXYH14= -cloud.google.com/go/storage v1.21.0/go.mod h1:XmRlxkgPjlBONznT2dDUU/5XlpU2OjMnKuqnZI01LAA= +cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= +cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U= github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= @@ -68,8 +59,8 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3 h1:E+m3SkZCN0Bf5q7YdTs5lSm github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0 h1:Px2UA+2RvSSvv+RvJNuUB6n7rs5Wsel4dXLe90Um2n4= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= -github.com/Azure/azure-storage-blob-go v0.14.0 h1:1BCg74AmVdYwO3dlKwtFU1V0wU2PZdREkXvAmZJRUlM= -github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= +github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk= +github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= @@ -115,6 +106,7 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 h1:KeNholpO2xKjgaaSyd+DyQRrsQjhbSeS7qe4nEw8aQw= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -127,6 +119,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -136,8 +129,8 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/aws/aws-sdk-go v1.43.31 h1:yJZIr8nMV1hXjAvvOLUFqZRJcHV7udPQBfhJqawDzI0= -github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.253 h1:iqDd0okcH4ShfFexz2zzf4VmeDFf6NOMm07pHnEb8iY= +github.com/aws/aws-sdk-go v1.44.253/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -158,8 +151,9 @@ github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6 github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chmduquesne/rollinghash v4.0.0+incompatible h1:hnREQO+DXjqIw3rUTzWN7/+Dpw+N5Um8zpKV0JOEgbo= github.com/chmduquesne/rollinghash v4.0.0+incompatible/go.mod h1:Uc2I36RRfTAf7Dge82bi3RU0OQUmXT9iweIcPqvr8A0= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -194,6 +188,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -208,8 +203,11 @@ github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5O github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= +github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -232,8 +230,8 @@ github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQL github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -255,17 +253,18 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk= github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= @@ -292,6 +291,7 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg78 github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -301,8 +301,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ= -github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -320,7 +320,6 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -337,9 +336,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= @@ -360,9 +359,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -371,8 +369,7 @@ github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPg github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -384,10 +381,9 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.2 h1:WVtYAYuYxKeYajAmThMRYWP6K3wXkcqbGHeUgeubUHY= +github.com/google/s2a-go v0.1.2/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -395,16 +391,17 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0 h1:s7jOdKSaksJVOxE0Y/S32otcfiP+UQ0cL8/GTKaONwE= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc= +github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -417,6 +414,7 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hanwen/go-fuse/v2 v2.3.0 h1:t5ivNIH2PK+zw4OBul/iJjsoG9K6kXo4nMDoBpciC8A= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -436,8 +434,6 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -483,25 +479,28 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A= -github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= +github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/klauspost/reedsolomon v1.11.7 h1:9uaHU0slncktTEEg4+7Vl7q7XUNMBUOK4R9gnKhMjAU= +github.com/klauspost/reedsolomon v1.11.7/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ5MGv0Qd8a47h6A= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kopia/kopia v0.10.7 h1:6s0ZIZW3Ge2ozzefddASy7CIUadp/5tF9yCDKQfAKKI= -github.com/kopia/kopia v0.10.7/go.mod h1:0d9THPD+jwomPcXvPbCdmLyX6phQVP7AqcCcDEajfNA= +github.com/kopia/htmluibuild v0.0.0-20230326183719-f482ef17e2c9 h1:s5Wa89s8RlPjuwqd8K8kuf+T9Kz4+NsbKwR/pJ3PAT0= +github.com/kopia/kopia v0.13.0 h1:efxs/vw1cS9HldlHcQ8TPxlsYz+cWCkiS4IWMbR3D1s= +github.com/kopia/kopia v0.13.0/go.mod h1:Iic7CcKhsq+A7MLR9hh6VJfgpcJhLx3Kn+BgjY+azvI= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -524,27 +523,27 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI= github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.23 h1:NleyGQvAn9VQMU+YHVrgV4CX+EPtxPt/78lHOOTncy4= -github.com/minio/minio-go/v7 v7.0.23/go.mod h1:ei5JjmxwHaMrgsMrn4U/+Nmg+d8MKS1U2DAn1ou4+Do= +github.com/minio/minio-go/v7 v7.0.52 h1:8XhG36F6oKQUDDSuz6dY3rioMzovKjW40W6ANuN0Dps= +github.com/minio/minio-go/v7 v7.0.52/go.mod h1:IbbodHyjUAguneyucUaahv+VMNs/EOTV9du7A7/Z3HU= github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -624,38 +623,40 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= +github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.34.0 h1:RBmGO9d/FVjqHT0yUGQwBJhkwKV+wPCn7KGpvfab0uE= -github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/robfig/cron v1.1.0 h1:jk4/Hud3TTdcrJgUOBgsqrZBarcxl6ADIjSC2iniwLY= github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4= -github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= +github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -666,8 +667,9 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -699,8 +701,9 @@ github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -708,9 +711,12 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tg123/go-htpasswd v1.2.1 h1:i4wfsX1KvvkyoMiHZzjS0VzbAPWfxzI8INcZAKtutoU= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -732,6 +738,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zalando/go-keyring v0.2.2 h1:f0xmpYiSrHtSNAVgwip93Cg8tuF45HJM6rHq/A5RI/4= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= @@ -759,12 +767,15 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= +go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= +go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= @@ -772,6 +783,8 @@ go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= +go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= +go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.starlark.net v0.0.0-20201006213952-227f4aabceb5 h1:ApvY/1gw+Yiqb/FKeks3KnVPWpkR3xzij82XPKLjJVw= @@ -782,17 +795,16 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= -go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -804,13 +816,14 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -821,8 +834,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20210916165020-5cb4fee858ee h1:qlrAyYdKz4o7rWVUjiKqQJMa4PEpd55fqBU8jpsl4Iw= -golang.org/x/exp v0.0.0-20210916165020-5cb4fee858ee/go.mod h1:a3o/VtDNHN+dCVLEpzjjUHOzR+Ln3DHX056ZPzoZGGA= +golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f h1:Al51T6tzvuh3oiwX11vex3QgJ2XTedFPGmbEVh8cdoc= +golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -849,8 +862,9 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -899,7 +913,6 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -908,10 +921,10 @@ golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -925,14 +938,10 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb h1:8tDJ3aechhddbdPAxpycgXHJRMLpk/Ab+aa4OgdN5/g= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -943,8 +952,10 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -976,7 +987,6 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -993,7 +1003,6 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1012,35 +1021,31 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1050,8 +1055,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1121,17 +1127,16 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -1156,24 +1161,8 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.64.0/go.mod h1:931CdxA8Rm4t6zqTFGSsgwbAEZ2+GMYurbndwSimebM= -google.golang.org/api v0.66.0/go.mod h1:I1dmXYpX7HGwz/ejRxwQp2qj5bFAz93HiCU1C1oYd9M= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.69.0/go.mod h1:boanBiw+h5c3s+tBPgEzLDRHfFLWV0qXxRHz3ws7C80= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0 h1:ExR2D+5TYIrMphWgs5JCgwRhEDlPDXXrLwHHMgPHTXE= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.120.0 h1:TTmhTei0mkR+kiBSW2UzZmAbkTaBfUUzfchyXnzG9Hs= +google.golang.org/api v0.120.0/go.mod h1:CrSvlNEFCFLae9ZUtL1z+61+rEBD7J/aCYwVYKZoWFU= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1226,41 +1215,11 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220201184016-50beb8ab5c44/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220211171837-173942840c17/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220216160803-4663080d8bc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb h1:0m9wktIpOxGw+SSKmydXWB3Z3GTfcPP6+q75HCQa6HI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1283,16 +1242,11 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1306,8 +1260,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1322,8 +1276,8 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= -gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= diff --git a/golangci.yaml b/golangci.yaml index 997c709eb..d8d363367 100644 --- a/golangci.yaml +++ b/golangci.yaml @@ -302,6 +302,7 @@ linters: - bodyclose - dogsled - durationcheck + - errcheck - exportloopref - goconst - gofmt diff --git a/hack/build-image/Dockerfile b/hack/build-image/Dockerfile index 79ff68f75..2ce809754 100644 --- a/hack/build-image/Dockerfile +++ b/hack/build-image/Dockerfile @@ -30,7 +30,7 @@ RUN wget --quiet https://github.com/kubernetes-sigs/kubebuilder/releases/downloa chmod +x /usr/local/kubebuilder/bin/kubebuilder # get controller-tools -RUN go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.7.0 +RUN go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.12.0 # get goimports (the revision is pinned so we don't indiscriminately update, but the particular commit # is not important) diff --git a/hack/crd-gen/v1/main.go b/hack/crd-gen/v1/main.go index e61763531..5f45b04e0 100644 --- a/hack/crd-gen/v1/main.go +++ b/hack/crd-gen/v1/main.go @@ -14,8 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// This code embeds the CRD manifests in config/crd/v1/bases in -// config/crd/v1/crds/crds.go. +// This code embeds the CRD manifests in ../bases in ../crds/crds.go package main diff --git a/hack/update-1fmt.sh b/hack/update-1fmt.sh index a9d020c30..fef575451 100755 --- a/hack/update-1fmt.sh +++ b/hack/update-1fmt.sh @@ -35,13 +35,11 @@ fi files="$(find . -type f -name '*.go' -not -path './.go/*' -not -path './vendor/*' -not -path './site/*' -not -path '*/generated/*' -not -name 'zz_generated*' -not -path '*/mocks/*')" echo "${ACTION} gofmt" -for file in ${files}; do - output=$(gofmt "${MODE}" -s "${file}") - if [[ -n "${output}" ]]; then - VERIFY_FMT_FAILED=1 - echo "${output}" - fi -done +output=$(printf '%s\n' "${files}" | xargs gofmt "${MODE}" -s) +if [[ -n "${output}" ]]; then + VERIFY_FMT_FAILED=1 + echo "${output}" +fi if [[ -n "${VERIFY_FMT_FAILED:-}" ]]; then echo "${ACTION} gofmt - failed! Please run 'make update'." else @@ -49,13 +47,11 @@ else fi echo "${ACTION} goimports" -for file in ${files}; do - output=$(goimports "${MODE}" -local github.com/vmware-tanzu/velero "${file}") - if [[ -n "${output}" ]]; then - VERIFY_IMPORTS_FAILED=1 - echo "${output}" - fi -done +output=$(printf '%s\n' "${files}" | xargs goimports "${MODE}" -local github.com/vmware-tanzu/velero) +if [[ -n "${output}" ]]; then + VERIFY_IMPORTS_FAILED=1 + echo "${output}" +fi if [[ -n "${VERIFY_IMPORTS_FAILED:-}" ]]; then echo "${ACTION} goimports - failed! Please run 'make update'." else diff --git a/hack/update-3generated-crd-code.sh b/hack/update-3generated-crd-code.sh index 9f9242619..6bd185cc3 100755 --- a/hack/update-3generated-crd-code.sh +++ b/hack/update-3generated-crd-code.sh @@ -40,20 +40,38 @@ ${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" \ + "velero:v1,v2alpha1" \ --go-header-file ./hack/boilerplate.go.txt \ --output-base ../../.. \ $@ # Generate apiextensions.k8s.io/v1 -# Generate manifests e.g. CRD, RBAC etc. + +# Generate CRD for v1. controller-gen \ crd:crdVersions=v1 \ paths=./pkg/apis/velero/v1/... \ - rbac:roleName=velero-perms \ paths=./pkg/controller/... \ output:crd:artifacts:config=config/crd/v1/bases \ object \ paths=./pkg/apis/velero/v1/... +# Generate CRD for v2alpha1. +controller-gen \ + crd:crdVersions=v1 \ + paths=./pkg/apis/velero/v2alpha1/... \ + paths=./pkg/controller/... \ + output:crd:artifacts:config=config/crd/v2alpha1/bases \ + object \ + paths=./pkg/apis/velero/v2alpha1/... + +# Generate RBAC. +controller-gen \ + paths=./pkg/apis/velero/v1/... \ + paths=./pkg/apis/velero/v2alpha1/... \ + paths=./pkg/controller/... \ + rbac:roleName=velero-perms + go generate ./config/crd/v1/crds + +go generate ./config/crd/v2alpha1/crds diff --git a/hack/verify-generated-crd-code.sh b/hack/verify-generated-crd-code.sh index 387acd74e..1ae3cef94 100755 --- a/hack/verify-generated-crd-code.sh +++ b/hack/verify-generated-crd-code.sh @@ -19,7 +19,7 @@ HACK_DIR=$(dirname "${BASH_SOURCE}") ${HACK_DIR}/update-3generated-crd-code.sh # ensure no changes to generated CRDs -if ! git diff --exit-code config/crd/v1/crds/crds.go >/dev/null; then +if [! git diff --exit-code config/crd/v1/crds/crds.go config/crd/v2alpha1/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 diff --git a/internal/delete/delete_item_action_handler.go b/internal/delete/delete_item_action_handler.go index f91127d7d..d85df8a6a 100644 --- a/internal/delete/delete_item_action_handler.go +++ b/internal/delete/delete_item_action_handler.go @@ -63,7 +63,12 @@ func InvokeDeleteActions(ctx *Context) error { if err != nil { return errors.Wrapf(err, "error extracting backup") } - defer ctx.Filesystem.RemoveAll(dir) + defer func() { + if err := ctx.Filesystem.RemoveAll(dir); err != nil { + ctx.Log.Errorf("error removing temporary directory %s: %s", dir, err.Error()) + } + }() + ctx.Log.Debugf("Downloaded and extracted the backup file to: %s", dir) backupResources, err := archive.NewParser(ctx.Log, ctx.Filesystem).Parse(dir) diff --git a/pkg/apis/velero/v1/pod_volume_operation_progress.go b/pkg/apis/velero/shared/data_move_operation_progress.go similarity index 80% rename from pkg/apis/velero/v1/pod_volume_operation_progress.go rename to pkg/apis/velero/shared/data_move_operation_progress.go index e5b3344c7..f92b3e533 100644 --- a/pkg/apis/velero/v1/pod_volume_operation_progress.go +++ b/pkg/apis/velero/shared/data_move_operation_progress.go @@ -14,11 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1 +package shared -// PodVolumeOperationProgress represents the progress of a -// PodVolumeBackup/Restore operation -type PodVolumeOperationProgress struct { +// DataMoveOperationProgress represents the progress of a +// data movement operation + +// +k8s:deepcopy-gen=true +type DataMoveOperationProgress struct { // +optional TotalBytes int64 `json:"totalBytes,omitempty"` diff --git a/pkg/apis/velero/v1/backup_types.go b/pkg/apis/velero/v1/backup_types.go index f38f35320..e25e1463d 100644 --- a/pkg/apis/velero/v1/backup_types.go +++ b/pkg/apis/velero/v1/backup_types.go @@ -165,6 +165,16 @@ type BackupSpec struct { // ResourcePolicy specifies the referenced resource policies that backup should follow // +optional ResourcePolicy *v1.TypedLocalObjectReference `json:"resourcePolicy,omitempty"` + + // SnapshotMoveData specifies whether snapshot data should be moved + // +optional + // +nullable + SnapshotMoveData *bool `json:"snapshotMoveData,omitempty"` + + // DataMover specifies the data mover to be used by the backup. + // If DataMover is "" or "velero", the built-in data mover will be used. + // +optional + DataMover string `json:"datamover,omitempty"` } // BackupHooks contains custom behaviors that should be executed at different phases of the backup. diff --git a/pkg/apis/velero/v1/pod_volume_backup_types.go b/pkg/apis/velero/v1/pod_volume_backup_types.go index d34e09f6c..a9a2ad4e8 100644 --- a/pkg/apis/velero/v1/pod_volume_backup_types.go +++ b/pkg/apis/velero/v1/pod_volume_backup_types.go @@ -19,6 +19,8 @@ package v1 import ( corev1api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/vmware-tanzu/velero/pkg/apis/velero/shared" ) // PodVolumeBackupSpec is the specification for a PodVolumeBackup. @@ -100,7 +102,7 @@ type PodVolumeBackupStatus struct { // number of backed up bytes. This can be used to display progress information // about the backup operation. // +optional - Progress PodVolumeOperationProgress `json:"progress,omitempty"` + Progress shared.DataMoveOperationProgress `json:"progress,omitempty"` } // TODO(2.0) After converting all resources to use the runttime-controller client, diff --git a/pkg/apis/velero/v1/pod_volume_restore_type.go b/pkg/apis/velero/v1/pod_volume_restore_type.go index 72c3b891c..84ffc770f 100644 --- a/pkg/apis/velero/v1/pod_volume_restore_type.go +++ b/pkg/apis/velero/v1/pod_volume_restore_type.go @@ -19,6 +19,8 @@ package v1 import ( corev1api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/vmware-tanzu/velero/pkg/apis/velero/shared" ) // PodVolumeRestoreSpec is the specification for a PodVolumeRestore. @@ -86,7 +88,7 @@ type PodVolumeRestoreStatus struct { // number of restored bytes. This can be used to display progress information // about the restore operation. // +optional - Progress PodVolumeOperationProgress `json:"progress,omitempty"` + Progress shared.DataMoveOperationProgress `json:"progress,omitempty"` } // TODO(2.0) After converting all resources to use the runtime-controller client, the genclient and k8s:deepcopy markers will no longer be needed and should be removed. diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index 2d00769c7..a64379ebd 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -376,6 +376,11 @@ func (in *BackupSpec) DeepCopyInto(out *BackupSpec) { *out = new(corev1.TypedLocalObjectReference) (*in).DeepCopyInto(*out) } + if in.SnapshotMoveData != nil { + in, out := &in.SnapshotMoveData, &out.SnapshotMoveData + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupSpec. @@ -977,21 +982,6 @@ func (in *PodVolumeBackupStatus) DeepCopy() *PodVolumeBackupStatus { return out } -// 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 -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodVolumeOperationProgress. -func (in *PodVolumeOperationProgress) DeepCopy() *PodVolumeOperationProgress { - if in == nil { - return nil - } - out := new(PodVolumeOperationProgress) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PodVolumeRestore) DeepCopyInto(out *PodVolumeRestore) { *out = *in diff --git a/pkg/apis/velero/v2alpha1/data_download_types.go b/pkg/apis/velero/v2alpha1/data_download_types.go new file mode 100644 index 000000000..b880393ae --- /dev/null +++ b/pkg/apis/velero/v2alpha1/data_download_types.go @@ -0,0 +1,156 @@ +/* +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 v2alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/vmware-tanzu/velero/pkg/apis/velero/shared" +) + +// DataDownloadSpec is the specification for a DataDownload. +type DataDownloadSpec struct { + // TargetVolume is the information of the target PVC and PV. + TargetVolume TargetVolumeSpec `json:"targetVolume"` + + // BackupStorageLocation is the name of the backup storage location + // where the backup repository is stored. + BackupStorageLocation string `json:"backupStorageLocation"` + + // DataMover specifies the data mover to be used by the backup. + // If DataMover is "" or "velero", the built-in data mover will be used. + // +optional + DataMover string `json:"datamover,omitempty"` + + // SnapshotID is the ID of the Velero backup snapshot to be restored from. + SnapshotID string `json:"snapshotID"` + + // SourceNamespace is the original namespace where the volume is backed up from. + // It may be different from SourcePVC's namespace if namespace is remapped during restore. + SourceNamespace string `json:"sourceNamespace"` + + // DataMoverConfig is for data-mover-specific configuration fields. + // +optional + DataMoverConfig map[string]string `json:"dataMoverConfig,omitempty"` + + // Cancel indicates request to cancel the ongoing DataDownload. It can be set + // when the DataDownload is in InProgress phase + Cancel bool `json:"cancel,omitempty"` + + // OperationTimeout specifies the time used to wait internal operations, + // before returning error as timeout. + OperationTimeout metav1.Duration `json:"operationTimeout"` +} + +// TargetPVCSpec is the specification for a target PVC. +type TargetVolumeSpec struct { + // PVC is the name of the target PVC that is created by Velero restore + PVC string `json:"pvc"` + + // PV is the name of the target PV that is created by Velero restore + PV string `json:"pv"` + + // Namespace is the target namespace + Namespace string `json:"namespace"` +} + +// DataDownloadPhase represents the lifecycle phase of a DataDownload. +// +kubebuilder:validation:Enum=New;Accepted;Prepared;InProgress;Canceling;Canceled;Completed;Failed +type DataDownloadPhase string + +const ( + DataDownloadPhaseNew DataDownloadPhase = "New" + DataDownloadPhaseAccepted DataDownloadPhase = "Accepted" + DataDownloadPhasePrepared DataDownloadPhase = "Prepared" + DataDownloadPhaseInProgress DataDownloadPhase = "InProgress" + DataDownloadPhaseCanceling DataDownloadPhase = "Canceling" + DataDownloadPhaseCanceled DataDownloadPhase = "Canceled" + DataDownloadPhaseCompleted DataDownloadPhase = "Completed" + DataDownloadPhaseFailed DataDownloadPhase = "Failed" +) + +// DataDownloadStatus is the current status of a DataDownload. +type DataDownloadStatus struct { + // Phase is the current state of the DataDownload. + // +optional + Phase DataDownloadPhase `json:"phase,omitempty"` + + // Message is a message about the DataDownload's status. + // +optional + Message string `json:"message,omitempty"` + + // StartTimestamp records the time a restore was started. + // The server's time is used for StartTimestamps + // +optional + // +nullable + StartTimestamp *metav1.Time `json:"startTimestamp,omitempty"` + + // CompletionTimestamp records the time a restore was completed. + // Completion time is recorded even on failed restores. + // The server's time is used for CompletionTimestamps + // +optional + // +nullable + CompletionTimestamp *metav1.Time `json:"completionTimestamp,omitempty"` + + // 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. + // +optional + Progress shared.DataMoveOperationProgress `json:"progress,omitempty"` +} + +// TODO(2.0) After converting all resources to use the runtime-controller client, the genclient and k8s:deepcopy markers will no longer be needed and should be removed. +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:generate=true +// +kubebuilder:object:root=true +// +kubebuilder:storageversion +// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.phase",description="DataDownload status such as New/InProgress" +// +kubebuilder:printcolumn:name="Started",type="date",JSONPath=".status.startTimestamp",description="Time duration since this DataDownload was started" +// +kubebuilder:printcolumn:name="Bytes Done",type="integer",format="int64",JSONPath=".status.progress.bytesDone",description="Completed bytes" +// +kubebuilder:printcolumn:name="Total Bytes",type="integer",format="int64",JSONPath=".status.progress.totalBytes",description="Total bytes" +// +kubebuilder:printcolumn:name="Storage Location",type="string",JSONPath=".spec.backupStorageLocation",description="Name of the Backup Storage Location where the backup data is stored" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since this DataDownload was created" + +type DataDownload struct { + metav1.TypeMeta `json:",inline"` + + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +optional + Spec DataDownloadSpec `json:"spec,omitempty"` + + // +optional + Status DataDownloadStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:generate=true +// +kubebuilder:object:root=true +// +kubebuilder:rbac:groups=velero.io,resources=datadownloads,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=velero.io,resources=datadownloads/status,verbs=get;update;patch + +// DataDownloadList is a list of DataDownloads. +type DataDownloadList struct { + metav1.TypeMeta `json:",inline"` + + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + + Items []DataDownload `json:"items"` +} diff --git a/pkg/apis/velero/v2alpha1/data_upload_types.go b/pkg/apis/velero/v2alpha1/data_upload_types.go new file mode 100644 index 000000000..e12d7478f --- /dev/null +++ b/pkg/apis/velero/v2alpha1/data_upload_types.go @@ -0,0 +1,209 @@ +/* +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 v2alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/vmware-tanzu/velero/pkg/apis/velero/shared" +) + +// DataUploadSpec is the specification for a DataUpload. +type DataUploadSpec struct { + // SnapshotType is the type of the snapshot to be backed up. + SnapshotType SnapshotType `json:"snapshotType"` + + // If SnapshotType is CSI, CSISnapshot provides the information of the CSI snapshot. + // +optional + // +nullable + CSISnapshot *CSISnapshotSpec `json:"csiSnapshot"` + + // SourcePVC is the name of the PVC which the snapshot is taken for. + SourcePVC string `json:"sourcePVC"` + + // DataMover specifies the data mover to be used by the backup. + // If DataMover is "" or "velero", the built-in data mover will be used. + // +optional + DataMover string `json:"datamover,omitempty"` + + // BackupStorageLocation is the name of the backup storage location + // where the backup repository is stored. + BackupStorageLocation string `json:"backupStorageLocation"` + + // SourceNamespace is the original namespace where the volume is backed up from. + // It is the same namespace for SourcePVC and CSI namespaced objects. + SourceNamespace string `json:"sourceNamespace"` + + // DataMoverConfig is for data-mover-specific configuration fields. + // +optional + // +nullable + DataMoverConfig *map[string]string `json:"dataMoverConfig,omitempty"` + + // Cancel indicates request to cancel the ongoing DataUpload. It can be set + // when the DataUpload is in InProgress phase + Cancel bool `json:"cancel,omitempty"` + + // OperationTimeout specifies the time used to wait internal operations, + // before returning error as timeout. + OperationTimeout metav1.Duration `json:"operationTimeout"` +} + +type SnapshotType string + +const ( + SnapshotTypeCSI SnapshotType = "CSI" +) + +// CSISnapshotSpec is the specification for a CSI snapshot. +type CSISnapshotSpec struct { + // VolumeSnapshot is the name of the volume snapshot to be backed up + VolumeSnapshot string `json:"volumeSnapshot"` + + // StorageClass is the name of the storage class of the PVC that the volume snapshot is created from + StorageClass string `json:"storageClass"` + + // StorageClass is the name of the snapshot class that the volume snapshot is created with + // +optional + SnapshotClass string `json:"snapshotClass"` +} + +// DataUploadPhase represents the lifecycle phase of a DataUpload. +// +kubebuilder:validation:Enum=New;Accepted;Prepared;InProgress;Canceling;Canceled;Completed;Failed +type DataUploadPhase string + +const ( + DataUploadPhaseNew DataUploadPhase = "New" + DataUploadPhaseAccepted DataUploadPhase = "Accepted" + DataUploadPhasePrepared DataUploadPhase = "Prepared" + DataUploadPhaseInProgress DataUploadPhase = "InProgress" + DataUploadPhaseCanceling DataUploadPhase = "Canceling" + DataUploadPhaseCanceled DataUploadPhase = "Canceled" + DataUploadPhaseCompleted DataUploadPhase = "Completed" + DataUploadPhaseFailed DataUploadPhase = "Failed" +) + +// DataUploadStatus is the current status of a DataUpload. +type DataUploadStatus struct { + // Phase is the current state of the DataUpload. + // +optional + Phase DataUploadPhase `json:"phase,omitempty"` + + // Path is the full path of the snapshot volume being backed up. + // +optional + Path string `json:"path,omitempty"` + + // SnapshotID is the identifier for the snapshot in the backup repository. + // +optional + SnapshotID string `json:"snapshotID,omitempty"` + + // DataMoverResult stores data-mover-specific information as a result of the DataUpload. + // +optional + // +nullable + DataMoverResult *map[string]string `json:"dataMoverResult,omitempty"` + + // Message is a message about the DataUpload's status. + // +optional + Message string `json:"message,omitempty"` + + // 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 + // +optional + // +nullable + StartTimestamp *metav1.Time `json:"startTimestamp,omitempty"` + + // 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 + // +optional + // +nullable + CompletionTimestamp *metav1.Time `json:"completionTimestamp,omitempty"` + + // 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. + // +optional + Progress shared.DataMoveOperationProgress `json:"progress,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 +// +kubebuilder:object:generate=true +// +kubebuilder:storageversion +// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.phase",description="DataUpload status such as New/InProgress" +// +kubebuilder:printcolumn:name="Started",type="date",JSONPath=".status.startTimestamp",description="Time duration since this DataUpload was started" +// +kubebuilder:printcolumn:name="Bytes Done",type="integer",format="int64",JSONPath=".status.progress.bytesDone",description="Completed bytes" +// +kubebuilder:printcolumn:name="Total Bytes",type="integer",format="int64",JSONPath=".status.progress.totalBytes",description="Total bytes" +// +kubebuilder:printcolumn:name="Storage Location",type="string",JSONPath=".spec.backupStorageLocation",description="Name of the Backup Storage Location where this backup should be stored" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since this DataUpload was created" + +type DataUpload struct { + metav1.TypeMeta `json:",inline"` + + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +optional + Spec DataUploadSpec `json:"spec,omitempty"` + + // +optional + Status DataUploadStatus `json:"status,omitempty"` +} + +// TODO(2.0) After converting all resources to use the runtime-controller client, +// the k8s:deepcopy marker will no longer be needed and should be removed. +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root=true +// +kubebuilder:rbac:groups=velero.io,resources=datauploads,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=velero.io,resources=datauploads/status,verbs=get;update;patch + +// DataUploadList is a list of DataUploads. +type DataUploadList struct { + metav1.TypeMeta `json:",inline"` + + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + + Items []DataUpload `json:"items"` +} + +// DataUploadResult represents the SnasphotBackup result to be used by DataDownload. +type DataUploadResult struct { + // BackupStorageLocation is the name of the backup storage location + // where the backup repository is stored. + BackupStorageLocation string `json:"backupStorageLocation"` + + // DataMover specifies the data mover used by the DataUpload + // +optional + DataMover string `json:"datamover,omitempty"` + + // SnapshotID is the identifier for the snapshot in the backup repository. + SnapshotID string `json:"snapshotID,omitempty"` + + // SourceNamespace is the original namespace where the volume is backed up from. + SourceNamespace string `json:"sourceNamespace"` + + // DataMoverResult stores data-mover-specific information as a result of the DataUpload. + // +optional + // +nullable + DataMoverResult *map[string]string `json:"dataMoverResult,omitempty"` +} diff --git a/pkg/apis/velero/v2alpha1/doc.go b/pkg/apis/velero/v2alpha1/doc.go new file mode 100644 index 000000000..9bab0a40c --- /dev/null +++ b/pkg/apis/velero/v2alpha1/doc.go @@ -0,0 +1,21 @@ +/* +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. +*/ + +// +k8s:deepcopy-gen=package + +// Package v2alpha1 is the v2alpha1 version of the API. +// +groupName=velero.io +package v2alpha1 diff --git a/pkg/apis/velero/v2alpha1/groupversion_info.go b/pkg/apis/velero/v2alpha1/groupversion_info.go new file mode 100644 index 000000000..645c95653 --- /dev/null +++ b/pkg/apis/velero/v2alpha1/groupversion_info.go @@ -0,0 +1,36 @@ +/* +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 v2alpha1 contains API Schema definitions for the velero v2alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=velero.io +package v2alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: "velero.io", Version: "v2alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + 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/v2alpha1/register.go b/pkg/apis/velero/v2alpha1/register.go new file mode 100644 index 000000000..31b471281 --- /dev/null +++ b/pkg/apis/velero/v2alpha1/register.go @@ -0,0 +1,60 @@ +/* +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. +*/ + +package v2alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// Resource gets a Velero GroupResource for a specified resource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +type typeInfo struct { + PluralName string + ItemType runtime.Object + ItemListType runtime.Object +} + +func newTypeInfo(pluralName string, itemType, itemListType runtime.Object) typeInfo { + return typeInfo{ + PluralName: pluralName, + ItemType: itemType, + ItemListType: itemListType, + } +} + +// CustomResources returns a map of all custom resources within the Velero +// API group, keyed on Kind. +func CustomResources() map[string]typeInfo { + return map[string]typeInfo{ + "DataUpload": newTypeInfo("datauploads", &DataUpload{}, &DataUploadList{}), + "DataDownload": newTypeInfo("datadownloads", &DataDownload{}, &DataDownloadList{}), + } +} + +func addKnownTypes(scheme *runtime.Scheme) error { + for _, typeInfo := range CustomResources() { + scheme.AddKnownTypes(SchemeGroupVersion, typeInfo.ItemType, typeInfo.ItemListType) + } + + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/apis/velero/v2alpha1/zz_generated.deepcopy.go b/pkg/apis/velero/v2alpha1/zz_generated.deepcopy.go new file mode 100644 index 000000000..9a9afaa6d --- /dev/null +++ b/pkg/apis/velero/v2alpha1/zz_generated.deepcopy.go @@ -0,0 +1,299 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by controller-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CSISnapshotSpec) DeepCopyInto(out *CSISnapshotSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CSISnapshotSpec. +func (in *CSISnapshotSpec) DeepCopy() *CSISnapshotSpec { + if in == nil { + return nil + } + out := new(CSISnapshotSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DataDownload) DeepCopyInto(out *DataDownload) { + *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 DataDownload. +func (in *DataDownload) DeepCopy() *DataDownload { + if in == nil { + return nil + } + out := new(DataDownload) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DataDownload) 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 *DataDownloadList) DeepCopyInto(out *DataDownloadList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]DataDownload, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataDownloadList. +func (in *DataDownloadList) DeepCopy() *DataDownloadList { + if in == nil { + return nil + } + out := new(DataDownloadList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DataDownloadList) 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 *DataDownloadSpec) DeepCopyInto(out *DataDownloadSpec) { + *out = *in + out.TargetVolume = in.TargetVolume + if in.DataMoverConfig != nil { + in, out := &in.DataMoverConfig, &out.DataMoverConfig + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + out.OperationTimeout = in.OperationTimeout +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataDownloadSpec. +func (in *DataDownloadSpec) DeepCopy() *DataDownloadSpec { + if in == nil { + return nil + } + out := new(DataDownloadSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DataDownloadStatus) DeepCopyInto(out *DataDownloadStatus) { + *out = *in + if in.StartTimestamp != nil { + in, out := &in.StartTimestamp, &out.StartTimestamp + *out = (*in).DeepCopy() + } + if in.CompletionTimestamp != nil { + in, out := &in.CompletionTimestamp, &out.CompletionTimestamp + *out = (*in).DeepCopy() + } + out.Progress = in.Progress +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataDownloadStatus. +func (in *DataDownloadStatus) DeepCopy() *DataDownloadStatus { + if in == nil { + return nil + } + out := new(DataDownloadStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DataUpload) DeepCopyInto(out *DataUpload) { + *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 DataUpload. +func (in *DataUpload) DeepCopy() *DataUpload { + if in == nil { + return nil + } + out := new(DataUpload) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DataUpload) 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 *DataUploadList) DeepCopyInto(out *DataUploadList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]DataUpload, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataUploadList. +func (in *DataUploadList) DeepCopy() *DataUploadList { + if in == nil { + return nil + } + out := new(DataUploadList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DataUploadList) 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 *DataUploadResult) DeepCopyInto(out *DataUploadResult) { + *out = *in + if in.DataMoverResult != nil { + in, out := &in.DataMoverResult, &out.DataMoverResult + *out = new(map[string]string) + if **in != nil { + in, out := *in, *out + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataUploadResult. +func (in *DataUploadResult) DeepCopy() *DataUploadResult { + if in == nil { + return nil + } + out := new(DataUploadResult) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DataUploadSpec) DeepCopyInto(out *DataUploadSpec) { + *out = *in + if in.CSISnapshot != nil { + in, out := &in.CSISnapshot, &out.CSISnapshot + *out = new(CSISnapshotSpec) + **out = **in + } + if in.DataMoverConfig != nil { + in, out := &in.DataMoverConfig, &out.DataMoverConfig + *out = new(map[string]string) + if **in != nil { + in, out := *in, *out + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + out.OperationTimeout = in.OperationTimeout +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataUploadSpec. +func (in *DataUploadSpec) DeepCopy() *DataUploadSpec { + if in == nil { + return nil + } + out := new(DataUploadSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DataUploadStatus) DeepCopyInto(out *DataUploadStatus) { + *out = *in + if in.DataMoverResult != nil { + in, out := &in.DataMoverResult, &out.DataMoverResult + *out = new(map[string]string) + if **in != nil { + in, out := *in, *out + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + if in.StartTimestamp != nil { + in, out := &in.StartTimestamp, &out.StartTimestamp + *out = (*in).DeepCopy() + } + if in.CompletionTimestamp != nil { + in, out := &in.CompletionTimestamp, &out.CompletionTimestamp + *out = (*in).DeepCopy() + } + out.Progress = in.Progress +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataUploadStatus. +func (in *DataUploadStatus) DeepCopy() *DataUploadStatus { + if in == nil { + return nil + } + out := new(DataUploadStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TargetVolumeSpec) DeepCopyInto(out *TargetVolumeSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetVolumeSpec. +func (in *TargetVolumeSpec) DeepCopy() *TargetVolumeSpec { + if in == nil { + return nil + } + out := new(TargetVolumeSpec) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index 34cb42baa..4067712dd 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -635,7 +635,11 @@ func (kb *kubernetesBackupper) FinalizeBackup(log logrus.FieldLogger, } // write new tar archive replacing files in original with content updateFiles for matches - buildFinalTarball(tr, tw, updateFiles) + if err := buildFinalTarball(tr, tw, updateFiles); err != nil { + log.Errorf("Error building final tarball: %s", err.Error()) + return err + } + log.WithField("progress", "").Infof("Updated a total of %d items", len(backupRequest.BackedUpItems)) return nil diff --git a/pkg/client/factory.go b/pkg/client/factory.go index aa5119c26..36a6a0459 100644 --- a/pkg/client/factory.go +++ b/pkg/client/factory.go @@ -32,6 +32,7 @@ import ( "k8s.io/client-go/rest" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" ) @@ -153,10 +154,21 @@ func (f *factory) KubebuilderClient() (kbclient.Client, error) { } scheme := runtime.NewScheme() - velerov1api.AddToScheme(scheme) - k8scheme.AddToScheme(scheme) - apiextv1beta1.AddToScheme(scheme) - apiextv1.AddToScheme(scheme) + if err := velerov1api.AddToScheme(scheme); err != nil { + return nil, err + } + if err := velerov2alpha1api.AddToScheme(scheme); err != nil { + return nil, err + } + if err := k8scheme.AddToScheme(scheme); err != nil { + return nil, err + } + if err := apiextv1beta1.AddToScheme(scheme); err != nil { + return nil, err + } + if err := apiextv1.AddToScheme(scheme); err != nil { + return nil, err + } kubebuilderClient, err := kbclient.New(clientConfig, kbclient.Options{ Scheme: scheme, }) diff --git a/pkg/cmd/cli/bug/bug.go b/pkg/cmd/cli/bug/bug.go index a8237e1c3..9f87e3f5a 100644 --- a/pkg/cmd/cli/bug/bug.go +++ b/pkg/cmd/cli/bug/bug.go @@ -162,7 +162,9 @@ func getKubectlVersion() (string, error) { case <-time.After(kubectlTimeout): // we don't care about the possible error returned from Kill() here, // just return an empty string - kubectlCmd.Process.Kill() + if err := kubectlCmd.Process.Kill(); err != nil { + return "", fmt.Errorf("error killing kubectl process: %w", err) + } return "", errors.New("timeout waiting for kubectl version") case err := <-done: diff --git a/pkg/cmd/cli/completion/completion.go b/pkg/cmd/cli/completion/completion.go index ee1c28509..9aff563d4 100644 --- a/pkg/cmd/cli/completion/completion.go +++ b/pkg/cmd/cli/completion/completion.go @@ -64,7 +64,10 @@ $ velero completion fish > ~/.config/fish/completions/velero.fish shell := args[0] switch shell { case "bash": - cmd.Root().GenBashCompletion(os.Stdout) + if err := cmd.Root().GenBashCompletion(os.Stdout); err != nil { + fmt.Println("fail to generate bash completion script", err) + os.Exit(1) + } case "zsh": // # fix #4912 // cobra does not support zsh completion ouptput used by source command @@ -72,11 +75,20 @@ $ velero completion fish > ~/.config/fish/completions/velero.fish // Need to append compdef manually to do that. zshHead := "#compdef velero\ncompdef _velero velero\n" out := os.Stdout - out.Write([]byte(zshHead)) + if _, err := out.Write([]byte(zshHead)); err != nil { + fmt.Println("fail to append compdef command into zsh completion script: ", err) + os.Exit(1) + } - cmd.Root().GenZshCompletion(out) + if err := cmd.Root().GenZshCompletion(out); err != nil { + fmt.Println("fail to generate zsh completion script: ", err) + os.Exit(1) + } case "fish": - cmd.Root().GenFishCompletion(os.Stdout, true) + if err := cmd.Root().GenFishCompletion(os.Stdout, true); err != nil { + fmt.Println("fail to generate fish completion script: ", err) + os.Exit(1) + } default: fmt.Println("Invalid shell specified, specify bash, zsh, or fish") os.Exit(1) diff --git a/pkg/cmd/cli/nodeagent/server.go b/pkg/cmd/cli/nodeagent/server.go index df9470759..167109c04 100644 --- a/pkg/cmd/cli/nodeagent/server.go +++ b/pkg/cmd/cli/nodeagent/server.go @@ -44,6 +44,7 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" "github.com/vmware-tanzu/velero/pkg/buildinfo" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/cmd" @@ -134,9 +135,22 @@ func newNodeAgentServer(logger logrus.FieldLogger, factory client.Factory, confi ctrl.SetLogger(zap.New(zap.UseDevMode(true))) - velerov1api.AddToScheme(scheme) - v1.AddToScheme(scheme) - storagev1api.AddToScheme(scheme) + if err := velerov1api.AddToScheme(scheme); err != nil { + cancelFunc() + return nil, err + } + if err := velerov2alpha1api.AddToScheme(scheme); err != nil { + cancelFunc() + return nil, err + } + if err := v1.AddToScheme(scheme); err != nil { + cancelFunc() + return nil, err + } + if err := storagev1api.AddToScheme(scheme); err != nil { + cancelFunc() + return nil, err + } nodeName := os.Getenv("NODE_NAME") diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 0242da726..ec108536e 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -55,6 +55,7 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" "github.com/vmware-tanzu/velero/internal/storage" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/buildinfo" "github.com/vmware-tanzu/velero/pkg/client" @@ -313,9 +314,22 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s } scheme := runtime.NewScheme() - velerov1api.AddToScheme(scheme) - corev1api.AddToScheme(scheme) - snapshotv1api.AddToScheme(scheme) + if err := velerov1api.AddToScheme(scheme); err != nil { + cancelFunc() + return nil, err + } + if err := velerov2alpha1api.AddToScheme(scheme); err != nil { + cancelFunc() + return nil, err + } + if err := corev1api.AddToScheme(scheme); err != nil { + cancelFunc() + return nil, err + } + if err := snapshotv1api.AddToScheme(scheme); err != nil { + cancelFunc() + return nil, err + } ctrl.SetLogger(logrusr.New(logger)) diff --git a/pkg/cmd/util/output/backup_describer.go b/pkg/cmd/util/output/backup_describer.go index 983c0e6b0..7fa210c18 100644 --- a/pkg/cmd/util/output/backup_describer.go +++ b/pkg/cmd/util/output/backup_describer.go @@ -32,6 +32,7 @@ import ( "github.com/fatih/color" kbclient "sigs.k8s.io/controller-runtime/pkg/client" + veleroapishared "github.com/vmware-tanzu/velero/pkg/apis/velero/shared" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/cmd/util/downloadrequest" "github.com/vmware-tanzu/velero/pkg/features" @@ -597,7 +598,7 @@ type volumesByPod struct { // Add adds a pod volume with the specified pod namespace, name // and volume to the appropriate group. -func (v *volumesByPod) Add(namespace, name, volume, phase string, progress velerov1api.PodVolumeOperationProgress) { +func (v *volumesByPod) Add(namespace, name, volume, phase string, progress veleroapishared.DataMoveOperationProgress) { if v.volumesByPodMap == nil { v.volumesByPodMap = make(map[string]*podVolumeGroup) } diff --git a/pkg/cmd/util/output/output.go b/pkg/cmd/util/output/output.go index e964d698b..5b3095cd6 100644 --- a/pkg/cmd/util/output/output.go +++ b/pkg/cmd/util/output/output.go @@ -61,7 +61,9 @@ func ClearOutputFlagDefault(cmd *cobra.Command) { return } f.DefValue = "" - f.Value.Set("") + if err := f.Value.Set(""); err != nil { + fmt.Printf("error clear the default value of output flag: %s\n", err.Error()) + } } // GetOutputFlagValue returns the value of the "output" flag diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index d485562f8..90462a60f 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -561,8 +561,7 @@ func (b *backupReconciler) validateAndGetSnapshotLocations(backup *velerov1api.B continue } location := &velerov1api.VolumeSnapshotLocation{} - b.kbClient.Get(context.Background(), kbclient.ObjectKey{Namespace: backup.Namespace, Name: defaultLocation}, location) - if err != nil { + if err := b.kbClient.Get(context.Background(), kbclient.ObjectKey{Namespace: backup.Namespace, Name: defaultLocation}, location); err != nil { errors = append(errors, fmt.Sprintf("error getting volume snapshot location named %s: %v", defaultLocation, err)) continue } diff --git a/pkg/controller/backup_repository_controller.go b/pkg/controller/backup_repository_controller.go index 34e4b23e2..12d293051 100644 --- a/pkg/controller/backup_repository_controller.go +++ b/pkg/controller/backup_repository_controller.go @@ -96,7 +96,9 @@ func (r *BackupRepoReconciler) invalidateBackupReposForBSL(bslObj client.Object) for i := range list.Items { r.logger.WithField("BSL", bsl.Name).Infof("Invalidating Backup Repository %s", list.Items[i].Name) - r.patchBackupRepository(context.Background(), &list.Items[i], repoNotReady("re-establish on BSL change")) + if err := r.patchBackupRepository(context.Background(), &list.Items[i], repoNotReady("re-establish on BSL change")); err != nil { + r.logger.WithField("BSL", bsl.Name).WithError(err).Errorf("fail to patch BackupRepository %s", list.Items[i].Name) + } } return []reconcile.Request{} diff --git a/pkg/controller/backup_sync_controller.go b/pkg/controller/backup_sync_controller.go index 606077fa1..0ffc0f977 100644 --- a/pkg/controller/backup_sync_controller.go +++ b/pkg/controller/backup_sync_controller.go @@ -18,6 +18,7 @@ package controller import ( "context" + "fmt" "time" snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" @@ -397,7 +398,10 @@ func backupSyncSourceOrderFunc(objList client.ObjectList) client.ObjectList { cpBsl := bsl bslArray = append(bslArray, &cpBsl) } - meta.SetList(resultBSLList, bslArray) + if err := meta.SetList(resultBSLList, bslArray); err != nil { + fmt.Printf("fail to sort BSL list: %s", err.Error()) + return &velerov1api.BackupStorageLocationList{} + } return resultBSLList } diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 7c57d7bde..9b4a4cb8c 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -33,6 +33,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/vmware-tanzu/velero/internal/credentials" + veleroapishared "github.com/vmware-tanzu/velero/pkg/apis/velero/shared" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/datapath" "github.com/vmware-tanzu/velero/pkg/exposer" @@ -264,7 +265,7 @@ func (r *PodVolumeBackupReconciler) OnDataPathProgress(ctx context.Context, name } original := pvb.DeepCopy() - pvb.Status.Progress = velerov1api.PodVolumeOperationProgress{TotalBytes: progress.TotalBytes, BytesDone: progress.BytesDone} + pvb.Status.Progress = veleroapishared.DataMoveOperationProgress{TotalBytes: progress.TotalBytes, BytesDone: progress.BytesDone} if err := r.Client.Patch(ctx, &pvb, client.MergeFrom(original)); err != nil { log.WithError(err).Error("Failed to update progress") @@ -358,10 +359,10 @@ func UpdatePVBStatusToFailed(ctx context.Context, c client.Client, pvb *velerov1 pvb.Status.Message = errString pvb.Status.CompletionTimestamp = &metav1.Time{Time: time} - if err := c.Patch(ctx, pvb, client.MergeFrom(original)); err != nil { + err := c.Patch(ctx, pvb, client.MergeFrom(original)) + if err != nil { log.WithError(err).Error("error updating PodVolumeBackup status") - return err - } else { - return nil } + + return err } diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 3d1b707d1..3e75422d3 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -38,6 +38,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" "github.com/vmware-tanzu/velero/internal/credentials" + veleroapishared "github.com/vmware-tanzu/velero/pkg/apis/velero/shared" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/datapath" "github.com/vmware-tanzu/velero/pkg/exposer" @@ -167,12 +168,12 @@ func UpdatePVRStatusToFailed(ctx context.Context, c client.Client, pvb *velerov1 pvb.Status.Message = errString pvb.Status.CompletionTimestamp = &metav1.Time{Time: time} - if err := c.Patch(ctx, pvb, client.MergeFrom(original)); err != nil { + err := c.Patch(ctx, pvb, client.MergeFrom(original)) + if err != nil { log.WithError(err).Error("error updating PodVolumeRestore status") - return err - } else { - return nil } + + return err } func (c *PodVolumeRestoreReconciler) shouldProcess(ctx context.Context, log logrus.FieldLogger, pvr *velerov1api.PodVolumeRestore) (bool, *corev1api.Pod, error) { @@ -357,7 +358,7 @@ func (c *PodVolumeRestoreReconciler) OnDataPathProgress(ctx context.Context, nam } original := pvr.DeepCopy() - pvr.Status.Progress = velerov1api.PodVolumeOperationProgress{TotalBytes: progress.TotalBytes, BytesDone: progress.BytesDone} + pvr.Status.Progress = veleroapishared.DataMoveOperationProgress{TotalBytes: progress.TotalBytes, BytesDone: progress.BytesDone} if err := c.Client.Patch(ctx, &pvr, client.MergeFrom(original)); err != nil { log.WithError(err).Error("Failed to update progress") diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index 501893f7d..c63de4fa5 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -305,10 +305,7 @@ func (r *restoreReconciler) validateAndComplete(restore *api.Restore) backupInfo })) backupList := &api.BackupList{} - r.kbClient.List(context.Background(), backupList, &client.ListOptions{ - LabelSelector: selector, - }) - if err != nil { + if err := r.kbClient.List(context.Background(), backupList, &client.ListOptions{LabelSelector: selector}); err != nil { restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, "Unable to list backups for schedule") return backupInfo{} } diff --git a/pkg/controller/restore_operations_controller.go b/pkg/controller/restore_operations_controller.go index 8f5945742..100fef214 100644 --- a/pkg/controller/restore_operations_controller.go +++ b/pkg/controller/restore_operations_controller.go @@ -96,7 +96,6 @@ func (r *restoreOperationsReconciler) SetupWithManager(mgr ctrl.Manager) error { // +kubebuilder:rbac:groups=velero.io,resources=restores,verbs=get;list;watch;update // +kubebuilder:rbac:groups=velero.io,resources=restores/status,verbs=get -// +kubebuilder:rbac:groups=velero.io,resources=restorestoragelocations,verbs=get func (r *restoreOperationsReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := r.logger.WithField("restore operations for restore", req.String()) diff --git a/pkg/controller/suite_test.go b/pkg/controller/suite_test.go index f68ab84a0..44c858bcf 100644 --- a/pkg/controller/suite_test.go +++ b/pkg/controller/suite_test.go @@ -36,6 +36,7 @@ import ( . "github.com/onsi/gomega" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" @@ -98,6 +99,9 @@ func newTestEnvironment() *testEnvironment { err := velerov1api.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) + err = velerov2alpha1api.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + env = &envtest.Environment{ CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, } diff --git a/pkg/generated/clientset/versioned/clientset.go b/pkg/generated/clientset/versioned/clientset.go index c98ad605e..881dee994 100644 --- a/pkg/generated/clientset/versioned/clientset.go +++ b/pkg/generated/clientset/versioned/clientset.go @@ -22,6 +22,7 @@ import ( "fmt" velerov1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" + velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v2alpha1" discovery "k8s.io/client-go/discovery" rest "k8s.io/client-go/rest" flowcontrol "k8s.io/client-go/util/flowcontrol" @@ -30,13 +31,15 @@ import ( type Interface interface { Discovery() discovery.DiscoveryInterface VeleroV1() velerov1.VeleroV1Interface + VeleroV2alpha1() velerov2alpha1.VeleroV2alpha1Interface } // Clientset contains the clients for groups. Each group has exactly one // version included in a Clientset. type Clientset struct { *discovery.DiscoveryClient - veleroV1 *velerov1.VeleroV1Client + veleroV1 *velerov1.VeleroV1Client + veleroV2alpha1 *velerov2alpha1.VeleroV2alpha1Client } // VeleroV1 retrieves the VeleroV1Client @@ -44,6 +47,11 @@ func (c *Clientset) VeleroV1() velerov1.VeleroV1Interface { return c.veleroV1 } +// VeleroV2alpha1 retrieves the VeleroV2alpha1Client +func (c *Clientset) VeleroV2alpha1() velerov2alpha1.VeleroV2alpha1Interface { + return c.veleroV2alpha1 +} + // Discovery retrieves the DiscoveryClient func (c *Clientset) Discovery() discovery.DiscoveryInterface { if c == nil { @@ -69,6 +77,10 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { if err != nil { return nil, err } + cs.veleroV2alpha1, err = velerov2alpha1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) if err != nil { @@ -82,6 +94,7 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { func NewForConfigOrDie(c *rest.Config) *Clientset { var cs Clientset cs.veleroV1 = velerov1.NewForConfigOrDie(c) + cs.veleroV2alpha1 = velerov2alpha1.NewForConfigOrDie(c) cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) return &cs @@ -91,6 +104,7 @@ func NewForConfigOrDie(c *rest.Config) *Clientset { func New(c rest.Interface) *Clientset { var cs Clientset cs.veleroV1 = velerov1.New(c) + cs.veleroV2alpha1 = velerov2alpha1.New(c) cs.DiscoveryClient = discovery.NewDiscoveryClient(c) return &cs diff --git a/pkg/generated/clientset/versioned/fake/clientset_generated.go b/pkg/generated/clientset/versioned/fake/clientset_generated.go index 971ea5491..d514b27c1 100644 --- a/pkg/generated/clientset/versioned/fake/clientset_generated.go +++ b/pkg/generated/clientset/versioned/fake/clientset_generated.go @@ -22,6 +22,8 @@ import ( clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" velerov1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" fakevelerov1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1/fake" + velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v2alpha1" + fakevelerov2alpha1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/discovery" @@ -83,3 +85,8 @@ var ( func (c *Clientset) VeleroV1() velerov1.VeleroV1Interface { return &fakevelerov1.FakeVeleroV1{Fake: &c.Fake} } + +// VeleroV2alpha1 retrieves the VeleroV2alpha1Client +func (c *Clientset) VeleroV2alpha1() velerov2alpha1.VeleroV2alpha1Interface { + return &fakevelerov2alpha1.FakeVeleroV2alpha1{Fake: &c.Fake} +} diff --git a/pkg/generated/clientset/versioned/fake/register.go b/pkg/generated/clientset/versioned/fake/register.go index 5839b1517..8e9316a47 100644 --- a/pkg/generated/clientset/versioned/fake/register.go +++ b/pkg/generated/clientset/versioned/fake/register.go @@ -20,6 +20,7 @@ package fake import ( velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" @@ -32,6 +33,7 @@ var codecs = serializer.NewCodecFactory(scheme) var localSchemeBuilder = runtime.SchemeBuilder{ velerov1.AddToScheme, + velerov2alpha1.AddToScheme, } // AddToScheme adds all types of this clientset into the given scheme. This allows composition diff --git a/pkg/generated/clientset/versioned/scheme/register.go b/pkg/generated/clientset/versioned/scheme/register.go index f94b9a72c..12654733e 100644 --- a/pkg/generated/clientset/versioned/scheme/register.go +++ b/pkg/generated/clientset/versioned/scheme/register.go @@ -20,6 +20,7 @@ package scheme import ( velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" @@ -32,6 +33,7 @@ var Codecs = serializer.NewCodecFactory(Scheme) var ParameterCodec = runtime.NewParameterCodec(Scheme) var localSchemeBuilder = runtime.SchemeBuilder{ velerov1.AddToScheme, + velerov2alpha1.AddToScheme, } // AddToScheme adds all types of this clientset into the given scheme. This allows composition diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/datadownload.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/datadownload.go new file mode 100644 index 000000000..511677675 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/datadownload.go @@ -0,0 +1,195 @@ +/* +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 v2alpha1 + +import ( + "context" + "time" + + v2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" + scheme "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" + v1 "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" +) + +// DataDownloadsGetter has a method to return a DataDownloadInterface. +// A group's client should implement this interface. +type DataDownloadsGetter interface { + DataDownloads(namespace string) DataDownloadInterface +} + +// DataDownloadInterface has methods to work with DataDownload resources. +type DataDownloadInterface interface { + Create(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.CreateOptions) (*v2alpha1.DataDownload, error) + Update(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.UpdateOptions) (*v2alpha1.DataDownload, error) + UpdateStatus(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.UpdateOptions) (*v2alpha1.DataDownload, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v2alpha1.DataDownload, error) + List(ctx context.Context, opts v1.ListOptions) (*v2alpha1.DataDownloadList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2alpha1.DataDownload, err error) + DataDownloadExpansion +} + +// dataDownloads implements DataDownloadInterface +type dataDownloads struct { + client rest.Interface + ns string +} + +// newDataDownloads returns a DataDownloads +func newDataDownloads(c *VeleroV2alpha1Client, namespace string) *dataDownloads { + return &dataDownloads{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the dataDownload, and returns the corresponding dataDownload object, and an error if there is any. +func (c *dataDownloads) Get(ctx context.Context, name string, options v1.GetOptions) (result *v2alpha1.DataDownload, err error) { + result = &v2alpha1.DataDownload{} + err = c.client.Get(). + Namespace(c.ns). + Resource("datadownloads"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of DataDownloads that match those selectors. +func (c *dataDownloads) List(ctx context.Context, opts v1.ListOptions) (result *v2alpha1.DataDownloadList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v2alpha1.DataDownloadList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("datadownloads"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested dataDownloads. +func (c *dataDownloads) Watch(ctx context.Context, opts v1.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("datadownloads"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a dataDownload and creates it. Returns the server's representation of the dataDownload, and an error, if there is any. +func (c *dataDownloads) Create(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.CreateOptions) (result *v2alpha1.DataDownload, err error) { + result = &v2alpha1.DataDownload{} + err = c.client.Post(). + Namespace(c.ns). + Resource("datadownloads"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(dataDownload). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a dataDownload and updates it. Returns the server's representation of the dataDownload, and an error, if there is any. +func (c *dataDownloads) Update(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.UpdateOptions) (result *v2alpha1.DataDownload, err error) { + result = &v2alpha1.DataDownload{} + err = c.client.Put(). + Namespace(c.ns). + Resource("datadownloads"). + Name(dataDownload.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(dataDownload). + Do(ctx). + 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 *dataDownloads) UpdateStatus(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.UpdateOptions) (result *v2alpha1.DataDownload, err error) { + result = &v2alpha1.DataDownload{} + err = c.client.Put(). + Namespace(c.ns). + Resource("datadownloads"). + Name(dataDownload.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(dataDownload). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the dataDownload and deletes it. Returns an error if one occurs. +func (c *dataDownloads) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("datadownloads"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *dataDownloads) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("datadownloads"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched dataDownload. +func (c *dataDownloads) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2alpha1.DataDownload, err error) { + result = &v2alpha1.DataDownload{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("datadownloads"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/dataupload.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/dataupload.go new file mode 100644 index 000000000..4da27d527 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/dataupload.go @@ -0,0 +1,195 @@ +/* +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 v2alpha1 + +import ( + "context" + "time" + + v2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" + scheme "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" + v1 "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" +) + +// DataUploadsGetter has a method to return a DataUploadInterface. +// A group's client should implement this interface. +type DataUploadsGetter interface { + DataUploads(namespace string) DataUploadInterface +} + +// DataUploadInterface has methods to work with DataUpload resources. +type DataUploadInterface interface { + Create(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.CreateOptions) (*v2alpha1.DataUpload, error) + Update(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.UpdateOptions) (*v2alpha1.DataUpload, error) + UpdateStatus(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.UpdateOptions) (*v2alpha1.DataUpload, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v2alpha1.DataUpload, error) + List(ctx context.Context, opts v1.ListOptions) (*v2alpha1.DataUploadList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2alpha1.DataUpload, err error) + DataUploadExpansion +} + +// dataUploads implements DataUploadInterface +type dataUploads struct { + client rest.Interface + ns string +} + +// newDataUploads returns a DataUploads +func newDataUploads(c *VeleroV2alpha1Client, namespace string) *dataUploads { + return &dataUploads{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the dataUpload, and returns the corresponding dataUpload object, and an error if there is any. +func (c *dataUploads) Get(ctx context.Context, name string, options v1.GetOptions) (result *v2alpha1.DataUpload, err error) { + result = &v2alpha1.DataUpload{} + err = c.client.Get(). + Namespace(c.ns). + Resource("datauploads"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of DataUploads that match those selectors. +func (c *dataUploads) List(ctx context.Context, opts v1.ListOptions) (result *v2alpha1.DataUploadList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v2alpha1.DataUploadList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("datauploads"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested dataUploads. +func (c *dataUploads) Watch(ctx context.Context, opts v1.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("datauploads"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a dataUpload and creates it. Returns the server's representation of the dataUpload, and an error, if there is any. +func (c *dataUploads) Create(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.CreateOptions) (result *v2alpha1.DataUpload, err error) { + result = &v2alpha1.DataUpload{} + err = c.client.Post(). + Namespace(c.ns). + Resource("datauploads"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(dataUpload). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a dataUpload and updates it. Returns the server's representation of the dataUpload, and an error, if there is any. +func (c *dataUploads) Update(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.UpdateOptions) (result *v2alpha1.DataUpload, err error) { + result = &v2alpha1.DataUpload{} + err = c.client.Put(). + Namespace(c.ns). + Resource("datauploads"). + Name(dataUpload.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(dataUpload). + Do(ctx). + 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 *dataUploads) UpdateStatus(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.UpdateOptions) (result *v2alpha1.DataUpload, err error) { + result = &v2alpha1.DataUpload{} + err = c.client.Put(). + Namespace(c.ns). + Resource("datauploads"). + Name(dataUpload.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(dataUpload). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the dataUpload and deletes it. Returns an error if one occurs. +func (c *dataUploads) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("datauploads"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *dataUploads) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("datauploads"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched dataUpload. +func (c *dataUploads) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2alpha1.DataUpload, err error) { + result = &v2alpha1.DataUpload{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("datauploads"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/doc.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/doc.go new file mode 100644 index 000000000..18b5cb4d4 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/doc.go @@ -0,0 +1,20 @@ +/* +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. + +// This package has the automatically generated typed clients. +package v2alpha1 diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/doc.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/doc.go new file mode 100644 index 000000000..de930591e --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/doc.go @@ -0,0 +1,20 @@ +/* +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 has the automatically generated clients. +package fake diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_datadownload.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_datadownload.go new file mode 100644 index 000000000..40db6018b --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_datadownload.go @@ -0,0 +1,142 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" + 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" +) + +// FakeDataDownloads implements DataDownloadInterface +type FakeDataDownloads struct { + Fake *FakeVeleroV2alpha1 + ns string +} + +var datadownloadsResource = schema.GroupVersionResource{Group: "velero.io", Version: "v2alpha1", Resource: "datadownloads"} + +var datadownloadsKind = schema.GroupVersionKind{Group: "velero.io", Version: "v2alpha1", Kind: "DataDownload"} + +// Get takes name of the dataDownload, and returns the corresponding dataDownload object, and an error if there is any. +func (c *FakeDataDownloads) Get(ctx context.Context, name string, options v1.GetOptions) (result *v2alpha1.DataDownload, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(datadownloadsResource, c.ns, name), &v2alpha1.DataDownload{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.DataDownload), err +} + +// List takes label and field selectors, and returns the list of DataDownloads that match those selectors. +func (c *FakeDataDownloads) List(ctx context.Context, opts v1.ListOptions) (result *v2alpha1.DataDownloadList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(datadownloadsResource, datadownloadsKind, c.ns, opts), &v2alpha1.DataDownloadList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v2alpha1.DataDownloadList{ListMeta: obj.(*v2alpha1.DataDownloadList).ListMeta} + for _, item := range obj.(*v2alpha1.DataDownloadList).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 dataDownloads. +func (c *FakeDataDownloads) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(datadownloadsResource, c.ns, opts)) + +} + +// Create takes the representation of a dataDownload and creates it. Returns the server's representation of the dataDownload, and an error, if there is any. +func (c *FakeDataDownloads) Create(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.CreateOptions) (result *v2alpha1.DataDownload, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(datadownloadsResource, c.ns, dataDownload), &v2alpha1.DataDownload{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.DataDownload), err +} + +// Update takes the representation of a dataDownload and updates it. Returns the server's representation of the dataDownload, and an error, if there is any. +func (c *FakeDataDownloads) Update(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.UpdateOptions) (result *v2alpha1.DataDownload, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(datadownloadsResource, c.ns, dataDownload), &v2alpha1.DataDownload{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.DataDownload), 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 *FakeDataDownloads) UpdateStatus(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.UpdateOptions) (*v2alpha1.DataDownload, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(datadownloadsResource, "status", c.ns, dataDownload), &v2alpha1.DataDownload{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.DataDownload), err +} + +// Delete takes name of the dataDownload and deletes it. Returns an error if one occurs. +func (c *FakeDataDownloads) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(datadownloadsResource, c.ns, name), &v2alpha1.DataDownload{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeDataDownloads) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(datadownloadsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v2alpha1.DataDownloadList{}) + return err +} + +// Patch applies the patch and returns the patched dataDownload. +func (c *FakeDataDownloads) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2alpha1.DataDownload, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(datadownloadsResource, c.ns, name, pt, data, subresources...), &v2alpha1.DataDownload{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.DataDownload), err +} diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_dataupload.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_dataupload.go new file mode 100644 index 000000000..d40b50874 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_dataupload.go @@ -0,0 +1,142 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" + 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" +) + +// FakeDataUploads implements DataUploadInterface +type FakeDataUploads struct { + Fake *FakeVeleroV2alpha1 + ns string +} + +var datauploadsResource = schema.GroupVersionResource{Group: "velero.io", Version: "v2alpha1", Resource: "datauploads"} + +var datauploadsKind = schema.GroupVersionKind{Group: "velero.io", Version: "v2alpha1", Kind: "DataUpload"} + +// Get takes name of the dataUpload, and returns the corresponding dataUpload object, and an error if there is any. +func (c *FakeDataUploads) Get(ctx context.Context, name string, options v1.GetOptions) (result *v2alpha1.DataUpload, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(datauploadsResource, c.ns, name), &v2alpha1.DataUpload{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.DataUpload), err +} + +// List takes label and field selectors, and returns the list of DataUploads that match those selectors. +func (c *FakeDataUploads) List(ctx context.Context, opts v1.ListOptions) (result *v2alpha1.DataUploadList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(datauploadsResource, datauploadsKind, c.ns, opts), &v2alpha1.DataUploadList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v2alpha1.DataUploadList{ListMeta: obj.(*v2alpha1.DataUploadList).ListMeta} + for _, item := range obj.(*v2alpha1.DataUploadList).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 dataUploads. +func (c *FakeDataUploads) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(datauploadsResource, c.ns, opts)) + +} + +// Create takes the representation of a dataUpload and creates it. Returns the server's representation of the dataUpload, and an error, if there is any. +func (c *FakeDataUploads) Create(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.CreateOptions) (result *v2alpha1.DataUpload, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(datauploadsResource, c.ns, dataUpload), &v2alpha1.DataUpload{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.DataUpload), err +} + +// Update takes the representation of a dataUpload and updates it. Returns the server's representation of the dataUpload, and an error, if there is any. +func (c *FakeDataUploads) Update(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.UpdateOptions) (result *v2alpha1.DataUpload, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(datauploadsResource, c.ns, dataUpload), &v2alpha1.DataUpload{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.DataUpload), 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 *FakeDataUploads) UpdateStatus(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.UpdateOptions) (*v2alpha1.DataUpload, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(datauploadsResource, "status", c.ns, dataUpload), &v2alpha1.DataUpload{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.DataUpload), err +} + +// Delete takes name of the dataUpload and deletes it. Returns an error if one occurs. +func (c *FakeDataUploads) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(datauploadsResource, c.ns, name), &v2alpha1.DataUpload{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeDataUploads) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(datauploadsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v2alpha1.DataUploadList{}) + return err +} + +// Patch applies the patch and returns the patched dataUpload. +func (c *FakeDataUploads) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2alpha1.DataUpload, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(datauploadsResource, c.ns, name, pt, data, subresources...), &v2alpha1.DataUpload{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.DataUpload), err +} diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_velero_client.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_velero_client.go new file mode 100644 index 000000000..25fee2e7a --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_velero_client.go @@ -0,0 +1,44 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v2alpha1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v2alpha1" + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" +) + +type FakeVeleroV2alpha1 struct { + *testing.Fake +} + +func (c *FakeVeleroV2alpha1) DataDownloads(namespace string) v2alpha1.DataDownloadInterface { + return &FakeDataDownloads{c, namespace} +} + +func (c *FakeVeleroV2alpha1) DataUploads(namespace string) v2alpha1.DataUploadInterface { + return &FakeDataUploads{c, namespace} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeVeleroV2alpha1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/generated_expansion.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/generated_expansion.go new file mode 100644 index 000000000..1ea0b5ae2 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/generated_expansion.go @@ -0,0 +1,23 @@ +/* +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 v2alpha1 + +type DataDownloadExpansion interface{} + +type DataUploadExpansion interface{} diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/velero_client.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/velero_client.go new file mode 100644 index 000000000..6b2ea0980 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/velero_client.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 client-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + v2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" + "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" + rest "k8s.io/client-go/rest" +) + +type VeleroV2alpha1Interface interface { + RESTClient() rest.Interface + DataDownloadsGetter + DataUploadsGetter +} + +// VeleroV2alpha1Client is used to interact with features provided by the velero.io group. +type VeleroV2alpha1Client struct { + restClient rest.Interface +} + +func (c *VeleroV2alpha1Client) DataDownloads(namespace string) DataDownloadInterface { + return newDataDownloads(c, namespace) +} + +func (c *VeleroV2alpha1Client) DataUploads(namespace string) DataUploadInterface { + return newDataUploads(c, namespace) +} + +// NewForConfig creates a new VeleroV2alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*VeleroV2alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &VeleroV2alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new VeleroV2alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *VeleroV2alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new VeleroV2alpha1Client for the given RESTClient. +func New(c rest.Interface) *VeleroV2alpha1Client { + return &VeleroV2alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v2alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *VeleroV2alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go index 605887024..7e0533afc 100644 --- a/pkg/generated/informers/externalversions/generic.go +++ b/pkg/generated/informers/externalversions/generic.go @@ -22,6 +22,7 @@ import ( "fmt" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + v2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" schema "k8s.io/apimachinery/pkg/runtime/schema" cache "k8s.io/client-go/tools/cache" ) @@ -76,6 +77,12 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource case v1.SchemeGroupVersion.WithResource("volumesnapshotlocations"): return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().VolumeSnapshotLocations().Informer()}, nil + // Group=velero.io, Version=v2alpha1 + case v2alpha1.SchemeGroupVersion.WithResource("datadownloads"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V2alpha1().DataDownloads().Informer()}, nil + case v2alpha1.SchemeGroupVersion.WithResource("datauploads"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V2alpha1().DataUploads().Informer()}, nil + } return nil, fmt.Errorf("no informer found for %v", resource) diff --git a/pkg/generated/informers/externalversions/velero/interface.go b/pkg/generated/informers/externalversions/velero/interface.go index 767e18b41..87fc652e6 100644 --- a/pkg/generated/informers/externalversions/velero/interface.go +++ b/pkg/generated/informers/externalversions/velero/interface.go @@ -21,12 +21,15 @@ package velero import ( internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" v1 "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" + v2alpha1 "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v2alpha1" ) // Interface provides access to each of this group's versions. type Interface interface { // V1 provides access to shared informers for resources in V1. V1() v1.Interface + // V2alpha1 provides access to shared informers for resources in V2alpha1. + V2alpha1() v2alpha1.Interface } type group struct { @@ -44,3 +47,8 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList func (g *group) V1() v1.Interface { return v1.New(g.factory, g.namespace, g.tweakListOptions) } + +// V2alpha1 returns a new v2alpha1.Interface. +func (g *group) V2alpha1() v2alpha1.Interface { + return v2alpha1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/pkg/generated/informers/externalversions/velero/v2alpha1/datadownload.go b/pkg/generated/informers/externalversions/velero/v2alpha1/datadownload.go new file mode 100644 index 000000000..3b539b332 --- /dev/null +++ b/pkg/generated/informers/externalversions/velero/v2alpha1/datadownload.go @@ -0,0 +1,90 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + "context" + time "time" + + velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" + versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" + internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" + v2alpha1 "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v2alpha1" + v1 "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" +) + +// DataDownloadInformer provides access to a shared informer and lister for +// DataDownloads. +type DataDownloadInformer interface { + Informer() cache.SharedIndexInformer + Lister() v2alpha1.DataDownloadLister +} + +type dataDownloadInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewDataDownloadInformer constructs a new informer for DataDownload 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 NewDataDownloadInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredDataDownloadInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredDataDownloadInformer constructs a new informer for DataDownload 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 NewFilteredDataDownloadInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.VeleroV2alpha1().DataDownloads(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.VeleroV2alpha1().DataDownloads(namespace).Watch(context.TODO(), options) + }, + }, + &velerov2alpha1.DataDownload{}, + resyncPeriod, + indexers, + ) +} + +func (f *dataDownloadInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredDataDownloadInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *dataDownloadInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&velerov2alpha1.DataDownload{}, f.defaultInformer) +} + +func (f *dataDownloadInformer) Lister() v2alpha1.DataDownloadLister { + return v2alpha1.NewDataDownloadLister(f.Informer().GetIndexer()) +} diff --git a/pkg/generated/informers/externalversions/velero/v2alpha1/dataupload.go b/pkg/generated/informers/externalversions/velero/v2alpha1/dataupload.go new file mode 100644 index 000000000..f7e8f8d07 --- /dev/null +++ b/pkg/generated/informers/externalversions/velero/v2alpha1/dataupload.go @@ -0,0 +1,90 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + "context" + time "time" + + velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" + versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" + internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" + v2alpha1 "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v2alpha1" + v1 "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" +) + +// DataUploadInformer provides access to a shared informer and lister for +// DataUploads. +type DataUploadInformer interface { + Informer() cache.SharedIndexInformer + Lister() v2alpha1.DataUploadLister +} + +type dataUploadInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewDataUploadInformer constructs a new informer for DataUpload 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 NewDataUploadInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredDataUploadInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredDataUploadInformer constructs a new informer for DataUpload 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 NewFilteredDataUploadInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.VeleroV2alpha1().DataUploads(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.VeleroV2alpha1().DataUploads(namespace).Watch(context.TODO(), options) + }, + }, + &velerov2alpha1.DataUpload{}, + resyncPeriod, + indexers, + ) +} + +func (f *dataUploadInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredDataUploadInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *dataUploadInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&velerov2alpha1.DataUpload{}, f.defaultInformer) +} + +func (f *dataUploadInformer) Lister() v2alpha1.DataUploadLister { + return v2alpha1.NewDataUploadLister(f.Informer().GetIndexer()) +} diff --git a/pkg/generated/informers/externalversions/velero/v2alpha1/interface.go b/pkg/generated/informers/externalversions/velero/v2alpha1/interface.go new file mode 100644 index 000000000..41f8edabf --- /dev/null +++ b/pkg/generated/informers/externalversions/velero/v2alpha1/interface.go @@ -0,0 +1,52 @@ +/* +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 v2alpha1 + +import ( + internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // DataDownloads returns a DataDownloadInformer. + DataDownloads() DataDownloadInformer + // DataUploads returns a DataUploadInformer. + DataUploads() DataUploadInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// DataDownloads returns a DataDownloadInformer. +func (v *version) DataDownloads() DataDownloadInformer { + return &dataDownloadInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + +// DataUploads returns a DataUploadInformer. +func (v *version) DataUploads() DataUploadInformer { + return &dataUploadInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/generated/listers/velero/v2alpha1/datadownload.go b/pkg/generated/listers/velero/v2alpha1/datadownload.go new file mode 100644 index 000000000..dadf14b60 --- /dev/null +++ b/pkg/generated/listers/velero/v2alpha1/datadownload.go @@ -0,0 +1,99 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + v2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// DataDownloadLister helps list DataDownloads. +// All objects returned here must be treated as read-only. +type DataDownloadLister interface { + // List lists all DataDownloads in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v2alpha1.DataDownload, err error) + // DataDownloads returns an object that can list and get DataDownloads. + DataDownloads(namespace string) DataDownloadNamespaceLister + DataDownloadListerExpansion +} + +// dataDownloadLister implements the DataDownloadLister interface. +type dataDownloadLister struct { + indexer cache.Indexer +} + +// NewDataDownloadLister returns a new DataDownloadLister. +func NewDataDownloadLister(indexer cache.Indexer) DataDownloadLister { + return &dataDownloadLister{indexer: indexer} +} + +// List lists all DataDownloads in the indexer. +func (s *dataDownloadLister) List(selector labels.Selector) (ret []*v2alpha1.DataDownload, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v2alpha1.DataDownload)) + }) + return ret, err +} + +// DataDownloads returns an object that can list and get DataDownloads. +func (s *dataDownloadLister) DataDownloads(namespace string) DataDownloadNamespaceLister { + return dataDownloadNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// DataDownloadNamespaceLister helps list and get DataDownloads. +// All objects returned here must be treated as read-only. +type DataDownloadNamespaceLister interface { + // List lists all DataDownloads in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v2alpha1.DataDownload, err error) + // Get retrieves the DataDownload from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v2alpha1.DataDownload, error) + DataDownloadNamespaceListerExpansion +} + +// dataDownloadNamespaceLister implements the DataDownloadNamespaceLister +// interface. +type dataDownloadNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all DataDownloads in the indexer for a given namespace. +func (s dataDownloadNamespaceLister) List(selector labels.Selector) (ret []*v2alpha1.DataDownload, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v2alpha1.DataDownload)) + }) + return ret, err +} + +// Get retrieves the DataDownload from the indexer for a given namespace and name. +func (s dataDownloadNamespaceLister) Get(name string) (*v2alpha1.DataDownload, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v2alpha1.Resource("datadownload"), name) + } + return obj.(*v2alpha1.DataDownload), nil +} diff --git a/pkg/generated/listers/velero/v2alpha1/dataupload.go b/pkg/generated/listers/velero/v2alpha1/dataupload.go new file mode 100644 index 000000000..0dbe6bed1 --- /dev/null +++ b/pkg/generated/listers/velero/v2alpha1/dataupload.go @@ -0,0 +1,99 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + v2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// DataUploadLister helps list DataUploads. +// All objects returned here must be treated as read-only. +type DataUploadLister interface { + // List lists all DataUploads in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v2alpha1.DataUpload, err error) + // DataUploads returns an object that can list and get DataUploads. + DataUploads(namespace string) DataUploadNamespaceLister + DataUploadListerExpansion +} + +// dataUploadLister implements the DataUploadLister interface. +type dataUploadLister struct { + indexer cache.Indexer +} + +// NewDataUploadLister returns a new DataUploadLister. +func NewDataUploadLister(indexer cache.Indexer) DataUploadLister { + return &dataUploadLister{indexer: indexer} +} + +// List lists all DataUploads in the indexer. +func (s *dataUploadLister) List(selector labels.Selector) (ret []*v2alpha1.DataUpload, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v2alpha1.DataUpload)) + }) + return ret, err +} + +// DataUploads returns an object that can list and get DataUploads. +func (s *dataUploadLister) DataUploads(namespace string) DataUploadNamespaceLister { + return dataUploadNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// DataUploadNamespaceLister helps list and get DataUploads. +// All objects returned here must be treated as read-only. +type DataUploadNamespaceLister interface { + // List lists all DataUploads in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v2alpha1.DataUpload, err error) + // Get retrieves the DataUpload from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v2alpha1.DataUpload, error) + DataUploadNamespaceListerExpansion +} + +// dataUploadNamespaceLister implements the DataUploadNamespaceLister +// interface. +type dataUploadNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all DataUploads in the indexer for a given namespace. +func (s dataUploadNamespaceLister) List(selector labels.Selector) (ret []*v2alpha1.DataUpload, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v2alpha1.DataUpload)) + }) + return ret, err +} + +// Get retrieves the DataUpload from the indexer for a given namespace and name. +func (s dataUploadNamespaceLister) Get(name string) (*v2alpha1.DataUpload, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v2alpha1.Resource("dataupload"), name) + } + return obj.(*v2alpha1.DataUpload), nil +} diff --git a/pkg/generated/listers/velero/v2alpha1/expansion_generated.go b/pkg/generated/listers/velero/v2alpha1/expansion_generated.go new file mode 100644 index 000000000..1bdb85ec0 --- /dev/null +++ b/pkg/generated/listers/velero/v2alpha1/expansion_generated.go @@ -0,0 +1,35 @@ +/* +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 v2alpha1 + +// DataDownloadListerExpansion allows custom methods to be added to +// DataDownloadLister. +type DataDownloadListerExpansion interface{} + +// DataDownloadNamespaceListerExpansion allows custom methods to be added to +// DataDownloadNamespaceLister. +type DataDownloadNamespaceListerExpansion interface{} + +// DataUploadListerExpansion allows custom methods to be added to +// DataUploadLister. +type DataUploadListerExpansion interface{} + +// DataUploadNamespaceListerExpansion allows custom methods to be added to +// DataUploadNamespaceLister. +type DataUploadNamespaceListerExpansion interface{} diff --git a/pkg/install/resources.go b/pkg/install/resources.go index a51137400..d7014c22c 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -17,6 +17,7 @@ limitations under the License. package install import ( + "fmt" "time" corev1 "k8s.io/api/core/v1" @@ -27,6 +28,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" v1crds "github.com/vmware-tanzu/velero/config/crd/v1/crds" + v2alpha1crds "github.com/vmware-tanzu/velero/config/crd/v2alpha1/crds" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" ) @@ -250,7 +252,16 @@ func AllCRDs() *unstructured.UnstructuredList { for _, crd := range v1crds.CRDs { crd.SetLabels(Labels()) - appendUnstructured(resources, crd) + if err := appendUnstructured(resources, crd); err != nil { + fmt.Printf("error appending v1 CRD %s: %s\n", crd.GetName(), err.Error()) + } + } + + for _, crd := range v2alpha1crds.CRDs { + crd.SetLabels(Labels()) + if err := appendUnstructured(resources, crd); err != nil { + fmt.Printf("error appending v2alpha1 CRD %s: %s\n", crd.GetName(), err.Error()) + } } return resources @@ -262,32 +273,44 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { resources := AllCRDs() ns := Namespace(o.Namespace) - appendUnstructured(resources, ns) + if err := appendUnstructured(resources, ns); err != nil { + fmt.Printf("error appending Namespace %s: %s\n", ns.GetName(), err.Error()) + } serviceAccountName := defaultServiceAccountName if o.ServiceAccountName == "" { crb := ClusterRoleBinding(o.Namespace) - appendUnstructured(resources, crb) + if err := appendUnstructured(resources, crb); err != nil { + fmt.Printf("error appending ClusterRoleBinding %s: %s\n", crb.GetName(), err.Error()) + } sa := ServiceAccount(o.Namespace, o.ServiceAccountAnnotations) - appendUnstructured(resources, sa) + if err := appendUnstructured(resources, sa); err != nil { + fmt.Printf("error appending ServiceAccount %s: %s\n", sa.GetName(), err.Error()) + } } else { serviceAccountName = o.ServiceAccountName } if o.SecretData != nil { sec := Secret(o.Namespace, o.SecretData) - appendUnstructured(resources, sec) + if err := appendUnstructured(resources, sec); err != nil { + fmt.Printf("error appending Secret %s: %s\n", sec.GetName(), err.Error()) + } } if !o.NoDefaultBackupLocation { bsl := BackupStorageLocation(o.Namespace, o.ProviderName, o.Bucket, o.Prefix, o.BSLConfig, o.CACertData) - appendUnstructured(resources, bsl) + if err := appendUnstructured(resources, bsl); err != nil { + fmt.Printf("error appending BackupStorageLocation %s: %s\n", bsl.GetName(), err.Error()) + } } // A snapshot location may not be desirable for users relying on pod volume backup/restore if o.UseVolumeSnapshots { vsl := VolumeSnapshotLocation(o.Namespace, o.ProviderName, o.VSLConfig) - appendUnstructured(resources, vsl) + if err := appendUnstructured(resources, vsl); err != nil { + fmt.Printf("error appending VolumeSnapshotLocation %s: %s\n", vsl.GetName(), err.Error()) + } } secretPresent := o.SecretData != nil @@ -322,7 +345,9 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { deploy := Deployment(o.Namespace, deployOpts...) - appendUnstructured(resources, deploy) + if err := appendUnstructured(resources, deploy); err != nil { + fmt.Printf("error appending Deployment %s: %s\n", deploy.GetName(), err.Error()) + } if o.UseNodeAgent { dsOpts := []podTemplateOption{ @@ -337,7 +362,9 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { dsOpts = append(dsOpts, WithFeatures(o.Features)) } ds := DaemonSet(o.Namespace, dsOpts...) - appendUnstructured(resources, ds) + if err := appendUnstructured(resources, ds); err != nil { + fmt.Printf("error appending DaemonSet %s: %s\n", ds.GetName(), err.Error()) + } } return resources diff --git a/pkg/plugin/framework/common/plugin_config.go b/pkg/plugin/framework/common/plugin_config.go new file mode 100644 index 000000000..58f9bafc6 --- /dev/null +++ b/pkg/plugin/framework/common/plugin_config.go @@ -0,0 +1,58 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package common + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" +) + +func PluginConfigLabelSelector(kind PluginKind, name string) string { + return fmt.Sprintf("velero.io/plugin-config,%s=%s", name, kind) +} + +func GetPluginConfig(kind PluginKind, name string, client corev1client.ConfigMapInterface) (*corev1.ConfigMap, error) { + opts := metav1.ListOptions{ + // velero.io/plugin-config: true + // velero.io/pod-volume-restore: RestoreItemAction + LabelSelector: PluginConfigLabelSelector(kind, name), + } + + list, err := client.List(context.Background(), opts) + if err != nil { + return nil, errors.WithStack(err) + } + + if len(list.Items) == 0 { + return nil, nil + } + + if len(list.Items) > 1 { + var items []string + for _, item := range list.Items { + items = append(items, item.Name) + } + return nil, errors.Errorf("found more than one ConfigMap matching label selector %q: %v", opts.LabelSelector, items) + } + + return &list.Items[0], nil +} diff --git a/pkg/plugin/framework/common/plugin_config_test.go b/pkg/plugin/framework/common/plugin_config_test.go new file mode 100644 index 000000000..3a69e1ef0 --- /dev/null +++ b/pkg/plugin/framework/common/plugin_config_test.go @@ -0,0 +1,121 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package common + +import ( + "reflect" + "testing" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/fake" + + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +func TestGetPluginConfig(t *testing.T) { + type args struct { + kind PluginKind + name string + objects []runtime.Object + } + pluginLabelsMap := map[string]string{"velero.io/plugin-config": "", "foo": "RestoreItemAction"} + testConfigMap := &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-config", + Namespace: velerov1.DefaultNamespace, + Labels: pluginLabelsMap, + }, + } + tests := []struct { + name string + args args + want *corev1.ConfigMap + wantErr bool + }{ + { + name: "should return nil if no config map found", + args: args{ + kind: PluginKindRestoreItemAction, + name: "foo", + objects: []runtime.Object{}, + }, + want: nil, + wantErr: false, + }, + { + name: "should return error if more than one config map found", + args: args{ + kind: PluginKindRestoreItemAction, + name: "foo", + objects: []runtime.Object{ + &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-config", + Namespace: velerov1.DefaultNamespace, + Labels: pluginLabelsMap, + }, + }, + &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-config-duplicate", + Namespace: velerov1.DefaultNamespace, + Labels: pluginLabelsMap, + }, + }, + }, + }, + want: nil, + wantErr: true, + }, + { + name: "should return pointer to configmap if only one config map with label found", + args: args{ + kind: PluginKindRestoreItemAction, + name: "foo", + objects: []runtime.Object{ + testConfigMap, + }, + }, + want: testConfigMap, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fakeClient := fake.NewSimpleClientset(tt.args.objects...) + got, err := GetPluginConfig(tt.args.kind, tt.args.name, fakeClient.CoreV1().ConfigMaps(velerov1.DefaultNamespace)) + if (err != nil) != tt.wantErr { + t.Errorf("GetPluginConfig() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetPluginConfig() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/plugin/framework/object_store_client.go b/pkg/plugin/framework/object_store_client.go index 7f40921c2..3cc5cd6e9 100644 --- a/pkg/plugin/framework/object_store_client.go +++ b/pkg/plugin/framework/object_store_client.go @@ -87,7 +87,9 @@ func (c *ObjectStoreGRPCClient) PutObject(bucket, key string, body io.Reader) er return nil } if err != nil { - stream.CloseSend() + if err := stream.CloseSend(); err != nil { + return common.FromGRPCError(err) + } return errors.WithStack(err) } diff --git a/pkg/plugin/framework/server.go b/pkg/plugin/framework/server.go index 4fdae230a..3c3871fac 100644 --- a/pkg/plugin/framework/server.go +++ b/pkg/plugin/framework/server.go @@ -232,7 +232,10 @@ func getNames(command string, kind common.PluginKind, plugin Interface) []Plugin func (s *server) Serve() { if s.flagSet != nil && !s.flagSet.Parsed() { s.log.Debugf("Parsing flags") - s.flagSet.Parse(os.Args[1:]) + if err := s.flagSet.Parse(os.Args[1:]); err != nil { + s.log.Errorf("fail to parse the flags: %s", err.Error()) + return + } } s.log.Level = s.logLevelFlag.Parse() diff --git a/pkg/podvolume/backupper.go b/pkg/podvolume/backupper.go index 870300739..3e99636be 100644 --- a/pkg/podvolume/backupper.go +++ b/pkg/podvolume/backupper.go @@ -131,7 +131,7 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api. if len(volumesToBackup) == 0 { return nil, nil } - + log.Infof("pod %s/%s has volumes to backup: %v", pod.Namespace, pod.Name, volumesToBackup) err := kube.IsPodRunning(pod) if err != nil { for _, volumeName := range volumesToBackup { diff --git a/pkg/podvolume/backupper_test.go b/pkg/podvolume/backupper_test.go index fb0cacd1a..f86d9ebc1 100644 --- a/pkg/podvolume/backupper_test.go +++ b/pkg/podvolume/backupper_test.go @@ -17,13 +17,19 @@ limitations under the License. package podvolume import ( + "bytes" "context" + "fmt" "testing" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" corev1api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/vmware-tanzu/velero/internal/resourcepolicies" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" ) func TestIsHostPathVolume(t *testing.T) { @@ -139,3 +145,62 @@ func (g *fakePVGetter) Get(ctx context.Context, name string, opts metav1.GetOpti return nil, errors.New("item not found") } + +func Test_backupper_BackupPodVolumes_log_test(t *testing.T) { + type args struct { + backup *velerov1api.Backup + pod *corev1api.Pod + volumesToBackup []string + resPolicies *resourcepolicies.Policies + } + tests := []struct { + name string + args args + wantLog string + }{ + { + name: "backup pod volumes should log volume names", + args: args{ + backup: &velerov1api.Backup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "backup-1", + Namespace: "ns-1", + }, + }, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod-1", + Namespace: "ns-1", + }, + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + { + Name: "vol-1", + }, + { + Name: "vol-2", + }, + }, + }, + }, + volumesToBackup: []string{"vol-1", "vol-2"}, + resPolicies: nil, + }, + wantLog: "pod ns-1/pod-1 has volumes to backup: [vol-1 vol-2]", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := &backupper{ + ctx: context.Background(), + } + logOutput := bytes.Buffer{} + var log = logrus.New() + log.SetOutput(&logOutput) + b.BackupPodVolumes(tt.args.backup, tt.args.pod, tt.args.volumesToBackup, tt.args.resPolicies, log) + fmt.Println(logOutput.String()) + assert.Contains(t, logOutput.String(), tt.wantLog) + + }) + } +} diff --git a/pkg/repository/mocks/repository_writer.go b/pkg/repository/mocks/repository_writer.go index c3e9964be..dc9f92b03 100644 --- a/pkg/repository/mocks/repository_writer.go +++ b/pkg/repository/mocks/repository_writer.go @@ -1,18 +1,4 @@ -/* -Copyright The Velero Contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ +// Code generated by mockery v2.22.1. DO NOT EDIT. package mocks @@ -64,11 +50,39 @@ func (_m *RepositoryWriter) Close(ctx context.Context) error { return r0 } +// ConcatenateObjects provides a mock function with given fields: ctx, objectIDs +func (_m *RepositoryWriter) ConcatenateObjects(ctx context.Context, objectIDs []object.ID) (object.ID, error) { + ret := _m.Called(ctx, objectIDs) + + var r0 object.ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []object.ID) (object.ID, error)); ok { + return rf(ctx, objectIDs) + } + if rf, ok := ret.Get(0).(func(context.Context, []object.ID) object.ID); ok { + r0 = rf(ctx, objectIDs) + } else { + r0 = ret.Get(0).(object.ID) + } + + if rf, ok := ret.Get(1).(func(context.Context, []object.ID) error); ok { + r1 = rf(ctx, objectIDs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // ContentInfo provides a mock function with given fields: ctx, contentID func (_m *RepositoryWriter) ContentInfo(ctx context.Context, contentID index.ID) (index.Info, error) { ret := _m.Called(ctx, contentID) var r0 index.Info + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, index.ID) (index.Info, error)); ok { + return rf(ctx, contentID) + } if rf, ok := ret.Get(0).(func(context.Context, index.ID) index.Info); ok { r0 = rf(ctx, contentID) } else { @@ -77,7 +91,6 @@ func (_m *RepositoryWriter) ContentInfo(ctx context.Context, contentID index.ID) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, index.ID) error); ok { r1 = rf(ctx, contentID) } else { @@ -106,6 +119,10 @@ func (_m *RepositoryWriter) FindManifests(ctx context.Context, labels map[string ret := _m.Called(ctx, labels) var r0 []*manifest.EntryMetadata + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, map[string]string) ([]*manifest.EntryMetadata, error)); ok { + return rf(ctx, labels) + } if rf, ok := ret.Get(0).(func(context.Context, map[string]string) []*manifest.EntryMetadata); ok { r0 = rf(ctx, labels) } else { @@ -114,7 +131,6 @@ func (_m *RepositoryWriter) FindManifests(ctx context.Context, labels map[string } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, map[string]string) error); ok { r1 = rf(ctx, labels) } else { @@ -143,6 +159,10 @@ func (_m *RepositoryWriter) GetManifest(ctx context.Context, id manifest.ID, dat ret := _m.Called(ctx, id, data) var r0 *manifest.EntryMetadata + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, manifest.ID, interface{}) (*manifest.EntryMetadata, error)); ok { + return rf(ctx, id, data) + } if rf, ok := ret.Get(0).(func(context.Context, manifest.ID, interface{}) *manifest.EntryMetadata); ok { r0 = rf(ctx, id, data) } else { @@ -151,7 +171,6 @@ func (_m *RepositoryWriter) GetManifest(ctx context.Context, id manifest.ID, dat } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, manifest.ID, interface{}) error); ok { r1 = rf(ctx, id, data) } else { @@ -182,6 +201,11 @@ func (_m *RepositoryWriter) NewWriter(ctx context.Context, opt repo.WriteSession ret := _m.Called(ctx, opt) var r0 context.Context + var r1 repo.RepositoryWriter + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, repo.WriteSessionOptions) (context.Context, repo.RepositoryWriter, error)); ok { + return rf(ctx, opt) + } if rf, ok := ret.Get(0).(func(context.Context, repo.WriteSessionOptions) context.Context); ok { r0 = rf(ctx, opt) } else { @@ -190,7 +214,6 @@ func (_m *RepositoryWriter) NewWriter(ctx context.Context, opt repo.WriteSession } } - var r1 repo.RepositoryWriter if rf, ok := ret.Get(1).(func(context.Context, repo.WriteSessionOptions) repo.RepositoryWriter); ok { r1 = rf(ctx, opt) } else { @@ -199,7 +222,6 @@ func (_m *RepositoryWriter) NewWriter(ctx context.Context, opt repo.WriteSession } } - var r2 error if rf, ok := ret.Get(2).(func(context.Context, repo.WriteSessionOptions) error); ok { r2 = rf(ctx, opt) } else { @@ -209,11 +231,20 @@ func (_m *RepositoryWriter) NewWriter(ctx context.Context, opt repo.WriteSession return r0, r1, r2 } +// OnSuccessfulFlush provides a mock function with given fields: callback +func (_m *RepositoryWriter) OnSuccessfulFlush(callback repo.RepositoryWriterCallback) { + _m.Called(callback) +} + // OpenObject provides a mock function with given fields: ctx, id func (_m *RepositoryWriter) OpenObject(ctx context.Context, id object.ID) (object.Reader, error) { ret := _m.Called(ctx, id) var r0 object.Reader + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, object.ID) (object.Reader, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, object.ID) object.Reader); ok { r0 = rf(ctx, id) } else { @@ -222,7 +253,6 @@ func (_m *RepositoryWriter) OpenObject(ctx context.Context, id object.ID) (objec } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, object.ID) error); ok { r1 = rf(ctx, id) } else { @@ -253,6 +283,10 @@ func (_m *RepositoryWriter) PrefetchObjects(ctx context.Context, objectIDs []obj ret := _m.Called(ctx, objectIDs, hint) var r0 []index.ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []object.ID, string) ([]index.ID, error)); ok { + return rf(ctx, objectIDs, hint) + } if rf, ok := ret.Get(0).(func(context.Context, []object.ID, string) []index.ID); ok { r0 = rf(ctx, objectIDs, hint) } else { @@ -261,7 +295,6 @@ func (_m *RepositoryWriter) PrefetchObjects(ctx context.Context, objectIDs []obj } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []object.ID, string) error); ok { r1 = rf(ctx, objectIDs, hint) } else { @@ -276,13 +309,16 @@ func (_m *RepositoryWriter) PutManifest(ctx context.Context, labels map[string]s ret := _m.Called(ctx, labels, payload) var r0 manifest.ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, map[string]string, interface{}) (manifest.ID, error)); ok { + return rf(ctx, labels, payload) + } if rf, ok := ret.Get(0).(func(context.Context, map[string]string, interface{}) manifest.ID); ok { r0 = rf(ctx, labels, payload) } else { r0 = ret.Get(0).(manifest.ID) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, map[string]string, interface{}) error); ok { r1 = rf(ctx, labels, payload) } else { @@ -306,6 +342,30 @@ func (_m *RepositoryWriter) Refresh(ctx context.Context) error { return r0 } +// ReplaceManifests provides a mock function with given fields: ctx, labels, payload +func (_m *RepositoryWriter) ReplaceManifests(ctx context.Context, labels map[string]string, payload interface{}) (manifest.ID, error) { + ret := _m.Called(ctx, labels, payload) + + var r0 manifest.ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, map[string]string, interface{}) (manifest.ID, error)); ok { + return rf(ctx, labels, payload) + } + if rf, ok := ret.Get(0).(func(context.Context, map[string]string, interface{}) manifest.ID); ok { + r0 = rf(ctx, labels, payload) + } else { + r0 = ret.Get(0).(manifest.ID) + } + + if rf, ok := ret.Get(1).(func(context.Context, map[string]string, interface{}) error); ok { + r1 = rf(ctx, labels, payload) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Time provides a mock function with given fields: func (_m *RepositoryWriter) Time() time.Time { ret := _m.Called() @@ -330,6 +390,10 @@ func (_m *RepositoryWriter) VerifyObject(ctx context.Context, id object.ID) ([]i ret := _m.Called(ctx, id) var r0 []index.ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, object.ID) ([]index.ID, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, object.ID) []index.ID); ok { r0 = rf(ctx, id) } else { @@ -338,7 +402,6 @@ func (_m *RepositoryWriter) VerifyObject(ctx context.Context, id object.ID) ([]i } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, object.ID) error); ok { r1 = rf(ctx, id) } else { @@ -347,3 +410,18 @@ func (_m *RepositoryWriter) VerifyObject(ctx context.Context, id object.ID) ([]i return r0, r1 } + +type mockConstructorTestingTNewRepositoryWriter interface { + mock.TestingT + Cleanup(func()) +} + +// NewRepositoryWriter creates a new instance of RepositoryWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewRepositoryWriter(t mockConstructorTestingTNewRepositoryWriter) *RepositoryWriter { + mock := &RepositoryWriter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/repository/provider/unified_repo.go b/pkg/repository/provider/unified_repo.go index 65571044f..b599db836 100644 --- a/pkg/repository/provider/unified_repo.go +++ b/pkg/repository/provider/unified_repo.go @@ -18,6 +18,7 @@ package provider import ( "context" + "encoding/base64" "fmt" "net/url" "path" @@ -496,6 +497,10 @@ func getStorageVariables(backupLocation *velerov1api.BackupStorageLocation, repo result[udmrepo.StoreOptionS3Endpoint] = strings.Trim(s3URL, "/") result[udmrepo.StoreOptionS3DisableTLSVerify] = config["insecureSkipTLSVerify"] result[udmrepo.StoreOptionS3DisableTLS] = strconv.FormatBool(disableTLS) + + if backupLocation.Spec.ObjectStorage != nil && backupLocation.Spec.ObjectStorage.CACert != nil { + result[udmrepo.StoreOptionS3CustomCA] = base64.StdEncoding.EncodeToString(backupLocation.Spec.ObjectStorage.CACert) + } } else if backendType == repoconfig.AzureBackend { domain, err := getAzureStorageDomain(config) if err != nil { diff --git a/pkg/repository/provider/unified_repo_test.go b/pkg/repository/provider/unified_repo_test.go index 0a0401aec..0999957ee 100644 --- a/pkg/repository/provider/unified_repo_test.go +++ b/pkg/repository/provider/unified_repo_test.go @@ -18,6 +18,7 @@ package provider import ( "context" + "encoding/base64" "errors" "testing" @@ -366,6 +367,42 @@ func TestGetStorageVariables(t *testing.T) { "skipTLSVerify": "false", }, }, + { + name: "aws, ObjectStorage section exists in BSL, s3Url exist, https, custom CA exist", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/aws", + Config: map[string]string{ + "bucket": "fake-bucket-config", + "prefix": "fake-prefix-config", + "region": "fake-region", + "s3Url": "https://fake-url/", + "insecureSkipTLSVerify": "false", + }, + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ + Bucket: "fake-bucket-object-store", + Prefix: "fake-prefix-object-store", + CACert: []byte{0x01, 0x02, 0x03, 0x04, 0x05}, + }, + }, + }, + }, + getS3BucketRegion: func(bucket string) (string, error) { + return "region from bucket: " + bucket, nil + }, + repoBackend: "fake-repo-type", + expected: map[string]string{ + "bucket": "fake-bucket-object-store", + "prefix": "fake-prefix-object-store/fake-repo-type/", + "region": "fake-region", + "fspath": "", + "endpoint": "fake-url", + "doNotUseTLS": "false", + "skipTLSVerify": "false", + "customCA": base64.StdEncoding.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04, 0x05}), + }, + }, { name: "azure, getAzureStorageDomain fail", backupLocation: velerov1api.BackupStorageLocation{ diff --git a/pkg/repository/udmrepo/kopialib/backend/azure.go b/pkg/repository/udmrepo/kopialib/backend/azure.go index 7aedc33d8..d5243820f 100644 --- a/pkg/repository/udmrepo/kopialib/backend/azure.go +++ b/pkg/repository/udmrepo/kopialib/backend/azure.go @@ -56,5 +56,5 @@ func (c *AzureBackend) Setup(ctx context.Context, flags map[string]string) error } func (c *AzureBackend) Connect(ctx context.Context, isCreate bool) (blob.Storage, error) { - return azure.New(ctx, &c.options) + return azure.New(ctx, &c.options, false) } diff --git a/pkg/repository/udmrepo/kopialib/backend/common.go b/pkg/repository/udmrepo/kopialib/backend/common.go index 7aa888e3a..90eb473e0 100644 --- a/pkg/repository/udmrepo/kopialib/backend/common.go +++ b/pkg/repository/udmrepo/kopialib/backend/common.go @@ -25,8 +25,8 @@ import ( "github.com/kopia/kopia/repo/blob/throttling" "github.com/kopia/kopia/repo/content" "github.com/kopia/kopia/repo/encryption" + "github.com/kopia/kopia/repo/format" "github.com/kopia/kopia/repo/hashing" - "github.com/kopia/kopia/repo/object" "github.com/kopia/kopia/repo/splitter" "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" @@ -51,12 +51,12 @@ func setupLimits(ctx context.Context, flags map[string]string) throttling.Limits // SetupNewRepositoryOptions setups the options when creating a new Kopia repository func SetupNewRepositoryOptions(ctx context.Context, flags map[string]string) repo.NewRepositoryOptions { return repo.NewRepositoryOptions{ - BlockFormat: content.FormattingOptions{ + BlockFormat: format.ContentFormat{ Hash: optionalHaveStringWithDefault(udmrepo.StoreOptionGenHashAlgo, flags, hashing.DefaultAlgorithm), Encryption: optionalHaveStringWithDefault(udmrepo.StoreOptionGenEncryptAlgo, flags, encryption.DefaultAlgorithm), }, - ObjectFormat: object.Format{ + ObjectFormat: format.ObjectFormat{ Splitter: optionalHaveStringWithDefault(udmrepo.StoreOptionGenSplitAlgo, flags, splitter.DefaultAlgorithm), }, diff --git a/pkg/repository/udmrepo/kopialib/backend/gcs.go b/pkg/repository/udmrepo/kopialib/backend/gcs.go index b998c9702..ee96abc51 100644 --- a/pkg/repository/udmrepo/kopialib/backend/gcs.go +++ b/pkg/repository/udmrepo/kopialib/backend/gcs.go @@ -50,5 +50,5 @@ func (c *GCSBackend) Setup(ctx context.Context, flags map[string]string) error { } func (c *GCSBackend) Connect(ctx context.Context, isCreate bool) (blob.Storage, error) { - return gcs.New(ctx, &c.options) + return gcs.New(ctx, &c.options, false) } diff --git a/pkg/repository/udmrepo/kopialib/backend/mocks/DirectRepository.go b/pkg/repository/udmrepo/kopialib/backend/mocks/DirectRepository.go index c32787882..27dfb89ed 100644 --- a/pkg/repository/udmrepo/kopialib/backend/mocks/DirectRepository.go +++ b/pkg/repository/udmrepo/kopialib/backend/mocks/DirectRepository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.22.1. DO NOT EDIT. package mocks @@ -8,8 +8,12 @@ import ( context "context" + format "github.com/kopia/kopia/repo/format" + index "github.com/kopia/kopia/repo/content/index" + indexblob "github.com/kopia/kopia/repo/content/indexblob" + manifest "github.com/kopia/kopia/repo/manifest" mock "github.com/stretchr/testify/mock" @@ -44,20 +48,6 @@ func (_m *DirectRepository) AlsoLogToContentLog(ctx context.Context) context.Con return r0 } -// BlobCfg provides a mock function with given fields: -func (_m *DirectRepository) BlobCfg() content.BlobCfgBlob { - ret := _m.Called() - - var r0 content.BlobCfgBlob - if rf, ok := ret.Get(0).(func() content.BlobCfgBlob); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(content.BlobCfgBlob) - } - - return r0 -} - // BlobReader provides a mock function with given fields: func (_m *DirectRepository) BlobReader() blob.Reader { ret := _m.Called() @@ -137,6 +127,10 @@ func (_m *DirectRepository) ContentInfo(ctx context.Context, contentID index.ID) ret := _m.Called(ctx, contentID) var r0 index.Info + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, index.ID) (index.Info, error)); ok { + return rf(ctx, contentID) + } if rf, ok := ret.Get(0).(func(context.Context, index.ID) index.Info); ok { r0 = rf(ctx, contentID) } else { @@ -145,7 +139,6 @@ func (_m *DirectRepository) ContentInfo(ctx context.Context, contentID index.ID) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, index.ID) error); ok { r1 = rf(ctx, contentID) } else { @@ -171,22 +164,6 @@ func (_m *DirectRepository) ContentReader() content.Reader { return r0 } -// Crypter provides a mock function with given fields: -func (_m *DirectRepository) Crypter() *content.Crypter { - ret := _m.Called() - - var r0 *content.Crypter - if rf, ok := ret.Get(0).(func() *content.Crypter); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*content.Crypter) - } - } - - return r0 -} - // DeriveKey provides a mock function with given fields: purpose, keyLength func (_m *DirectRepository) DeriveKey(purpose []byte, keyLength int) []byte { ret := _m.Called(purpose, keyLength) @@ -213,6 +190,10 @@ func (_m *DirectRepository) FindManifests(ctx context.Context, labels map[string ret := _m.Called(ctx, labels) var r0 []*manifest.EntryMetadata + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, map[string]string) ([]*manifest.EntryMetadata, error)); ok { + return rf(ctx, labels) + } if rf, ok := ret.Get(0).(func(context.Context, map[string]string) []*manifest.EntryMetadata); ok { r0 = rf(ctx, labels) } else { @@ -221,7 +202,6 @@ func (_m *DirectRepository) FindManifests(ctx context.Context, labels map[string } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, map[string]string) error); ok { r1 = rf(ctx, labels) } else { @@ -231,11 +211,31 @@ func (_m *DirectRepository) FindManifests(ctx context.Context, labels map[string return r0, r1 } +// FormatManager provides a mock function with given fields: +func (_m *DirectRepository) FormatManager() *format.Manager { + ret := _m.Called() + + var r0 *format.Manager + if rf, ok := ret.Get(0).(func() *format.Manager); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*format.Manager) + } + } + + return r0 +} + // GetManifest provides a mock function with given fields: ctx, id, data func (_m *DirectRepository) GetManifest(ctx context.Context, id manifest.ID, data interface{}) (*manifest.EntryMetadata, error) { ret := _m.Called(ctx, id, data) var r0 *manifest.EntryMetadata + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, manifest.ID, interface{}) (*manifest.EntryMetadata, error)); ok { + return rf(ctx, id, data) + } if rf, ok := ret.Get(0).(func(context.Context, manifest.ID, interface{}) *manifest.EntryMetadata); ok { r0 = rf(ctx, id, data) } else { @@ -244,7 +244,6 @@ func (_m *DirectRepository) GetManifest(ctx context.Context, id manifest.ID, dat } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, manifest.ID, interface{}) error); ok { r1 = rf(ctx, id, data) } else { @@ -255,19 +254,22 @@ func (_m *DirectRepository) GetManifest(ctx context.Context, id manifest.ID, dat } // IndexBlobs provides a mock function with given fields: ctx, includeInactive -func (_m *DirectRepository) IndexBlobs(ctx context.Context, includeInactive bool) ([]content.IndexBlobInfo, error) { +func (_m *DirectRepository) IndexBlobs(ctx context.Context, includeInactive bool) ([]indexblob.Metadata, error) { ret := _m.Called(ctx, includeInactive) - var r0 []content.IndexBlobInfo - if rf, ok := ret.Get(0).(func(context.Context, bool) []content.IndexBlobInfo); ok { + var r0 []indexblob.Metadata + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, bool) ([]indexblob.Metadata, error)); ok { + return rf(ctx, includeInactive) + } + if rf, ok := ret.Get(0).(func(context.Context, bool) []indexblob.Metadata); ok { r0 = rf(ctx, includeInactive) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]content.IndexBlobInfo) + r0 = ret.Get(0).([]indexblob.Metadata) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, bool) error); ok { r1 = rf(ctx, includeInactive) } else { @@ -282,6 +284,11 @@ func (_m *DirectRepository) NewDirectWriter(ctx context.Context, opt repo.WriteS ret := _m.Called(ctx, opt) var r0 context.Context + var r1 repo.DirectRepositoryWriter + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, repo.WriteSessionOptions) (context.Context, repo.DirectRepositoryWriter, error)); ok { + return rf(ctx, opt) + } if rf, ok := ret.Get(0).(func(context.Context, repo.WriteSessionOptions) context.Context); ok { r0 = rf(ctx, opt) } else { @@ -290,7 +297,6 @@ func (_m *DirectRepository) NewDirectWriter(ctx context.Context, opt repo.WriteS } } - var r1 repo.DirectRepositoryWriter if rf, ok := ret.Get(1).(func(context.Context, repo.WriteSessionOptions) repo.DirectRepositoryWriter); ok { r1 = rf(ctx, opt) } else { @@ -299,7 +305,6 @@ func (_m *DirectRepository) NewDirectWriter(ctx context.Context, opt repo.WriteS } } - var r2 error if rf, ok := ret.Get(2).(func(context.Context, repo.WriteSessionOptions) error); ok { r2 = rf(ctx, opt) } else { @@ -314,6 +319,11 @@ func (_m *DirectRepository) NewWriter(ctx context.Context, opt repo.WriteSession ret := _m.Called(ctx, opt) var r0 context.Context + var r1 repo.RepositoryWriter + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, repo.WriteSessionOptions) (context.Context, repo.RepositoryWriter, error)); ok { + return rf(ctx, opt) + } if rf, ok := ret.Get(0).(func(context.Context, repo.WriteSessionOptions) context.Context); ok { r0 = rf(ctx, opt) } else { @@ -322,7 +332,6 @@ func (_m *DirectRepository) NewWriter(ctx context.Context, opt repo.WriteSession } } - var r1 repo.RepositoryWriter if rf, ok := ret.Get(1).(func(context.Context, repo.WriteSessionOptions) repo.RepositoryWriter); ok { r1 = rf(ctx, opt) } else { @@ -331,7 +340,6 @@ func (_m *DirectRepository) NewWriter(ctx context.Context, opt repo.WriteSession } } - var r2 error if rf, ok := ret.Get(2).(func(context.Context, repo.WriteSessionOptions) error); ok { r2 = rf(ctx, opt) } else { @@ -342,14 +350,14 @@ func (_m *DirectRepository) NewWriter(ctx context.Context, opt repo.WriteSession } // ObjectFormat provides a mock function with given fields: -func (_m *DirectRepository) ObjectFormat() object.Format { +func (_m *DirectRepository) ObjectFormat() format.ObjectFormat { ret := _m.Called() - var r0 object.Format - if rf, ok := ret.Get(0).(func() object.Format); ok { + var r0 format.ObjectFormat + if rf, ok := ret.Get(0).(func() format.ObjectFormat); ok { r0 = rf() } else { - r0 = ret.Get(0).(object.Format) + r0 = ret.Get(0).(format.ObjectFormat) } return r0 @@ -360,6 +368,10 @@ func (_m *DirectRepository) OpenObject(ctx context.Context, id object.ID) (objec ret := _m.Called(ctx, id) var r0 object.Reader + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, object.ID) (object.Reader, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, object.ID) object.Reader); ok { r0 = rf(ctx, id) } else { @@ -368,7 +380,6 @@ func (_m *DirectRepository) OpenObject(ctx context.Context, id object.ID) (objec } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, object.ID) error); ok { r1 = rf(ctx, id) } else { @@ -399,6 +410,10 @@ func (_m *DirectRepository) PrefetchObjects(ctx context.Context, objectIDs []obj ret := _m.Called(ctx, objectIDs, hint) var r0 []index.ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []object.ID, string) ([]index.ID, error)); ok { + return rf(ctx, objectIDs, hint) + } if rf, ok := ret.Get(0).(func(context.Context, []object.ID, string) []index.ID); ok { r0 = rf(ctx, objectIDs, hint) } else { @@ -407,7 +422,6 @@ func (_m *DirectRepository) PrefetchObjects(ctx context.Context, objectIDs []obj } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []object.ID, string) error); ok { r1 = rf(ctx, objectIDs, hint) } else { @@ -466,13 +480,16 @@ func (_m *DirectRepository) Token(password string) (string, error) { ret := _m.Called(password) var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(string) (string, error)); ok { + return rf(password) + } if rf, ok := ret.Get(0).(func(string) string); ok { r0 = rf(password) } else { r0 = ret.Get(0).(string) } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(password) } else { @@ -508,6 +525,10 @@ func (_m *DirectRepository) VerifyObject(ctx context.Context, id object.ID) ([]i ret := _m.Called(ctx, id) var r0 []index.ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, object.ID) ([]index.ID, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, object.ID) []index.ID); ok { r0 = rf(ctx, id) } else { @@ -516,7 +537,6 @@ func (_m *DirectRepository) VerifyObject(ctx context.Context, id object.ID) ([]i } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, object.ID) error); ok { r1 = rf(ctx, id) } else { diff --git a/pkg/repository/udmrepo/kopialib/backend/mocks/DirectRepositoryWriter.go b/pkg/repository/udmrepo/kopialib/backend/mocks/DirectRepositoryWriter.go index 2116a025c..e0ff04fe7 100644 --- a/pkg/repository/udmrepo/kopialib/backend/mocks/DirectRepositoryWriter.go +++ b/pkg/repository/udmrepo/kopialib/backend/mocks/DirectRepositoryWriter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.22.1. DO NOT EDIT. package mocks @@ -8,8 +8,12 @@ import ( context "context" + format "github.com/kopia/kopia/repo/format" + index "github.com/kopia/kopia/repo/content/index" + indexblob "github.com/kopia/kopia/repo/content/indexblob" + manifest "github.com/kopia/kopia/repo/manifest" mock "github.com/stretchr/testify/mock" @@ -44,20 +48,6 @@ func (_m *DirectRepositoryWriter) AlsoLogToContentLog(ctx context.Context) conte return r0 } -// BlobCfg provides a mock function with given fields: -func (_m *DirectRepositoryWriter) BlobCfg() content.BlobCfgBlob { - ret := _m.Called() - - var r0 content.BlobCfgBlob - if rf, ok := ret.Get(0).(func() content.BlobCfgBlob); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(content.BlobCfgBlob) - } - - return r0 -} - // BlobReader provides a mock function with given fields: func (_m *DirectRepositoryWriter) BlobReader() blob.Reader { ret := _m.Called() @@ -106,20 +96,6 @@ func (_m *DirectRepositoryWriter) BlobVolume() blob.Volume { return r0 } -// ChangePassword provides a mock function with given fields: ctx, newPassword -func (_m *DirectRepositoryWriter) ChangePassword(ctx context.Context, newPassword string) error { - ret := _m.Called(ctx, newPassword) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(ctx, newPassword) - } else { - r0 = ret.Error(0) - } - - return r0 -} - // ClientOptions provides a mock function with given fields: func (_m *DirectRepositoryWriter) ClientOptions() repo.ClientOptions { ret := _m.Called() @@ -148,18 +124,28 @@ func (_m *DirectRepositoryWriter) Close(ctx context.Context) error { return r0 } -// CommitUpgrade provides a mock function with given fields: ctx -func (_m *DirectRepositoryWriter) CommitUpgrade(ctx context.Context) error { - ret := _m.Called(ctx) +// ConcatenateObjects provides a mock function with given fields: ctx, objectIDs +func (_m *DirectRepositoryWriter) ConcatenateObjects(ctx context.Context, objectIDs []object.ID) (object.ID, error) { + ret := _m.Called(ctx, objectIDs) - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(ctx) + var r0 object.ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []object.ID) (object.ID, error)); ok { + return rf(ctx, objectIDs) + } + if rf, ok := ret.Get(0).(func(context.Context, []object.ID) object.ID); ok { + r0 = rf(ctx, objectIDs) } else { - r0 = ret.Error(0) + r0 = ret.Get(0).(object.ID) } - return r0 + if rf, ok := ret.Get(1).(func(context.Context, []object.ID) error); ok { + r1 = rf(ctx, objectIDs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } // ConfigFilename provides a mock function with given fields: @@ -181,6 +167,10 @@ func (_m *DirectRepositoryWriter) ContentInfo(ctx context.Context, contentID ind ret := _m.Called(ctx, contentID) var r0 index.Info + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, index.ID) (index.Info, error)); ok { + return rf(ctx, contentID) + } if rf, ok := ret.Get(0).(func(context.Context, index.ID) index.Info); ok { r0 = rf(ctx, contentID) } else { @@ -189,7 +179,6 @@ func (_m *DirectRepositoryWriter) ContentInfo(ctx context.Context, contentID ind } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, index.ID) error); ok { r1 = rf(ctx, contentID) } else { @@ -231,22 +220,6 @@ func (_m *DirectRepositoryWriter) ContentReader() content.Reader { return r0 } -// Crypter provides a mock function with given fields: -func (_m *DirectRepositoryWriter) Crypter() *content.Crypter { - ret := _m.Called() - - var r0 *content.Crypter - if rf, ok := ret.Get(0).(func() *content.Crypter); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*content.Crypter) - } - } - - return r0 -} - // DeleteManifest provides a mock function with given fields: ctx, id func (_m *DirectRepositoryWriter) DeleteManifest(ctx context.Context, id manifest.ID) error { ret := _m.Called(ctx, id) @@ -287,6 +260,10 @@ func (_m *DirectRepositoryWriter) FindManifests(ctx context.Context, labels map[ ret := _m.Called(ctx, labels) var r0 []*manifest.EntryMetadata + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, map[string]string) ([]*manifest.EntryMetadata, error)); ok { + return rf(ctx, labels) + } if rf, ok := ret.Get(0).(func(context.Context, map[string]string) []*manifest.EntryMetadata); ok { r0 = rf(ctx, labels) } else { @@ -295,7 +272,6 @@ func (_m *DirectRepositoryWriter) FindManifests(ctx context.Context, labels map[ } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, map[string]string) error); ok { r1 = rf(ctx, labels) } else { @@ -319,11 +295,31 @@ func (_m *DirectRepositoryWriter) Flush(ctx context.Context) error { return r0 } +// FormatManager provides a mock function with given fields: +func (_m *DirectRepositoryWriter) FormatManager() *format.Manager { + ret := _m.Called() + + var r0 *format.Manager + if rf, ok := ret.Get(0).(func() *format.Manager); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*format.Manager) + } + } + + return r0 +} + // GetManifest provides a mock function with given fields: ctx, id, data func (_m *DirectRepositoryWriter) GetManifest(ctx context.Context, id manifest.ID, data interface{}) (*manifest.EntryMetadata, error) { ret := _m.Called(ctx, id, data) var r0 *manifest.EntryMetadata + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, manifest.ID, interface{}) (*manifest.EntryMetadata, error)); ok { + return rf(ctx, id, data) + } if rf, ok := ret.Get(0).(func(context.Context, manifest.ID, interface{}) *manifest.EntryMetadata); ok { r0 = rf(ctx, id, data) } else { @@ -332,7 +328,6 @@ func (_m *DirectRepositoryWriter) GetManifest(ctx context.Context, id manifest.I } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, manifest.ID, interface{}) error); ok { r1 = rf(ctx, id, data) } else { @@ -343,19 +338,22 @@ func (_m *DirectRepositoryWriter) GetManifest(ctx context.Context, id manifest.I } // IndexBlobs provides a mock function with given fields: ctx, includeInactive -func (_m *DirectRepositoryWriter) IndexBlobs(ctx context.Context, includeInactive bool) ([]content.IndexBlobInfo, error) { +func (_m *DirectRepositoryWriter) IndexBlobs(ctx context.Context, includeInactive bool) ([]indexblob.Metadata, error) { ret := _m.Called(ctx, includeInactive) - var r0 []content.IndexBlobInfo - if rf, ok := ret.Get(0).(func(context.Context, bool) []content.IndexBlobInfo); ok { + var r0 []indexblob.Metadata + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, bool) ([]indexblob.Metadata, error)); ok { + return rf(ctx, includeInactive) + } + if rf, ok := ret.Get(0).(func(context.Context, bool) []indexblob.Metadata); ok { r0 = rf(ctx, includeInactive) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]content.IndexBlobInfo) + r0 = ret.Get(0).([]indexblob.Metadata) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, bool) error); ok { r1 = rf(ctx, includeInactive) } else { @@ -370,6 +368,11 @@ func (_m *DirectRepositoryWriter) NewDirectWriter(ctx context.Context, opt repo. ret := _m.Called(ctx, opt) var r0 context.Context + var r1 repo.DirectRepositoryWriter + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, repo.WriteSessionOptions) (context.Context, repo.DirectRepositoryWriter, error)); ok { + return rf(ctx, opt) + } if rf, ok := ret.Get(0).(func(context.Context, repo.WriteSessionOptions) context.Context); ok { r0 = rf(ctx, opt) } else { @@ -378,7 +381,6 @@ func (_m *DirectRepositoryWriter) NewDirectWriter(ctx context.Context, opt repo. } } - var r1 repo.DirectRepositoryWriter if rf, ok := ret.Get(1).(func(context.Context, repo.WriteSessionOptions) repo.DirectRepositoryWriter); ok { r1 = rf(ctx, opt) } else { @@ -387,7 +389,6 @@ func (_m *DirectRepositoryWriter) NewDirectWriter(ctx context.Context, opt repo. } } - var r2 error if rf, ok := ret.Get(2).(func(context.Context, repo.WriteSessionOptions) error); ok { r2 = rf(ctx, opt) } else { @@ -418,6 +419,11 @@ func (_m *DirectRepositoryWriter) NewWriter(ctx context.Context, opt repo.WriteS ret := _m.Called(ctx, opt) var r0 context.Context + var r1 repo.RepositoryWriter + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, repo.WriteSessionOptions) (context.Context, repo.RepositoryWriter, error)); ok { + return rf(ctx, opt) + } if rf, ok := ret.Get(0).(func(context.Context, repo.WriteSessionOptions) context.Context); ok { r0 = rf(ctx, opt) } else { @@ -426,7 +432,6 @@ func (_m *DirectRepositoryWriter) NewWriter(ctx context.Context, opt repo.WriteS } } - var r1 repo.RepositoryWriter if rf, ok := ret.Get(1).(func(context.Context, repo.WriteSessionOptions) repo.RepositoryWriter); ok { r1 = rf(ctx, opt) } else { @@ -435,7 +440,6 @@ func (_m *DirectRepositoryWriter) NewWriter(ctx context.Context, opt repo.WriteS } } - var r2 error if rf, ok := ret.Get(2).(func(context.Context, repo.WriteSessionOptions) error); ok { r2 = rf(ctx, opt) } else { @@ -446,24 +450,33 @@ func (_m *DirectRepositoryWriter) NewWriter(ctx context.Context, opt repo.WriteS } // ObjectFormat provides a mock function with given fields: -func (_m *DirectRepositoryWriter) ObjectFormat() object.Format { +func (_m *DirectRepositoryWriter) ObjectFormat() format.ObjectFormat { ret := _m.Called() - var r0 object.Format - if rf, ok := ret.Get(0).(func() object.Format); ok { + var r0 format.ObjectFormat + if rf, ok := ret.Get(0).(func() format.ObjectFormat); ok { r0 = rf() } else { - r0 = ret.Get(0).(object.Format) + r0 = ret.Get(0).(format.ObjectFormat) } return r0 } +// OnSuccessfulFlush provides a mock function with given fields: callback +func (_m *DirectRepositoryWriter) OnSuccessfulFlush(callback repo.RepositoryWriterCallback) { + _m.Called(callback) +} + // OpenObject provides a mock function with given fields: ctx, id func (_m *DirectRepositoryWriter) OpenObject(ctx context.Context, id object.ID) (object.Reader, error) { ret := _m.Called(ctx, id) var r0 object.Reader + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, object.ID) (object.Reader, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, object.ID) object.Reader); ok { r0 = rf(ctx, id) } else { @@ -472,7 +485,6 @@ func (_m *DirectRepositoryWriter) OpenObject(ctx context.Context, id object.ID) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, object.ID) error); ok { r1 = rf(ctx, id) } else { @@ -503,6 +515,10 @@ func (_m *DirectRepositoryWriter) PrefetchObjects(ctx context.Context, objectIDs ret := _m.Called(ctx, objectIDs, hint) var r0 []index.ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []object.ID, string) ([]index.ID, error)); ok { + return rf(ctx, objectIDs, hint) + } if rf, ok := ret.Get(0).(func(context.Context, []object.ID, string) []index.ID); ok { r0 = rf(ctx, objectIDs, hint) } else { @@ -511,7 +527,6 @@ func (_m *DirectRepositoryWriter) PrefetchObjects(ctx context.Context, objectIDs } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []object.ID, string) error); ok { r1 = rf(ctx, objectIDs, hint) } else { @@ -526,13 +541,16 @@ func (_m *DirectRepositoryWriter) PutManifest(ctx context.Context, labels map[st ret := _m.Called(ctx, labels, payload) var r0 manifest.ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, map[string]string, interface{}) (manifest.ID, error)); ok { + return rf(ctx, labels, payload) + } if rf, ok := ret.Get(0).(func(context.Context, map[string]string, interface{}) manifest.ID); ok { r0 = rf(ctx, labels, payload) } else { r0 = ret.Get(0).(manifest.ID) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, map[string]string, interface{}) error); ok { r1 = rf(ctx, labels, payload) } else { @@ -556,50 +574,23 @@ func (_m *DirectRepositoryWriter) Refresh(ctx context.Context) error { return r0 } -// RollbackUpgrade provides a mock function with given fields: ctx -func (_m *DirectRepositoryWriter) RollbackUpgrade(ctx context.Context) error { - ret := _m.Called(ctx) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(ctx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// SetParameters provides a mock function with given fields: ctx, m, blobcfg -func (_m *DirectRepositoryWriter) SetParameters(ctx context.Context, m content.MutableParameters, blobcfg content.BlobCfgBlob) error { - ret := _m.Called(ctx, m, blobcfg) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, content.MutableParameters, content.BlobCfgBlob) error); ok { - r0 = rf(ctx, m, blobcfg) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// SetUpgradeLockIntent provides a mock function with given fields: ctx, l -func (_m *DirectRepositoryWriter) SetUpgradeLockIntent(ctx context.Context, l content.UpgradeLock) (*content.UpgradeLock, error) { - ret := _m.Called(ctx, l) - - var r0 *content.UpgradeLock - if rf, ok := ret.Get(0).(func(context.Context, content.UpgradeLock) *content.UpgradeLock); ok { - r0 = rf(ctx, l) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*content.UpgradeLock) - } - } +// ReplaceManifests provides a mock function with given fields: ctx, labels, payload +func (_m *DirectRepositoryWriter) ReplaceManifests(ctx context.Context, labels map[string]string, payload interface{}) (manifest.ID, error) { + ret := _m.Called(ctx, labels, payload) + var r0 manifest.ID var r1 error - if rf, ok := ret.Get(1).(func(context.Context, content.UpgradeLock) error); ok { - r1 = rf(ctx, l) + if rf, ok := ret.Get(0).(func(context.Context, map[string]string, interface{}) (manifest.ID, error)); ok { + return rf(ctx, labels, payload) + } + if rf, ok := ret.Get(0).(func(context.Context, map[string]string, interface{}) manifest.ID); ok { + r0 = rf(ctx, labels, payload) + } else { + r0 = ret.Get(0).(manifest.ID) + } + + if rf, ok := ret.Get(1).(func(context.Context, map[string]string, interface{}) error); ok { + r1 = rf(ctx, labels, payload) } else { r1 = ret.Error(1) } @@ -642,13 +633,16 @@ func (_m *DirectRepositoryWriter) Token(password string) (string, error) { ret := _m.Called(password) var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(string) (string, error)); ok { + return rf(password) + } if rf, ok := ret.Get(0).(func(string) string); ok { r0 = rf(password) } else { r0 = ret.Get(0).(string) } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(password) } else { @@ -684,6 +678,10 @@ func (_m *DirectRepositoryWriter) VerifyObject(ctx context.Context, id object.ID ret := _m.Called(ctx, id) var r0 []index.ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, object.ID) ([]index.ID, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, object.ID) []index.ID); ok { r0 = rf(ctx, id) } else { @@ -692,7 +690,6 @@ func (_m *DirectRepositoryWriter) VerifyObject(ctx context.Context, id object.ID } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, object.ID) error); ok { r1 = rf(ctx, id) } else { diff --git a/pkg/repository/udmrepo/kopialib/backend/mocks/Logger.go b/pkg/repository/udmrepo/kopialib/backend/mocks/Logger.go index 90a219910..e9a2b8247 100644 --- a/pkg/repository/udmrepo/kopialib/backend/mocks/Logger.go +++ b/pkg/repository/udmrepo/kopialib/backend/mocks/Logger.go @@ -1,62 +1,99 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.22.1. DO NOT EDIT. package mocks -import mock "github.com/stretchr/testify/mock" +import ( + mock "github.com/stretchr/testify/mock" + zapcore "go.uber.org/zap/zapcore" +) -// Logger is an autogenerated mock type for the Logger type -type Logger struct { +// Core is an autogenerated mock type for the Core type +type Core struct { mock.Mock } -// Debugf provides a mock function with given fields: msg, args -func (_m *Logger) Debugf(msg string, args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, msg) - _ca = append(_ca, args...) - _m.Called(_ca...) +// Check provides a mock function with given fields: _a0, _a1 +func (_m *Core) Check(_a0 zapcore.Entry, _a1 *zapcore.CheckedEntry) *zapcore.CheckedEntry { + ret := _m.Called(_a0, _a1) + + var r0 *zapcore.CheckedEntry + if rf, ok := ret.Get(0).(func(zapcore.Entry, *zapcore.CheckedEntry) *zapcore.CheckedEntry); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*zapcore.CheckedEntry) + } + } + + return r0 } -// Debugw provides a mock function with given fields: msg, keyValuePairs -func (_m *Logger) Debugw(msg string, keyValuePairs ...interface{}) { - var _ca []interface{} - _ca = append(_ca, msg) - _ca = append(_ca, keyValuePairs...) - _m.Called(_ca...) +// Enabled provides a mock function with given fields: _a0 +func (_m *Core) Enabled(_a0 zapcore.Level) bool { + ret := _m.Called(_a0) + + var r0 bool + if rf, ok := ret.Get(0).(func(zapcore.Level) bool); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 } -// Errorf provides a mock function with given fields: msg, args -func (_m *Logger) Errorf(msg string, args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, msg) - _ca = append(_ca, args...) - _m.Called(_ca...) +// Sync provides a mock function with given fields: +func (_m *Core) Sync() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 } -// Infof provides a mock function with given fields: msg, args -func (_m *Logger) Infof(msg string, args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, msg) - _ca = append(_ca, args...) - _m.Called(_ca...) +// With provides a mock function with given fields: _a0 +func (_m *Core) With(_a0 []zapcore.Field) zapcore.Core { + ret := _m.Called(_a0) + + var r0 zapcore.Core + if rf, ok := ret.Get(0).(func([]zapcore.Field) zapcore.Core); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(zapcore.Core) + } + } + + return r0 } -// Warnf provides a mock function with given fields: msg, args -func (_m *Logger) Warnf(msg string, args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, msg) - _ca = append(_ca, args...) - _m.Called(_ca...) +// Write provides a mock function with given fields: _a0, _a1 +func (_m *Core) Write(_a0 zapcore.Entry, _a1 []zapcore.Field) error { + ret := _m.Called(_a0, _a1) + + var r0 error + if rf, ok := ret.Get(0).(func(zapcore.Entry, []zapcore.Field) error); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 } -type mockConstructorTestingTNewLogger interface { +type mockConstructorTestingTNewCore interface { mock.TestingT Cleanup(func()) } -// NewLogger creates a new instance of Logger. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewLogger(t mockConstructorTestingTNewLogger) *Logger { - mock := &Logger{} +// NewCore creates a new instance of Core. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewCore(t mockConstructorTestingTNewCore) *Core { + mock := &Core{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/pkg/repository/udmrepo/kopialib/backend/s3.go b/pkg/repository/udmrepo/kopialib/backend/s3.go index 90097c2ea..e2e01de75 100644 --- a/pkg/repository/udmrepo/kopialib/backend/s3.go +++ b/pkg/repository/udmrepo/kopialib/backend/s3.go @@ -52,6 +52,7 @@ func (c *S3Backend) Setup(ctx context.Context, flags map[string]string) error { c.options.DoNotUseTLS = optionalHaveBool(ctx, udmrepo.StoreOptionS3DisableTLS, flags) c.options.DoNotVerifyTLS = optionalHaveBool(ctx, udmrepo.StoreOptionS3DisableTLSVerify, flags) c.options.SessionToken = optionalHaveString(udmrepo.StoreOptionS3Token, flags) + c.options.RootCA = optionalHaveBase64(ctx, udmrepo.StoreOptionS3CustomCA, flags) c.options.Limits = setupLimits(ctx, flags) @@ -59,5 +60,5 @@ func (c *S3Backend) Setup(ctx context.Context, flags map[string]string) error { } func (c *S3Backend) Connect(ctx context.Context, isCreate bool) (blob.Storage, error) { - return s3.New(ctx, &c.options) + return s3.New(ctx, &c.options, false) } diff --git a/pkg/repository/udmrepo/kopialib/backend/utils.go b/pkg/repository/udmrepo/kopialib/backend/utils.go index 97fa2ab57..19fa7b278 100644 --- a/pkg/repository/udmrepo/kopialib/backend/utils.go +++ b/pkg/repository/udmrepo/kopialib/backend/utils.go @@ -18,6 +18,7 @@ package backend import ( "context" + "encoding/base64" "strconv" "time" @@ -84,6 +85,19 @@ func optionalHaveDuration(ctx context.Context, key string, flags map[string]stri return 0 } +func optionalHaveBase64(ctx context.Context, key string, flags map[string]string) []byte { + if value, exist := flags[key]; exist { + ret, err := base64.StdEncoding.DecodeString(value) + if err == nil { + return ret + } + + backendLog()(ctx).Errorf("Ignore %s, value [%s] is invalid, err %v", key, value, err) + } + + return nil +} + func backendLog() func(ctx context.Context) logging.Logger { return logging.Module("kopialib-bd") } diff --git a/pkg/repository/udmrepo/kopialib/backend/utils_test.go b/pkg/repository/udmrepo/kopialib/backend/utils_test.go index cb3323625..0eb238196 100644 --- a/pkg/repository/udmrepo/kopialib/backend/utils_test.go +++ b/pkg/repository/udmrepo/kopialib/backend/utils_test.go @@ -18,12 +18,13 @@ package backend import ( "context" - "fmt" "testing" "github.com/kopia/kopia/repo/logging" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" storagemocks "github.com/vmware-tanzu/velero/pkg/repository/udmrepo/kopialib/backend/mocks" ) @@ -31,13 +32,13 @@ import ( func TestOptionalHaveBool(t *testing.T) { var expectMsg string testCases := []struct { - name string - key string - flags map[string]string - logger *storagemocks.Logger - retFuncErrorf func(mock.Arguments) - expectMsg string - retValue bool + name string + key string + flags map[string]string + logger *storagemocks.Core + retFuncCheck func(mock.Arguments) + expectMsg string + retValue bool }{ { name: "key not exist", @@ -59,9 +60,12 @@ func TestOptionalHaveBool(t *testing.T) { flags: map[string]string{ "fake-key": "fake-value", }, - logger: new(storagemocks.Logger), - retFuncErrorf: func(args mock.Arguments) { - expectMsg = fmt.Sprintf(args[0].(string), args[1].(string), args[2].(string), args[3].(error)) + logger: new(storagemocks.Core), + retFuncCheck: func(args mock.Arguments) { + ent := args[0].(zapcore.Entry) + if ent.Level == zapcore.ErrorLevel { + expectMsg = ent.Message + } }, expectMsg: "Ignore fake-key, value [fake-value] is invalid, err strconv.ParseBool: parsing \"fake-value\": invalid syntax", retValue: false, @@ -71,11 +75,12 @@ func TestOptionalHaveBool(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { if tc.logger != nil { - tc.logger.On("Errorf", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Run(tc.retFuncErrorf) + tc.logger.On("Enabled", mock.Anything).Return(true) + tc.logger.On("Check", mock.Anything, mock.Anything).Run(tc.retFuncCheck).Return(&zapcore.CheckedEntry{}) } ctx := logging.WithLogger(context.Background(), func(module string) logging.Logger { - return tc.logger + return zap.New(tc.logger).Sugar() }) retValue := optionalHaveBool(ctx, tc.key, tc.flags) diff --git a/pkg/repository/udmrepo/kopialib/lib_repo.go b/pkg/repository/udmrepo/kopialib/lib_repo.go index bb924c2f1..7802555fc 100644 --- a/pkg/repository/udmrepo/kopialib/lib_repo.go +++ b/pkg/repository/udmrepo/kopialib/lib_repo.go @@ -19,7 +19,6 @@ package kopialib import ( "context" "os" - "runtime" "strings" "sync/atomic" "time" @@ -234,7 +233,12 @@ func (kr *kopiaRepository) OpenObject(ctx context.Context, id udmrepo.ID) (udmre return nil, errors.New("repo is closed or not open") } - reader, err := kr.rawRepo.OpenObject(logging.SetupKopiaLog(ctx, kr.logger), object.ID(id)) + objID, err := object.ParseID(string(id)) + if err != nil { + return nil, errors.Wrapf(err, "error to parse object ID from %v", id) + } + + reader, err := kr.rawRepo.OpenObject(logging.SetupKopiaLog(ctx, kr.logger), objID) if err != nil { return nil, errors.Wrap(err, "error to open object") } @@ -309,8 +313,8 @@ func (kr *kopiaRepository) NewObjectWriter(ctx context.Context, opt udmrepo.Obje writer := kr.rawWriter.NewObjectWriter(logging.SetupKopiaLog(ctx, kr.logger), object.WriterOptions{ Description: opt.Description, - Prefix: index.ID(opt.Prefix), - AsyncWrites: getAsyncWrites(), + Prefix: index.IDPrefix(opt.Prefix), + AsyncWrites: opt.AsyncWrites, Compressor: getCompressorForObject(opt), }) @@ -438,7 +442,7 @@ func (kow *kopiaObjectWriter) Checkpoint() (udmrepo.ID, error) { return udmrepo.ID(""), errors.Wrap(err, "error to checkpoint object") } - return udmrepo.ID(id), nil + return udmrepo.ID(id.String()), nil } func (kow *kopiaObjectWriter) Result() (udmrepo.ID, error) { @@ -451,7 +455,7 @@ func (kow *kopiaObjectWriter) Result() (udmrepo.ID, error) { return udmrepo.ID(""), errors.Wrap(err, "error to wait object") } - return udmrepo.ID(id), nil + return udmrepo.ID(id.String()), nil } func (kow *kopiaObjectWriter) Close() error { @@ -469,11 +473,6 @@ func (kow *kopiaObjectWriter) Close() error { return nil } -// getAsyncWrites returns the number of concurrent async writes -func getAsyncWrites() int { - return runtime.NumCPU() -} - // getCompressorForObject returns the compressor for an object, at present, we don't support compression func getCompressorForObject(opt udmrepo.ObjectWriteOptions) compression.Name { return "" diff --git a/pkg/repository/udmrepo/kopialib/lib_repo_test.go b/pkg/repository/udmrepo/kopialib/lib_repo_test.go index e92bd5ebd..19b2089a8 100644 --- a/pkg/repository/udmrepo/kopialib/lib_repo_test.go +++ b/pkg/repository/udmrepo/kopialib/lib_repo_test.go @@ -23,6 +23,7 @@ import ( "time" "github.com/kopia/kopia/repo" + "github.com/kopia/kopia/repo/manifest" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -262,14 +263,14 @@ func TestMaintain(t *testing.T) { func TestWriteInitParameters(t *testing.T) { var directRpo *repomocks.DirectRepository testCases := []struct { - name string - repoOptions udmrepo.RepoOptions - returnRepo *repomocks.DirectRepository - returnRepoWriter *repomocks.DirectRepositoryWriter - repoOpen func(context.Context, string, string, *repo.Options) (repo.Repository, error) - newRepoWriterError error - findManifestError error - expectedErr string + name string + repoOptions udmrepo.RepoOptions + returnRepo *repomocks.DirectRepository + returnRepoWriter *repomocks.DirectRepositoryWriter + repoOpen func(context.Context, string, string, *repo.Options) (repo.Repository, error) + newRepoWriterError error + replaceManifestError error + expectedErr string }{ { name: "repo open fail, repo not exist", @@ -315,10 +316,10 @@ func TestWriteInitParameters(t *testing.T) { repoOpen: func(context.Context, string, string, *repo.Options) (repo.Repository, error) { return directRpo, nil }, - returnRepo: new(repomocks.DirectRepository), - returnRepoWriter: new(repomocks.DirectRepositoryWriter), - findManifestError: errors.New("fake-find-manifest-error"), - expectedErr: "error to init write repo parameters: error to set maintenance params: error looking for maintenance manifest: fake-find-manifest-error", + returnRepo: new(repomocks.DirectRepository), + returnRepoWriter: new(repomocks.DirectRepositoryWriter), + replaceManifestError: errors.New("fake-replace-manifest-error"), + expectedErr: "error to init write repo parameters: error to set maintenance params: put manifest: fake-replace-manifest-error", }, } @@ -343,7 +344,7 @@ func TestWriteInitParameters(t *testing.T) { if tc.returnRepoWriter != nil { tc.returnRepoWriter.On("Close", mock.Anything).Return(nil) - tc.returnRepoWriter.On("FindManifests", mock.Anything, mock.Anything).Return(nil, tc.findManifestError) + tc.returnRepoWriter.On("ReplaceManifests", mock.Anything, mock.Anything, mock.Anything).Return(manifest.ID(""), tc.replaceManifestError) } err := writeInitParameters(ctx, tc.repoOptions, logger) diff --git a/pkg/repository/udmrepo/repo.go b/pkg/repository/udmrepo/repo.go index 6bec44189..c186bab4b 100644 --- a/pkg/repository/udmrepo/repo.go +++ b/pkg/repository/udmrepo/repo.go @@ -68,6 +68,7 @@ type ObjectWriteOptions struct { Prefix ID // A prefix of the name used to save the object AccessMode int // OBJECT_DATA_ACCESS_* BackupMode int // OBJECT_DATA_BACKUP_* + AsyncWrites int // Num of async writes for the object, 0 means no async write } // BackupRepoService is used to initialize, open or maintain a backup repository diff --git a/pkg/repository/udmrepo/repo_options.go b/pkg/repository/udmrepo/repo_options.go index 4f1276514..9337018f2 100644 --- a/pkg/repository/udmrepo/repo_options.go +++ b/pkg/repository/udmrepo/repo_options.go @@ -42,6 +42,7 @@ const ( StoreOptionS3Endpoint = "endpoint" StoreOptionS3DisableTLS = "doNotUseTLS" StoreOptionS3DisableTLSVerify = "skipTLSVerify" + StoreOptionS3CustomCA = "customCA" StoreOptionAzureKey = "storageKey" StoreOptionAzureDomain = "storageDomain" diff --git a/pkg/restore/change_image_name_action_test.go b/pkg/restore/change_image_name_action_test.go index cc2c8ff66..d851e7c0d 100644 --- a/pkg/restore/change_image_name_action_test.go +++ b/pkg/restore/change_image_name_action_test.go @@ -55,7 +55,7 @@ func TestChangeImageRepositoryActionExecute(t *testing.T) { Image: "1.1.1.1:5000/abc:test", }).Result(), configMap: builder.ForConfigMap("velero", "change-image-name"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-image-name", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-image-name", "RestoreItemAction")). Data("case1", "1.1.1.1:5000 , 2.2.2.2:3000"). Result(), freshedImageName: "2.2.2.2:3000/abc:test", @@ -70,7 +70,7 @@ func TestChangeImageRepositoryActionExecute(t *testing.T) { Image: "1.1.1.1:5000/abc:test", }).Result(), configMap: builder.ForConfigMap("velero", "change-image-name"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-image-name", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-image-name", "RestoreItemAction")). Data("specific", "1.1.1.1:5000,2.2.2.2:3000"). Result(), freshedImageName: "2.2.2.2:3000/abc:test", @@ -85,7 +85,7 @@ func TestChangeImageRepositoryActionExecute(t *testing.T) { Image: "1.1.1.1:5000/abc:test", }).Result(), configMap: builder.ForConfigMap("velero", "change-image-name"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-image-name", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-image-name", "RestoreItemAction")). Data("specific", "abc:test,myproject:latest"). Result(), freshedImageName: "1.1.1.1:5000/myproject:latest", @@ -100,7 +100,7 @@ func TestChangeImageRepositoryActionExecute(t *testing.T) { Image: "1.1.1.1:5000/abc:test", }).Result(), configMap: builder.ForConfigMap("velero", "change-image-name"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-image-name", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-image-name", "RestoreItemAction")). Data("specific", "5000,3333"). Result(), freshedImageName: "1.1.1.1:5000/abc:test", @@ -115,7 +115,7 @@ func TestChangeImageRepositoryActionExecute(t *testing.T) { Image: "1.1.1.1:5000/abc:test", }).Result(), configMap: builder.ForConfigMap("velero", "change-image-name"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-image-name", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-image-name", "RestoreItemAction")). Data("specific", "test,latest"). Result(), freshedImageName: "1.1.1.1:5000/abc:test", @@ -130,7 +130,7 @@ func TestChangeImageRepositoryActionExecute(t *testing.T) { Image: "dev/image1:dev", }).Result(), configMap: builder.ForConfigMap("velero", "change-image-name"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-image-name", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-image-name", "RestoreItemAction")). Data("specific", "dev/,test/"). Result(), freshedImageName: "dev/image1:dev", diff --git a/pkg/restore/change_pvc_node_selector.go b/pkg/restore/change_pvc_node_selector.go index c051b918b..5934cd266 100644 --- a/pkg/restore/change_pvc_node_selector.go +++ b/pkg/restore/change_pvc_node_selector.go @@ -140,7 +140,7 @@ func (p *ChangePVCNodeSelectorAction) Execute(input *velero.RestoreItemActionExe func getNewNodeFromConfigMap(client corev1client.ConfigMapInterface, node string) (string, error) { // fetch node mapping from configMap - config, err := getPluginConfig(common.PluginKindRestoreItemAction, "velero.io/change-pvc-node-selector", client) + config, err := common.GetPluginConfig(common.PluginKindRestoreItemAction, "velero.io/change-pvc-node-selector", client) if err != nil { return "", err } diff --git a/pkg/restore/change_pvc_node_selector_test.go b/pkg/restore/change_pvc_node_selector_test.go index faca154ae..7cfbd170c 100644 --- a/pkg/restore/change_pvc_node_selector_test.go +++ b/pkg/restore/change_pvc_node_selector_test.go @@ -57,7 +57,7 @@ func TestChangePVCNodeSelectorActionExecute(t *testing.T) { builder.WithAnnotations("volume.kubernetes.io/selected-node", "source-node"), ).Result(), configMap: builder.ForConfigMap("velero", "change-pvc-node"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-pvc-node-selector", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-pvc-node-selector", "RestoreItemAction")). Data("source-node", "dest-node"). Result(), newNode: builder.ForNode("dest-node").Result(), @@ -73,7 +73,7 @@ func TestChangePVCNodeSelectorActionExecute(t *testing.T) { builder.WithAnnotations("volume.kubernetes.io/selected-node", "source-node"), ).Result(), configMap: builder.ForConfigMap("velero", "change-pvc-node"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/some-other-plugin", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/some-other-plugin", "RestoreItemAction")). Data("source-noed", "dest-node"). Result(), want: builder.ForPersistentVolumeClaim("source-ns", "pvc-1").Result(), @@ -85,7 +85,7 @@ func TestChangePVCNodeSelectorActionExecute(t *testing.T) { builder.WithAnnotations("volume.kubernetes.io/selected-node", "source-node"), ).Result(), configMap: builder.ForConfigMap("velero", "change-pvc-node"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-pvc-node-selector", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-pvc-node-selector", "RestoreItemAction")). Result(), want: builder.ForPersistentVolumeClaim("source-ns", "pvc-1").Result(), }, @@ -96,7 +96,7 @@ func TestChangePVCNodeSelectorActionExecute(t *testing.T) { builder.WithAnnotations("volume.kubernetes.io/selected-node", "source-node"), ).Result(), configMap: builder.ForConfigMap("velero", "change-pvc-node"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-pvc-node-selector", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-pvc-node-selector", "RestoreItemAction")). Result(), // MAYANK TODO node: builder.ForNode("source-node").Result(), @@ -109,7 +109,7 @@ func TestChangePVCNodeSelectorActionExecute(t *testing.T) { name: "when persistent volume claim has no node selector, the item is returned as-is", pvc: builder.ForPersistentVolumeClaim("source-ns", "pvc-1").Result(), configMap: builder.ForConfigMap("velero", "change-pvc-node"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-pvc-node-selector", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-pvc-node-selector", "RestoreItemAction")). Data("source-node", "dest-node"). Result(), want: builder.ForPersistentVolumeClaim("source-ns", "pvc-1").Result(), @@ -121,7 +121,7 @@ func TestChangePVCNodeSelectorActionExecute(t *testing.T) { builder.WithAnnotations("volume.kubernetes.io/selected-node", "source-node"), ).Result(), configMap: builder.ForConfigMap("velero", "change-pvc-node"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-pvc-node-selector", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-pvc-node-selector", "RestoreItemAction")). Data("source-node-1", "dest-node"). Result(), want: builder.ForPersistentVolumeClaim("source-ns", "pvc-1").Result(), diff --git a/pkg/restore/change_storageclass_action.go b/pkg/restore/change_storageclass_action.go index a02b7c484..91384c64e 100644 --- a/pkg/restore/change_storageclass_action.go +++ b/pkg/restore/change_storageclass_action.go @@ -69,7 +69,7 @@ func (a *ChangeStorageClassAction) Execute(input *velero.RestoreItemActionExecut defer a.logger.Info("Done executing ChangeStorageClassAction") a.logger.Debug("Getting plugin config") - config, err := getPluginConfig(common.PluginKindRestoreItemAction, "velero.io/change-storage-class", a.configMapClient) + config, err := common.GetPluginConfig(common.PluginKindRestoreItemAction, "velero.io/change-storage-class", a.configMapClient) if err != nil { return nil, err } diff --git a/pkg/restore/change_storageclass_action_test.go b/pkg/restore/change_storageclass_action_test.go index 65de052db..7f701a0d6 100644 --- a/pkg/restore/change_storageclass_action_test.go +++ b/pkg/restore/change_storageclass_action_test.go @@ -53,7 +53,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "a valid mapping for a persistent volume is applied correctly", pvOrPvcOrSTS: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-storage-class", "RestoreItemAction")). Data("storageclass-1", "storageclass-2"). Result(), storageClass: builder.ForStorageClass("storageclass-2").Result(), @@ -63,7 +63,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "a valid mapping for a persistent volume claim is applied correctly", pvOrPvcOrSTS: builder.ForPersistentVolumeClaim("velero", "pvc-1").StorageClass("storageclass-1").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-storage-class", "RestoreItemAction")). Data("storageclass-1", "storageclass-2"). Result(), storageClass: builder.ForStorageClass("storageclass-2").Result(), @@ -73,7 +73,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "when no config map exists for the plugin, the item is returned as-is", pvOrPvcOrSTS: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/some-other-plugin", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/some-other-plugin", "RestoreItemAction")). Data("storageclass-1", "storageclass-2"). Result(), want: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(), @@ -82,7 +82,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "when no storage class mappings exist in the plugin config map, the item is returned as-is", pvOrPvcOrSTS: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-storage-class", "RestoreItemAction")). Result(), want: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(), }, @@ -90,7 +90,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "when persistent volume has no storage class, the item is returned as-is", pvOrPvcOrSTS: builder.ForPersistentVolume("pv-1").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-storage-class", "RestoreItemAction")). Data("storageclass-1", "storageclass-2"). Result(), want: builder.ForPersistentVolume("pv-1").Result(), @@ -99,7 +99,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "when persistent volume claim has no storage class, the item is returned as-is", pvOrPvcOrSTS: builder.ForPersistentVolumeClaim("velero", "pvc-1").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-storage-class", "RestoreItemAction")). Data("storageclass-1", "storageclass-2"). Result(), want: builder.ForPersistentVolumeClaim("velero", "pvc-1").Result(), @@ -108,7 +108,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "when persistent volume's storage class has no mapping in the config map, the item is returned as-is", pvOrPvcOrSTS: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-storage-class", "RestoreItemAction")). Data("storageclass-3", "storageclass-4"). Result(), want: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(), @@ -117,7 +117,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "when persistent volume claim's storage class has no mapping in the config map, the item is returned as-is", pvOrPvcOrSTS: builder.ForPersistentVolumeClaim("velero", "pvc-1").StorageClass("storageclass-1").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-storage-class", "RestoreItemAction")). Data("storageclass-3", "storageclass-4"). Result(), want: builder.ForPersistentVolumeClaim("velero", "pvc-1").StorageClass("storageclass-1").Result(), @@ -126,7 +126,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "when persistent volume's storage class is mapped to a nonexistent storage class, an error is returned", pvOrPvcOrSTS: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-storage-class", "RestoreItemAction")). Data("storageclass-1", "nonexistent-storage-class"). Result(), wantErr: errors.New("error getting storage class nonexistent-storage-class from API: storageclasses.storage.k8s.io \"nonexistent-storage-class\" not found"), @@ -135,7 +135,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "when persistent volume claim's storage class is mapped to a nonexistent storage class, an error is returned", pvOrPvcOrSTS: builder.ForPersistentVolumeClaim("velero", "pvc-1").StorageClass("storageclass-1").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-storage-class", "RestoreItemAction")). Data("storageclass-1", "nonexistent-storage-class"). Result(), wantErr: errors.New("error getting storage class nonexistent-storage-class from API: storageclasses.storage.k8s.io \"nonexistent-storage-class\" not found"), @@ -144,7 +144,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "when statefulset's VolumeClaimTemplates has only one pvc, a valid mapping for a statefulset is applied correctly", pvOrPvcOrSTS: builder.ForStatefulSet("velero", "sts-1").StorageClass("storageclass-1").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-storage-class", "RestoreItemAction")). Data("storageclass-1", "storageclass-2"). Result(), storageClass: builder.ForStorageClass("storageclass-2").Result(), @@ -154,7 +154,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "when statefulset's VolumeClaimTemplates has more than one same pvc's storageClassName, a valid mapping for a statefulset is applied correctly", pvOrPvcOrSTS: builder.ForStatefulSet("velero", "sts-1").StorageClass("storageclass-1", "storageclass-1").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-storage-class", "RestoreItemAction")). Data("storageclass-1", "storageclass-2", "storageclass-3", "storageclass-4"). Result(), storageClass: builder.ForStorageClass("storageclass-2").Result(), @@ -164,7 +164,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "when statefulset's VolumeClaimTemplates has more than one different pvc's storageClassName, a valid mapping for a statefulset is applied correctly", pvOrPvcOrSTS: builder.ForStatefulSet("velero", "sts-1").StorageClass("storageclass-1", "storageclass-2", "storageclass-3").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-storage-class", "RestoreItemAction")). Data("storageclass-1", "storageclass-a", "storageclass-2", "storageclass-b", "storageclass-3", "storageclass-c"). Result(), storageClassSlice: builder.ForStorageClassSlice("storageclass-a", "storageclass-b", "storageclass-c").SliceResult(), @@ -174,7 +174,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "when no config map exists for the plugin, the statefulset item is returned as-is", pvOrPvcOrSTS: builder.ForStatefulSet("velero", "sts-1").StorageClass("storageclass-1").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/some-other-plugin", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/some-other-plugin", "RestoreItemAction")). Data("storageclass-1", "storageclass-2"). Result(), want: builder.ForStatefulSet("velero", "sts-1").StorageClass("storageclass-1").Result(), @@ -183,7 +183,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "when no storage class mappings exist in the plugin config map, the statefulset item is returned as-is", pvOrPvcOrSTS: builder.ForStatefulSet("velero", "sts-1").StorageClass("storageclass-1").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-storage-class", "RestoreItemAction")). Result(), want: builder.ForStatefulSet("velero", "sts-1").StorageClass("storageclass-1").Result(), }, @@ -191,7 +191,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "when persistent volume claim has no storage class, the statefulset item is returned as-is", pvOrPvcOrSTS: builder.ForStatefulSet("velero", "sts-1").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-storage-class", "RestoreItemAction")). Result(), want: builder.ForStatefulSet("velero", "sts-1").Result(), }, @@ -199,7 +199,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "when statefulset's storage class has no mapping in the config map, the item is returned as-is", pvOrPvcOrSTS: builder.ForStatefulSet("velero", "sts-1").StorageClass("storageclass-1").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-storage-class", "RestoreItemAction")). Data("storageclass-3", "storageclass-4"). Result(), want: builder.ForStatefulSet("velero", "sts-1").StorageClass("storageclass-1").Result(), @@ -208,7 +208,7 @@ func TestChangeStorageClassActionExecute(t *testing.T) { name: "when statefulset's storage class is mapped to a nonexistent storage class, an error is returned", pvOrPvcOrSTS: builder.ForStatefulSet("velero", "sts-1").StorageClass("storageclass-1").Result(), configMap: builder.ForConfigMap("velero", "change-storage-classs"). - ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "", "velero.io/change-storage-class", "RestoreItemAction")). Data("storageclass-1", "nonexistent-storage-class"). Result(), wantErr: errors.New("error getting storage class nonexistent-storage-class from API: storageclasses.storage.k8s.io \"nonexistent-storage-class\" not found"), diff --git a/pkg/restore/pod_volume_restore_action.go b/pkg/restore/pod_volume_restore_action.go index 9044ab797..e5d77b18e 100644 --- a/pkg/restore/pod_volume_restore_action.go +++ b/pkg/restore/pod_volume_restore_action.go @@ -24,7 +24,6 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" @@ -108,7 +107,7 @@ func (a *PodVolumeRestoreAction) Execute(input *velero.RestoreItemActionExecuteI // TODO we might want/need to get plugin config at the top of this method at some point; for now, wait // until we know we're doing a restore before getting config. log.Debugf("Getting plugin config") - config, err := getPluginConfig(common.PluginKindRestoreItemAction, "velero.io/pod-volume-restore", a.client) + config, err := common.GetPluginConfig(common.PluginKindRestoreItemAction, "velero.io/pod-volume-restore", a.client) if err != nil { return nil, err } @@ -259,35 +258,6 @@ func getSecurityContext(log logrus.FieldLogger, config *corev1.ConfigMap) (strin config.Data["secCtx"] } -// TODO eventually this can move to pkg/plugin/framework since it'll be used across multiple -// plugins. -func getPluginConfig(kind common.PluginKind, name string, client corev1client.ConfigMapInterface) (*corev1.ConfigMap, error) { - opts := metav1.ListOptions{ - // velero.io/plugin-config: true - // velero.io/pod-volume-restore: RestoreItemAction - LabelSelector: fmt.Sprintf("velero.io/plugin-config,%s=%s", name, kind), - } - - list, err := client.List(context.TODO(), opts) - if err != nil { - return nil, errors.WithStack(err) - } - - if len(list.Items) == 0 { - return nil, nil - } - - if len(list.Items) > 1 { - var items []string - for _, item := range list.Items { - items = append(items, item.Name) - } - return nil, errors.Errorf("found more than one ConfigMap matching label selector %q: %v", opts.LabelSelector, items) - } - - return &list.Items[0], nil -} - func newRestoreInitContainerBuilder(image, restoreUID string) *builder.ContainerBuilder { return builder.ForContainer(restorehelper.WaitInitContainer, image). Args(restoreUID). diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 8783da19a..ad48a9425 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -401,7 +401,11 @@ func (ctx *restoreContext) execute() (results.Result, results.Result) { errs.AddVeleroError(err) return warnings, errs } - defer ctx.fileSystem.RemoveAll(dir) + defer func() { + if err := ctx.fileSystem.RemoveAll(dir); err != nil { + ctx.log.Errorf("error removing temporary directory %s: %s", dir, err.Error()) + } + }() // Need to set this for additionalItems to be restored. ctx.restoreDir = dir diff --git a/pkg/test/fake_controller_runtime_client.go b/pkg/test/fake_controller_runtime_client.go index 97487d9ab..b64f57809 100644 --- a/pkg/test/fake_controller_runtime_client.go +++ b/pkg/test/fake_controller_runtime_client.go @@ -27,12 +27,15 @@ import ( k8sfake "sigs.k8s.io/controller-runtime/pkg/client/fake" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" ) func NewFakeControllerRuntimeClientBuilder(t *testing.T) *k8sfake.ClientBuilder { scheme := runtime.NewScheme() err := velerov1api.AddToScheme(scheme) require.NoError(t, err) + err = velerov2alpha1api.AddToScheme(scheme) + require.NoError(t, err) err = corev1api.AddToScheme(scheme) require.NoError(t, err) err = snapshotv1api.AddToScheme(scheme) @@ -44,6 +47,8 @@ func NewFakeControllerRuntimeClient(t *testing.T, initObjs ...runtime.Object) cl scheme := runtime.NewScheme() err := velerov1api.AddToScheme(scheme) require.NoError(t, err) + err = velerov2alpha1api.AddToScheme(scheme) + require.NoError(t, err) err = corev1api.AddToScheme(scheme) require.NoError(t, err) err = snapshotv1api.AddToScheme(scheme) diff --git a/pkg/test/fake_file_system.go b/pkg/test/fake_file_system.go index 89dc036f8..7a744647b 100644 --- a/pkg/test/fake_file_system.go +++ b/pkg/test/fake_file_system.go @@ -81,7 +81,7 @@ func (fs *FakeFileSystem) Stat(path string) (os.FileInfo, error) { func (fs *FakeFileSystem) WithFile(path string, data []byte) *FakeFileSystem { file, _ := fs.fs.Create(path) - file.Write(data) + _, _ = file.Write(data) file.Close() return fs @@ -89,14 +89,14 @@ func (fs *FakeFileSystem) WithFile(path string, data []byte) *FakeFileSystem { func (fs *FakeFileSystem) WithFileAndMode(path string, data []byte, mode os.FileMode) *FakeFileSystem { file, _ := fs.fs.OpenFile(path, os.O_CREATE|os.O_RDWR, mode) - file.Write(data) + _, _ = file.Write(data) file.Close() return fs } func (fs *FakeFileSystem) WithDirectory(path string) *FakeFileSystem { - fs.fs.MkdirAll(path, 0755) + _ = fs.fs.MkdirAll(path, 0755) return fs } diff --git a/pkg/test/test_logger.go b/pkg/test/test_logger.go index 5f3c172d6..05fc942cd 100644 --- a/pkg/test/test_logger.go +++ b/pkg/test/test_logger.go @@ -27,3 +27,10 @@ func NewLogger() logrus.FieldLogger { logger.Out = io.Discard return logrus.NewEntry(logger) } + +func NewLoggerWithLevel(level logrus.Level) logrus.FieldLogger { + logger := logrus.New() + logger.Out = io.Discard + logger.Level = level + return logrus.NewEntry(logger) +} diff --git a/pkg/uploader/kopia/progress.go b/pkg/uploader/kopia/progress.go index 6854b6f2f..fab980a64 100644 --- a/pkg/uploader/kopia/progress.go +++ b/pkg/uploader/kopia/progress.go @@ -151,3 +151,5 @@ func (p *Progress) ProgressBytes(processedBytes int64, totalBytes int64) { atomic.StoreInt64(&p.estimatedTotalBytes, totalBytes) p.UpdateProgress() } + +func (p *Progress) FinishedFile(fname string, err error) {} diff --git a/pkg/uploader/kopia/shim.go b/pkg/uploader/kopia/shim.go index 1ae3c6de1..23fb6c4a3 100644 --- a/pkg/uploader/kopia/shim.go +++ b/pkg/uploader/kopia/shim.go @@ -55,7 +55,7 @@ func NewShimRepo(repo udmrepo.BackupRepo) repo.RepositoryWriter { // OpenObject open specific object func (sr *shimRepository) OpenObject(ctx context.Context, id object.ID) (object.Reader, error) { - reader, err := sr.udmRepo.OpenObject(ctx, udmrepo.ID(id)) + reader, err := sr.udmRepo.OpenObject(ctx, udmrepo.ID(id.String())) if err != nil { return nil, errors.Wrapf(err, "failed to open object with id %v", id) } @@ -70,7 +70,7 @@ func (sr *shimRepository) OpenObject(ctx context.Context, id object.ID) (object. // VerifyObject not supported func (sr *shimRepository) VerifyObject(ctx context.Context, id object.ID) ([]content.ID, error) { - return nil, errors.New("not supported") + return nil, errors.New("VerifyObject is not supported") } // Get one or more manifest data that match the specific manifest id @@ -135,12 +135,12 @@ func (sr *shimRepository) ClientOptions() repo.ClientOptions { // Refresh not supported func (sr *shimRepository) Refresh(ctx context.Context) error { - return errors.New("not supported") + return errors.New("Refresh is not supported") } // ContentInfo not supported func (sr *shimRepository) ContentInfo(ctx context.Context, contentID content.ID) (content.Info, error) { - return nil, errors.New("not supported") + return nil, errors.New("ContentInfo is not supported") } // PrefetchContents is not supported by unified repo @@ -150,7 +150,7 @@ func (sr *shimRepository) PrefetchContents(ctx context.Context, contentIDs []con // PrefetchObjects is not supported by unified repo func (sr *shimRepository) PrefetchObjects(ctx context.Context, objectIDs []object.ID, hint string) ([]content.ID, error) { - return nil, errors.New("not supported") + return nil, errors.New("PrefetchObjects is not supported") } // UpdateDescription is not supported by unified repo @@ -159,7 +159,7 @@ func (sr *shimRepository) UpdateDescription(d string) { // NewWriter is not supported by unified repo func (sr *shimRepository) NewWriter(ctx context.Context, option repo.WriteSessionOptions) (context.Context, repo.RepositoryWriter, error) { - return nil, nil, errors.New("not supported") + return nil, nil, errors.New("NewWriter is not supported") } // Close will close unified repo @@ -174,6 +174,7 @@ func (sr *shimRepository) NewObjectWriter(ctx context.Context, option object.Wri opt.Prefix = udmrepo.ID(option.Prefix) opt.FullPath = "" opt.AccessMode = udmrepo.ObjectDataAccessModeFile + opt.AsyncWrites = option.AsyncWrites if strings.HasPrefix(option.Description, "DIR:") { opt.DataType = udmrepo.ObjectDataTypeMetadata @@ -208,11 +209,22 @@ func (sr *shimRepository) DeleteManifest(ctx context.Context, id manifest.ID) er return sr.udmRepo.DeleteManifest(ctx, udmrepo.ID(id)) } +func (sr *shimRepository) ReplaceManifests(ctx context.Context, labels map[string]string, payload interface{}) (manifest.ID, error) { + return manifest.ID(""), errors.New("ReplaceManifests is not supported") +} + // Flush all the unifited repository data func (sr *shimRepository) Flush(ctx context.Context) error { return sr.udmRepo.Flush(ctx) } +func (sr *shimRepository) ConcatenateObjects(ctx context.Context, objectIDs []object.ID) (object.ID, error) { + return object.ID{}, errors.New("ConcatenateObjects is not supported") +} + +func (sr *shimRepository) OnSuccessfulFlush(callback repo.RepositoryWriterCallback) { +} + // Flush all the unifited repository data func (sr *shimObjectReader) Read(p []byte) (n int, err error) { return sr.repoReader.Read(p) @@ -240,13 +252,31 @@ func (sr *shimObjectWriter) Write(p []byte) (n int, err error) { // Periodically called to preserve the state of data written to the repo so far. func (sr *shimObjectWriter) Checkpoint() (object.ID, error) { id, err := sr.repoWriter.Checkpoint() - return object.ID(id), err + if err != nil { + return object.ID{}, err + } + + objID, err := object.ParseID(string(id)) + if err != nil { + return object.ID{}, errors.Wrapf(err, "error to parse object ID from %v", id) + } + + return objID, err } // Result returns the object's unified identifier after the write completes. func (sr *shimObjectWriter) Result() (object.ID, error) { id, err := sr.repoWriter.Result() - return object.ID(id), err + if err != nil { + return object.ID{}, err + } + + objID, err := object.ParseID(string(id)) + if err != nil { + return object.ID{}, errors.Wrapf(err, "error to parse object ID from %v", id) + } + + return objID, err } // Close closes the repository and releases all resources. diff --git a/pkg/uploader/kopia/snapshot.go b/pkg/uploader/kopia/snapshot.go index 17a6ac39c..369316187 100644 --- a/pkg/uploader/kopia/snapshot.go +++ b/pkg/uploader/kopia/snapshot.go @@ -44,9 +44,7 @@ import ( ) // All function mainly used to make testing more convenient -var treeForSourceFunc = policy.TreeForSource var applyRetentionPolicyFunc = policy.ApplyRetentionPolicy -var setPolicyFunc = policy.SetPolicy var saveSnapshotFunc = snapshot.SaveSnapshot var loadSnapshotFunc = snapshot.LoadSnapshot @@ -72,24 +70,17 @@ func newOptionalBool(b bool) *policy.OptionalBool { } // setupDefaultPolicy set default policy for kopia -func setupDefaultPolicy(ctx context.Context, rep repo.RepositoryWriter, sourceInfo snapshot.SourceInfo) error { - return setPolicyFunc(ctx, rep, sourceInfo, &policy.Policy{ - RetentionPolicy: policy.RetentionPolicy{ - KeepLatest: newOptionalInt(math.MaxInt32), - }, - CompressionPolicy: policy.CompressionPolicy{ - CompressorName: "none", - }, - UploadPolicy: policy.UploadPolicy{ - MaxParallelFileReads: newOptionalInt(runtime.NumCPU()), - }, - SchedulingPolicy: policy.SchedulingPolicy{ - Manual: true, - }, - ErrorHandlingPolicy: policy.ErrorHandlingPolicy{ - IgnoreUnknownTypes: newOptionalBool(true), - }, - }) +func setupDefaultPolicy() *policy.Tree { + defaultPolicy := *policy.DefaultPolicy + + defaultPolicy.RetentionPolicy.KeepLatest = newOptionalInt(math.MaxInt32) + defaultPolicy.CompressionPolicy.CompressorName = "none" + defaultPolicy.UploadPolicy.MaxParallelFileReads = newOptionalInt(runtime.NumCPU()) + defaultPolicy.UploadPolicy.ParallelUploadAboveSize = nil + defaultPolicy.SchedulingPolicy.Manual = true + defaultPolicy.ErrorHandlingPolicy.IgnoreUnknownTypes = newOptionalBool(true) + + return policy.BuildTree(nil, &defaultPolicy) } // Backup backup specific sourcePath and update progress @@ -198,17 +189,9 @@ func SnapshotSource( } } - var manifest *snapshot.Manifest - if err := setupDefaultPolicy(ctx, rep, sourceInfo); err != nil { - return "", 0, errors.Wrapf(err, "unable to set policy for si %v", sourceInfo) - } + policyTree := setupDefaultPolicy() - policyTree, err := treeForSourceFunc(ctx, rep, sourceInfo) - if err != nil { - return "", 0, errors.Wrapf(err, "unable to create policy getter for si %v", sourceInfo) - } - - manifest, err = u.Upload(ctx, rootDir, policyTree, sourceInfo, previous...) + manifest, err := u.Upload(ctx, rootDir, policyTree, sourceInfo, previous...) if err != nil { return "", 0, errors.Wrapf(err, "Failed to upload the kopia snapshot for si %v", sourceInfo) } @@ -254,7 +237,7 @@ func reportSnapshotStatus(manifest *snapshot.Manifest, policyTree *policy.Tree) // findPreviousSnapshotManifest returns the list of previous snapshots for a given source, including // last complete snapshot following it. -func findPreviousSnapshotManifest(ctx context.Context, rep repo.Repository, sourceInfo snapshot.SourceInfo, snapshotTags map[string]string, noLaterThan *time.Time) ([]*snapshot.Manifest, error) { +func findPreviousSnapshotManifest(ctx context.Context, rep repo.Repository, sourceInfo snapshot.SourceInfo, snapshotTags map[string]string, noLaterThan *fs.UTCTimestamp) ([]*snapshot.Manifest, error) { man, err := snapshot.ListSnapshots(ctx, rep, sourceInfo) if err != nil { return nil, err @@ -313,6 +296,11 @@ func Restore(ctx context.Context, rep repo.RepositoryWriter, progress *Progress, IgnorePermissionErrors: true, } + err = output.Init(ctx) + if err != nil { + return 0, 0, errors.Wrap(err, "error to init output") + } + stat, err := restore.Entry(kopiaCtx, rep, output, rootEntry, restore.Options{ Parallel: runtime.NumCPU(), RestoreDirEntryAtDepth: math.MaxInt32, diff --git a/pkg/uploader/kopia/snapshot_test.go b/pkg/uploader/kopia/snapshot_test.go index a42078cc8..ea927dd44 100644 --- a/pkg/uploader/kopia/snapshot_test.go +++ b/pkg/uploader/kopia/snapshot_test.go @@ -50,8 +50,6 @@ func injectSnapshotFuncs() *snapshotMockes { repoWriterMock: &repomocks.RepositoryWriter{}, } - setPolicyFunc = s.policyMock.SetPolicy - treeForSourceFunc = s.policyMock.TreeForSource applyRetentionPolicyFunc = s.policyMock.ApplyRetentionPolicy loadSnapshotFunc = s.snapshotMock.LoadSnapshot saveSnapshotFunc = s.snapshotMock.SaveSnapshot @@ -141,19 +139,6 @@ func TestSnapshotSource(t *testing.T) { }, notError: false, }, - { - name: "failed to set policy", - args: []mockArgs{ - {methodName: "LoadSnapshot", returns: []interface{}{manifest, nil}}, - {methodName: "SaveSnapshot", returns: []interface{}{manifest.ID, nil}}, - {methodName: "TreeForSource", returns: []interface{}{nil, nil}}, - {methodName: "ApplyRetentionPolicy", returns: []interface{}{nil, nil}}, - {methodName: "SetPolicy", returns: []interface{}{errors.New("failed to set policy")}}, - {methodName: "Upload", returns: []interface{}{manifest, nil}}, - {methodName: "Flush", returns: []interface{}{nil}}, - }, - notError: false, - }, { name: "failed to upload snapshot", args: []mockArgs{ diff --git a/pkg/util/logging/kopia_log.go b/pkg/util/logging/kopia_log.go index d70784d23..82dffc056 100644 --- a/pkg/util/logging/kopia_log.go +++ b/pkg/util/logging/kopia_log.go @@ -21,6 +21,8 @@ import ( "github.com/kopia/kopia/repo/logging" "github.com/sirupsen/logrus" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" ) type kopiaLog struct { @@ -32,59 +34,129 @@ type kopiaLog struct { // call the logger in the context to write logs func SetupKopiaLog(ctx context.Context, logger logrus.FieldLogger) context.Context { return logging.WithLogger(ctx, func(module string) logging.Logger { - return &kopiaLog{ - module: module, - logger: logger, - } + kpLog := &kopiaLog{module, logger} + return zap.New(kpLog).Sugar() }) } -func (kl *kopiaLog) Debugf(msg string, args ...interface{}) { - logger := kl.logger.WithField("logModule", kl.getLogModule()) - logger.Debugf(msg, args...) +// Enabled decides whether a given logging level is enabled when logging a message +func (kl *kopiaLog) Enabled(level zapcore.Level) bool { + entry := kl.logger.WithField("null", "null") + switch level { + case zapcore.DebugLevel: + return (entry.Logger.GetLevel() >= logrus.DebugLevel) + case zapcore.InfoLevel: + return (entry.Logger.GetLevel() >= logrus.InfoLevel) + case zapcore.WarnLevel: + return (entry.Logger.GetLevel() >= logrus.WarnLevel) + case zapcore.ErrorLevel: + return (entry.Logger.GetLevel() >= logrus.ErrorLevel) + case zapcore.DPanicLevel: + return (entry.Logger.GetLevel() >= logrus.PanicLevel) + case zapcore.PanicLevel: + return (entry.Logger.GetLevel() >= logrus.PanicLevel) + case zapcore.FatalLevel: + return (entry.Logger.GetLevel() >= logrus.FatalLevel) + default: + return false + } } -func (kl *kopiaLog) Debugw(msg string, keyValuePairs ...interface{}) { - logger := kl.logger.WithField("logModule", kl.getLogModule()) - logger.WithFields(getLogFields(keyValuePairs...)).Debug(msg) +// With adds structured context to the Core. +func (kl *kopiaLog) With(fields []zapcore.Field) zapcore.Core { + copied := kl.logrusFields(fields) + + return &kopiaLog{ + module: kl.module, + logger: kl.logger.WithFields(copied), + } } -func (kl *kopiaLog) Infof(msg string, args ...interface{}) { - logger := kl.logger.WithField("logModule", kl.getLogModule()) - logger.Infof(msg, args...) +// Check determines whether the supplied Entry should be logged. If the entry +// should be logged, the Core adds itself to the CheckedEntry and returns the result. +func (kl *kopiaLog) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry { + if kl.Enabled(ent.Level) { + return ce.AddCore(ent, kl) + } + + return ce } -func (kl *kopiaLog) Warnf(msg string, args ...interface{}) { - logger := kl.logger.WithField("logModule", kl.getLogModule()) - logger.Warnf(msg, args...) +// Write serializes the Entry and any Fields supplied at the log site and writes them to their destination. +func (kl *kopiaLog) Write(ent zapcore.Entry, fields []zapcore.Field) error { + copied := kl.logrusFieldsForWrite(ent, fields) + logger := kl.logger.WithFields(copied) + + switch ent.Level { + case zapcore.DebugLevel: + logger.Debug(ent.Message) + case zapcore.InfoLevel: + logger.Info(ent.Message) + case zapcore.WarnLevel: + logger.Warn(ent.Message) + case zapcore.ErrorLevel: + // We see Kopia generates error logs for some normal cases or non-critical + // cases. So Kopia's error logs are regarded as warning logs so that they don't + // affect Velero's workflow. + logger.Warn(ent.Message) + case zapcore.DPanicLevel: + logger.Panic(ent.Message) + case zapcore.PanicLevel: + logger.Panic(ent.Message) + case zapcore.FatalLevel: + logger.Fatal(ent.Message) + } + + return nil } -// We see Kopia generates error logs for some normal cases or non-critical -// cases. So Kopia's error logs are regarded as warning logs so that they don't -// affect Velero's workflow. -func (kl *kopiaLog) Errorf(msg string, args ...interface{}) { - logger := kl.logger.WithFields(logrus.Fields{ - "logModule": kl.getLogModule(), - "sublevel": "error", - }) +// Sync flushes buffered logs (if any). +func (kl *kopiaLog) Sync() error { + return nil +} - logger.Warnf(msg, args...) +func (kl *kopiaLog) logrusFields(fields []zapcore.Field) logrus.Fields { + if fields == nil { + return logrus.Fields{} + } + + m := zapcore.NewMapObjectEncoder() + for _, field := range fields { + field.AddTo(m) + } + + return m.Fields } func (kl *kopiaLog) getLogModule() string { return "kopia/" + kl.module } -func getLogFields(keyValuePairs ...interface{}) map[string]interface{} { - m := map[string]interface{}{} - for i := 0; i+1 < len(keyValuePairs); i += 2 { - s, ok := keyValuePairs[i].(string) - if !ok { - s = "non-string-key" - } +func (kl *kopiaLog) logrusFieldsForWrite(ent zapcore.Entry, fields []zapcore.Field) logrus.Fields { + copied := kl.logrusFields(fields) - m[s] = keyValuePairs[i+1] + copied["logModule"] = kl.getLogModule() + + if ent.Caller.Function != "" { + copied["function"] = ent.Caller.Function } - return m + path := ent.Caller.FullPath() + if path != "undefined" { + copied["path"] = path + } + + if ent.LoggerName != "" { + copied["logger name"] = ent.LoggerName + } + + if ent.Stack != "" { + copied["stack"] = ent.Stack + } + + if ent.Level == zap.ErrorLevel { + copied["sublevel"] = "error" + } + + return copied } diff --git a/pkg/util/logging/kopia_log_test.go b/pkg/util/logging/kopia_log_test.go index 2498cb418..1180988b8 100644 --- a/pkg/util/logging/kopia_log_test.go +++ b/pkg/util/logging/kopia_log_test.go @@ -19,66 +19,147 @@ package logging import ( "testing" + "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" + + "github.com/vmware-tanzu/velero/pkg/test" ) -func TestGetLogFields(t *testing.T) { +func TestEnabled(t *testing.T) { testCases := []struct { name string - pairs []interface{} - expected map[string]interface{} + level logrus.Level + zapLevel zapcore.Level + expected bool }{ { - name: "normal", - pairs: []interface{}{ - "fake-key1", - "fake-value1", - "fake-key2", - 10, - "fake-key3", - struct{ v int }{v: 10}, + name: "check debug again debug", + level: logrus.DebugLevel, + zapLevel: zapcore.DebugLevel, + expected: true, + }, + { + name: "check debug again info", + level: logrus.InfoLevel, + zapLevel: zapcore.DebugLevel, + expected: false, + }, + { + name: "check info again debug", + level: logrus.DebugLevel, + zapLevel: zapcore.InfoLevel, + expected: true, + }, + { + name: "check info again info", + level: logrus.InfoLevel, + zapLevel: zapcore.InfoLevel, + expected: true, + }, + { + name: "check info again error", + level: logrus.ErrorLevel, + zapLevel: zapcore.InfoLevel, + expected: false, + }, + { + name: "check error again error", + level: logrus.ErrorLevel, + zapLevel: zapcore.ErrorLevel, + expected: true, + }, + { + name: "check panic again error", + level: logrus.ErrorLevel, + zapLevel: zapcore.PanicLevel, + expected: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + log := kopiaLog{ + logger: test.NewLoggerWithLevel(tc.level), + } + m := log.Enabled(tc.zapLevel) + + require.Equal(t, tc.expected, m) + }) + } +} + +func TestWrite(t *testing.T) { + testCases := []struct { + name string + module string + zapEntry zapcore.Entry + zapFields []zapcore.Field + expected logrus.Fields + }{ + { + name: "debug with nil fields", + module: "module-01", + zapEntry: zapcore.Entry{ + Level: zapcore.DebugLevel, }, - expected: map[string]interface{}{ - "fake-key1": "fake-value1", - "fake-key2": 10, - "fake-key3": struct{ v int }{v: 10}, + zapFields: nil, + expected: logrus.Fields{ + "logModule": "kopia/module-01", }, }, { - name: "non string key", - pairs: []interface{}{ - "fake-key1", - "fake-value1", - 10, - 10, - "fake-key3", - struct{ v int }{v: 10}, + name: "error with nil fields", + module: "module-02", + zapEntry: zapcore.Entry{ + Level: zapcore.ErrorLevel, }, - expected: map[string]interface{}{ - "fake-key1": "fake-value1", - "non-string-key": 10, - "fake-key3": struct{ v int }{v: 10}, + zapFields: nil, + expected: logrus.Fields{ + "logModule": "kopia/module-02", + "sublevel": "error", }, }, { - name: "missing value", - pairs: []interface{}{ - "fake-key1", - "fake-value1", - "fake-key2", - 10, - "fake-key3", + name: "info with nil string filed", + module: "module-03", + zapEntry: zapcore.Entry{ + Level: zapcore.InfoLevel, }, - expected: map[string]interface{}{ - "fake-key1": "fake-value1", - "fake-key2": 10, + zapFields: []zapcore.Field{ + { + Key: "key-01", + Type: zapcore.StringType, + String: "value-01", + }, + }, + expected: logrus.Fields{ + "logModule": "kopia/module-03", + "key-01": "value-01", + }, + }, + { + name: "info with logger name", + module: "module-04", + zapEntry: zapcore.Entry{ + Level: zapcore.InfoLevel, + LoggerName: "logger-name-01", + }, + zapFields: nil, + expected: logrus.Fields{ + "logModule": "kopia/module-04", + "logger name": "logger-name-01", }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - m := getLogFields(tc.pairs...) + log := kopiaLog{ + module: tc.module, + logger: test.NewLogger(), + } + m := log.logrusFieldsForWrite(tc.zapEntry, tc.zapFields) require.Equal(t, tc.expected, m) }) diff --git a/site/content/docs/main/api-types/schedule.md b/site/content/docs/main/api-types/schedule.md index 58a4c454b..1bc4a81c3 100644 --- a/site/content/docs/main/api-types/schedule.md +++ b/site/content/docs/main/api-types/schedule.md @@ -94,6 +94,13 @@ spec: matchLabels: app: velero component: server + # Individual object when matched with any of the label selector specified in the set are to be included in the backup. Optional. + # orLabelSelectors as well as labelSelector cannot co-exist, only one of them can be specified in the backup request + orLabelSelectors: + - matchLabels: + app: velero + - matchLabels: + app: data-protection # Whether to snapshot volumes. Valid values are true, false, and null/unset. If unset, Velero performs snapshots as long as # a persistent volume provider is configured for Velero. snapshotVolumes: null diff --git a/site/content/posts/2023-04-26-Velero-1.11.md b/site/content/posts/2023-04-26-Velero-1.11.md new file mode 100644 index 000000000..f39a161af --- /dev/null +++ b/site/content/posts/2023-04-26-Velero-1.11.md @@ -0,0 +1,109 @@ +--- +title: "Velero 1.11: New Actions, New Horizons" +excerpt: In this release, we've grown the team and continue to welcome new members to our community.We're thrilled to have such significant contributions from the community and we're proud to deliver Velero 1.11. +author_name: Orlin Vasilev +slug: Velero-1.11 +categories: ['velero','release'] +image: /img/posts/post-1.11.jpg +# Tag should match author to drive author pages +tags: ['Velero Team', 'Orlin Vasilev', 'Velero Release'] +--- + +We haven't posted for while, but this deserves your attention! +Last week during KubeCon Europe in Amsterdam we released [Velero v1.11](https://github.com/vmware-tanzu/velero/releases/tag/v1.11.0), which brings significant improvements in its functionality, flexibility, and performance. In this blog post, we will discuss the new features and changes that come with Velero v1.11 and how they can benefit users. + +The theme of the v1.11 release is most definitely more flexibility, not only with the features added, but also in terms of the Velero contribution, development, and quality assurance processes. + +In case you missed it we have new Product Manager - [Pradeep Kumar Chaturvedi](https://github.com/pradeepkchaturvedi) and few new contributors from companies like DELL and Microsoft. + +### Full list of changes can be found [here](https://github.com/vmware-tanzu/velero/releases/tag/v1.11.0) + +## Release Highlights + +### BackupItemAction v2 +This feature implements the BackupItemAction v2. BIA v2 has two new methods: Progress() and Cancel() and modifies the Execute() return value. + +The API change is needed to facilitate long-running BackupItemAction plugin actions that may not be complete when the Execute() method returns. This will allow long-running BackupItemAction plugin actions to continue in the background while the Velero moves to the following plugin or the next item. +[https://github.com/vmware-tanzu/velero/pull/5442](https://github.com/vmware-tanzu/velero/pull/5442) + +### RestoreItemAction v2 +This feature implemented the RestoreItemAction v2. RIA v2 has three new methods: Progress(), Cancel(), and AreAdditionalItemsReady(), and it modifies RestoreItemActionExecuteOutput() structure in the RIA return value. + +The Progress() and Cancel() methods are needed to facilitate long-running RestoreItemAction plugin actions that may not be complete when the Execute() method returns. This will allow long-running RestoreItemAction plugin actions to continue in the background while the Velero moves to the following plugin or the next item. The AreAdditionalItemsReady() method is needed to allow plugins to tell Velero to wait until the returned additional items have been restored and are ready for use in the cluster before restoring the current item. + +[https://github.com/vmware-tanzu/velero/pull/5569](https://github.com/vmware-tanzu/velero/pull/5569) + +### Plugin Progress Monitoring +This is intended as a replacement for the previously-approved Upload Progress Monitoring design ([Upload Progress Monitoring](https://github.com/vmware-tanzu/velero/blob/main/design/upload-progress.md)) to expand the supported use cases beyond snapshot upload to include what was previously called Async Backup/Restore Item Actions. + +### Flexible resource policy that can filter volumes to skip in the backup +This feature provides a flexible policy to filter volumes in the backup without requiring patching any labels or annotations to the pods or volumes. This policy is configured as k8s ConfigMap and maintained by the users themselves, and it can be extended to more scenarios in the future. By now, the policy rules out volumes from backup depending on the CSI driver, NFS setting, volume size, and StorageClass setting. Please refer to [Resource policies rules](https://velero.io/docs/v1.11/resource-filtering/#resource-policies) for the policy's ConifgMap format. It is not guaranteed to work on unofficial third-party plugins as it may not follow the existing backup workflow code logic of Velero. + +### Resource Filters that can distinguish cluster scope and namespace scope resources +This feature adds four new resource filters for backup. The new filters are separated into cluster scope and namespace scope. Before this feature, Velero could not filter cluster scope resources precisely. This feature provides the ability and refactors existing resource filter parameters. + +### New parameter in installation to customize the ServiceAccount name +The `velero install` sub-command now includes a new parameter,`--service-account-name`, which allows users to specify the ServiceAccountName for the Velero and node-agent pods. This feature may be particularly useful for users who utilize IRSA (IAM Roles for Service Accounts) in Amazon EKS (Elastic Kubernetes Service)." + +### Add a parameter for setting the Velero server connection with the k8s API server's timeout +In Velero, some code pieces need to communicate with the k8s API server. Before v1.11, these code pieces used hard-code timeout settings. This feature adds a resource-timeout parameter in the velero server binary to make it configurable. + +### Add resource list in the output of the restore describe command +Before this feature, Velero restore didn't have a restored resources list as the Velero backup. It's not convenient for users to learn what is restored. This feature adds the resources list and the handling result of the resources (including created, updated, failed, and skipped). + +### Support JSON format output of backup describe command +Before the Velero v1.11 release, users could not choose Velero's backup describe command's output format. The command output format is friendly for human reading, but it's not a structured output, and it's not easy for other programs to get information from it. Velero v1.11 adds a JSON format output for the backup describe command. + +### Refactor controllers with controller-runtime +In v1.11, Backup Controller and Restore controller are refactored with controller-runtime. Till v1.11, all Velero controllers use the controller-runtime framework. + +### Runtime and dependencies +To fix CVEs and keep pace with Golang, Velero made changes as follows: +* Bump Golang runtime to v1.19.8. +* Bump several dependent libraries to new versions. +* Compile Restic (v0.15.0) with Golang v1.19.8 instead of packaging the official binary. + + +## Breaking changes +* The Velero CSI plugin now determines whether to restore Volume's data from snapshots on the restore's restorePVs setting. Before v1.11, the CSI plugin doesn't check the restorePVs parameter setting. + + +## Limitations/Known issues +* The Flexible resource policy that can filter volumes to skip in the backup is not guaranteed to work on unofficial third-party plugins because the plugins may not follow the existing backup workflow code logic of Velero. The ConfigMap used as the policy is supposed to be maintained by users. + + +### Improving Developer Experience + +As we continue to grow our community of contributors, we want to lower the barrier to entry for making contributions to the Velero project. +We’ve made huge improvements to the developer experience during this release cycle by introducing Tilt to the developer workflow. +Using Tilt enables developers to make changes to Velero and its plugins, and have those changes automatically built and deployed to your cluster. +This removes the need for any manual building or pushing of images, and provides a faster and much simpler workflow. +Our Tilt configuration also enables contributors to more easily debug the Velero process using Delve, which has integrations with many editors and IDEs. +If you would like to try it out, please see our [documentation](https://velero.io/docs/v1.11/tilt/). + +### Looking Forward + +We have more exciting additions and improvements to Velero earmarked for future releases. +For v1.12, we would like to have your input again [here](https://github.com/vmware-tanzu/velero/discussions/6217) +See our [1.12 RoadMap](https://github.com/vmware-tanzu/velero/wiki/1.12-Roadmap) for the complete list. + + +### Join the Community and Make Velero Better + +Velero is better because of our contributors and maintainers. +It is because of you that we can bring great software to the community. +Please join us during our online [community meetings every Tuesday](https://hackmd.io/Jq6F5zqZR7S80CeDWUklkA?view) and catch up with past meetings on YouTube on the [Velero Community Meetings playlist](https://www.youtube.com/watch?v=nc48ocI-6go&list=PL7bmigfV0EqQRysvqvqOtRNk4L5S7uqwM). + +You can always find the latest project information at [velero.io](https://velero.io). +Look for issues on GitHub marked ["Good first issue"](https://github.com/vmware-tanzu/velero/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+first+issue%22) or ["Help wanted"](https://github.com/vmware-tanzu/velero/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+wanted%22+) if you want to roll up your sleeves and write some code with us. + +For opportunities to help and be helped, visit our [Community Support Q&A on GitHub](https://github.com/vmware-tanzu/velero/discussions/categories/community-support-q-a). + +You can chat with us on [Kubernetes Slack in the #velero channel](https://kubernetes.slack.com/messages/C6VCGP4MT) and follow us on Twitter at [@projectvelero](https://twitter.com/projectvelero). + +Orlin Vasilev +Velero Community Lead + + +Photo by [Markus Spiske on Unsplash](https://unsplash.com/@markusspiske?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) + diff --git a/site/layouts/_default/footer.html b/site/layouts/_default/footer.html index 61a2d4da2..3db0d96df 100644 --- a/site/layouts/_default/footer.html +++ b/site/layouts/_default/footer.html @@ -43,6 +43,7 @@ © {{ now.Format "2006" }} {{ .Site.Params.Author }}. +
This Website Does Not Use Cookies or Other Tracking Technology diff --git a/site/static/img/posts/post-1.11.jpg b/site/static/img/posts/post-1.11.jpg new file mode 100644 index 000000000..211efadd2 Binary files /dev/null and b/site/static/img/posts/post-1.11.jpg differ diff --git a/test/e2e/backup/backup.go b/test/e2e/backup/backup.go index 7532c22e4..68e0c55cd 100644 --- a/test/e2e/backup/backup.go +++ b/test/e2e/backup/backup.go @@ -58,7 +58,7 @@ func BackupRestoreTest(useVolumeSnapshots bool) { var err error flag.Parse() UUIDgen, err = uuid.NewRandom() - kibishiiNamespace = "kibishii-workload" + UUIDgen.String() + kibishiiNamespace = "k-" + UUIDgen.String() Expect(err).To(Succeed()) }) @@ -87,6 +87,9 @@ func BackupRestoreTest(useVolumeSnapshots bool) { if useVolumeSnapshots { //Install node agent also veleroCfg.UseNodeAgent = useVolumeSnapshots + // DefaultVolumesToFsBackup should be mutually exclusive with useVolumeSnapshots in installation CLI, + // otherwise DefaultVolumesToFsBackup need to be set to false in backup CLI when taking volume snapshot + // Make sure DefaultVolumesToFsBackup was set to false in backup CLI veleroCfg.DefaultVolumesToFsBackup = useVolumeSnapshots } else { veleroCfg.DefaultVolumesToFsBackup = !useVolumeSnapshots @@ -121,9 +124,11 @@ func BackupRestoreTest(useVolumeSnapshots bool) { if veleroCfg.InstallVelero { if useVolumeSnapshots { veleroCfg.DefaultVolumesToFsBackup = !useVolumeSnapshots - } else { + } else { //FS volume backup // Install VolumeSnapshots also veleroCfg.UseVolumeSnapshots = !useVolumeSnapshots + // DefaultVolumesToFsBackup is false in installation CLI here, + // so must set DefaultVolumesToFsBackup to be true in backup CLI come after veleroCfg.DefaultVolumesToFsBackup = useVolumeSnapshots } @@ -144,7 +149,7 @@ func BackupRestoreTest(useVolumeSnapshots bool) { Expect(CreateSecretFromFiles(context.TODO(), *veleroCfg.ClientToInstallVelero, veleroCfg.VeleroNamespace, secretName, files)).To(Succeed()) // Create additional BSL using credential - additionalBsl := fmt.Sprintf("bsl-%s", UUIDgen) + additionalBsl := "add-bsl" Expect(VeleroCreateBackupLocation(context.TODO(), veleroCfg.VeleroCLI, veleroCfg.VeleroNamespace, @@ -169,7 +174,8 @@ func BackupRestoreTest(useVolumeSnapshots bool) { restoreName = fmt.Sprintf("%s-%s", restoreName, UUIDgen) } veleroCfg.ProvideSnapshotsVolumeParam = !provideSnapshotVolumesParmInBackup - Expect(RunKibishiiTests(veleroCfg, backupName, restoreName, bsl, kibishiiNamespace, useVolumeSnapshots, !useVolumeSnapshots)).To(Succeed(), + workloadNmespace := kibishiiNamespace + bsl + Expect(RunKibishiiTests(veleroCfg, backupName, restoreName, bsl, workloadNmespace, useVolumeSnapshots, !useVolumeSnapshots)).To(Succeed(), "Failed to successfully backup and restore Kibishii namespace using BSL %s", bsl) } }) diff --git a/test/e2e/backups/deletion.go b/test/e2e/backups/deletion.go index aaef02652..df90b7014 100644 --- a/test/e2e/backups/deletion.go +++ b/test/e2e/backups/deletion.go @@ -150,7 +150,7 @@ func runBackupDeletionTests(client TestClient, veleroCfg VeleroConfig, backupNam } var snapshotCheckPoint SnapshotCheckPoint if useVolumeSnapshots { - snapshotCheckPoint, err = GetSnapshotCheckPoint(client, veleroCfg, 2, deletionTest, backupName, KibishiiPodNameList) + snapshotCheckPoint, err = GetSnapshotCheckPoint(client, veleroCfg, 2, deletionTest, backupName, KibishiiPVCNameList) Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") err = SnapshotsShouldBeCreatedInCloud(veleroCfg.CloudProvider, veleroCfg.CloudCredentialsFile, veleroCfg.BSLBucket, bslConfig, diff --git a/test/e2e/backups/sync_backups.go b/test/e2e/backups/sync_backups.go index c8b230528..1bfea90b2 100644 --- a/test/e2e/backups/sync_backups.go +++ b/test/e2e/backups/sync_backups.go @@ -50,8 +50,6 @@ func (b *SyncBackups) Init() { func BackupsSyncTest() { test := new(SyncBackups) - ctx, ctxCancel := context.WithCancel(context.Background()) - defer ctxCancel() var ( err error ) @@ -79,7 +77,8 @@ func BackupsSyncTest() { It("Backups in object storage should be synced to a new Velero successfully", func() { test.Init() - + ctx, ctxCancel := context.WithTimeout(context.Background(), 30*time.Minute) + defer ctxCancel() By(fmt.Sprintf("Prepare workload as target to backup by creating namespace %s namespace", test.testNS)) Expect(CreateNamespace(ctx, *VeleroCfg.ClientToInstallVelero, test.testNS)).To(Succeed(), fmt.Sprintf("Failed to create %s namespace", test.testNS)) @@ -114,12 +113,13 @@ func BackupsSyncTest() { }) By("Check all backups in object storage are synced to Velero", func() { - Expect(test.IsBackupsSynced()).To(Succeed(), fmt.Sprintf("Failed to sync backup %s from object storage", test.backupName)) + Expect(test.IsBackupsSynced(ctx, ctxCancel)).To(Succeed(), fmt.Sprintf("Failed to sync backup %s from object storage", test.backupName)) }) }) It("Deleted backups in object storage are synced to be deleted in Velero", func() { test.Init() + ctx, ctxCancel := context.WithTimeout(context.Background(), 30*time.Minute) defer ctxCancel() By(fmt.Sprintf("Prepare workload as target to backup by creating namespace in %s namespace", test.testNS), func() { Expect(CreateNamespace(ctx, *VeleroCfg.ClientToInstallVelero, test.testNS)).To(Succeed(), @@ -163,8 +163,7 @@ func BackupsSyncTest() { }) } -func (b *SyncBackups) IsBackupsSynced() error { - ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) +func (b *SyncBackups) IsBackupsSynced(ctx context.Context, ctxCancel context.CancelFunc) error { defer ctxCancel() return WaitForBackupToBeCreated(ctx, VeleroCfg.VeleroCLI, b.backupName, 10*time.Minute) } diff --git a/test/e2e/backups/ttl.go b/test/e2e/backups/ttl.go index 3dc0ec0e2..92a563199 100644 --- a/test/e2e/backups/ttl.go +++ b/test/e2e/backups/ttl.go @@ -56,8 +56,6 @@ func (b *TTL) Init() { } func TTLTest() { - ctx, contextCancel := context.WithCancel(context.Background()) - defer contextCancel() var err error var veleroCfg VeleroConfig useVolumeSnapshots := true @@ -65,8 +63,6 @@ func TTLTest() { veleroCfg = VeleroCfg client := *veleroCfg.ClientToInstallVelero - //Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests") - BeforeEach(func() { flag.Parse() veleroCfg = VeleroCfg @@ -87,12 +83,16 @@ func TTLTest() { if veleroCfg.InstallVelero { Expect(VeleroUninstall(context.Background(), veleroCfg.VeleroCLI, veleroCfg.VeleroNamespace)).To(Succeed()) } + ctx, ctxCancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer ctxCancel() Expect(DeleteNamespace(ctx, client, test.testNS, false)).To(Succeed(), fmt.Sprintf("Failed to delete the namespace %s", test.testNS)) } }) It("Backups in object storage should be synced to a new Velero successfully", func() { test.Init() + ctx, ctxCancel := context.WithTimeout(context.Background(), 1*time.Hour) + defer ctxCancel() By(fmt.Sprintf("Prepare workload as target to backup by creating namespace %s namespace", test.testNS), func() { Expect(CreateNamespace(ctx, client, test.testNS)).To(Succeed(), fmt.Sprintf("Failed to create %s namespace", test.testNS)) @@ -128,7 +128,7 @@ func TTLTest() { test.testNS, 2)).To(Succeed()) }) } - snapshotCheckPoint, err = GetSnapshotCheckPoint(client, veleroCfg, 2, test.testNS, test.backupName, KibishiiPodNameList) + snapshotCheckPoint, err = GetSnapshotCheckPoint(client, veleroCfg, 2, test.testNS, test.backupName, KibishiiPVCNameList) Expect(err).NotTo(HaveOccurred(), "Fail to get Azure CSI snapshot checkpoint") Expect(SnapshotsShouldBeCreatedInCloud(veleroCfg.CloudProvider, diff --git a/test/e2e/basic/api-group/enable_api_group_versions.go b/test/e2e/basic/api-group/enable_api_group_versions.go index cc6290546..ba1d73f6e 100644 --- a/test/e2e/basic/api-group/enable_api_group_versions.go +++ b/test/e2e/basic/api-group/enable_api_group_versions.go @@ -100,7 +100,7 @@ func APIGropuVersionsTest() { DeleteBackups(context.Background(), *veleroCfg.ClientToInstallVelero) }) if veleroCfg.InstallVelero { - By("Uninstall Velero", func() { + By("Uninstall Velero in api group version case", func() { Expect(VeleroUninstall(ctx, veleroCfg.VeleroCLI, veleroCfg.VeleroNamespace)).NotTo(HaveOccurred()) }) } diff --git a/test/e2e/basic/namespace-mapping.go b/test/e2e/basic/namespace-mapping.go index 0de695305..a15680d0d 100644 --- a/test/e2e/basic/namespace-mapping.go +++ b/test/e2e/basic/namespace-mapping.go @@ -23,12 +23,13 @@ type NamespaceMapping struct { const NamespaceBaseName string = "ns-mp-" -var OneNamespaceMappingResticTest func() = TestFunc(&NamespaceMapping{TestCase: TestCase{NSBaseName: NamespaceBaseName, NSIncluded: &[]string{NamespaceBaseName + "1"}, UseVolumeSnapshots: false}}) -var MultiNamespacesMappingResticTest func() = TestFunc(&NamespaceMapping{TestCase: TestCase{NSBaseName: NamespaceBaseName, NSIncluded: &[]string{NamespaceBaseName + "1", NamespaceBaseName + "2"}, UseVolumeSnapshots: false}}) -var OneNamespaceMappingSnapshotTest func() = TestFunc(&NamespaceMapping{TestCase: TestCase{NSBaseName: NamespaceBaseName, NSIncluded: &[]string{NamespaceBaseName + "1"}, UseVolumeSnapshots: true}}) -var MultiNamespacesMappingSnapshotTest func() = TestFunc(&NamespaceMapping{TestCase: TestCase{NSBaseName: NamespaceBaseName, NSIncluded: &[]string{NamespaceBaseName + "1", NamespaceBaseName + "2"}, UseVolumeSnapshots: true}}) +var OneNamespaceMappingResticTest func() = TestFunc(&NamespaceMapping{TestCase: TestCase{NamespacesTotal: 1, UseVolumeSnapshots: false}}) +var MultiNamespacesMappingResticTest func() = TestFunc(&NamespaceMapping{TestCase: TestCase{NamespacesTotal: 2, UseVolumeSnapshots: false}}) +var OneNamespaceMappingSnapshotTest func() = TestFunc(&NamespaceMapping{TestCase: TestCase{NamespacesTotal: 1, UseVolumeSnapshots: true}}) +var MultiNamespacesMappingSnapshotTest func() = TestFunc(&NamespaceMapping{TestCase: TestCase{NamespacesTotal: 2, UseVolumeSnapshots: true}}) func (n *NamespaceMapping) Init() error { + n.TestCase.Init() n.VeleroCfg = VeleroCfg n.Client = *n.VeleroCfg.ClientToInstallVelero n.VeleroCfg.UseVolumeSnapshots = n.UseVolumeSnapshots @@ -38,29 +39,28 @@ func (n *NamespaceMapping) Init() error { if n.UseVolumeSnapshots { backupType = "snapshot" } + n.CaseBaseName = "ns-mp-" + n.UUIDgen + var mappedNS string + var mappedNSList []string + n.NSIncluded = &[]string{} + for nsNum := 0; nsNum < n.NamespacesTotal; nsNum++ { + createNSName := fmt.Sprintf("%s-%00000d", n.CaseBaseName, nsNum) + *n.NSIncluded = append(*n.NSIncluded, createNSName) + mappedNS = mappedNS + createNSName + ":" + createNSName + "-mapped" + mappedNSList = append(mappedNSList, createNSName+"-mapped") + mappedNS = mappedNS + "," + } + mappedNS = strings.TrimRightFunc(mappedNS, func(r rune) bool { + return r == ',' + }) + n.TestMsg = &TestMSG{ Desc: fmt.Sprintf("Restore namespace %s with namespace mapping by %s test", *n.NSIncluded, backupType), FailedMSG: "Failed to restore with namespace mapping", Text: fmt.Sprintf("should restore namespace %s with namespace mapping by %s", *n.NSIncluded, backupType), } - return nil -} - -func (n *NamespaceMapping) StartRun() error { - var mappedNS string - var mappedNSList []string - - for index, ns := range *n.NSIncluded { - mappedNS = mappedNS + ns + ":" + ns + UUIDgen.String() - mappedNSList = append(mappedNSList, ns+UUIDgen.String()) - if index+1 != len(*n.NSIncluded) { - mappedNS = mappedNS + "," - } - n.BackupName = n.BackupName + ns - n.RestoreName = n.RestoreName + ns - } - n.BackupName = n.BackupName + UUIDgen.String() - n.RestoreName = n.RestoreName + UUIDgen.String() + n.BackupName = "backup-" + n.CaseBaseName + n.RestoreName = "restore-" + n.CaseBaseName n.MappedNamespaceList = mappedNSList fmt.Println(mappedNSList) @@ -81,6 +81,7 @@ func (n *NamespaceMapping) StartRun() error { } return nil } + func (n *NamespaceMapping) CreateResources() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 60*time.Minute) defer ctxCancel() @@ -115,3 +116,19 @@ func (n *NamespaceMapping) Verify() error { } return nil } + +func (n *NamespaceMapping) Clean() error { + if !n.VeleroCfg.Debug { + if err := DeleteStorageClass(context.Background(), n.Client, "kibishii-storage-class"); err != nil { + return err + } + for _, ns := range n.MappedNamespaceList { + if err := DeleteNamespace(context.Background(), n.Client, ns, false); err != nil { + return err + } + } + + return n.GetTestCase().Clean() + } + return nil +} diff --git a/test/e2e/basic/nodeport.go b/test/e2e/basic/nodeport.go index 3faa518bc..b0b5d9bf3 100644 --- a/test/e2e/basic/nodeport.go +++ b/test/e2e/basic/nodeport.go @@ -3,6 +3,7 @@ package basic import ( "context" "fmt" + "strings" "time" . "github.com/onsi/ginkgo" @@ -20,40 +21,42 @@ import ( type NodePort struct { TestCase - replica int32 labels map[string]string - containers *[]v1.Container serviceName string - serviceSpec *v1.ServiceSpec + namespaceToCollision []string nodePort int32 - namespaceToCollision string - namespace string } const NodeportBaseName string = "nodeport-" -var NodePortTest func() = TestFunc(&NodePort{namespace: NodeportBaseName + "1", TestCase: TestCase{NSBaseName: NodeportBaseName}}) +var NodePortTest func() = TestFunc(&NodePort{}) func (n *NodePort) Init() error { + n.TestCase.Init() + n.CaseBaseName = NodeportBaseName + n.UUIDgen + n.BackupName = "backup-" + n.CaseBaseName + n.RestoreName = "restore-" + n.CaseBaseName + n.serviceName = "nginx-service-" + n.CaseBaseName n.VeleroCfg = VeleroCfg n.Client = *n.VeleroCfg.ClientToInstallVelero - n.NSBaseName = NodeportBaseName + n.NamespacesTotal = 1 n.TestMsg = &TestMSG{ - Desc: fmt.Sprintf("Nodeport preservation"), + Desc: "Nodeport preservation", FailedMSG: "Failed to restore with nodeport preservation", - Text: fmt.Sprintf("Nodeport can be preserved or omit during restore"), + Text: "Nodeport can be preserved or omit during restore", + } + + n.labels = map[string]string{"app": "nginx"} + n.NSIncluded = &[]string{} + for nsNum := 0; nsNum < n.NamespacesTotal; nsNum++ { + createNSName := fmt.Sprintf("%s-%00000d", n.CaseBaseName, nsNum) + n.namespaceToCollision = append(n.namespaceToCollision, createNSName+"tmp") + *n.NSIncluded = append(*n.NSIncluded, createNSName) } - n.BackupName = "backup-nodeport-" + UUIDgen.String() - n.RestoreName = "restore-" + UUIDgen.String() - n.serviceName = "nginx-service-" + UUIDgen.String() - n.labels = map[string]string{"app": "nginx"} - return nil -} -func (n *NodePort) StartRun() error { n.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", n.BackupName, - "--include-namespaces", n.namespace, "--wait", + "--include-namespaces", strings.Join(*n.NSIncluded, ","), "--wait", } n.RestoreArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "restore", @@ -66,37 +69,40 @@ func (n *NodePort) CreateResources() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 60*time.Minute) defer ctxCancel() - By(fmt.Sprintf("Creating service %s in namespaces %s ......\n", n.serviceName, n.namespace), func() { - Expect(CreateNamespace(ctx, n.Client, n.namespace)).To(Succeed(), fmt.Sprintf("Failed to create namespace %s", n.namespace)) - Expect(createServiceWithNodeport(ctx, n.Client, n.namespace, n.serviceName, n.labels, 0)).To(Succeed(), fmt.Sprintf("Failed to create service %s", n.serviceName)) - service, err := GetService(ctx, n.Client, n.namespace, n.serviceName) - Expect(err).To(Succeed()) - Expect(len(service.Spec.Ports)).To(Equal(1)) - n.nodePort = service.Spec.Ports[0].NodePort - _, err = GetAllService(ctx) - Expect(err).To(Succeed(), "fail to get service") - }) + for _, ns := range *n.NSIncluded { + By(fmt.Sprintf("Creating service %s in namespaces %s ......\n", n.serviceName, ns), func() { + Expect(CreateNamespace(ctx, n.Client, ns)).To(Succeed(), fmt.Sprintf("Failed to create namespace %s", ns)) + Expect(createServiceWithNodeport(ctx, n.Client, ns, n.serviceName, n.labels, 0)).To(Succeed(), fmt.Sprintf("Failed to create service %s", n.serviceName)) + service, err := GetService(ctx, n.Client, ns, n.serviceName) + Expect(err).To(Succeed()) + Expect(len(service.Spec.Ports)).To(Equal(1)) + n.nodePort = service.Spec.Ports[0].NodePort + _, err = GetAllService(ctx) + Expect(err).To(Succeed(), "fail to get service") + }) + } return nil } func (n *NodePort) Destroy() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 60*time.Minute) defer ctxCancel() - By(fmt.Sprintf("Start to destroy namespace %s......", n.NSBaseName), func() { - Expect(CleanupNamespacesWithPoll(ctx, n.Client, NodeportBaseName)).To(Succeed(), - fmt.Sprintf("Failed to delete namespace %s", n.NSBaseName)) - Expect(WaitForServiceDelete(n.Client, n.namespace, n.serviceName, false)).To(Succeed(), "fail to delete service") - _, err := GetAllService(ctx) - Expect(err).To(Succeed(), "fail to get service") - }) + for i, ns := range *n.NSIncluded { + By(fmt.Sprintf("Start to destroy namespace %s......", n.CaseBaseName), func() { + Expect(CleanupNamespacesWithPoll(ctx, n.Client, NodeportBaseName)).To(Succeed(), + fmt.Sprintf("Failed to delete namespace %s", n.CaseBaseName)) + Expect(WaitForServiceDelete(n.Client, ns, n.serviceName, false)).To(Succeed(), "fail to delete service") + _, err := GetAllService(ctx) + Expect(err).To(Succeed(), "fail to get service") + }) - n.namespaceToCollision = NodeportBaseName + "tmp" - By(fmt.Sprintf("Creating a new service which has the same nodeport as backed up service has in a new namespaces for nodeport collision ...%s\n", n.namespaceToCollision), func() { - Expect(CreateNamespace(ctx, n.Client, n.namespaceToCollision)).To(Succeed(), fmt.Sprintf("Failed to create namespace %s", n.namespaceToCollision)) - Expect(createServiceWithNodeport(ctx, n.Client, n.namespaceToCollision, n.serviceName, n.labels, n.nodePort)).To(Succeed(), fmt.Sprintf("Failed to create service %s", n.serviceName)) - _, err := GetAllService(ctx) - Expect(err).To(Succeed(), "fail to get service") - }) + By(fmt.Sprintf("Creating a new service which has the same nodeport as backed up service has in a new namespaces for nodeport collision ...%s\n", n.namespaceToCollision[i]), func() { + Expect(CreateNamespace(ctx, n.Client, n.namespaceToCollision[i])).To(Succeed(), fmt.Sprintf("Failed to create namespace %s", n.namespaceToCollision[i])) + Expect(createServiceWithNodeport(ctx, n.Client, n.namespaceToCollision[i], n.serviceName, n.labels, n.nodePort)).To(Succeed(), fmt.Sprintf("Failed to create service %s", n.serviceName)) + _, err := GetAllService(ctx) + Expect(err).To(Succeed(), "fail to get service") + }) + } return nil } @@ -134,37 +140,38 @@ func (n *NodePort) Restore() error { }) }) - By(fmt.Sprintf("Delete service %s by deleting namespace %s", n.serviceName, n.namespace), func() { - service, err := GetService(ctx, n.Client, n.namespace, n.serviceName) - Expect(err).To(Succeed()) - Expect(len(service.Spec.Ports)).To(Equal(1)) - fmt.Println(service.Spec.Ports) - Expect(DeleteNamespace(ctx, n.Client, n.namespace, true)).To(Succeed()) - }) - - By(fmt.Sprintf("Start to delete service %s in namespace %s ......", n.serviceName, n.namespaceToCollision), func() { - Expect(WaitForServiceDelete(n.Client, n.namespaceToCollision, n.serviceName, true)).To(Succeed(), "fail to delete service") - _, err := GetAllService(ctx) - Expect(err).To(Succeed(), "fail to get service") - }) - - args = n.RestoreArgs - args = append(args[:index], append([]string{restoreName2}, args[index:]...)...) - args = append(args, "--preserve-nodeports=true") - By(fmt.Sprintf("Start to restore %s with nodeports preservation ......", restoreName2), func() { - Expect(VeleroRestoreExec(ctx, n.VeleroCfg.VeleroCLI, n.VeleroCfg.VeleroNamespace, - restoreName2, args, velerov1api.RestorePhaseCompleted)).To(Succeed(), func() string { - RunDebug(context.Background(), n.VeleroCfg.VeleroCLI, n.VeleroCfg.VeleroNamespace, "", restoreName2) - return "Fail to restore workload" + for i, ns := range *n.NSIncluded { + By(fmt.Sprintf("Delete service %s by deleting namespace %s", n.serviceName, ns), func() { + service, err := GetService(ctx, n.Client, ns, n.serviceName) + Expect(err).To(Succeed()) + Expect(len(service.Spec.Ports)).To(Equal(1)) + fmt.Println(service.Spec.Ports) + Expect(DeleteNamespace(ctx, n.Client, ns, true)).To(Succeed()) }) - }) - By(fmt.Sprintf("Verify service %s was restore successfully with the origin nodeport.", n.namespace), func() { - service, err := GetService(ctx, n.Client, n.namespace, n.serviceName) - Expect(err).To(Succeed()) - Expect(len(service.Spec.Ports)).To(Equal(1)) - Expect(service.Spec.Ports[0].NodePort).To(Equal(n.nodePort)) - }) + By(fmt.Sprintf("Start to delete service %s in namespace %s ......", n.serviceName, n.namespaceToCollision[i]), func() { + Expect(WaitForServiceDelete(n.Client, n.namespaceToCollision[i], n.serviceName, true)).To(Succeed(), "fail to delete service") + _, err := GetAllService(ctx) + Expect(err).To(Succeed(), "fail to get service") + }) + args = n.RestoreArgs + args = append(args[:index], append([]string{restoreName2}, args[index:]...)...) + args = append(args, "--preserve-nodeports=true") + By(fmt.Sprintf("Start to restore %s with nodeports preservation ......", restoreName2), func() { + Expect(VeleroRestoreExec(ctx, n.VeleroCfg.VeleroCLI, n.VeleroCfg.VeleroNamespace, + restoreName2, args, velerov1api.RestorePhaseCompleted)).To(Succeed(), func() string { + RunDebug(context.Background(), n.VeleroCfg.VeleroCLI, n.VeleroCfg.VeleroNamespace, "", restoreName2) + return "Fail to restore workload" + }) + }) + + By(fmt.Sprintf("Verify service %s was restore successfully with the origin nodeport.", ns), func() { + service, err := GetService(ctx, n.Client, ns, n.serviceName) + Expect(err).To(Succeed()) + Expect(len(service.Spec.Ports)).To(Equal(1)) + Expect(service.Spec.Ports[0].NodePort).To(Equal(n.nodePort)) + }) + } return nil } diff --git a/test/e2e/basic/pvc-selected-node-changing.go b/test/e2e/basic/pvc-selected-node-changing.go index 9e085addf..29cd03201 100644 --- a/test/e2e/basic/pvc-selected-node-changing.go +++ b/test/e2e/basic/pvc-selected-node-changing.go @@ -31,24 +31,22 @@ type PVCSelectedNodeChanging struct { ann string } -const PSNCBaseName string = "psnc-" - -var PVCSelectedNodeChangingTest func() = TestFunc(&PVCSelectedNodeChanging{ - namespace: PSNCBaseName + "1", TestCase: TestCase{NSBaseName: PSNCBaseName}}) +var PVCSelectedNodeChangingTest func() = TestFunc(&PVCSelectedNodeChanging{}) func (p *PVCSelectedNodeChanging) Init() error { + p.TestCase.Init() + p.CaseBaseName = "psnc-" + p.UUIDgen + p.namespace = p.CaseBaseName + p.mappedNS = p.namespace + "-mapped" p.VeleroCfg = VeleroCfg p.Client = *p.VeleroCfg.ClientToInstallVelero - p.NSBaseName = PSNCBaseName - p.namespace = p.NSBaseName + UUIDgen.String() - p.mappedNS = p.namespace + "-mapped" p.TestMsg = &TestMSG{ Desc: "Changing PVC node selector", FailedMSG: "Failed to changing PVC node selector", Text: "Change node selectors of persistent volume claims during restores", } - p.BackupName = "backup-sc-" + UUIDgen.String() - p.RestoreName = "restore-" + UUIDgen.String() + p.BackupName = "backup-" + p.CaseBaseName + p.RestoreName = "restore-" + p.CaseBaseName p.labels = map[string]string{"velero.io/plugin-config": "", "velero.io/change-pvc-node-selector": "RestoreItemAction"} p.configmaptName = "change-pvc-node-selector-config" @@ -56,12 +54,6 @@ func (p *PVCSelectedNodeChanging) Init() error { p.podName = "pod-1" p.pvcName = "pvc-1" p.ann = "volume.kubernetes.io/selected-node" - return nil -} - -func (p *PVCSelectedNodeChanging) StartRun() error { - p.BackupName = p.BackupName + "backup-" + UUIDgen.String() - p.RestoreName = p.RestoreName + "restore-" + UUIDgen.String() p.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", p.BackupName, "--include-namespaces", p.namespace, @@ -73,6 +65,7 @@ func (p *PVCSelectedNodeChanging) StartRun() error { } return nil } + func (p *PVCSelectedNodeChanging) CreateResources() error { By(fmt.Sprintf("Create namespace %s", p.namespace), func() { Expect(CreateNamespace(context.Background(), p.Client, p.namespace)).To(Succeed(), @@ -116,9 +109,9 @@ func (p *PVCSelectedNodeChanging) CreateResources() error { } func (p *PVCSelectedNodeChanging) Destroy() error { - By(fmt.Sprintf("Start to destroy namespace %s......", p.NSBaseName), func() { - Expect(CleanupNamespacesWithPoll(context.Background(), p.Client, p.NSBaseName)).To(Succeed(), - fmt.Sprintf("Failed to delete namespace %s", p.NSBaseName)) + By(fmt.Sprintf("Start to destroy namespace %s......", p.CaseBaseName), func() { + Expect(CleanupNamespacesWithPoll(context.Background(), p.Client, p.CaseBaseName)).To(Succeed(), + fmt.Sprintf("Failed to delete namespace %s", p.CaseBaseName)) }) return nil } @@ -152,3 +145,13 @@ func (p *PVCSelectedNodeChanging) Verify() error { }) return nil } + +func (p *PVCSelectedNodeChanging) Clean() error { + if !p.VeleroCfg.Debug { + p.TestCase.Clean() + By(fmt.Sprintf("Clean namespace with prefix %s after test", p.mappedNS), func() { + CleanupNamespaces(context.Background(), p.Client, p.mappedNS) + }) + } + return nil +} diff --git a/test/e2e/basic/resources-check/namespaces.go b/test/e2e/basic/resources-check/namespaces.go index 3acf299a1..1ac6f3455 100644 --- a/test/e2e/basic/resources-check/namespaces.go +++ b/test/e2e/basic/resources-check/namespaces.go @@ -19,11 +19,9 @@ package basic import ( "context" "fmt" - "math/rand" "strings" "time" - "github.com/google/uuid" "github.com/pkg/errors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -40,11 +38,11 @@ type MultiNSBackup struct { } func (m *MultiNSBackup) Init() error { - rand.Seed(time.Now().UnixNano()) - UUIDgen, _ = uuid.NewRandom() - m.BackupName = "backup-" + UUIDgen.String() - m.RestoreName = "restore-" + UUIDgen.String() - m.NSBaseName = "nstest-" + UUIDgen.String() + m.TestCase.Init() + m.CaseBaseName = "nstest-" + m.UUIDgen + m.BackupName = "backup-" + m.CaseBaseName + m.RestoreName = "restore-" + m.CaseBaseName + m.VeleroCfg = VeleroCfg m.Client = *m.VeleroCfg.ClientToInstallVelero m.NSExcluded = &[]string{} @@ -64,10 +62,7 @@ func (m *MultiNSBackup) Init() error { FailedMSG: "Failed to successfully backup and restore multiple namespaces", } } - return nil -} -func (m *MultiNSBackup) StartRun() error { // Currently it's hard to build a large list of namespaces to include and wildcards do not work so instead // we will exclude all of the namespaces that existed prior to the test from the backup namespaces, err := m.Client.ClientGo.CoreV1().Namespaces().List(context.Background(), v1.ListOptions{}) @@ -100,7 +95,7 @@ func (m *MultiNSBackup) CreateResources() error { "ns-test": "true", } for nsNum := 0; nsNum < m.NamespacesTotal; nsNum++ { - createNSName := fmt.Sprintf("%s-%00000d", m.NSBaseName, nsNum) + createNSName := fmt.Sprintf("%s-%00000d", m.CaseBaseName, nsNum) if err := CreateNamespaceWithLabel(ctx, m.Client, createNSName, labels); err != nil { return errors.Wrapf(err, "Failed to create namespace %s", createNSName) } @@ -113,7 +108,7 @@ func (m *MultiNSBackup) Verify() error { defer ctxCancel() // Verify that we got back all of the namespaces we created for nsNum := 0; nsNum < m.NamespacesTotal; nsNum++ { - checkNSName := fmt.Sprintf("%s-%00000d", m.NSBaseName, nsNum) + checkNSName := fmt.Sprintf("%s-%00000d", m.CaseBaseName, nsNum) checkNS, err := GetNamespace(ctx, m.Client, checkNSName) if err != nil { return errors.Wrapf(err, "Could not retrieve test namespace %s", checkNSName) @@ -127,7 +122,7 @@ func (m *MultiNSBackup) Verify() error { func (m *MultiNSBackup) Destroy() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 60*time.Minute) defer ctxCancel() - err := CleanupNamespaces(ctx, m.Client, m.NSBaseName) + err := CleanupNamespaces(ctx, m.Client, m.CaseBaseName) if err != nil { return errors.Wrap(err, "Could cleanup retrieve namespaces") } diff --git a/test/e2e/basic/resources-check/namespaces_annotation.go b/test/e2e/basic/resources-check/namespaces_annotation.go index 3d41e5a59..4a8061ac5 100644 --- a/test/e2e/basic/resources-check/namespaces_annotation.go +++ b/test/e2e/basic/resources-check/namespaces_annotation.go @@ -19,11 +19,9 @@ package basic import ( "context" "fmt" - "math/rand" "strings" "time" - "github.com/google/uuid" "github.com/pkg/errors" . "github.com/vmware-tanzu/velero/test/e2e" @@ -36,17 +34,17 @@ type NSAnnotationCase struct { } func (n *NSAnnotationCase) Init() error { - rand.Seed(time.Now().UnixNano()) - UUIDgen, _ = uuid.NewRandom() - n.BackupName = "backup-namespace-annotations" + UUIDgen.String() - n.RestoreName = "restore-namespace-annotations" + UUIDgen.String() - n.NSBaseName = "namespace-annotations-" + UUIDgen.String() + n.TestCase.Init() + n.CaseBaseName = "namespace-annotations-" + n.UUIDgen + n.BackupName = "backup-" + n.CaseBaseName + n.RestoreName = "restore-" + n.CaseBaseName + n.NamespacesTotal = 1 n.NSIncluded = &[]string{} n.VeleroCfg = VeleroCfg n.Client = *n.VeleroCfg.ClientToInstallVelero for nsNum := 0; nsNum < n.NamespacesTotal; nsNum++ { - createNSName := fmt.Sprintf("%s-%00000d", n.NSBaseName, nsNum) + createNSName := fmt.Sprintf("%s-%00000d", n.CaseBaseName, nsNum) *n.NSIncluded = append(*n.NSIncluded, createNSName) } n.TestMsg = &TestMSG{ @@ -71,8 +69,8 @@ func (n *NSAnnotationCase) CreateResources() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 60*time.Minute) defer ctxCancel() for nsNum := 0; nsNum < n.NamespacesTotal; nsNum++ { - createNSName := fmt.Sprintf("%s-%00000d", n.NSBaseName, nsNum) - createAnnotationName := fmt.Sprintf("annotation-%s-%00000d", n.NSBaseName, nsNum) + createNSName := fmt.Sprintf("%s-%00000d", n.CaseBaseName, nsNum) + createAnnotationName := fmt.Sprintf("annotation-%s-%00000d", n.CaseBaseName, nsNum) if err := CreateNamespaceWithAnnotation(ctx, n.Client, createNSName, map[string]string{"testAnnotation": createAnnotationName}); err != nil { return errors.Wrapf(err, "Failed to create namespace %s", createNSName) } @@ -84,8 +82,8 @@ func (n *NSAnnotationCase) Verify() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 60*time.Minute) defer ctxCancel() for nsNum := 0; nsNum < n.NamespacesTotal; nsNum++ { - checkNSName := fmt.Sprintf("%s-%00000d", n.NSBaseName, nsNum) - checkAnnoName := fmt.Sprintf("annotation-%s-%00000d", n.NSBaseName, nsNum) + checkNSName := fmt.Sprintf("%s-%00000d", n.CaseBaseName, nsNum) + checkAnnoName := fmt.Sprintf("annotation-%s-%00000d", n.CaseBaseName, nsNum) checkNS, err := GetNamespace(ctx, n.Client, checkNSName) if err != nil { diff --git a/test/e2e/basic/resources-check/rbac.go b/test/e2e/basic/resources-check/rbac.go index b4eaf7d37..7c147ad69 100644 --- a/test/e2e/basic/resources-check/rbac.go +++ b/test/e2e/basic/resources-check/rbac.go @@ -35,11 +35,9 @@ package basic import ( "context" "fmt" - "math/rand" "strings" "time" - "github.com/google/uuid" "github.com/pkg/errors" . "github.com/vmware-tanzu/velero/test/e2e" @@ -52,15 +50,14 @@ type RBACCase struct { } func (r *RBACCase) Init() error { - rand.Seed(time.Now().UnixNano()) - UUIDgen, _ = uuid.NewRandom() - r.BackupName = "backup-rbac" + UUIDgen.String() - r.RestoreName = "restore-rbac" + UUIDgen.String() - r.NSBaseName = "rabc-" + UUIDgen.String() + r.TestCase.Init() + r.CaseBaseName = "rabc-" + r.UUIDgen + r.BackupName = "backup-" + r.CaseBaseName + r.RestoreName = "restore-" + r.CaseBaseName r.NamespacesTotal = 1 r.NSIncluded = &[]string{} for nsNum := 0; nsNum < r.NamespacesTotal; nsNum++ { - createNSName := fmt.Sprintf("%s-%00000d", r.NSBaseName, nsNum) + createNSName := fmt.Sprintf("%s-%00000d", r.CaseBaseName, nsNum) *r.NSIncluded = append(*r.NSIncluded, createNSName) } r.TestMsg = &TestMSG{ @@ -87,18 +84,18 @@ func (r *RBACCase) CreateResources() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) defer ctxCancel() for nsNum := 0; nsNum < r.NamespacesTotal; nsNum++ { - createNSName := fmt.Sprintf("%s-%00000d", r.NSBaseName, nsNum) + createNSName := fmt.Sprintf("%s-%00000d", r.CaseBaseName, nsNum) fmt.Printf("Creating namespaces ...%s\n", createNSName) if err := CreateNamespace(ctx, r.Client, createNSName); err != nil { return errors.Wrapf(err, "Failed to create namespace %s", createNSName) } - serviceAccountName := fmt.Sprintf("service-account-%s-%00000d", r.NSBaseName, nsNum) + serviceAccountName := fmt.Sprintf("service-account-%s-%00000d", r.CaseBaseName, nsNum) fmt.Printf("Creating service account ...%s\n", createNSName) if err := CreateServiceAccount(ctx, r.Client, createNSName, serviceAccountName); err != nil { return errors.Wrapf(err, "Failed to create service account %s", serviceAccountName) } - clusterRoleName := fmt.Sprintf("clusterrole-%s-%00000d", r.NSBaseName, nsNum) - clusterRoleBindingName := fmt.Sprintf("clusterrolebinding-%s-%00000d", r.NSBaseName, nsNum) + clusterRoleName := fmt.Sprintf("clusterrole-%s-%00000d", r.CaseBaseName, nsNum) + clusterRoleBindingName := fmt.Sprintf("clusterrolebinding-%s-%00000d", r.CaseBaseName, nsNum) if err := CreateRBACWithBindingSA(ctx, r.Client, createNSName, serviceAccountName, clusterRoleName, clusterRoleBindingName); err != nil { return errors.Wrapf(err, "Failed to create cluster role %s with role binding %s", clusterRoleName, clusterRoleBindingName) } @@ -110,10 +107,10 @@ func (r *RBACCase) Verify() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) defer ctxCancel() for nsNum := 0; nsNum < r.NamespacesTotal; nsNum++ { - checkNSName := fmt.Sprintf("%s-%00000d", r.NSBaseName, nsNum) - checkServiceAccountName := fmt.Sprintf("service-account-%s-%00000d", r.NSBaseName, nsNum) - checkClusterRoleName := fmt.Sprintf("clusterrole-%s-%00000d", r.NSBaseName, nsNum) - checkClusterRoleBindingName := fmt.Sprintf("clusterrolebinding-%s-%00000d", r.NSBaseName, nsNum) + checkNSName := fmt.Sprintf("%s-%00000d", r.CaseBaseName, nsNum) + checkServiceAccountName := fmt.Sprintf("service-account-%s-%00000d", r.CaseBaseName, nsNum) + checkClusterRoleName := fmt.Sprintf("clusterrole-%s-%00000d", r.CaseBaseName, nsNum) + checkClusterRoleBindingName := fmt.Sprintf("clusterrolebinding-%s-%00000d", r.CaseBaseName, nsNum) checkNS, err := GetNamespace(ctx, r.Client, checkNSName) if err != nil { return errors.Wrapf(err, "Could not retrieve test namespace %s", checkNSName) @@ -169,18 +166,18 @@ func (r *RBACCase) Destroy() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) defer ctxCancel() //cleanup clusterrole - err := CleanupClusterRole(ctx, r.Client, r.NSBaseName) + err := CleanupClusterRole(ctx, r.Client, r.CaseBaseName) if err != nil { return errors.Wrap(err, "Could not cleanup clusterroles") } //cleanup cluster rolebinding - err = CleanupClusterRoleBinding(ctx, r.Client, r.NSBaseName) + err = CleanupClusterRoleBinding(ctx, r.Client, r.CaseBaseName) if err != nil { return errors.Wrap(err, "Could not cleanup clusterrolebindings") } - err = CleanupNamespacesWithPoll(ctx, r.Client, r.NSBaseName) + err = CleanupNamespacesWithPoll(ctx, r.Client, r.CaseBaseName) if err != nil { return errors.Wrap(err, "Could cleanup retrieve namespaces") } @@ -189,5 +186,8 @@ func (r *RBACCase) Destroy() error { } func (r *RBACCase) Clean() error { - return r.Destroy() + if !r.VeleroCfg.Debug { + return r.Destroy() + } + return nil } diff --git a/test/e2e/basic/resources-check/resources_check.go b/test/e2e/basic/resources-check/resources_check.go index 4fc32d568..9db5198bc 100644 --- a/test/e2e/basic/resources-check/resources_check.go +++ b/test/e2e/basic/resources-check/resources_check.go @@ -33,15 +33,14 @@ limitations under the License. package basic import ( - . "github.com/vmware-tanzu/velero/test/e2e" . "github.com/vmware-tanzu/velero/test/e2e/test" ) func GetResourcesCheckTestCases() []VeleroBackupRestoreTest { return []VeleroBackupRestoreTest{ - &NSAnnotationCase{TestCase{VeleroCfg: VeleroCfg}}, - &MultiNSBackup{IsScalTest: false, TestCase: TestCase{VeleroCfg: VeleroCfg}}, - &RBACCase{TestCase{VeleroCfg: VeleroCfg}}, + &NSAnnotationCase{}, + &MultiNSBackup{IsScalTest: false}, + &RBACCase{}, } } diff --git a/test/e2e/basic/storage-class-changing.go b/test/e2e/basic/storage-class-changing.go index ee931588a..a25bfc81b 100644 --- a/test/e2e/basic/storage-class-changing.go +++ b/test/e2e/basic/storage-class-changing.go @@ -31,15 +31,17 @@ type StorageClasssChanging struct { const SCCBaseName string = "scc-" -var StorageClasssChangingTest func() = TestFunc(&StorageClasssChanging{ - namespace: SCCBaseName + "1", TestCase: TestCase{NSBaseName: SCCBaseName}}) +var StorageClasssChangingTest func() = TestFunc(&StorageClasssChanging{}) func (s *StorageClasssChanging) Init() error { + s.TestCase.Init() + s.CaseBaseName = SCCBaseName + s.UUIDgen + s.namespace = s.CaseBaseName + s.BackupName = "backup-" + s.CaseBaseName + s.RestoreName = "restore-" + s.CaseBaseName + s.mappedNS = s.namespace + "-mapped" s.VeleroCfg = VeleroCfg s.Client = *s.VeleroCfg.ClientToInstallVelero - s.NSBaseName = SCCBaseName - s.namespace = s.NSBaseName + UUIDgen.String() - s.mappedNS = s.namespace + "-mapped" s.TestMsg = &TestMSG{ Desc: "Changing PV/PVC Storage Classes", FailedMSG: "Failed to changing PV/PVC Storage Classes", @@ -49,17 +51,14 @@ func (s *StorageClasssChanging) Init() error { s.BackupName = "backup-sc-" + UUIDgen.String() s.RestoreName = "restore-" + UUIDgen.String() s.srcStorageClass = "default" - s.desStorageClass = "e2e-storage-class" + s.desStorageClass = StorageClassName s.labels = map[string]string{"velero.io/change-storage-class": "RestoreItemAction", "velero.io/plugin-config": ""} s.data = map[string]string{s.srcStorageClass: s.desStorageClass} s.configmaptName = "change-storage-class-config" s.volume = "volume-1" s.podName = "pod-1" - return nil -} -func (s *StorageClasssChanging) StartRun() error { s.BackupName = s.BackupName + "backup-" + UUIDgen.String() s.RestoreName = s.RestoreName + "restore-" + UUIDgen.String() s.BackupArgs = []string{ @@ -109,9 +108,9 @@ func (s *StorageClasssChanging) Destroy() error { fmt.Sprintf("PV storage %s is not as expected %s", pv.Spec.StorageClassName, s.srcStorageClass)) }) - By(fmt.Sprintf("Start to destroy namespace %s......", s.NSBaseName), func() { - Expect(CleanupNamespacesWithPoll(ctx, s.Client, s.NSBaseName)).To(Succeed(), - fmt.Sprintf("Failed to delete namespace %s", s.NSBaseName)) + By(fmt.Sprintf("Start to destroy namespace %s......", s.CaseBaseName), func() { + Expect(CleanupNamespacesWithPoll(ctx, s.Client, s.CaseBaseName)).To(Succeed(), + fmt.Sprintf("Failed to delete namespace %s", s.CaseBaseName)) }) return nil } @@ -147,3 +146,14 @@ func (s *StorageClasssChanging) Verify() error { }) return nil } + +func (s *StorageClasssChanging) Clean() error { + ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) + defer ctxCancel() + if !s.VeleroCfg.Debug { + DeleteConfigmap(s.Client.ClientGo, s.VeleroCfg.VeleroNamespace, s.configmaptName) + DeleteStorageClass(ctx, s.Client, s.desStorageClass) + s.TestCase.Clean() + } + return nil +} diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 2dfd3820b..f4e1a4ddf 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -84,7 +84,7 @@ func init() { } -var _ = Describe("[APIGroup][Common] Velero tests with various CRD API group versions", APIGropuVersionsTest) +var _ = Describe("[APIGroup][APIVersion] Velero tests with various CRD API group versions", APIGropuVersionsTest) var _ = Describe("[APIGroup][APIExtensions] CRD of apiextentions v1beta1 should be B/R successfully from cluster(k8s version < 1.22) to cluster(k8s version >= 1.22)", APIExtensionsVersionsTest) // Test backup and restore of Kibishi using restic diff --git a/test/e2e/migration/migration.go b/test/e2e/migration/migration.go index 1511cc307..481d52a95 100644 --- a/test/e2e/migration/migration.go +++ b/test/e2e/migration/migration.go @@ -208,7 +208,7 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version) snapshotCheckPoint.NamespaceBackedUp = migrationNamespace By("Snapshot should be created in cloud object store", func() { snapshotCheckPoint, err := GetSnapshotCheckPoint(*veleroCfg.DefaultClient, veleroCfg, 2, - migrationNamespace, backupName, KibishiiPodNameList) + migrationNamespace, backupName, KibishiiPVCNameList) Expect(err).NotTo(HaveOccurred(), "Fail to get snapshot checkpoint") Expect(SnapshotsShouldBeCreatedInCloud(veleroCfg.CloudProvider, veleroCfg.CloudCredentialsFile, veleroCfg.BSLBucket, diff --git a/test/e2e/pkg/client/factory.go b/test/e2e/pkg/client/factory.go index 3a0933af7..75bffae82 100644 --- a/test/e2e/pkg/client/factory.go +++ b/test/e2e/pkg/client/factory.go @@ -32,6 +32,7 @@ import ( "k8s.io/client-go/rest" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" ) @@ -154,6 +155,7 @@ func (f *factory) KubebuilderClient() (kbclient.Client, error) { scheme := runtime.NewScheme() velerov1api.AddToScheme(scheme) + velerov2alpha1api.AddToScheme(scheme) k8scheme.AddToScheme(scheme) apiextv1beta1.AddToScheme(scheme) apiextv1.AddToScheme(scheme) diff --git a/test/e2e/pv-backup/pv-backup-filter.go b/test/e2e/pv-backup/pv-backup-filter.go index a144a7a7e..03dda7d96 100644 --- a/test/e2e/pv-backup/pv-backup-filter.go +++ b/test/e2e/pv-backup/pv-backup-filter.go @@ -31,30 +31,22 @@ var OptInPVBackupTest func() = TestFunc(&PVBackupFiltering{annotation: OPT_IN_AN var OptOutPVBackupTest func() = TestFunc(&PVBackupFiltering{annotation: OPT_OUT_ANN, id: "opt-out"}) func (p *PVBackupFiltering) Init() error { + p.TestCase.Init() + p.CaseBaseName = "pv-filter-" + p.UUIDgen + p.BackupName = "backup-" + p.CaseBaseName + p.id + p.RestoreName = "restore-" + p.CaseBaseName + p.id p.VeleroCfg = VeleroCfg p.Client = *p.VeleroCfg.ClientToInstallVelero p.VeleroCfg.UseVolumeSnapshots = false p.VeleroCfg.UseNodeAgent = true - p.NSBaseName = "ns" - p.NSIncluded = &[]string{fmt.Sprintf("%s-%s-%d", p.NSBaseName, p.id, 1), fmt.Sprintf("%s-%s-%d", p.NSBaseName, p.id, 2)} + p.NSIncluded = &[]string{fmt.Sprintf("%s-%s-%d", p.CaseBaseName, p.id, 1), fmt.Sprintf("%s-%s-%d", p.CaseBaseName, p.id, 2)} p.TestMsg = &TestMSG{ Desc: "Backup PVs filtering by opt-in/opt-out annotation", FailedMSG: "Failed to PVs filtering by opt-in/opt-out annotation", Text: fmt.Sprintf("Should backup PVs in namespace %s according to annotation %s", *p.NSIncluded, p.annotation), } - return nil -} -func (p *PVBackupFiltering) StartRun() error { - ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) - defer ctxCancel() - err := InstallStorageClass(ctx, fmt.Sprintf("testdata/storage-class/%s.yaml", VeleroCfg.CloudProvider)) - if err != nil { - return err - } - p.BackupName = p.BackupName + "backup-" + p.id + "-" + UUIDgen.String() - p.RestoreName = p.RestoreName + "restore-" + p.id + "-" + UUIDgen.String() p.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", p.BackupName, "--include-namespaces", strings.Join(*p.NSIncluded, ","), @@ -72,9 +64,16 @@ func (p *PVBackupFiltering) StartRun() error { } return nil } + func (p *PVBackupFiltering) CreateResources() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) defer ctxCancel() + + err := InstallStorageClass(ctx, fmt.Sprintf("testdata/storage-class/%s.yaml", VeleroCfg.CloudProvider)) + if err != nil { + return errors.Wrapf(err, "failed to install storage class for pv backup filtering test") + } + for _, ns := range *p.NSIncluded { By(fmt.Sprintf("Create namespaces %s for workload\n", ns), func() { Expect(CreateNamespace(ctx, p.Client, ns)).To(Succeed(), fmt.Sprintf("Failed to create namespace %s", ns)) @@ -99,7 +98,7 @@ func (p *PVBackupFiltering) CreateResources() error { podName := fmt.Sprintf("pod-%d", i) pods = append(pods, podName) By(fmt.Sprintf("Create pod %s in namespace %s", podName, ns), func() { - pod, err := CreatePod(p.Client, ns, podName, "e2e-storage-class", "", volumes, nil, nil) + pod, err := CreatePod(p.Client, ns, podName, StorageClassName, "", volumes, nil, nil) Expect(err).To(Succeed()) ann := map[string]string{ p.annotation: volumesToAnnotation, diff --git a/test/e2e/resource-filtering/base.go b/test/e2e/resource-filtering/base.go index 1d8ce9026..1e3fc81e0 100644 --- a/test/e2e/resource-filtering/base.go +++ b/test/e2e/resource-filtering/base.go @@ -19,10 +19,8 @@ package filtering import ( "context" "fmt" - "math/rand" "time" - "github.com/google/uuid" "github.com/pkg/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -43,8 +41,8 @@ var testInBackup = FilteringCase{IsTestInBackup: true} var testInRestore = FilteringCase{IsTestInBackup: false} func (f *FilteringCase) Init() error { - rand.Seed(time.Now().UnixNano()) - UUIDgen, _ = uuid.NewRandom() + f.TestCase.Init() + f.replica = int32(2) f.labels = map[string]string{"resourcefiltering": "true"} f.labelSelector = "resourcefiltering" @@ -69,23 +67,14 @@ func (f *FilteringCase) CreateResources() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) defer ctxCancel() for nsNum := 0; nsNum < f.NamespacesTotal; nsNum++ { - namespace := fmt.Sprintf("%s-%00000d", f.NSBaseName, nsNum) + namespace := fmt.Sprintf("%s-%00000d", f.CaseBaseName, nsNum) fmt.Printf("Creating resources in namespace ...%s\n", namespace) if err := CreateNamespace(ctx, f.Client, namespace); err != nil { return errors.Wrapf(err, "Failed to create namespace %s", namespace) } - serviceAccountName := "default" - // wait until the service account is created before patch the image pull secret - if err := WaitUntilServiceAccountCreated(ctx, f.Client, namespace, serviceAccountName, 10*time.Minute); err != nil { - return errors.Wrapf(err, "failed to wait the service account %q created under the namespace %q", serviceAccountName, namespace) - } - // add the image pull secret to avoid the image pull limit issue of Docker Hub - if err := PatchServiceAccountWithImagePullSecret(ctx, f.Client, namespace, serviceAccountName, VeleroCfg.RegistryCredentialFile); err != nil { - return errors.Wrapf(err, "failed to patch the service account %q under the namespace %q", serviceAccountName, namespace) - } //Create deployment fmt.Printf("Creating deployment in namespaces ...%s\n", namespace) - deployment := NewDeployment(f.NSBaseName, namespace, f.replica, f.labels, nil).Result() + deployment := NewDeployment(f.CaseBaseName, namespace, f.replica, f.labels, nil).Result() deployment, err := CreateDeployment(f.Client.ClientGo, namespace, deployment) if err != nil { return errors.Wrap(err, fmt.Sprintf("failed to delete the namespace %q", namespace)) @@ -95,7 +84,7 @@ func (f *FilteringCase) CreateResources() error { return errors.Wrap(err, fmt.Sprintf("failed to ensure job completion in namespace: %q", namespace)) } //Create Secret - secretName := f.NSBaseName + secretName := f.CaseBaseName fmt.Printf("Creating secret %s in namespaces ...%s\n", secretName, namespace) _, err = CreateSecret(f.Client.ClientGo, namespace, secretName, f.labels) if err != nil { @@ -106,7 +95,7 @@ func (f *FilteringCase) CreateResources() error { return errors.Wrap(err, fmt.Sprintf("failed to ensure secret completion in namespace: %q", namespace)) } //Create Configmap - configmaptName := f.NSBaseName + configmaptName := f.CaseBaseName fmt.Printf("Creating configmap %s in namespaces ...%s\n", configmaptName, namespace) _, err = CreateConfigMap(f.Client.ClientGo, namespace, configmaptName, f.labels, nil) if err != nil { @@ -124,7 +113,7 @@ func (f *FilteringCase) Verify() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) defer ctxCancel() for nsNum := 0; nsNum < f.NamespacesTotal; nsNum++ { - namespace := fmt.Sprintf("%s-%00000d", f.NSBaseName, nsNum) + namespace := fmt.Sprintf("%s-%00000d", f.CaseBaseName, nsNum) fmt.Printf("Checking resources in namespaces ...%s\n", namespace) //Check namespace checkNS, err := GetNamespace(ctx, f.Client, namespace) @@ -135,7 +124,7 @@ func (f *FilteringCase) Verify() error { return errors.Errorf("Retrieved namespace for %s has name %s instead", namespace, checkNS.Name) } //Check deployment - _, err = GetDeployment(f.Client.ClientGo, namespace, f.NSBaseName) + _, err = GetDeployment(f.Client.ClientGo, namespace, f.CaseBaseName) if err != nil { return errors.Wrap(err, fmt.Sprintf("failed to list deployment in namespace: %q", namespace)) } diff --git a/test/e2e/resource-filtering/exclude_label.go b/test/e2e/resource-filtering/exclude_label.go index b2202cdec..290c46527 100644 --- a/test/e2e/resource-filtering/exclude_label.go +++ b/test/e2e/resource-filtering/exclude_label.go @@ -45,16 +45,17 @@ var ExcludeFromBackupTest func() = TestFunc(&ExcludeFromBackup{testInBackup}) func (e *ExcludeFromBackup) Init() error { e.FilteringCase.Init() - e.BackupName = "backup-exclude-from-backup-" + UUIDgen.String() - e.RestoreName = "restore-" + UUIDgen.String() - e.NSBaseName = "exclude-from-backup-" + UUIDgen.String() + e.CaseBaseName = "exclude-from-backup-" + e.UUIDgen + e.BackupName = "backup-" + e.CaseBaseName + e.RestoreName = "restore-" + e.CaseBaseName + e.TestMsg = &TestMSG{ Desc: "Backup with the label velero.io/exclude-from-backup=true are not included test", Text: "Should not backup resources with the label velero.io/exclude-from-backup=true", FailedMSG: "Failed to backup resources with the label velero.io/exclude-from-backup=true", } for nsNum := 0; nsNum < e.NamespacesTotal; nsNum++ { - createNSName := fmt.Sprintf("%s-%00000d", e.NSBaseName, nsNum) + createNSName := fmt.Sprintf("%s-%00000d", e.CaseBaseName, nsNum) *e.NSIncluded = append(*e.NSIncluded, createNSName) } e.labels = map[string]string{ @@ -64,7 +65,7 @@ func (e *ExcludeFromBackup) Init() error { e.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", e.BackupName, - "--include-namespaces", e.NSBaseName, + "--include-namespaces", e.CaseBaseName, "--default-volumes-to-fs-backup", "--wait", } @@ -78,7 +79,7 @@ func (e *ExcludeFromBackup) Init() error { func (e *ExcludeFromBackup) CreateResources() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) defer ctxCancel() - namespace := e.NSBaseName + namespace := e.CaseBaseName // These 2 labels for resources to be included label1 := map[string]string{ "meaningless-label-resource-to-include": "true", @@ -90,18 +91,9 @@ func (e *ExcludeFromBackup) CreateResources() error { if err := CreateNamespace(ctx, e.Client, namespace); err != nil { return errors.Wrapf(err, "Failed to create namespace %s", namespace) } - serviceAccountName := "default" - // wait until the service account is created before patch the image pull secret - if err := WaitUntilServiceAccountCreated(ctx, e.Client, namespace, serviceAccountName, 10*time.Minute); err != nil { - return errors.Wrapf(err, "failed to wait the service account %q created under the namespace %q", serviceAccountName, namespace) - } - // add the image pull secret to avoid the image pull limit issue of Docker Hub - if err := PatchServiceAccountWithImagePullSecret(ctx, e.Client, namespace, serviceAccountName, VeleroCfg.RegistryCredentialFile); err != nil { - return errors.Wrapf(err, "failed to patch the service account %q under the namespace %q", serviceAccountName, namespace) - } //Create deployment: to be included fmt.Printf("Creating deployment in namespaces ...%s\n", namespace) - deployment := NewDeployment(e.NSBaseName, namespace, e.replica, label2, nil).Result() + deployment := NewDeployment(e.CaseBaseName, namespace, e.replica, label2, nil).Result() deployment, err := CreateDeployment(e.Client.ClientGo, namespace, deployment) if err != nil { return errors.Wrap(err, fmt.Sprintf("failed to delete the namespace %q", namespace)) @@ -111,7 +103,7 @@ func (e *ExcludeFromBackup) CreateResources() error { return errors.Wrap(err, fmt.Sprintf("failed to ensure job completion in namespace: %q", namespace)) } //Create Secret - secretName := e.NSBaseName + secretName := e.CaseBaseName fmt.Printf("Creating secret %s in namespaces ...%s\n", secretName, namespace) _, err = CreateSecret(e.Client.ClientGo, namespace, secretName, e.labels) if err != nil { @@ -122,11 +114,11 @@ func (e *ExcludeFromBackup) CreateResources() error { return errors.Wrap(err, fmt.Sprintf("failed to ensure secret completion in namespace: %q", namespace)) } By(fmt.Sprintf("Checking secret %s should exists in namespaces ...%s\n", secretName, namespace), func() { - _, err = GetSecret(e.Client.ClientGo, namespace, e.NSBaseName) + _, err = GetSecret(e.Client.ClientGo, namespace, e.CaseBaseName) Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("failed to list deployment in namespace: %q", namespace)) }) //Create Configmap: to be included - configmaptName := e.NSBaseName + configmaptName := e.CaseBaseName fmt.Printf("Creating configmap %s in namespaces ...%s\n", configmaptName, namespace) _, err = CreateConfigMap(e.Client.ClientGo, namespace, configmaptName, label1, nil) if err != nil { @@ -142,7 +134,7 @@ func (e *ExcludeFromBackup) CreateResources() error { func (e *ExcludeFromBackup) Verify() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) defer ctxCancel() - namespace := e.NSBaseName + namespace := e.CaseBaseName By(fmt.Sprintf("Checking resources in namespaces ...%s\n", namespace), func() { //Check namespace checkNS, err := GetNamespace(ctx, e.Client, namespace) @@ -150,16 +142,16 @@ func (e *ExcludeFromBackup) Verify() error { Expect(checkNS.Name == namespace).To(Equal(true), fmt.Sprintf("Retrieved namespace for %s has name %s instead", namespace, checkNS.Name)) //Check deployment: should be included - _, err = GetDeployment(e.Client.ClientGo, namespace, e.NSBaseName) + _, err = GetDeployment(e.Client.ClientGo, namespace, e.CaseBaseName) Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("failed to list deployment in namespace: %q", namespace)) //Check secrets: secrets should not be included - _, err = GetSecret(e.Client.ClientGo, namespace, e.NSBaseName) + _, err = GetSecret(e.Client.ClientGo, namespace, e.CaseBaseName) Expect(err).Should(HaveOccurred(), fmt.Sprintf("failed to list deployment in namespace: %q", namespace)) Expect(apierrors.IsNotFound(err)).To(Equal(true)) //Check configmap: should be included - _, err = GetConfigmap(e.Client.ClientGo, namespace, e.NSBaseName) + _, err = GetConfigmap(e.Client.ClientGo, namespace, e.CaseBaseName) Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("failed to list configmap in namespace: %q", namespace)) }) return nil diff --git a/test/e2e/resource-filtering/exclude_namespaces.go b/test/e2e/resource-filtering/exclude_namespaces.go index 05485e8e2..ebe14f6ac 100644 --- a/test/e2e/resource-filtering/exclude_namespaces.go +++ b/test/e2e/resource-filtering/exclude_namespaces.go @@ -50,19 +50,20 @@ var RestoreWithExcludeNamespaces func() = TestFunc(&ExcludeNamespaces{FilteringC func (e *ExcludeNamespaces) Init() error { e.FilteringCase.Init() + e.CaseBaseName = "exclude-namespaces-" + e.UUIDgen e.namespacesExcluded = e.NamespacesTotal / 2 - e.NSBaseName = "exclude-namespaces-" + UUIDgen.String() + if e.IsTestInBackup { - e.BackupName = "backup-exclude-namespaces-" + UUIDgen.String() - e.RestoreName = "restore-" + UUIDgen.String() + e.BackupName = "backup-" + e.CaseBaseName + e.RestoreName = "restore-" + e.UUIDgen e.TestMsg = &TestMSG{ Desc: "Backup resources with exclude namespace test", FailedMSG: "Failed to backup and restore with namespace include", Text: fmt.Sprintf("should not backup %d namespaces of %d", e.namespacesExcluded, e.NamespacesTotal), } } else { - e.BackupName = "backup-" + UUIDgen.String() - e.RestoreName = "restore-exclude-namespaces-" + UUIDgen.String() + e.BackupName = "backup-" + e.UUIDgen + e.RestoreName = "restore-" + e.CaseBaseName e.TestMsg = &TestMSG{ Desc: "Restore resources with exclude namespace test", FailedMSG: "Failed to restore with namespace exclude", @@ -71,7 +72,7 @@ func (e *ExcludeNamespaces) Init() error { } e.nsExcluded = &[]string{} for nsNum := 0; nsNum < e.NamespacesTotal; nsNum++ { - createNSName := fmt.Sprintf("%s-%00000d", e.NSBaseName, nsNum) + createNSName := fmt.Sprintf("%s-%00000d", e.CaseBaseName, nsNum) if nsNum < e.namespacesExcluded { *e.nsExcluded = append(*e.nsExcluded, createNSName) } else { @@ -112,7 +113,7 @@ func (e *ExcludeNamespaces) CreateResources() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) defer ctxCancel() for nsNum := 0; nsNum < e.NamespacesTotal; nsNum++ { - createNSName := fmt.Sprintf("%s-%00000d", e.NSBaseName, nsNum) + createNSName := fmt.Sprintf("%s-%00000d", e.CaseBaseName, nsNum) fmt.Printf("Creating namespaces ...%s\n", createNSName) if err := CreateNamespace(ctx, e.Client, createNSName); err != nil { return errors.Wrapf(err, "Failed to create namespace %s", createNSName) @@ -126,7 +127,7 @@ func (e *ExcludeNamespaces) Verify() error { defer ctxCancel() // Verify that we got back all of the namespaces we created for nsNum := 0; nsNum < e.namespacesExcluded; nsNum++ { - excludeNSName := fmt.Sprintf("%s-%00000d", e.NSBaseName, nsNum) + excludeNSName := fmt.Sprintf("%s-%00000d", e.CaseBaseName, nsNum) _, err := GetNamespace(ctx, e.Client, excludeNSName) if err == nil { return errors.Wrapf(err, "Resource filtering with exclude namespace but exclude namespace %s exist", excludeNSName) @@ -138,7 +139,7 @@ func (e *ExcludeNamespaces) Verify() error { } for nsNum := e.namespacesExcluded; nsNum < e.NamespacesTotal; nsNum++ { - checkNSName := fmt.Sprintf("%s-%00000d", e.NSBaseName, nsNum) + checkNSName := fmt.Sprintf("%s-%00000d", e.CaseBaseName, nsNum) checkNS, err := GetNamespace(ctx, e.Client, checkNSName) if err != nil { return errors.Wrapf(err, "Could not retrieve test namespace %s", checkNSName) diff --git a/test/e2e/resource-filtering/exclude_resources.go b/test/e2e/resource-filtering/exclude_resources.go index 844a4243e..4b02b0ef5 100644 --- a/test/e2e/resource-filtering/exclude_resources.go +++ b/test/e2e/resource-filtering/exclude_resources.go @@ -49,9 +49,9 @@ var RestoreWithExcludeResources func() = TestFunc(&ExcludeResources{testInRestor func (e *ExcludeResources) Init() error { e.FilteringCase.Init() - e.NSBaseName = "exclude-resources-" + UUIDgen.String() + e.CaseBaseName = "exclude-resources-" + e.UUIDgen for nsNum := 0; nsNum < e.NamespacesTotal; nsNum++ { - createNSName := fmt.Sprintf("%s-%00000d", e.NSBaseName, nsNum) + createNSName := fmt.Sprintf("%s-%00000d", e.CaseBaseName, nsNum) *e.NSIncluded = append(*e.NSIncluded, createNSName) } if e.IsTestInBackup { // testing case backup with exclude-resources option @@ -60,7 +60,7 @@ func (e *ExcludeResources) Init() error { Text: "Should not backup resources which is excluded others should be backup", FailedMSG: "Failed to backup with resource exclude", } - e.BackupName = "backup-exclude-resources-" + UUIDgen.String() + e.BackupName = "backup-" + e.CaseBaseName e.RestoreName = "restore-" + UUIDgen.String() e.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", e.BackupName, @@ -74,15 +74,13 @@ func (e *ExcludeResources) Init() error { "--from-backup", e.BackupName, "--wait", } } else { // testing case restore with exclude-resources option - e.BackupName = "backup-" + UUIDgen.String() - e.RestoreName = "restore-exclude-resources-" + UUIDgen.String() + e.BackupName = "backup-" + e.UUIDgen + e.RestoreName = "restore-" + e.CaseBaseName e.TestMsg = &TestMSG{ Desc: "Restore resources with resources included test", Text: "Should not restore resources which is excluded others should be backup", FailedMSG: "Failed to restore with resource exclude", } - e.BackupName = "backup-exclude-resources-" + UUIDgen.String() - e.RestoreName = "restore-exclude-resources-" + UUIDgen.String() e.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", e.BackupName, "--include-namespaces", strings.Join(*e.NSIncluded, ","), @@ -99,10 +97,10 @@ func (e *ExcludeResources) Init() error { func (e *ExcludeResources) Verify() error { for nsNum := 0; nsNum < e.NamespacesTotal; nsNum++ { - namespace := fmt.Sprintf("%s-%00000d", e.NSBaseName, nsNum) + namespace := fmt.Sprintf("%s-%00000d", e.CaseBaseName, nsNum) fmt.Printf("Checking resources in namespaces ...%s\n", namespace) //Check deployment - _, err := GetDeployment(e.Client.ClientGo, namespace, e.NSBaseName) + _, err := GetDeployment(e.Client.ClientGo, namespace, e.CaseBaseName) if err != nil { return errors.Wrap(err, fmt.Sprintf("failed to list deployment in namespace: %q", namespace)) } diff --git a/test/e2e/resource-filtering/include_namespaces.go b/test/e2e/resource-filtering/include_namespaces.go index 003c99d99..08a15879c 100644 --- a/test/e2e/resource-filtering/include_namespaces.go +++ b/test/e2e/resource-filtering/include_namespaces.go @@ -51,11 +51,11 @@ var RestoreWithIncludeNamespaces func() = TestFunc(&IncludeNamespaces{FilteringC func (i *IncludeNamespaces) Init() error { i.FilteringCase.Init() + i.CaseBaseName = "include-namespaces-" + i.UUIDgen i.namespacesIncluded = i.NamespacesTotal / 2 i.allTestNamespaces = &[]string{} - i.NSBaseName = "include-namespaces-" + UUIDgen.String() for nsNum := 0; nsNum < i.NamespacesTotal; nsNum++ { - createNSName := fmt.Sprintf("%s-%00000d", i.NSBaseName, nsNum) + createNSName := fmt.Sprintf("%s-%00000d", i.CaseBaseName, nsNum) if nsNum < i.namespacesIncluded { *i.NSIncluded = append(*i.NSIncluded, createNSName) } @@ -63,8 +63,8 @@ func (i *IncludeNamespaces) Init() error { } if i.IsTestInBackup { - i.BackupName = "backup-include-namespaces-" + UUIDgen.String() - i.RestoreName = "restore-" + UUIDgen.String() + i.BackupName = "backup-" + i.CaseBaseName + i.RestoreName = "restore-" + i.UUIDgen i.TestMsg = &TestMSG{ Desc: "Backup resources with include namespace test", FailedMSG: "Failed to backup with namespace include", @@ -82,8 +82,8 @@ func (i *IncludeNamespaces) Init() error { } } else { - i.BackupName = "backup-" + UUIDgen.String() - i.RestoreName = "restore-include-namespaces-" + UUIDgen.String() + i.BackupName = "backup-" + i.UUIDgen + i.RestoreName = "restore-" + i.CaseBaseName i.TestMsg = &TestMSG{ Desc: "Restore resources with include namespace test", FailedMSG: "Failed to restore with namespace include", @@ -108,7 +108,7 @@ func (i *IncludeNamespaces) CreateResources() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) defer ctxCancel() for nsNum := 0; nsNum < i.NamespacesTotal; nsNum++ { - createNSName := fmt.Sprintf("%s-%00000d", i.NSBaseName, nsNum) + createNSName := fmt.Sprintf("%s-%00000d", i.CaseBaseName, nsNum) fmt.Printf("Creating namespaces ...%s\n", createNSName) if err := CreateNamespace(ctx, i.Client, createNSName); err != nil { return errors.Wrapf(err, "Failed to create namespace %s", createNSName) @@ -122,7 +122,7 @@ func (i *IncludeNamespaces) Verify() error { defer ctxCancel() // Verify that we got back all of the namespaces we created for nsNum := 0; nsNum < i.namespacesIncluded; nsNum++ { - checkNSName := fmt.Sprintf("%s-%00000d", i.NSBaseName, nsNum) + checkNSName := fmt.Sprintf("%s-%00000d", i.CaseBaseName, nsNum) checkNS, err := GetNamespace(ctx, i.Client, checkNSName) if err != nil { return errors.Wrapf(err, "Could not retrieve test namespace %s", checkNSName) @@ -133,7 +133,7 @@ func (i *IncludeNamespaces) Verify() error { } for nsNum := i.namespacesIncluded; nsNum < i.NamespacesTotal; nsNum++ { - excludeNSName := fmt.Sprintf("%s-%00000d", i.NSBaseName, nsNum) + excludeNSName := fmt.Sprintf("%s-%00000d", i.CaseBaseName, nsNum) _, err := GetNamespace(ctx, i.Client, excludeNSName) if err == nil { return errors.Wrapf(err, "Resource filtering with include namespace but exclude namespace %s exist", excludeNSName) diff --git a/test/e2e/resource-filtering/include_resources.go b/test/e2e/resource-filtering/include_resources.go index 268ee6fb8..23c79261e 100644 --- a/test/e2e/resource-filtering/include_resources.go +++ b/test/e2e/resource-filtering/include_resources.go @@ -47,9 +47,9 @@ var RestoreWithIncludeResources func() = TestFunc(&IncludeResources{testInRestor func (i *IncludeResources) Init() error { i.FilteringCase.Init() - i.NSBaseName = "include-resources-" + UUIDgen.String() + i.CaseBaseName = "include-resources-" + i.UUIDgen for nsNum := 0; nsNum < i.NamespacesTotal; nsNum++ { - createNSName := fmt.Sprintf("%s-%00000d", i.NSBaseName, nsNum) + createNSName := fmt.Sprintf("%s-%00000d", i.CaseBaseName, nsNum) *i.NSIncluded = append(*i.NSIncluded, createNSName) } if i.IsTestInBackup { // testing case backup with include-resources option @@ -58,8 +58,8 @@ func (i *IncludeResources) Init() error { Text: "Should backup resources which is included others should not be backup", FailedMSG: "Failed to backup with resource include", } - i.BackupName = "backup-include-resources-" + UUIDgen.String() - i.RestoreName = "restore-" + UUIDgen.String() + i.BackupName = "backup-" + i.CaseBaseName + i.RestoreName = "restore-" + i.UUIDgen i.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", i.BackupName, "--include-resources", "deployments,configmaps", @@ -76,8 +76,8 @@ func (i *IncludeResources) Init() error { Text: "Should restore resources which is included others should not be backup", FailedMSG: "Failed to restore with resource include", } - i.BackupName = "backup-" + UUIDgen.String() - i.RestoreName = "restore-include-resources-" + UUIDgen.String() + i.BackupName = "backup-" + i.UUIDgen + i.RestoreName = "restore-" + i.CaseBaseName i.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", i.BackupName, "--include-namespaces", strings.Join(*i.NSIncluded, ","), @@ -94,10 +94,10 @@ func (i *IncludeResources) Init() error { func (i *IncludeResources) Verify() error { for nsNum := 0; nsNum < i.NamespacesTotal; nsNum++ { - namespace := fmt.Sprintf("%s-%00000d", i.NSBaseName, nsNum) + namespace := fmt.Sprintf("%s-%00000d", i.CaseBaseName, nsNum) fmt.Printf("Checking resources in namespaces ...%s\n", namespace) //Check deployment - _, err := GetDeployment(i.Client.ClientGo, namespace, i.NSBaseName) + _, err := GetDeployment(i.Client.ClientGo, namespace, i.CaseBaseName) if err != nil { return errors.Wrap(err, fmt.Sprintf("failed to list deployment in namespace: %q", namespace)) } diff --git a/test/e2e/resource-filtering/label_selector.go b/test/e2e/resource-filtering/label_selector.go index 90a4bdb8f..464326c75 100644 --- a/test/e2e/resource-filtering/label_selector.go +++ b/test/e2e/resource-filtering/label_selector.go @@ -44,11 +44,12 @@ var BackupWithLabelSelector func() = TestFunc(&LabelSelector{testInBackup}) func (l *LabelSelector) Init() error { l.FilteringCase.Init() - l.BackupName = "backup-label-selector-" + UUIDgen.String() - l.RestoreName = "restore-" + UUIDgen.String() - l.NSBaseName = "backup-label-selector-" + UUIDgen.String() + l.CaseBaseName = "backup-label-selector-" + l.UUIDgen + l.BackupName = "backup-" + l.CaseBaseName + l.RestoreName = "restore-" + l.CaseBaseName + for nsNum := 0; nsNum < l.NamespacesTotal; nsNum++ { - createNSName := fmt.Sprintf("%s-%00000d", l.NSBaseName, nsNum) + createNSName := fmt.Sprintf("%s-%00000d", l.CaseBaseName, nsNum) *l.NSIncluded = append(*l.NSIncluded, createNSName) } l.TestMsg = &TestMSG{ @@ -78,7 +79,7 @@ func (l *LabelSelector) CreateResources() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) defer ctxCancel() for nsNum := 0; nsNum < l.NamespacesTotal; nsNum++ { - namespace := fmt.Sprintf("%s-%00000d", l.NSBaseName, nsNum) + namespace := fmt.Sprintf("%s-%00000d", l.CaseBaseName, nsNum) fmt.Printf("Creating resources in namespace ...%s\n", namespace) labels := l.labels if nsNum%2 == 0 { @@ -89,20 +90,10 @@ func (l *LabelSelector) CreateResources() error { if err := CreateNamespaceWithLabel(ctx, l.Client, namespace, labels); err != nil { return errors.Wrapf(err, "Failed to create namespace %s", namespace) } - - serviceAccountName := "default" - // wait until the service account is created before patch the image pull secret - if err := WaitUntilServiceAccountCreated(ctx, l.Client, namespace, serviceAccountName, 10*time.Minute); err != nil { - return errors.Wrapf(err, "failed to wait the service account %q created under the namespace %q", serviceAccountName, namespace) - } - // add the image pull secret to avoid the image pull limit issue of Docker Hub - if err := PatchServiceAccountWithImagePullSecret(ctx, l.Client, namespace, serviceAccountName, VeleroCfg.RegistryCredentialFile); err != nil { - return errors.Wrapf(err, "failed to patch the service account %q under the namespace %q", serviceAccountName, namespace) - } //Create deployment fmt.Printf("Creating deployment in namespaces ...%s\n", namespace) - deployment := NewDeployment(l.NSBaseName, namespace, l.replica, labels, nil).Result() + deployment := NewDeployment(l.CaseBaseName, namespace, l.replica, labels, nil).Result() deployment, err := CreateDeployment(l.Client.ClientGo, namespace, deployment) if err != nil { return errors.Wrap(err, fmt.Sprintf("failed to delete the namespace %q", namespace)) @@ -112,7 +103,7 @@ func (l *LabelSelector) CreateResources() error { return errors.Wrap(err, fmt.Sprintf("failed to ensure job completion in namespace: %q", namespace)) } //Create Secret - secretName := l.NSBaseName + secretName := l.CaseBaseName fmt.Printf("Creating secret %s in namespaces ...%s\n", secretName, namespace) _, err = CreateSecret(l.Client.ClientGo, namespace, secretName, l.labels) if err != nil { @@ -128,10 +119,10 @@ func (l *LabelSelector) CreateResources() error { func (l *LabelSelector) Verify() error { for nsNum := 0; nsNum < l.NamespacesTotal; nsNum++ { - namespace := fmt.Sprintf("%s-%00000d", l.NSBaseName, nsNum) + namespace := fmt.Sprintf("%s-%00000d", l.CaseBaseName, nsNum) fmt.Printf("Checking resources in namespaces ...%s\n", namespace) //Check deployment - _, err := GetDeployment(l.Client.ClientGo, namespace, l.NSBaseName) + _, err := GetDeployment(l.Client.ClientGo, namespace, l.CaseBaseName) if nsNum%2 == 1 { //include if err != nil { return errors.Wrap(err, fmt.Sprintf("failed to list deployment in namespace: %q", namespace)) diff --git a/test/e2e/resourcepolicies/resource_policies.go b/test/e2e/resourcepolicies/resource_policies.go index 3d84de30d..7592f64d9 100644 --- a/test/e2e/resourcepolicies/resource_policies.go +++ b/test/e2e/resourcepolicies/resource_policies.go @@ -20,7 +20,6 @@ import ( "context" "fmt" "io/ioutil" - "math/rand" "os" "strings" "time" @@ -28,7 +27,6 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/google/uuid" "github.com/pkg/errors" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" @@ -61,19 +59,19 @@ type ResourcePoliciesCase struct { var ResourcePoliciesTest func() = TestFunc(&ResourcePoliciesCase{}) func (r *ResourcePoliciesCase) Init() error { - rand.Seed(time.Now().UnixNano()) - UUIDgen, _ = uuid.NewRandom() + r.TestCase.Init() + r.CaseBaseName = "resource-policies-" + r.UUIDgen + r.cmName = "cm-" + r.CaseBaseName r.yamlConfig = yamlData r.VeleroCfg = VeleroCfg r.Client = *r.VeleroCfg.ClientToInstallVelero r.VeleroCfg.UseVolumeSnapshots = false r.VeleroCfg.UseNodeAgent = true r.NamespacesTotal = 3 - r.NSBaseName = "resource-policies-" + UUIDgen.String() - r.cmName = "cm-resource-policies-sc" + r.NSIncluded = &[]string{} for nsNum := 0; nsNum < r.NamespacesTotal; nsNum++ { - createNSName := fmt.Sprintf("%s-%00000d", r.NSBaseName, nsNum) + createNSName := fmt.Sprintf("%s-%00000d", r.CaseBaseName, nsNum) *r.NSIncluded = append(*r.NSIncluded, createNSName) } @@ -117,12 +115,12 @@ func (r *ResourcePoliciesCase) CreateResources() error { }) for nsNum := 0; nsNum < r.NamespacesTotal; nsNum++ { - namespace := fmt.Sprintf("%s-%00000d", r.NSBaseName, nsNum) + namespace := fmt.Sprintf("%s-%00000d", r.CaseBaseName, nsNum) By(fmt.Sprintf("Create namespaces %s for workload\n", namespace), func() { Expect(CreateNamespace(ctx, r.Client, namespace)).To(Succeed(), fmt.Sprintf("Failed to create namespace %s", namespace)) }) - volName := fmt.Sprintf("vol-%s-%00000d", r.NSBaseName, nsNum) + volName := fmt.Sprintf("vol-%s-%00000d", r.CaseBaseName, nsNum) volList := PrepareVolumeList([]string{volName}) // Create PVC @@ -149,13 +147,13 @@ func (r *ResourcePoliciesCase) Verify() error { defer ctxCancel() for i, ns := range *r.NSIncluded { By(fmt.Sprintf("Verify pod data in namespace %s", ns), func() { - By(fmt.Sprintf("Waiting for deployment %s in namespace %s ready", r.NSBaseName, ns), func() { - Expect(WaitForReadyDeployment(r.Client.ClientGo, ns, r.NSBaseName)).To(Succeed(), fmt.Sprintf("Failed to waiting for deployment %s in namespace %s ready", r.NSBaseName, ns)) + By(fmt.Sprintf("Waiting for deployment %s in namespace %s ready", r.CaseBaseName, ns), func() { + Expect(WaitForReadyDeployment(r.Client.ClientGo, ns, r.CaseBaseName)).To(Succeed(), fmt.Sprintf("Failed to waiting for deployment %s in namespace %s ready", r.CaseBaseName, ns)) }) podList, err := ListPods(ctx, r.Client, ns) Expect(err).To(Succeed(), fmt.Sprintf("failed to list pods in namespace: %q with error %v", ns, err)) - volName := fmt.Sprintf("vol-%s-%00000d", r.NSBaseName, i) + volName := fmt.Sprintf("vol-%s-%00000d", r.CaseBaseName, i) for _, pod := range podList.Items { for _, vol := range pod.Spec.Volumes { if vol.Name != volName { @@ -183,15 +181,18 @@ func (r *ResourcePoliciesCase) Verify() error { } func (r *ResourcePoliciesCase) Clean() error { - if err := r.deleteTestStorageClassList([]string{"e2e-storage-class", "e2e-storage-class-2"}); err != nil { - return err - } + if !r.VeleroCfg.Debug { + if err := r.deleteTestStorageClassList([]string{"e2e-storage-class", "e2e-storage-class-2"}); err != nil { + return err + } - if err := DeleteConfigmap(r.Client.ClientGo, r.VeleroCfg.VeleroNamespace, r.cmName); err != nil { - return err - } + if err := DeleteConfigmap(r.Client.ClientGo, r.VeleroCfg.VeleroNamespace, r.cmName); err != nil { + return err + } - return r.GetTestCase().Clean() + return r.GetTestCase().Clean() + } + return nil } func (r *ResourcePoliciesCase) createPVC(index int, namespace string, volList []*v1.Volume) error { @@ -217,7 +218,7 @@ func (r *ResourcePoliciesCase) createPVC(index int, namespace string, volList [] } func (r *ResourcePoliciesCase) createDeploymentWithVolume(namespace string, volList []*v1.Volume) error { - deployment := NewDeployment(r.NSBaseName, namespace, 1, map[string]string{"resource-policies": "resource-policies"}, nil).WithVolume(volList).Result() + deployment := NewDeployment(r.CaseBaseName, namespace, 1, map[string]string{"resource-policies": "resource-policies"}, nil).WithVolume(volList).Result() deployment, err := CreateDeployment(r.Client.ClientGo, namespace, deployment) if err != nil { return errors.Wrap(err, fmt.Sprintf("failed to create deloyment %s the namespace %q", deployment.Name, namespace)) diff --git a/test/e2e/schedule/ordered_resources.go b/test/e2e/schedule/ordered_resources.go index 8be393c17..e4cdca778 100644 --- a/test/e2e/schedule/ordered_resources.go +++ b/test/e2e/schedule/ordered_resources.go @@ -19,13 +19,10 @@ limitations under the License. //the ordered resources test related to https://github.com/vmware-tanzu/velero/issues/4561 import ( "context" - "flag" "fmt" - "math/rand" "strings" "time" - "github.com/google/uuid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/pkg/errors" @@ -39,6 +36,8 @@ import ( . "github.com/vmware-tanzu/velero/test/e2e/util/velero" ) +var ScheduleOrderedResources func() = TestFunc(&OrderedResources{}) + type OrderedResources struct { Namespace string ScheduleName string @@ -47,87 +46,23 @@ type OrderedResources struct { TestCase } -func ScheduleOrderedResources() { - veleroCfg := VeleroCfg - BeforeEach(func() { - flag.Parse() - if veleroCfg.InstallVelero { - veleroCfg.UseVolumeSnapshots = false - Expect(VeleroInstall(context.Background(), &veleroCfg)).To(Succeed()) - } - }) - - AfterEach(func() { - if veleroCfg.InstallVelero && !veleroCfg.Debug { - Expect(VeleroUninstall(context.Background(), veleroCfg.VeleroCLI, veleroCfg.VeleroNamespace)).To(Succeed()) - } - }) - - It("Create a schedule to backup resources in a specific order should be successful", func() { - ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) - defer ctxCancel() - test := &OrderedResources{} - test.VeleroCfg = VeleroCfg - err := test.Init() - Expect(err).To(Succeed(), err) - defer func() { - Expect(DeleteNamespace(ctx, test.Client, test.Namespace, false)).To(Succeed(), fmt.Sprintf("Failed to delete the namespace %s", test.Namespace)) - err = VeleroScheduleDelete(ctx, veleroCfg.VeleroCLI, veleroCfg.VeleroNamespace, test.ScheduleName) - Expect(err).To(Succeed(), fmt.Sprintf("Failed to delete schedule with err %v", err)) - err = test.DeleteBackups() - Expect(err).To(Succeed(), fmt.Sprintf("Failed to delete backups with err %v", err)) - }() - - By(fmt.Sprintf("Prepare workload as target to backup in base namespace %s", test.Namespace), func() { - err = test.CreateResources() - Expect(err).To(Succeed(), fmt.Sprintf("Failed to create resources to backup with err %v", err)) - }) - - By(fmt.Sprintf("Create schedule the workload in %s namespace", test.Namespace), func() { - err = VeleroScheduleCreate(ctx, veleroCfg.VeleroCLI, veleroCfg.VeleroNamespace, test.ScheduleName, test.ScheduleArgs) - Expect(err).To(Succeed(), fmt.Sprintf("Failed to create schedule %s with err %v", test.ScheduleName, err)) - }) - - By(fmt.Sprintf("Checking resource order in %s schedule cr", test.ScheduleName), func() { - err = CheckScheduleWithResourceOrder(ctx, veleroCfg.VeleroCLI, veleroCfg.VeleroNamespace, test.ScheduleName, test.OrderMap) - Expect(err).To(Succeed(), fmt.Sprintf("Failed to check schedule %s with err %v", test.ScheduleName, err)) - }) - - By("Checking resource order in backup cr", func() { - backupList := new(velerov1api.BackupList) - err = waitutil.PollImmediate(10*time.Second, time.Minute*5, func() (bool, error) { - if err = test.Client.Kubebuilder.List(ctx, backupList, &kbclient.ListOptions{Namespace: veleroCfg.VeleroNamespace}); err != nil { - return false, fmt.Errorf("failed to list backup object in %s namespace with err %v", veleroCfg.VeleroNamespace, err) - } - - for _, backup := range backupList.Items { - if err = CheckBackupWithResourceOrder(ctx, veleroCfg.VeleroCLI, veleroCfg.VeleroNamespace, backup.Name, test.OrderMap); err == nil { - return true, nil - } - } - fmt.Printf("still finding backup created by schedule %s ...\n", test.ScheduleName) - return false, nil - }) - Expect(err).To(Succeed(), fmt.Sprintf("Failed to check schedule %s created backup with err %v", test.ScheduleName, err)) - }) - - }) -} - func (o *OrderedResources) Init() error { - rand.Seed(time.Now().UnixNano()) - UUIDgen, _ = uuid.NewRandom() + o.TestCase.Init() + o.CaseBaseName = "ordered-resources-" + o.UUIDgen + o.ScheduleName = "schedule-" + o.CaseBaseName + o.Namespace = o.CaseBaseName + "-" + o.UUIDgen o.VeleroCfg = VeleroCfg o.Client = *o.VeleroCfg.ClientToInstallVelero - o.ScheduleName = "schedule-ordered-resources-" + UUIDgen.String() - o.NSBaseName = "schedule-ordered-resources" - o.Namespace = o.NSBaseName + "-" + UUIDgen.String() o.OrderMap = map[string]string{ - "deployments": fmt.Sprintf("deploy-%s", o.NSBaseName), - "secrets": fmt.Sprintf("secret-%s", o.NSBaseName), - "configmaps": fmt.Sprintf("configmap-%s", o.NSBaseName), + "deployments": fmt.Sprintf("deploy-%s", o.CaseBaseName), + "secrets": fmt.Sprintf("secret-%s", o.CaseBaseName), + "configmaps": fmt.Sprintf("configmap-%s", o.CaseBaseName), + } + o.TestMsg = &TestMSG{ + Desc: "Create a schedule to backup resources in a specific order should be successful", + FailedMSG: "Failed to verify schedule backup resources in a specific order", + Text: "Create a schedule to backup resources in a specific order should be successful", } - o.ScheduleArgs = []string{"--schedule", "@every 1m", "--include-namespaces", o.Namespace, "--default-volumes-to-fs-backup", "--ordered-resources"} var orderStr string @@ -140,7 +75,6 @@ func (o *OrderedResources) Init() error { } func (o *OrderedResources) CreateResources() error { - veleroCfg := o.VeleroCfg ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) defer ctxCancel() label := map[string]string{ @@ -150,17 +84,8 @@ func (o *OrderedResources) CreateResources() error { if err := CreateNamespace(ctx, o.Client, o.Namespace); err != nil { return errors.Wrapf(err, "failed to create namespace %s", o.Namespace) } - serviceAccountName := "default" - // wait until the service account is created before patch the image pull secret - if err := WaitUntilServiceAccountCreated(ctx, o.Client, o.Namespace, serviceAccountName, 10*time.Minute); err != nil { - return errors.Wrapf(err, "failed to wait the service account %q created under the namespace %q", serviceAccountName, o.Namespace) - } - // add the image pull secret to avoid the image pull limit issue of Docker Hub - if err := PatchServiceAccountWithImagePullSecret(ctx, o.Client, o.Namespace, serviceAccountName, veleroCfg.RegistryCredentialFile); err != nil { - return errors.Wrapf(err, "failed to patch the service account %q under the namespace %q", serviceAccountName, o.Namespace) - } //Create deployment - deploymentName := fmt.Sprintf("deploy-%s", o.NSBaseName) + deploymentName := fmt.Sprintf("deploy-%s", o.CaseBaseName) fmt.Printf("Creating deployment %s in %s namespaces ...\n", deploymentName, o.Namespace) deployment := NewDeployment(deploymentName, o.Namespace, 1, label, nil).Result() deployment, err := CreateDeployment(o.Client.ClientGo, o.Namespace, deployment) @@ -172,7 +97,7 @@ func (o *OrderedResources) CreateResources() error { return errors.Wrap(err, fmt.Sprintf("failed to ensure job completion in namespace: %q", o.Namespace)) } //Create Secret - secretName := fmt.Sprintf("secret-%s", o.NSBaseName) + secretName := fmt.Sprintf("secret-%s", o.CaseBaseName) fmt.Printf("Creating secret %s in %s namespaces ...\n", secretName, o.Namespace) _, err = CreateSecret(o.Client.ClientGo, o.Namespace, secretName, label) if err != nil { @@ -183,7 +108,7 @@ func (o *OrderedResources) CreateResources() error { return errors.Wrap(err, fmt.Sprintf("failed to ensure secret completion in namespace: %q", o.Namespace)) } //Create Configmap - configmapName := fmt.Sprintf("configmap-%s", o.NSBaseName) + configmapName := fmt.Sprintf("configmap-%s", o.CaseBaseName) fmt.Printf("Creating configmap %s in %s namespaces ...\n", configmapName, o.Namespace) _, err = CreateConfigMap(o.Client.ClientGo, o.Namespace, configmapName, label, nil) if err != nil { @@ -196,16 +121,63 @@ func (o *OrderedResources) CreateResources() error { return nil } +func (o *OrderedResources) Backup() error { + ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) + defer ctxCancel() + By(fmt.Sprintf("Create schedule the workload in %s namespace", o.Namespace), func() { + err := VeleroScheduleCreate(ctx, o.VeleroCfg.VeleroCLI, o.VeleroCfg.VeleroNamespace, o.ScheduleName, o.ScheduleArgs) + Expect(err).To(Succeed(), fmt.Sprintf("Failed to create schedule %s with err %v", o.ScheduleName, err)) + }) + return nil +} + +func (o *OrderedResources) Verify() error { + ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) + defer ctxCancel() + By(fmt.Sprintf("Checking resource order in %s schedule cr", o.ScheduleName), func() { + err := CheckScheduleWithResourceOrder(ctx, o.VeleroCfg.VeleroCLI, o.VeleroCfg.VeleroNamespace, o.ScheduleName, o.OrderMap) + Expect(err).To(Succeed(), fmt.Sprintf("Failed to check schedule %s with err %v", o.ScheduleName, err)) + }) + + By("Checking resource order in backup cr", func() { + backupList := new(velerov1api.BackupList) + err := waitutil.PollImmediate(10*time.Second, time.Minute*5, func() (bool, error) { + if err := o.Client.Kubebuilder.List(ctx, backupList, &kbclient.ListOptions{Namespace: o.VeleroCfg.VeleroNamespace}); err != nil { + return false, fmt.Errorf("failed to list backup object in %s namespace with err %v", o.VeleroCfg.VeleroNamespace, err) + } + + for _, backup := range backupList.Items { + if err := CheckBackupWithResourceOrder(ctx, o.VeleroCfg.VeleroCLI, o.VeleroCfg.VeleroNamespace, backup.Name, o.OrderMap); err == nil { + return true, nil + } + } + fmt.Printf("still finding backup created by schedule %s ...\n", o.ScheduleName) + return false, nil + }) + Expect(err).To(Succeed(), fmt.Sprintf("Failed to check schedule %s created backup with err %v", o.ScheduleName, err)) + }) + return nil +} + +func (o *OrderedResources) Clean() error { + ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) + defer ctxCancel() + if !o.VeleroCfg.Debug { + Expect(VeleroScheduleDelete(ctx, o.VeleroCfg.VeleroCLI, o.VeleroCfg.VeleroNamespace, o.ScheduleName)).To(Succeed()) + Expect(o.TestCase.Clean()).To(Succeed()) + } + return nil +} + func (o *OrderedResources) DeleteBackups() error { ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) defer ctxCancel() - veleroCfg := o.VeleroCfg backupList := new(velerov1api.BackupList) - if err := o.Client.Kubebuilder.List(ctx, backupList, &kbclient.ListOptions{Namespace: veleroCfg.VeleroNamespace}); err != nil { - return fmt.Errorf("failed to list backup object in %s namespace with err %v", veleroCfg.VeleroNamespace, err) + if err := o.Client.Kubebuilder.List(ctx, backupList, &kbclient.ListOptions{Namespace: o.VeleroCfg.VeleroNamespace}); err != nil { + return fmt.Errorf("failed to list backup object in %s namespace with err %v", o.VeleroCfg.VeleroNamespace, err) } for _, backup := range backupList.Items { - if err := VeleroBackupDelete(ctx, veleroCfg.VeleroCLI, veleroCfg.VeleroNamespace, backup.Name); err != nil { + if err := VeleroBackupDelete(ctx, o.VeleroCfg.VeleroCLI, o.VeleroCfg.VeleroNamespace, backup.Name); err != nil { return err } } diff --git a/test/e2e/schedule/schedule-backup-creation.go b/test/e2e/schedule/schedule-backup-creation.go index 3e26149b7..8698426e6 100644 --- a/test/e2e/schedule/schedule-backup-creation.go +++ b/test/e2e/schedule/schedule-backup-creation.go @@ -31,9 +31,13 @@ type ScheduleBackupCreation struct { podSleepDuration time.Duration } -var ScheduleBackupCreationTest func() = TestFunc(&ScheduleBackupCreation{namespace: "sch1", TestCase: TestCase{NSBaseName: "schedule-backup-creation-test"}}) +var ScheduleBackupCreationTest func() = TestFunc(&ScheduleBackupCreation{}) func (n *ScheduleBackupCreation) Init() error { + n.TestCase.Init() + n.CaseBaseName = "schedule-backup-creation-test" + n.UUIDgen + n.ScheduleName = n.ScheduleName + "schedule-" + UUIDgen.String() + n.namespace = n.CaseBaseName n.VeleroCfg = VeleroCfg n.Client = *n.VeleroCfg.ClientToInstallVelero n.Period = 3 // Unit is minute @@ -53,14 +57,6 @@ func (n *ScheduleBackupCreation) Init() error { n.volume = "volume-1" n.podName = "pod-1" n.pvcName = "pvc-1" - return nil -} - -func (n *ScheduleBackupCreation) StartRun() error { - n.namespace = fmt.Sprintf("%s-%s", n.NSBaseName, "ns") - n.ScheduleName = n.ScheduleName + "schedule-" + UUIDgen.String() - n.RestoreName = n.RestoreName + "restore-ns-mapping-" + UUIDgen.String() - n.ScheduleArgs = []string{ "--include-namespaces", n.namespace, "--schedule=*/" + fmt.Sprintf("%v", n.Period) + " * * * *", @@ -68,6 +64,7 @@ func (n *ScheduleBackupCreation) StartRun() error { Expect(n.Period < 30).To(Equal(true)) return nil } + func (p *ScheduleBackupCreation) CreateResources() error { By(fmt.Sprintf("Create namespace %s", p.namespace), func() { Expect(CreateNamespace(context.Background(), p.Client, p.namespace)).To(Succeed(), @@ -84,7 +81,7 @@ func (p *ScheduleBackupCreation) CreateResources() error { } func (n *ScheduleBackupCreation) Backup() error { - ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) + ctx, ctxCancel := context.WithTimeout(context.Background(), 60*time.Minute) defer ctxCancel() // Wait until the beginning of the given period to create schedule, it will give us // a predictable period to wait for the first scheduled backup, and verify no immediate @@ -97,7 +94,7 @@ func (n *ScheduleBackupCreation) Backup() error { if triggerNow == 0 { Expect(VeleroScheduleCreate(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, n.ScheduleName, n.ScheduleArgs)).To(Succeed(), func() string { RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, "", "") - return "Fail to restore workload" + return "Fail to create schedule" }) break } @@ -133,6 +130,10 @@ func (n *ScheduleBackupCreation) Backup() error { return nil } -func (n *ScheduleBackupCreation) Restore() error { +func (n *ScheduleBackupCreation) Clean() error { + if !n.VeleroCfg.Debug { + Expect(VeleroScheduleDelete(context.Background(), n.VeleroCfg.VeleroCLI, n.VeleroCfg.VeleroNamespace, n.ScheduleName)).To(Succeed()) + Expect(n.TestCase.Clean()).To(Succeed()) + } return nil } diff --git a/test/e2e/schedule/schedule.go b/test/e2e/schedule/schedule.go index bcd7e1f91..b4c5070b7 100644 --- a/test/e2e/schedule/schedule.go +++ b/test/e2e/schedule/schedule.go @@ -25,9 +25,14 @@ type ScheduleBackup struct { verifyTimes int } -var ScheduleBackupTest func() = TestFunc(&ScheduleBackup{TestCase: TestCase{NSBaseName: "schedule-test"}}) +var ScheduleBackupTest func() = TestFunc(&ScheduleBackup{}) func (n *ScheduleBackup) Init() error { + n.TestCase.Init() + n.CaseBaseName = "schedule-backup-" + n.UUIDgen + n.NSIncluded = &[]string{n.CaseBaseName} + n.ScheduleName = "schedule-" + n.CaseBaseName + n.VeleroCfg = VeleroCfg n.Client = *n.VeleroCfg.ClientToInstallVelero n.Period = 3 // Unit is minute @@ -37,14 +42,6 @@ func (n *ScheduleBackup) Init() error { FailedMSG: "Failed to schedule a backup", Text: "should backup periodly according to the schedule", } - return nil -} - -func (n *ScheduleBackup) StartRun() error { - n.NSIncluded = &[]string{fmt.Sprintf("%s-%s", n.NSBaseName, "ns")} - n.ScheduleName = n.ScheduleName + "schedule-" + UUIDgen.String() - n.RestoreName = n.RestoreName + "restore-ns-mapping-" + UUIDgen.String() - n.ScheduleArgs = []string{ "--include-namespaces", strings.Join(*n.NSIncluded, ","), "--schedule=*/" + fmt.Sprintf("%v", n.Period) + " * * * *", @@ -52,6 +49,7 @@ func (n *ScheduleBackup) StartRun() error { Expect(n.Period < 30).To(Equal(true)) return nil } + func (n *ScheduleBackup) CreateResources() error { ctx, ctxCanel := context.WithTimeout(context.Background(), 60*time.Minute) defer ctxCanel() @@ -59,7 +57,7 @@ func (n *ScheduleBackup) CreateResources() error { By(fmt.Sprintf("Creating namespaces %s ......\n", ns), func() { Expect(CreateNamespace(ctx, n.Client, ns)).To(Succeed(), fmt.Sprintf("Failed to create namespace %s", ns)) }) - configmaptName := n.NSBaseName + configmaptName := n.CaseBaseName fmt.Printf("Creating configmap %s in namespaces ...%s\n", configmaptName, ns) _, err := CreateConfigMap(n.Client.ClientGo, ns, configmaptName, nil, nil) Expect(err).To(Succeed(), fmt.Sprintf("failed to create configmap in the namespace %q", ns)) @@ -70,7 +68,7 @@ func (n *ScheduleBackup) CreateResources() error { } func (n *ScheduleBackup) Backup() error { - ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) + ctx, ctxCancel := context.WithTimeout(context.Background(), 60*time.Minute) defer ctxCancel() // Wait until the beginning of the given period to create schedule, it will give us // a predictable period to wait for the first scheduled backup, and verify no immediate @@ -83,17 +81,13 @@ func (n *ScheduleBackup) Backup() error { if triggerNow == 0 { Expect(VeleroScheduleCreate(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, n.ScheduleName, n.ScheduleArgs)).To(Succeed(), func() string { RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, "", "") - return "Fail to restore workload" + return "Fail to create schedule" }) break } } }) - return nil -} -func (n *ScheduleBackup) Destroy() error { - ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) - defer ctxCancel() + By(fmt.Sprintf("Schedule %s is created without any delay\n", n.ScheduleName), func() { creationTimestamp, err := GetSchedule(context.Background(), VeleroCfg.VeleroNamespace, n.ScheduleName) Expect(err).To(Succeed()) @@ -121,7 +115,7 @@ func (n *ScheduleBackup) Destroy() error { }) By("Delay one more minute to make sure the new backup was created in the given period", func() { - time.Sleep(1 * time.Minute) + time.Sleep(time.Minute) }) By(fmt.Sprintf("Get backups every %d minute, and backups count should increase 1 more step in the same pace\n", n.Period), func() { @@ -149,15 +143,9 @@ func (n *ScheduleBackup) Destroy() error { n.BackupName = strings.Replace(n.randBackupName, " ", "", -1) By("Delete all namespaces", func() { - Expect(CleanupNamespacesWithPoll(ctx, n.Client, n.NSBaseName)).To(Succeed(), "Could cleanup retrieve namespaces") + Expect(CleanupNamespacesWithPoll(ctx, n.Client, n.CaseBaseName)).To(Succeed(), "Could cleanup retrieve namespaces") }) - n.RestoreArgs = []string{ - "create", "--namespace", VeleroCfg.VeleroNamespace, "restore", n.RestoreName, - "--from-backup", n.BackupName, - "--wait", - } - backupsInfo, err := GetScheduledBackupsCreationTime(context.Background(), VeleroCfg.VeleroCLI, "default", n.ScheduleName) Expect(err).To(Succeed(), fmt.Sprintf("Fail to get backups from schedule %s", n.ScheduleName)) fmt.Println(backupsInfo) @@ -166,7 +154,7 @@ func (n *ScheduleBackup) Destroy() error { By(fmt.Sprintf("Pause schedule %s ......\n", n.ScheduleName), func() { Expect(VeleroSchedulePause(ctx, VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, n.ScheduleName)).To(Succeed(), func() string { RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, "", "") - return "Fail to restore workload" + return "Fail to pause schedule" }) }) @@ -211,7 +199,7 @@ func (n *ScheduleBackup) Destroy() error { func (n *ScheduleBackup) Verify() error { By("Namespaces were restored", func() { for _, ns := range *n.NSIncluded { - configmap, err := GetConfigmap(n.Client.ClientGo, ns, n.NSBaseName) + configmap, err := GetConfigmap(n.Client.ClientGo, ns, n.CaseBaseName) fmt.Printf("Restored configmap is %v\n", configmap) Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("failed to list configmap in namespace: %q\n", ns)) } @@ -219,3 +207,15 @@ func (n *ScheduleBackup) Verify() error { }) return nil } + +func (n *ScheduleBackup) Destroy() error { + return nil +} + +func (n *ScheduleBackup) Clean() error { + if !n.VeleroCfg.Debug { + Expect(VeleroScheduleDelete(context.Background(), n.VeleroCfg.VeleroCLI, n.VeleroCfg.VeleroNamespace, n.ScheduleName)).To(Succeed()) + Expect(n.TestCase.Clean()).To(Succeed()) + } + return nil +} diff --git a/test/e2e/test/test.go b/test/e2e/test/test.go index 20c90dbbd..160444126 100644 --- a/test/e2e/test/test.go +++ b/test/e2e/test/test.go @@ -20,6 +20,7 @@ import ( "context" "flag" "fmt" + "math/rand" "strings" "time" @@ -42,7 +43,6 @@ depends on your test patterns. */ type VeleroBackupRestoreTest interface { Init() error - StartRun() error CreateResources() error Backup() error Destroy() error @@ -62,7 +62,7 @@ type TestMSG struct { type TestCase struct { BackupName string RestoreName string - NSBaseName string + CaseBaseName string BackupArgs []string RestoreArgs []string NamespacesTotal int @@ -72,17 +72,24 @@ type TestCase struct { UseVolumeSnapshots bool VeleroCfg VeleroConfig RestorePhaseExpect velerov1api.RestorePhase + Timeout time.Duration + UUIDgen string } func TestFunc(test VeleroBackupRestoreTest) func() { return func() { Expect(test.Init()).To(Succeed(), "Failed to instantiate test cases") veleroCfg := test.GetTestCase().VeleroCfg + // If TestCase.Timeout is not set, then make 10 minutes as default value for backup + // or restore CLI + if test.GetTestCase().Timeout == 0 { + test.GetTestCase().Timeout = 10 * time.Minute + } BeforeEach(func() { flag.Parse() veleroCfg := test.GetTestCase().VeleroCfg // TODO: Skip nodeport test until issue https://github.com/kubernetes/kubernetes/issues/114384 fixed - if veleroCfg.CloudProvider == "azure" && strings.Contains(test.GetTestCase().NSBaseName, "nodeport") { + if veleroCfg.CloudProvider == "azure" && strings.Contains(test.GetTestCase().CaseBaseName, "nodeport") { Skip("Skip due to issue https://github.com/kubernetes/kubernetes/issues/114384 on AKS") } if veleroCfg.InstallVelero { @@ -110,6 +117,9 @@ func TestFuncWithMultiIt(tests []VeleroBackupRestoreTest) func() { var veleroCfg VeleroConfig for k := range tests { Expect(tests[k].Init()).To(Succeed(), fmt.Sprintf("Failed to instantiate test %s case", tests[k].GetTestMsg().Desc)) + if tests[k].GetTestCase().Timeout == 0 { + tests[k].GetTestCase().Timeout = 10 * time.Minute + } veleroCfg = tests[k].GetTestCase().VeleroCfg useVolumeSnapshots = tests[k].GetTestCase().UseVolumeSnapshots } @@ -146,19 +156,21 @@ func TestFuncWithMultiIt(tests []VeleroBackupRestoreTest) func() { } func (t *TestCase) Init() error { + t.UUIDgen = t.GenerateUUID() return nil } +func (t *TestCase) GenerateUUID() string { + rand.Seed(time.Now().UnixNano()) + return fmt.Sprintf("%08d", rand.Intn(100000000)) +} + func (t *TestCase) CreateResources() error { return nil } -func (t *TestCase) StartRun() error { - return nil -} - func (t *TestCase) Backup() error { - ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) + ctx, ctxCancel := context.WithTimeout(context.Background(), t.Timeout) defer ctxCancel() veleroCfg := t.GetTestCase().VeleroCfg if err := VeleroBackupExec(ctx, veleroCfg.VeleroCLI, veleroCfg.VeleroNamespace, t.BackupName, t.BackupArgs); err != nil { @@ -169,16 +181,16 @@ func (t *TestCase) Backup() error { } func (t *TestCase) Destroy() error { - ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) + ctx, ctxCancel := context.WithTimeout(context.Background(), 60*time.Minute) defer ctxCancel() - By(fmt.Sprintf("Start to destroy namespace %s......", t.NSBaseName), func() { - Expect(CleanupNamespacesWithPoll(ctx, t.Client, t.NSBaseName)).To(Succeed(), "Could cleanup retrieve namespaces") + By(fmt.Sprintf("Start to destroy namespace %s......", t.CaseBaseName), func() { + Expect(CleanupNamespacesWithPoll(ctx, t.Client, t.CaseBaseName)).To(Succeed(), "Could cleanup retrieve namespaces") }) return nil } func (t *TestCase) Restore() error { - ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) + ctx, ctxCancel := context.WithTimeout(context.Background(), t.Timeout) defer ctxCancel() veleroCfg := t.GetTestCase().VeleroCfg // the snapshots of AWS may be still in pending status when do the restore, wait for a while @@ -206,12 +218,12 @@ func (t *TestCase) Verify() error { } func (t *TestCase) Clean() error { - ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Minute) + ctx, ctxCancel := context.WithTimeout(context.Background(), 60*time.Minute) defer ctxCancel() veleroCfg := t.GetTestCase().VeleroCfg if !veleroCfg.Debug { - By(fmt.Sprintf("Clean namespace with prefix %s after test", t.NSBaseName), func() { - CleanupNamespaces(ctx, t.Client, t.NSBaseName) + By(fmt.Sprintf("Clean namespace with prefix %s after test", t.CaseBaseName), func() { + CleanupNamespaces(ctx, t.Client, t.CaseBaseName) }) By("Clean backups after test", func() { DeleteBackups(ctx, t.Client) @@ -227,35 +239,38 @@ func (t *TestCase) GetTestMsg() *TestMSG { func (t *TestCase) GetTestCase() *TestCase { return t } + func RunTestCase(test VeleroBackupRestoreTest) error { - fmt.Printf("Running test case %s\n", test.GetTestMsg().Desc) + fmt.Printf("Running test case %s %s\n", test.GetTestMsg().Desc, time.Now().Format("2006-01-02 15:04:05")) if test == nil { return errors.New("No case should be tested") } defer test.Clean() - err := test.StartRun() - if err != nil { - return err - } - err = test.CreateResources() + fmt.Printf("CreateResources %s\n", time.Now().Format("2006-01-02 15:04:05")) + err := test.CreateResources() if err != nil { return err } + fmt.Printf("Backup %s\n", time.Now().Format("2006-01-02 15:04:05")) err = test.Backup() if err != nil { return err } + fmt.Printf("Destroy %s\n", time.Now().Format("2006-01-02 15:04:05")) err = test.Destroy() if err != nil { return err } + fmt.Printf("Restore %s\n", time.Now().Format("2006-01-02 15:04:05")) err = test.Restore() if err != nil { return err } + fmt.Printf("Verify %s\n", time.Now().Format("2006-01-02 15:04:05")) err = test.Verify() if err != nil { return err } + fmt.Printf("Finish run test %s\n", time.Now().Format("2006-01-02 15:04:05")) return nil } diff --git a/test/e2e/types.go b/test/e2e/types.go index 5c03be932..99f4c23cb 100644 --- a/test/e2e/types.go +++ b/test/e2e/types.go @@ -24,6 +24,8 @@ import ( . "github.com/vmware-tanzu/velero/test/e2e/util/k8s" ) +const StorageClassName = "e2e-storage-class" + var UUIDgen uuid.UUID var VeleroCfg VeleroConfig diff --git a/test/e2e/upgrade/upgrade.go b/test/e2e/upgrade/upgrade.go index c9f0c00ab..d2fe8830d 100644 --- a/test/e2e/upgrade/upgrade.go +++ b/test/e2e/upgrade/upgrade.go @@ -181,7 +181,7 @@ func BackupUpgradeRestoreTest(useVolumeSnapshots bool, veleroCLI2Version VeleroC snapshotCheckPoint.NamespaceBackedUp = upgradeNamespace By("Snapshot should be created in cloud object store", func() { snapshotCheckPoint, err := GetSnapshotCheckPoint(*veleroCfg.ClientToInstallVelero, veleroCfg, 2, - upgradeNamespace, backupName, KibishiiPodNameList) + upgradeNamespace, backupName, KibishiiPVCNameList) Expect(err).NotTo(HaveOccurred(), "Fail to get snapshot checkpoint") Expect(SnapshotsShouldBeCreatedInCloud(veleroCfg.CloudProvider, veleroCfg.CloudCredentialsFile, veleroCfg.BSLBucket, diff --git a/test/e2e/util/k8s/namespace.go b/test/e2e/util/k8s/namespace.go index e19582dd7..d04f4e456 100644 --- a/test/e2e/util/k8s/namespace.go +++ b/test/e2e/util/k8s/namespace.go @@ -89,14 +89,14 @@ func DeleteNamespace(ctx context.Context, client TestClient, namespace string, w }) } -func CleanupNamespacesWithPoll(ctx context.Context, client TestClient, nsBaseName string) error { +func CleanupNamespacesWithPoll(ctx context.Context, client TestClient, CaseBaseName string) error { namespaces, err := client.ClientGo.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) if err != nil { return errors.Wrap(err, "Could not retrieve namespaces") } for _, checkNamespace := range namespaces.Items { - if strings.HasPrefix(checkNamespace.Name, nsBaseName) { + if strings.HasPrefix(checkNamespace.Name, CaseBaseName) { err := DeleteNamespace(ctx, client, checkNamespace.Name, true) if err != nil { return errors.Wrapf(err, "Could not delete namespace %s", checkNamespace.Name) @@ -107,13 +107,13 @@ func CleanupNamespacesWithPoll(ctx context.Context, client TestClient, nsBaseNam return nil } -func CleanupNamespaces(ctx context.Context, client TestClient, nsBaseName string) error { +func CleanupNamespaces(ctx context.Context, client TestClient, CaseBaseName string) error { namespaces, err := client.ClientGo.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) if err != nil { return errors.Wrap(err, "Could not retrieve namespaces") } for _, checkNamespace := range namespaces.Items { - if strings.HasPrefix(checkNamespace.Name, nsBaseName) { + if strings.HasPrefix(checkNamespace.Name, CaseBaseName) { err = client.ClientGo.CoreV1().Namespaces().Delete(ctx, checkNamespace.Name, metav1.DeleteOptions{}) if err != nil { return errors.Wrapf(err, "Could not delete namespace %s", checkNamespace.Name) diff --git a/test/e2e/util/k8s/rbac.go b/test/e2e/util/k8s/rbac.go index 7486e8761..ac7cfb56d 100644 --- a/test/e2e/util/k8s/rbac.go +++ b/test/e2e/util/k8s/rbac.go @@ -75,7 +75,7 @@ func GetClusterRoleBinding(ctx context.Context, client TestClient, rolebinding s return client.ClientGo.RbacV1().ClusterRoleBindings().Get(ctx, rolebinding, metav1.GetOptions{}) } -func CleanupClusterRole(ctx context.Context, client TestClient, nsBaseName string) error { +func CleanupClusterRole(ctx context.Context, client TestClient, CaseBaseName string) error { clusterroles, err := client.ClientGo.RbacV1().ClusterRoles().List(ctx, metav1.ListOptions{}) if err != nil { @@ -83,7 +83,7 @@ func CleanupClusterRole(ctx context.Context, client TestClient, nsBaseName strin } for _, checkClusterRole := range clusterroles.Items { - if strings.HasPrefix(checkClusterRole.Name, "clusterrole-"+nsBaseName) { + if strings.HasPrefix(checkClusterRole.Name, "clusterrole-"+CaseBaseName) { fmt.Printf("Cleaning up clusterrole %s\n", checkClusterRole.Name) err = client.ClientGo.RbacV1().ClusterRoles().Delete(ctx, checkClusterRole.Name, metav1.DeleteOptions{}) if err != nil { @@ -94,7 +94,7 @@ func CleanupClusterRole(ctx context.Context, client TestClient, nsBaseName strin return nil } -func CleanupClusterRoleBinding(ctx context.Context, client TestClient, nsBaseName string) error { +func CleanupClusterRoleBinding(ctx context.Context, client TestClient, CaseBaseName string) error { clusterrolebindings, err := client.ClientGo.RbacV1().ClusterRoleBindings().List(ctx, metav1.ListOptions{}) if err != nil { @@ -102,7 +102,7 @@ func CleanupClusterRoleBinding(ctx context.Context, client TestClient, nsBaseNam } for _, checkClusterRoleBinding := range clusterrolebindings.Items { - if strings.HasPrefix(checkClusterRoleBinding.Name, "clusterrolebinding-"+nsBaseName) { + if strings.HasPrefix(checkClusterRoleBinding.Name, "clusterrolebinding-"+CaseBaseName) { fmt.Printf("Cleaning up clusterrolebinding %s\n", checkClusterRoleBinding.Name) err = client.ClientGo.RbacV1().ClusterRoleBindings().Delete(ctx, checkClusterRoleBinding.Name, metav1.DeleteOptions{}) if err != nil { diff --git a/test/e2e/util/kibishii/kibishii_utils.go b/test/e2e/util/kibishii/kibishii_utils.go index 2f103508a..e177a2c37 100644 --- a/test/e2e/util/kibishii/kibishii_utils.go +++ b/test/e2e/util/kibishii/kibishii_utils.go @@ -49,11 +49,12 @@ type KibishiiData struct { } var DefaultKibishiiData = &KibishiiData{2, 10, 10, 1024, 1024, 0, 2} -var KibishiiPodNameList = []string{"kibishii-deployment-0", "kibishii-deployment-1"} +var KibishiiPVCNameList = []string{"kibishii-data-kibishii-deployment-0", "kibishii-data-kibishii-deployment-1"} // RunKibishiiTests runs kibishii tests on the provider. func RunKibishiiTests(veleroCfg VeleroConfig, backupName, restoreName, backupLocation, kibishiiNamespace string, useVolumeSnapshots, defaultVolumesToFsBackup bool) error { + pvCount := len(KibishiiPVCNameList) client := *veleroCfg.ClientToInstallVelero oneHourTimeout, ctxCancel := context.WithTimeout(context.Background(), time.Minute*60) defer ctxCancel() @@ -79,12 +80,13 @@ func RunKibishiiTests(veleroCfg VeleroConfig, backupName, restoreName, backupLoc } } }() - + fmt.Printf("KibishiiPrepareBeforeBackup %s\n", time.Now().Format("2006-01-02 15:04:05")) if err := KibishiiPrepareBeforeBackup(oneHourTimeout, client, providerName, kibishiiNamespace, registryCredentialFile, veleroFeatures, kibishiiDirectory, useVolumeSnapshots, DefaultKibishiiData); err != nil { return errors.Wrapf(err, "Failed to install and prepare data for kibishii %s", kibishiiNamespace) } + fmt.Printf("KibishiiPrepareBeforeBackup done %s\n", time.Now().Format("2006-01-02 15:04:05")) var BackupCfg BackupConfig BackupCfg.BackupName = backupName @@ -94,17 +96,14 @@ func RunKibishiiTests(veleroCfg VeleroConfig, backupName, restoreName, backupLoc BackupCfg.DefaultVolumesToFsBackup = defaultVolumesToFsBackup BackupCfg.Selector = "" BackupCfg.ProvideSnapshotsVolumeParam = veleroCfg.ProvideSnapshotsVolumeParam + + fmt.Printf("VeleroBackupNamespace %s\n", time.Now().Format("2006-01-02 15:04:05")) if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, BackupCfg); err != nil { RunDebug(context.Background(), veleroCLI, veleroNamespace, backupName, "") return errors.Wrapf(err, "Failed to backup kibishii namespace %s", kibishiiNamespace) } - var snapshotCheckPoint SnapshotCheckPoint - var err error - pvbs, err := GetPVB(oneHourTimeout, veleroCfg.VeleroNamespace, kibishiiNamespace) + fmt.Printf("VeleroBackupNamespace done %s\n", time.Now().Format("2006-01-02 15:04:05")) if useVolumeSnapshots { - if err != nil || len(pvbs) != 0 { - return errors.Wrapf(err, "failed to get PVB for namespace %s", kibishiiNamespace) - } if providerName == "vsphere" { // Wait for uploads started by the Velero Plug-in for vSphere to complete // TODO - remove after upload progress monitoring is implemented @@ -113,7 +112,7 @@ func RunKibishiiTests(veleroCfg VeleroConfig, backupName, restoreName, backupLoc return errors.Wrapf(err, "Error waiting for uploads to complete") } } - snapshotCheckPoint, err = GetSnapshotCheckPoint(client, veleroCfg, 2, kibishiiNamespace, backupName, KibishiiPodNameList) + snapshotCheckPoint, err := GetSnapshotCheckPoint(client, veleroCfg, 2, kibishiiNamespace, backupName, KibishiiPVCNameList) if err != nil { return errors.Wrap(err, "Fail to get snapshot checkpoint") } @@ -124,9 +123,13 @@ func RunKibishiiTests(veleroCfg VeleroConfig, backupName, restoreName, backupLoc return errors.Wrap(err, "exceed waiting for snapshot created in cloud") } } else { - if err != nil || len(pvbs) != 2 { + pvbs, err := GetPVB(oneHourTimeout, veleroCfg.VeleroNamespace, kibishiiNamespace) + if err != nil { return errors.Wrapf(err, "failed to get PVB for namespace %s", kibishiiNamespace) } + if len(pvbs) != pvCount { + return errors.New(fmt.Sprintf("PVB count %d should be %d in namespace %s", len(pvbs), pvCount, kibishiiNamespace)) + } if providerName == "vsphere" { // Wait for uploads started by the Velero Plug-in for vSphere to complete // TODO - remove after upload progress monitoring is implemented @@ -143,11 +146,14 @@ func RunKibishiiTests(veleroCfg VeleroConfig, backupName, restoreName, backupLoc time.Sleep(5 * time.Minute) if strings.EqualFold(veleroFeatures, "EnableCSI") { _, err = GetSnapshotCheckPoint(*veleroCfg.ClientToInstallVelero, veleroCfg, 0, - kibishiiNamespace, backupName, KibishiiPodNameList) + kibishiiNamespace, backupName, KibishiiPVCNameList) + if err != nil { + return errors.Wrap(err, "failed to get snapshot checkPoint") + } } else { err = SnapshotsShouldNotExistInCloud(veleroCfg.CloudProvider, veleroCfg.CloudCredentialsFile, veleroCfg.BSLBucket, veleroCfg.BSLConfig, - backupName, snapshotCheckPoint) + backupName, SnapshotCheckPoint{}) if err != nil { return errors.Wrap(err, "exceed waiting for snapshot created in cloud") } @@ -155,7 +161,7 @@ func RunKibishiiTests(veleroCfg VeleroConfig, backupName, restoreName, backupLoc } } - fmt.Printf("Simulating a disaster by removing namespace %s\n", kibishiiNamespace) + fmt.Printf("Simulating a disaster by removing namespace %s %s\n", kibishiiNamespace, time.Now().Format("2006-01-02 15:04:05")) if err := DeleteNamespace(oneHourTimeout, client, kibishiiNamespace, true); err != nil { return errors.Wrapf(err, "failed to delete namespace %s", kibishiiNamespace) } @@ -168,21 +174,24 @@ func RunKibishiiTests(veleroCfg VeleroConfig, backupName, restoreName, backupLoc time.Sleep(5 * time.Minute) } + fmt.Printf("VeleroRestore %s\n", time.Now().Format("2006-01-02 15:04:05")) if err := VeleroRestore(oneHourTimeout, veleroCLI, veleroNamespace, restoreName, backupName, ""); err != nil { RunDebug(context.Background(), veleroCLI, veleroNamespace, "", restoreName) return errors.Wrapf(err, "Restore %s failed from backup %s", restoreName, backupName) } if !useVolumeSnapshots { pvrs, err := GetPVR(oneHourTimeout, veleroCfg.VeleroNamespace, kibishiiNamespace) - if err != nil || len(pvrs) != 2 { - return errors.Wrapf(err, "failed to get PVB for namespace %s", kibishiiNamespace) + if err != nil { + return errors.Wrapf(err, "failed to get PVR for namespace %s", kibishiiNamespace) + } else if len(pvrs) != pvCount { + return errors.New(fmt.Sprintf("PVR count %d is not as expected %d", len(pvrs), pvCount)) } } - + fmt.Printf("KibishiiVerifyAfterRestore %s\n", time.Now().Format("2006-01-02 15:04:05")) if err := KibishiiVerifyAfterRestore(client, kibishiiNamespace, oneHourTimeout, DefaultKibishiiData); err != nil { return errors.Wrapf(err, "Error verifying kibishii after restore") } - fmt.Printf("kibishii test completed successfully\n") + fmt.Printf("kibishii test completed successfully %s\n", time.Now().Format("2006-01-02 15:04:05")) return nil } @@ -281,35 +290,25 @@ func waitForKibishiiPods(ctx context.Context, client TestClient, kibishiiNamespa func KibishiiPrepareBeforeBackup(oneHourTimeout context.Context, client TestClient, providerName, kibishiiNamespace, registryCredentialFile, veleroFeatures, kibishiiDirectory string, useVolumeSnapshots bool, kibishiiData *KibishiiData) error { - serviceAccountName := "default" - - // wait until the service account is created before patch the image pull secret - if err := WaitUntilServiceAccountCreated(oneHourTimeout, client, kibishiiNamespace, serviceAccountName, 10*time.Minute); err != nil { - return errors.Wrapf(err, "failed to wait the service account %q created under the namespace %q", serviceAccountName, kibishiiNamespace) - } - - // add the image pull secret to avoid the image pull limit issue of Docker Hub - if err := PatchServiceAccountWithImagePullSecret(oneHourTimeout, client, kibishiiNamespace, serviceAccountName, registryCredentialFile); err != nil { - return errors.Wrapf(err, "failed to patch the service account %q under the namespace %q", serviceAccountName, kibishiiNamespace) - } - + fmt.Printf("installKibishii %s\n", time.Now().Format("2006-01-02 15:04:05")) if err := installKibishii(oneHourTimeout, kibishiiNamespace, providerName, veleroFeatures, kibishiiDirectory, useVolumeSnapshots); err != nil { return errors.Wrap(err, "Failed to install Kibishii workload") } - // wait for kibishii pod startup // TODO - Fix kibishii so we can check that it is ready to go - fmt.Printf("Waiting for kibishii pods to be ready\n") + fmt.Printf("Waiting for kibishii pods to be ready %s\n", time.Now().Format("2006-01-02 15:04:05")) if err := waitForKibishiiPods(oneHourTimeout, client, kibishiiNamespace); err != nil { return errors.Wrapf(err, "Failed to wait for ready status of kibishii pods in %s", kibishiiNamespace) } if kibishiiData == nil { kibishiiData = DefaultKibishiiData } + fmt.Printf("generateData %s\n", time.Now().Format("2006-01-02 15:04:05")) if err := generateData(oneHourTimeout, kibishiiNamespace, kibishiiData); err != nil { return errors.Wrap(err, "Failed to generate data") } + fmt.Printf("generateData done %s\n", time.Now().Format("2006-01-02 15:04:05")) return nil } diff --git a/test/e2e/util/velero/install.go b/test/e2e/util/velero/install.go index 34a58e802..112e041d5 100644 --- a/test/e2e/util/velero/install.go +++ b/test/e2e/util/velero/install.go @@ -55,6 +55,7 @@ type installOptions struct { } func VeleroInstall(ctx context.Context, veleroCfg *VeleroConfig) error { + fmt.Printf("Velero install %s\n", time.Now().Format("2006-01-02 15:04:05")) if veleroCfg.CloudProvider != "kind" { fmt.Printf("For cloud platforms, object store plugin provider will be set as cloud provider") veleroCfg.ObjectStoreProvider = veleroCfg.CloudProvider @@ -109,7 +110,7 @@ func VeleroInstall(ctx context.Context, veleroCfg *VeleroConfig) error { RunDebug(context.Background(), veleroCfg.VeleroCLI, veleroCfg.VeleroNamespace, "", "") return errors.WithMessagef(err, "Failed to install Velero in the cluster") } - + fmt.Printf("Finish velero install %s\n", time.Now().Format("2006-01-02 15:04:05")) return nil } diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index 3557710a2..500f6cd17 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -336,6 +336,20 @@ func VeleroBackupNamespace(ctx context.Context, veleroCLI, veleroNamespace strin if backupCfg.ProvideSnapshotsVolumeParam && !backupCfg.UseVolumeSnapshots { args = append(args, "--snapshot-volumes=false") } // if "--snapshot-volumes" is not provide, snapshot should be taken as default behavior. + } else { // DefaultVolumesToFsBackup is false + // Althrough DefaultVolumesToFsBackup is false, but probably DefaultVolumesToFsBackup + // was set to true in installation CLI in snapshot volume test, so set DefaultVolumesToFsBackup + // to false specifically to make sure volume snapshot was taken + if backupCfg.UseVolumeSnapshots { + if backupCfg.UseResticIfFSBackup { + args = append(args, "--default-volumes-to-restic=false") + } else { + args = append(args, "--default-volumes-to-fs-backup=false") + } + } + // Also Althrough DefaultVolumesToFsBackup is false, but probably DefaultVolumesToFsBackup + // was set to true in installation CLI in FS volume backup test, so do nothing here, no DefaultVolumesToFsBackup + // appear in backup CLI } if backupCfg.BackupLocation != "" { args = append(args, "--storage-location", backupCfg.BackupLocation) @@ -613,7 +627,7 @@ func WaitForVSphereUploadCompletion(ctx context.Context, timeout time.Duration, actualCount := 0 for _, curLine := range lines { - fmt.Println(curLine) + fmt.Printf("%s %s\n", curLine, time.Now().Format("2006-01-02 15:04:05")) comps := strings.Split(curLine, "=") // SnapshotPhase represents the lifecycle phase of a Snapshot. // New - No work yet, next phase is InProgress @@ -654,7 +668,7 @@ func WaitForVSphereUploadCompletion(ctx context.Context, timeout time.Duration, return err } -func GetVsphereSnapshotIDs(ctx context.Context, timeout time.Duration, namespace string, podNameList []string) ([]string, error) { +func GetVsphereSnapshotIDs(ctx context.Context, timeout time.Duration, namespace string, pvcNameList []string) ([]string, error) { checkSnapshotCmd := exec.CommandContext(ctx, "kubectl", "get", "-n", namespace, "snapshots.backupdriver.cnsdp.vmware.com", "-o=jsonpath='{range .items[*]}{.spec.resourceHandle.name}{\"=\"}{.status.snapshotID}{\"\\n\"}{end}'") fmt.Printf("checkSnapshotCmd cmd =%v\n", checkSnapshotCmd) @@ -674,8 +688,8 @@ func GetVsphereSnapshotIDs(ctx context.Context, timeout time.Duration, namespace continue } var Exist bool - for _, podName := range podNameList { - if podName != "" && strings.Contains(curLine, podName) { + for _, pvcName := range pvcNameList { + if pvcName != "" && strings.Contains(curLine, pvcName) { Exist = true break } @@ -1056,12 +1070,12 @@ func GetResticRepositories(ctx context.Context, veleroNamespace, targetNamespace return common.GetListByCmdPipes(ctx, cmds) } -func GetSnapshotCheckPoint(client TestClient, VeleroCfg VeleroConfig, expectCount int, namespaceBackedUp, backupName string, kibishiiPodNameList []string) (SnapshotCheckPoint, error) { +func GetSnapshotCheckPoint(client TestClient, VeleroCfg VeleroConfig, expectCount int, namespaceBackedUp, backupName string, KibishiiPVCNameList []string) (SnapshotCheckPoint, error) { var snapshotCheckPoint SnapshotCheckPoint var err error snapshotCheckPoint.ExpectCount = expectCount snapshotCheckPoint.NamespaceBackedUp = namespaceBackedUp - snapshotCheckPoint.PodName = kibishiiPodNameList + snapshotCheckPoint.PodName = KibishiiPVCNameList if VeleroCfg.CloudProvider == "azure" && strings.EqualFold(VeleroCfg.Features, "EnableCSI") { snapshotCheckPoint.EnableCSI = true if snapshotCheckPoint.SnapshotIDList, err = util.CheckVolumeSnapshotCR(client, backupName, expectCount); err != nil { diff --git a/tilt-resources/examples/deployment.yaml b/tilt-resources/examples/deployment.yaml index b5bf4fdf6..bda59ae28 100644 --- a/tilt-resources/examples/deployment.yaml +++ b/tilt-resources/examples/deployment.yaml @@ -101,7 +101,7 @@ spec: secret: secretName: cloud-credentials --- -apiVersion: rbac.authorization.k8s.io/v1beta1 +apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: labels: