Compare commits

...

142 Commits

Author SHA1 Message Date
Steve Kriss
9d9c232729 add v1.1.0-beta.1 changelog (#1733)
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-08-07 07:51:46 -06:00
Nolan Brubaker
d5dd39c941 Merge pull request #1732 from skriss/v1.1-beta.1-docs
generate v1.1.0-beta.1 docs
2019-08-06 18:01:10 -04:00
Steve Kriss
d881a10fba generate v1.1.0-beta.1 docs
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-08-06 15:14:16 -06:00
Steve Kriss
80692a8a39 Properly restore PVs with a reclaim policy of Retain and restic backups (#1713)
* reorganize persistent volume restore code for clarity

Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-08-06 13:40:35 -07:00
KubeKween
4accb8512a Restore from PodVolumeBackups (#1723)
* Restore from PodVolumeBackups

Signed-off-by: Carlisia <carlisiac@vmware.com>

* Partially address code reviews

Signed-off-by: Carlisia <carlisiac@vmware.com>

* Partially address code reviews #2

Signed-off-by: Carlisia <carlisiac@vmware.com>

* Clean up struct

Signed-off-by: Carlisia <carlisiac@vmware.com>

* Fix log messages

Signed-off-by: Carlisia <carlisiac@vmware.com>

* Fix tests

Signed-off-by: Carlisia <carlisiac@vmware.com>

* Clean up

Signed-off-by: Carlisia <carlisiac@vmware.com>

* Add changelog

Signed-off-by: Carlisia <carlisiac@vmware.com>
2019-08-06 13:17:36 -07:00
Adnan Abdulhussein
4e1b1f9457 use Backup CR labels as tags for snapshots (#1729)
* use Backup CR labels as tags for snapshots

This allows users to define custom tags to be added to snapshots, by
specifying custom labels on the Backup CR with the `velero backup create
--labels` flag.

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-08-06 13:01:36 -07:00
Nolan Brubaker
de442c1106 Merge pull request #1727 from skriss/fix-pvb-sync
fix bugs preventing pod volume backups from syncing properly
2019-08-06 14:13:32 -04:00
Steve Kriss
6522ba7c42 fix bugs preventing pod volume backups from syncing properly
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-08-06 09:23:49 -06:00
Steve Kriss
a590fc9468 refactor GetBackupVolumeSnapshots and GetPodVolumeBackups (#1721)
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-08-05 15:42:01 -06:00
Steve Kriss
539e0b54c6 use the persistent --namespace flag in 'velero install' (#1722)
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-08-05 15:40:39 -06:00
Nolan Brubaker
a4e70456a1 Add resource limits to restic init container (#1677)
* Add resource limits to restic init container

Fixes #1201

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Start restic restore item action tests

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Get initial tests for restore action working

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Add new test case

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Move resource parsing into a shared function

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Fetch request/limits from plugin's ConfigMap

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Use builders

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Use moved ParseResourceRequirements function

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Move init container building inline

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Drop CPU limit down a bit and clarify error message

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Fix godoc

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Add resource requirements to doc

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>
2019-08-05 13:18:11 -06:00
Adnan Abdulhussein
2254635bcb print resource list metadata in velero backup describe --details (#1714)
* print resource list metadata in velero backup describe details

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* rewrite TestGetDownloadURL to test more scenarios

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* move backup printer helpers to backup_printer.go

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* move describe printer helpers back to backup_describer

and rename to prefix with describe* to indicate that they are used for the describe command

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* split backup and restore tests for TestGetDownloadURL

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* friendlier error message when backup resource list missing

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-08-05 12:23:20 -06:00
Adnan Abdulhussein
07525bd593 store backup resource list metadata in object storage (#1709)
* move backedUpItems to pkg/backup.Request struct

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* construct resource itemKey field from gvk

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* store backup resource list metadata in object storage

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* remove debug log

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* fix formatting

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* add missing license blocks and split BackupInfo struct lines

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* add test for checking BackedUpItems matches tarball contents

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* add comment to explain test

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-08-05 11:15:55 -06:00
Nolan Brubaker
635dd27e1a Make secret file optional on install (#1699)
* Make secret file optional on install

Fixes #1689

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>
2019-08-01 15:57:36 -07:00
Adnan Abdulhussein
2a6929d453 add velero install flags for configuring restic resource requirements (#1710)
* add velero install flags for configuring restic resource requirements

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-07-31 12:24:47 -07:00
Steve Kriss
b24a603711 periodically check for stale restic repo locks (#1708)
* periodically check for stale restic repo locks

Signed-off-by: Steve Kriss <krisss@vmware.com>

* changelog

Signed-off-by: Steve Kriss <krisss@vmware.com>

* only try to init a restic repo if it doesn't already exist

Signed-off-by: Steve Kriss <krisss@vmware.com>

* reword comment

Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-31 10:52:23 -07:00
Steve Kriss
f2d06bc5e9 strip leading/trailing slashes from BSL bucket & prefix vals (#1694)
* strip leading/trailing slashes from BSL bucket & prefix vals

Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-31 08:27:12 -07:00
Steve Kriss
4543258970 Update tests to use object builders in pkg/builder (#1707)
* add pkg/builder with BackupBuilder and ObjectMeta functional options

Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-31 07:46:48 -07:00
carthewd
22eca22ac8 Add JSON as an option for log output (#1654)
* Add JSON as an option for log output

Signed-off-by: Donovan Carthew <donovan.carthew@gmail.com>
2019-07-30 16:29:34 -07:00
Pierre Ozoux
6188cdffb0 Fixes documentation about aws region. (#1690)
Signed-off-by: pierreozoux <pierre@ozoux.net>
2019-07-30 10:16:05 -06:00
Adnan Abdulhussein
935107e1a2 update backup resource list proposal to use gzipped json (#1706)
Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-07-29 16:08:43 -07:00
Steve Kriss
bf247836e6 Migrate restic backup tests (#1703)
* migrate pkg/backup restic tests to new structure

Signed-off-by: Steve Kriss <krisss@vmware.com>

* rename backup_new_test.go to backup_test.go

Signed-off-by: Steve Kriss <krisss@vmware.com>

* use pod volume backup builder

Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-29 15:29:44 -07:00
Steve Kriss
b24e940399 V1.1 docs updates (#1704)
* remove docs header about project rename

Signed-off-by: Steve Kriss <krisss@vmware.com>

* remove Upgrade to 1.0 page from master TOC

Signed-off-by: Steve Kriss <krisss@vmware.com>

* standardize TOC titles

Signed-off-by: Steve Kriss <krisss@vmware.com>

* add backup/restore docs for undocumented features

Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-29 14:37:18 -07:00
Adnan Abdulhussein
248ee89123 add configurable CPU/memory requests/limits for velero pod on install (#1678)
* add configurable CPU/memory requests/limits for velero pod on install

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-07-29 15:13:06 -04:00
Adnan Abdulhussein
52d97e7bd7 proposal: expose list of backed up resources in backup details (#1682)
* proposal: expose list of backed up resources in backup details

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-07-29 14:00:56 -04:00
ThoTischner
b8f3a008cb Add docu for restic pvc backup annotation watch controller (#1680)
Signed-off-by: Thomas Tischner <tti@bitsbeats.com>
2019-07-25 12:54:09 -06:00
Nolan Brubaker
25a481f6b1 Clarify language on the restic docs (#1700)
Signed-off-by: Nolan Brubaker <brubakern@vmware.com>
2019-07-25 08:59:47 -06:00
KubeKween
3b9af8c654 Store PodVolumeBackups in obj storage & use as source of truth (#1577)
* Store PodVolumeBackups in object storage

Signed-off-by: Carlisia <carlisiac@vmware.com>
2019-07-24 15:51:20 -04:00
Nolan Brubaker
f80e1dc390 Enable Jekyll incremental rebuilds (#1695)
Without the incremental switch, live rebuilds were taking 20s (on a
Linux host) for a single-line change. With the switch, they dropped to
8s.

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>
2019-07-23 12:45:54 -06:00
Steve Kriss
3c4dd3e526 fix typo in schedule create example (#1693)
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-23 11:02:38 -07:00
Steve Kriss
8336d95f57 tweak use case bullets in readme for clarity (#1692)
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-23 10:13:05 -07:00
Nolan Brubaker
5f409f12c8 Document --pod-annotations install argument (#1674)
* Document --pod-annotations install argument

Fixes #1667

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Revert v1.0.0 edits

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Make pod annotations example commands complete

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>
2019-07-19 12:12:57 -07:00
Adnan Abdulhussein
65906efffa fix Deployments in examples for apps/v1 selector requirement (#1684)
The apps/v1 API makes the selector in Deployments a required property.

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-07-19 11:40:47 -06:00
Nolan Brubaker
c8bac5bfae Merge pull request #1681 from jonasrosland/add-survey
Add link to the Velero user survey
2019-07-18 12:23:20 -04:00
jonasrosland
92ac710dab Add link to the Velero user survey
Signed-off-by: jonasrosland <jrosland@vmware.com>
2019-07-18 12:13:28 -04:00
Nolan Brubaker
c69b94da76 Merge pull request #1649 from skriss/fix-hooks-link
fix link for hooks example
2019-07-17 18:48:47 -04:00
Steve Kriss
927d2775bf add make gen-docs to generate a new versioned docs directory (#1662)
* add make gen-docs to generate a new versioned docs directory

Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-17 18:44:13 -04:00
Steve Kriss
1594bdc8d0 fix jekyll build errors (#1666)
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-17 14:24:18 -07:00
Nolan Brubaker
aa8c0cd471 Honor kube client flags in install command (#1656)
Flags specifying the kubeconfig or kubecontext to use weren't actually
being used by the install command.

Fixes #1651

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>
2019-07-17 14:19:14 -07:00
Nolan Brubaker
3124570c7f Remove references to apps/v1beta1 API group (#1673)
* Remove references to apps/v1beta1 API group

In Kubernetes v1.16, the apps/v1 API group will be the default served
for relevant resources.

Update any references to apps/v1beta1 for fowards compatibility.

Fixes #1672

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Update API group on plugin commands

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>
2019-07-17 15:03:48 -06:00
Nolan Brubaker
1d54996fce Document Helm secrets format. (#1665)
Signed-off-by: Nolan Brubaker <brubakern@vmware.com>
2019-07-17 14:03:31 -07:00
Nolan Brubaker
5841f82ee9 Merge pull request #1671 from prydonius/1663-update-azure-sp-creation-docs
docs: remove custom password option from azure service principle create command
2019-07-17 13:42:42 -04:00
Adnan Abdulhussein
1c69bafeeb docs: remove custom password option from azure service principle create command
Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-07-16 15:13:27 -07:00
Steve Kriss
8e098e2f6c document the ConfigMap convention for plugins (#1670)
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-16 14:21:40 -07:00
Adnan Abdulhussein
500d5485b1 omit storageClassName in nginx example to use default StorageClass (#1669)
Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-07-16 11:15:35 -06:00
Steve Kriss
1bb167ef90 add restore item action to change PVC/PV storage class name (#1621)
* add restore item action to change PVC/PV storage class name

Signed-off-by: Steve Kriss <krisss@vmware.com>

* code review

Signed-off-by: Steve Kriss <krisss@vmware.com>

* change existing plugin names to lowercase/hyphenated

Signed-off-by: Steve Kriss <krisss@vmware.com>

* add validation for new storage class name

Signed-off-by: Steve Kriss <krisss@vmware.com>

* add test cases

Signed-off-by: Steve Kriss <krisss@vmware.com>

* changelog

Signed-off-by: Steve Kriss <krisss@vmware.com>

* fix imports

Signed-off-by: Steve Kriss <krisss@vmware.com>

* update plugin names to be more consistent

Signed-off-by: Steve Kriss <krisss@vmware.com>

* update unit tests to use pkg/test object constructors

Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-15 11:19:38 -07:00
Steve Kriss
11194d1071 fix link for hooks example
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-11 12:25:16 -06:00
Nolan Brubaker
63964fc6f9 Append "/mount" to directory for CSI volumes (#1615)
CSI volumes are mounted one level deeper than "native" kubernetes
volumes, and this needs to be appended for proper restic support.

Fixes #1313.

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>
2019-07-10 15:16:21 -07:00
ThoTischner
d615cc6de0 openshift needs namespace annotation for restic to schedule on all nodes (#1629)
* openshift needs namespace annotation for restic to schedule on all nodes

Signed-off-by: Thomas Tischner <tti@bitsbeats.com>
2019-07-10 14:04:39 -07:00
Steve Kriss
8cde8fdbc7 update GitHub feature enhancement template (#1632)
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-10 13:45:54 -07:00
Steve Kriss
ac00185a5f add design/ dir and doc template (#1636)
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-10 13:35:45 -07:00
Nolan Brubaker
31973fbf04 Merge pull request #1640 from skriss/restore-test-refactor
Reorganize pkg/restore test code
2019-07-10 16:11:01 -04:00
Steve Kriss
d1025f7547 rename restore_new_test.go to restore_test.go
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-10 09:59:27 -06:00
Steve Kriss
678c02c560 move contents of restore_test.go into other files and delete it
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-10 09:59:01 -06:00
Nolan Brubaker
7724464017 Merge pull request #1605 from Prajyot-Parab/add-ppc64le-support
enable support for ppc64le architecture
2019-07-09 13:43:11 -04:00
Prajyot-Parab
f44bd53cf0 minor fixes
Signed-off-by: Prajyot-Parab <Prajyot.Parab@ibm.com>
2019-07-09 06:02:14 +00:00
Nolan Brubaker
2498ac6cab Merge pull request #1630 from jwmatthews/cli_namespace
bug fix: Fixed namespace usage with cli command 'version'
2019-07-08 13:42:29 -04:00
John Matthews
afa49f398f bug fix: Fixed namespace usage with cli command 'version'
Signed-off-by: John Matthews <jwmatthews@gmail.com>
2019-07-07 13:54:46 -04:00
Traci Kamp
f829dabcf4 Add pod-annotations CLI flag to the install command (#1626)
* allow users to specify additional Velero/restic pod annotations on the command line with the pod-annotations flag

Signed-off-by: Traci Kamp <traci.kamp@gmail.com>
2019-07-03 15:22:34 -07:00
Adnan Abdulhussein
eec5cc687e validate pod volumes hostpath mount on restic server startup (#1616)
Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-07-03 14:20:31 -06:00
Steve Kriss
bf00754280 fix schedule create examples to use hours as units (#1625)
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-03 12:20:49 -07:00
Nolan Brubaker
13cac85c1e Merge pull request #1614 from skriss/restore-pv-tests
migrate restore PV tests
2019-07-03 12:34:09 -04:00
Steve Kriss
567802299b move PV/PVC setup helpers into pkg/test
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-03 08:50:11 -06:00
Steve Kriss
adb93c33b1 migrate restore PV tests
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-07-03 08:50:09 -06:00
Nolan Brubaker
e371ba78b0 Merge pull request #1612 from skriss/restore-addl-items-bugs
properly filter additional items returned from restore item actions
2019-06-28 17:12:38 -04:00
Adnan Abdulhussein
2156124dfc record PodVolumeBackup/Restore start and completion timestamps (#1609)
* record PodVolumeBackup start and completion timestamps

adds startTimestamp and completionTimestamp fields to the
PodVolumeBackup status spec

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* record PodVolumeRestore start and completion timestamps

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-06-28 11:58:02 -04:00
Steve Kriss
db393ec199 properly filter additional items returned from restore item actions
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-28 08:59:23 -06:00
Steve Kriss
022099a62e Refactor pkg/restore tests (part 3) (#1611)
* add modifying restore item action tests

Signed-off-by: Steve Kriss <krisss@vmware.com>

* add restore item action additional items tests

Signed-off-by: Steve Kriss <krisss@vmware.com>

* migrate ShouldRestore test

Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-27 15:43:59 -07:00
Nolan Brubaker
55054f67a5 Merge pull request #1607 from skriss/fix-restore-action-bugs
Fix bugs in determining which restore item actions to run
2019-06-27 16:50:58 -04:00
Steve Kriss
75a96dfa92 fix bugs in finding applicable restore item actions
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-27 13:06:16 -06:00
Steve Kriss
81c2adc059 Refactor pkg/restore tests (part 2) (#1606)
* update TestPrioritizeResources to use real discovery helper

Signed-off-by: Steve Kriss <krisss@vmware.com>

* migrate invalid tarball contents tests

Signed-off-by: Steve Kriss <krisss@vmware.com>

* migrate item restore tests

Signed-off-by: Steve Kriss <krisss@vmware.com>

* migrate restore item action tests

Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-27 11:57:47 -07:00
Nolan Brubaker
dd96aa76db Merge pull request #1603 from skriss/restore-tests
Migrate restore tests to new structure (part 1)
2019-06-26 12:37:01 -04:00
Steve Kriss
0089fa4d93 migrate restore resource priorities test
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-26 10:24:02 -06:00
Nolan Brubaker
553df25710 Merge pull request #1604 from skriss/update-install-link
update README.md link to install docs, minor reorg install docs content
2019-06-26 12:12:16 -04:00
root
764de79cf5 add changelog
Signed-off-by: root <root@prajyot-ppc-velero-client.novalocal>
2019-06-26 11:13:49 +00:00
root
f4c64ae75a enable support for ppc64le architecture
Signed-off-by: root <root@prajyot-ppc-velero-client.novalocal>
2019-06-26 10:37:34 +00:00
Steve Kriss
c48586a8c7 update README.md link to install docs, minor reorg install docs content
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-25 09:00:40 -06:00
Steve Kriss
5d8ba1b90d migrate namespace mapping tests
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-24 16:57:30 -06:00
Steve Kriss
1a339f06ac migrate restore resource filtering tests
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-24 15:53:15 -06:00
Adnan Abdulhussein
b0bdaeea73 ensure correct backup item actions run with namespace selector (#1601)
* ensure correct backup item actions run with namespace selector

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* changelog

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* don't run backup item actions for namespace resources

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* simplify cluster-scope resources checks

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-06-24 15:50:25 -06:00
Nolan Brubaker
08fe7be851 Fix test conflicts from #1595 and #1598 (#1602)
Signed-off-by: Nolan Brubaker <brubakern@vmware.com>
2019-06-24 15:34:56 -06:00
Nolan Brubaker
67512a3808 Merge pull request #1595 from skriss/restore-test-refactor-1
initial refactoring for pkg/restore tests
2019-06-24 17:08:51 -04:00
Nolan Brubaker
37c7b618ad Merge pull request #1598 from skriss/rm-has-controller-owner
remove unused hasControllerOwner func and tests
2019-06-24 16:51:28 -04:00
Nolan Brubaker
d143137a70 Merge pull request #1599 from jonasrosland/add-github-link
Add GitHub link in the footer
2019-06-24 16:39:59 -04:00
Steve Kriss
5634a4f463 migrate backup hooks tests (#1591)
* migrate hooks tests

Signed-off-by: Steve Kriss <krisss@vmware.com>

* add more test cases

Signed-off-by: Steve Kriss <krisss@vmware.com>

* refactor to strongly typed expectation, add pre+post hook test case

Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-24 12:23:17 -07:00
Steve Kriss
19052994ed replace TestBackup with pkg/backup.Builder (#1593)
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-21 17:08:08 -04:00
jonasrosland
8cb9ee9eb8 Add GitHub link in the footer
Signed-off-by: jonasrosland <jrosland@vmware.com>
2019-06-21 16:53:00 -04:00
Steve Kriss
0d326a3903 remove unused hasControllerOwner func and tests
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-21 11:44:13 -06:00
Mickey Boxell
d916ae0a25 document Oracle Cloud Storage as a backup provider (#1570)
Signed-off-by: mickey.boxell <mickey.boxell@oracle.com>
2019-06-21 10:29:58 -06:00
Nolan Brubaker
b00e0e834a Merge pull request #1594 from jonasrosland/community-update
Add more community info - direct links to meetings and ZenHub
2019-06-21 12:20:34 -04:00
Steve Kriss
d421fcd85c initial structure for refactored pkg/restore tests
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-21 09:15:18 -06:00
Steve Kriss
0735ee7218 extract shared API test helpers to pkg/test
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-20 14:57:31 -06:00
jonasrosland
223aec8200 Add more community info - direct links to meetings and ZenHub
Signed-off-by: jonasrosland <jrosland@vmware.com>
2019-06-20 13:35:32 -04:00
Jonas Rosland
870743a28d Add redirect for docs (#1584)
* Add redirects for docs

Signed-off-by: jonasrosland <jrosland@vmware.com>
2019-06-19 12:20:56 -04:00
Adnan Abdulhussein
1f4139a5bf allow exclusion of resources using standard label (#1588)
* allow exclusion of resources using standard label

excludes any resources with the velero.io/exclude-from-backup=true label

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-06-19 10:58:02 -04:00
Steve Kriss
a2e88c4d3f Merge pull request #1585 from nrb/local-docs
Add make serve-docs target for local viewing
2019-06-18 14:00:09 -06:00
Adnan Abdulhussein
2d81e29276 ensure backup item action modifications reflected in tarball filepath (#1587)
* ensure backup item action modifications reflected in tarball filepath

This patch ensures the updated backup item's name and namespace are used
when constructing the filepath for the tarball.

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* changelog

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-06-18 12:19:00 -06:00
Ross Fenrick
5d3312b7b5 added top spacing and reduced bottom (#1583)
* added top spacing and reduced bottom

Signed-off-by: Ross Fenrick <rossf@heptio.com>

* added marign to h3's

Signed-off-by: Ross Fenrick <rossf@heptio.com>
2019-06-18 12:14:13 -06:00
Nolan Brubaker
fac3cd4a78 Merge pull request #1574 from skriss/backup-snapshot-tests
migrate backup PV snapshot tests
2019-06-17 14:47:38 -04:00
Stephen Carter
3ff95eaa40 Add restic instructions for Enterprise PKS (#1579)
* Add restic instructions for Enterprise PKS

Signed-off-by: Stephen Carter <carters@vmware.com>

* Add instructions to v1.0.0 docs

Signed-off-by: Stephen Carter <carters@vmware.com>
2019-06-14 15:35:44 -07:00
Nolan Brubaker
023d43d0fe Add make serve-docs target for local viewing
Signed-off-by: Nolan Brubaker <brubakern@vmware.com>
2019-06-14 15:15:36 -04:00
Nolan Brubaker
b807b449d6 Merge pull request #1582 from jonasrosland/add-adnan
Adding Adnan to the team
2019-06-14 15:07:01 -04:00
jonasrosland
1ded7c7207 Adding Adnan to the team
Signed-off-by: jonasrosland <jrosland@vmware.com>
2019-06-14 10:51:52 -04:00
Monica
f3850210aa Discontinue use of excerpt separators in blog posts. (#1578)
Signed-off-by: Monica rodriguezmo@vmware.com
2019-06-13 10:30:36 -07:00
Steve Kriss
e2bf39a027 migrate PV snapshot tests
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-12 13:03:32 -06:00
Steve Kriss
6513e8f30e migrate more backup action tests, remove obsolete test code (#1564)
* migrate more backup action tests, remove obsolete test code

Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-12 14:45:04 -04:00
Steve Kriss
49f52b54b2 Merge pull request #1562 from carlisia/c-bug-sort
Fix test comparison
2019-06-11 12:34:35 -06:00
Adnan Abdulhussein
74b575200c hide server commands in velero CLI (#1561)
* hide server commands in velero CLI

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* changelog

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* remove unnecessary comment

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-06-10 19:20:17 -06:00
Carlisia
520077c3a9 Fix test comparison
Signed-off-by: Carlisia <carlisiac@vmware.com>
2019-06-10 16:34:29 -07:00
Steve Kriss
e183c4b597 remove glog (#1559)
* remove glog refs, replace with klog equivalents

Signed-off-by: Steve Kriss <krisss@vmware.com>

* remove glog from deps

Signed-off-by: Steve Kriss <krisss@vmware.com>

* changelog

Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-10 15:44:44 -07:00
Steve Kriss
f0f7d31e1b move issue-template-gen from docs/ to hack/ (#1558)
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-10 15:22:27 -07:00
Steve Kriss
9a62d887d3 update go-plugin to latest for bug fix (#1560)
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-10 14:15:00 -07:00
Adnan Abdulhussein
d6c60b2dd5 fix panic when processing DeleteBackupRequest objs without labels (#1556)
This fix initialises an empty map if the request object's Labels map
is nil, allowing the controller to later add and modify labels on the
object.

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-06-10 10:04:51 -07:00
Steve Kriss
9e7ff4e3d9 Refactoring for backup item action tests (#1545)
* migrate and enhance backup item action tests

Signed-off-by: Steve Kriss <krisss@vmware.com>

* migrate terminating resource test

Signed-off-by: Steve Kriss <krisss@vmware.com>

* add godoc for test functions

Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-09 21:02:49 -07:00
Pranav Gaikwad
bb12cbd2d7 Support for multiple AWS profiles (#1548)
* added support for multiple AWS credential profiles

Signed-off-by: Pranav Gaikwad <pgaikwad@redhat.com>
2019-06-07 11:01:39 -07:00
Steve Kriss
16a08b82a9 Merge pull request #1543 from AliyunContainerService/master
add a link of velero plugin for alibabacloud to support-matrix docs
2019-06-07 08:13:30 -06:00
Shuwei Hao
c539e8ad63 add a link of velero plugin for alibabacloud to support-matrix docs
Signed-off-by: Shuwei Hao <haoshuwei24@gmail.com>
2019-06-07 22:00:55 +08:00
Adnan Abdulhussein
f1319be60b fixes inlining of command in GCP setup docs (#1547)
* fixes inlining of command in GCP setup docs

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
2019-06-06 16:39:05 -07:00
coonsd
7d9fc88eb3 Signed-off-by: David Coons <coonsd@oregonstate.edu> (#1550) 2019-06-06 06:10:31 -07:00
KubeKween
0a771e6a53 Add cmd to list plugins (#1535)
* Add cmd to list plugins

Signed-off-by: Carlisia <carlisiac@vmware.com>
2019-06-05 13:41:02 -04:00
Steve Kriss
bc7ee686d7 Merge pull request #1428 from guilhem/glob
Add ability to use wildcard in includes/excludes
2019-06-05 09:53:37 -06:00
Guilhem Lettron
81a26e4aad Add ability to use wildcard in includes/excludes
Use https://github.com/gobwas/glob as glob engine
Fix #373

Signed-off-by: Guilhem Lettron <guilhem@barpilot.io>
2019-06-05 16:28:25 +02:00
Steve Kriss
caa0bff5a3 Initial pkg/backup test refactoring (#1532)
Initial pkg/backup test refactoring

Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-06-04 19:47:22 -07:00
Steve Kriss
83c3143825 Merge pull request #1506 from nrb/ci-optimization
Only run CI when relevant files have changed
2019-05-31 09:57:56 -06:00
Nolan Brubaker
81287e4751 Only run CI when relevant files have changed
If nothing outside of the site directory was modified, don't run `make
ci`.

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>
2019-05-30 17:32:23 -04:00
KubeKween
0804f34644 Don't log error if restic volume is empty (#1480)
* Don't log error if restic volume is empty

Signed-off-by: Carlisia <carlisiac@vmware.com>
2019-05-30 12:48:21 -04:00
Steve Kriss
411d44a673 allow individual backup storage locations to be read-only (#1517)
* allow individual backup storage locations to be read-only

Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-05-29 14:21:25 -04:00
Steve Kriss
4e2e4cd5c4 Merge pull request #1490 from abessifi/restic-openshift
[DOC] Restic support on OpenShift
2019-05-29 08:29:42 -06:00
abessifi
108d826ca5 [DOC] Restic support on OpenShift
Signed-off-by: abessifi <ahmed.bessifi@gmail.com>
2019-05-29 14:12:10 +02:00
Jonas Rosland
bbb11a8d23 Docs updates - extra finishing touches (#1516)
* Docs formatting updates

Signed-off-by: Jonas Rosland <jrosland@vmware.com>
2019-05-28 12:42:27 -04:00
Nolan Brubaker
82e464672b Merge pull request #1526 from emenare/1521-emenare
1521: Change menu order for docs header.
2019-05-28 11:56:23 -04:00
Monica Rodriguez
035c297287 1521: Change menu order for docs header.
Signed-off-by: Monica rodriguezmo@vmware.com
2019-05-28 11:32:39 -04:00
Steve Kriss
02095d21d8 Merge pull request #1513 from jonasrosland/master
Fix formatting on AWS docs
2019-05-22 09:03:39 -06:00
Jonas Rosland
5f7bab945d Fix formatting on AWS docs
Signed-off-by: Jonas Rosland <jrosland@vmware.com>
2019-05-21 14:35:58 +02:00
Nolan Brubaker
ddb335475b Merge pull request #1510 from skriss/1.14-deps
upgrade to kubernetes 1.14 dependencies
2019-05-20 16:30:57 -04:00
Nolan Brubaker
915b3a1ddf Merge pull request #1509 from skriss/rm-cli-doc-gen-code
remove obsolete code for generating CLI ref docs (breaks go test ./...)
2019-05-20 16:28:16 -04:00
Steve Kriss
a1f26aa3a8 remove obsolete code for generating CLI ref docs (breaks go test ./...)
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-05-20 13:26:44 -06:00
Steve Kriss
9287505f62 fixes for breaking changes
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-05-20 13:25:36 -06:00
Steve Kriss
02c7df9ea0 update generated code
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-05-20 13:25:36 -06:00
Steve Kriss
13b6cbb4db update to kubernetes 1.14 deps
Signed-off-by: Steve Kriss <krisss@vmware.com>
2019-05-20 13:25:36 -06:00
Steve Kriss
029cee6bc3 Merge pull request #1504 from jonasrosland/1.0-blog
Add 1.0 release blog
2019-05-20 09:53:04 -06:00
Jonas Rosland
1ed5255f17 Add 1.0 release blog
Signed-off-by: Jonas Rosland <jrosland@vmware.com>
2019-05-20 14:54:06 +02:00
1102 changed files with 83618 additions and 35494 deletions

View File

@@ -4,6 +4,10 @@ about: Suggest an idea for this project
---
**Describe the problem/challenge you have**
[A description of the current limitation/problem/challenge that you are experiencing.]
**Describe the solution you'd like**
[A clear and concise description of what you want to happen.]

2
.gitignore vendored
View File

@@ -43,5 +43,7 @@ site/.sass-cache
site/.jekyll
site/.jekyll-metadata
site/.bundle
site/vendor
.ruby-version
.vs

View File

@@ -25,16 +25,21 @@ builds:
- amd64
- arm
- arm64
- ppc64le
ignore:
# don't build arm/arm64 for darwin or windows
- goos: darwin
goarch: arm
- goos: darwin
goarch: arm64
- goos: darwin
goarch: ppc64le
- goos: windows
goarch: arm
- goos: windows
goarch: arm64
- goos: windows
goarch: ppc64le
ldflags:
- -X "github.com/heptio/velero/pkg/buildinfo.Version={{ .Tag }}" -X "github.com/heptio/velero/pkg/buildinfo.GitSHA={{ .FullCommit }}" -X "github.com/heptio/velero/pkg/buildinfo.GitTreeState={{ .Env.GIT_TREE_STATE }}"
archive:

View File

@@ -8,4 +8,4 @@ sudo: required
services:
- docker
script: make ci
script: hack/ci-check.sh

View File

@@ -0,0 +1,19 @@
# Copyright 2019 the Velero contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
FROM ubuntu:bionic
LABEL maintainer="Steve Kriss <krisss@vmware.com>"
ENTRYPOINT ["/bin/bash", "-c", "while true; do sleep 10000; done"]

32
Dockerfile-velero-ppc64le Normal file
View File

@@ -0,0 +1,32 @@
# Copyright 2019 the Velero contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
FROM ubuntu:bionic
LABEL maintainer="Steve Kriss <krisss@vmware.com>"
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates wget && \
wget --quiet https://oplab9.parqtec.unicamp.br/pub/ppc64el/restic/restic-0.9.4 && \
mv restic-0.9.4 /usr/bin/restic && \
chmod +x /usr/bin/restic && \
apt-get remove -y wget && \
rm -rf /var/lib/apt/lists/*
ADD /bin/linux/ppc64le/velero /velero
USER nobody:nobody
ENTRYPOINT ["/velero"]

View File

@@ -0,0 +1,23 @@
# Copyright 2019 the Velero contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
FROM ubuntu:bionic
LABEL maintainer="Steve Kriss <krisss@vmware.com>"
ADD /bin/linux/ppc64le/velero-restic-restore-helper .
USER nobody:nobody
ENTRYPOINT [ "/velero-restic-restore-helper" ]

258
Gopkg.lock generated
View File

@@ -17,7 +17,7 @@
version = "v0.11.0"
[[projects]]
digest = "1:5b71d15be52cbb93f5115f51ace93798204f6b4a3df0992d0b6da8644f505984"
digest = "1:623dad7b6ddc6b93f983e9852a0785ed606f804d3541fa4b6178d7055b361306"
name = "github.com/Azure/azure-sdk-for-go"
packages = [
"services/compute/mgmt/2018-04-01/compute",
@@ -26,11 +26,11 @@
"version",
]
pruneopts = "NUT"
revision = "520918e6c8e8e1064154f51d13e02fad92b287b8"
version = "v19.0.0"
revision = "32916f57ad7b421f5fdaab86b73a795632fff117"
version = "v21.4.0"
[[projects]]
digest = "1:b825d8578481c8877ff3b9a3654d77a48577cc33e65f33c3678d7e3f134bf73d"
digest = "1:90df11ad9349a69d46e08211d47eb8db80311bf985a447dd88cb30d5b5f54add"
name = "github.com/Azure/go-autorest"
packages = [
"autorest",
@@ -39,11 +39,12 @@
"autorest/date",
"autorest/to",
"autorest/validation",
"logger",
"version",
]
pruneopts = "NUT"
revision = "bca49d5b51a50dc5bb17bbf6204c711c6dbded06"
version = "v10.14.0"
revision = "1ffcc8896ef6dfe022d90a4317d866f925cf0f9e"
version = "v11.1.2"
[[projects]]
digest = "1:f41188abdb95b92995643a927f5bdd208389822a8e1aba00d85633ae51b85c85"
@@ -128,14 +129,6 @@
pruneopts = "NUT"
revision = "944e07253867aacae43c04b2e6a239005443f33a"
[[projects]]
digest = "1:81466b4218bf6adddac2572a30ac733a9255919bc2f470b4827a317bd4ee1756"
name = "github.com/ghodss/yaml"
packages = ["."]
pruneopts = "NUT"
revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
version = "v1.0.0"
[[projects]]
digest = "1:021d6ee454d87208dd1cd731cd702d3521aa8a51ad2072fa7beffbb3d677d8bb"
name = "github.com/go-ini/ini"
@@ -144,6 +137,23 @@
revision = "20b96f641a5ea98f2f8619ff4f3e061cff4833bd"
version = "v1.28.2"
[[projects]]
digest = "1:6a7159c5f8f8826207545407a458b43ab6599c1c8a2271465c2e979ecea1dcd4"
name = "github.com/gobwas/glob"
packages = [
".",
"compiler",
"match",
"syntax",
"syntax/ast",
"syntax/lexer",
"util/runes",
"util/strings",
]
pruneopts = "NUT"
revision = "5ccd90ef52e1e632236f7326478d4faa74f99438"
version = "v0.2.3"
[[projects]]
digest = "1:a6afc27b2a73a5506832f3c5a1c19a30772cb69e7bd1ced4639eb36a55db224f"
name = "github.com/gogo/protobuf"
@@ -155,14 +165,6 @@
revision = "100ba4e885062801d56799d78530b73b178a78f3"
version = "v0.4"
[[projects]]
branch = "master"
digest = "1:e2b86e41f3d669fc36b50d31d32d22c8ac656c75aa5ea89717ce7177e134ff2a"
name = "github.com/golang/glog"
packages = ["."]
pruneopts = "NUT"
revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
[[projects]]
digest = "1:a98a0b00720dc3149bf3d0c8d5726188899e5bab2f5072b9a7ef82958fbc98b2"
name = "github.com/golang/protobuf"
@@ -178,14 +180,6 @@
revision = "b5d812f8a3706043e23a9cd5babf2e5423744d30"
version = "v1.3.1"
[[projects]]
branch = "master"
digest = "1:245bd4eb633039cd66106a5d340ae826d87f4e36a8602fcc940e14176fd26ea7"
name = "github.com/google/btree"
packages = ["."]
pruneopts = "NUT"
revision = "e89373fe6b4a7413d7acd6da1725b83ef713e6e4"
[[projects]]
branch = "master"
digest = "1:52c5834e2bebac9030c97cc0798ac11c3aa8a39f098aeb419f142533da6cd3cc"
@@ -214,17 +208,6 @@
revision = "ee43cbb60db7bd22502942cccbc39059117352ab"
version = "v0.1.0"
[[projects]]
branch = "master"
digest = "1:7fdf3223c7372d1ced0b98bf53457c5e89d89aecbad9a77ba9fcc6e01f9e5621"
name = "github.com/gregjones/httpcache"
packages = [
".",
"diskcache",
]
pruneopts = "NUT"
revision = "9cad4c3443a7200dd6400aef47183728de563a38"
[[projects]]
branch = "master"
digest = "1:32e5a56c443b5581e4bf6e74cdc78b5826d7e4c5df43883e2dc31e4d7f4ae98a"
@@ -234,14 +217,14 @@
revision = "ca137eb4b4389c9bc6f1a6d887f056bf16c00510"
[[projects]]
digest = "1:143aae8d04a6133eea9c6400b90a1f47ae1100b48a1636160aba861d1b26c5b2"
digest = "1:980fd2c6afd6c268284d46dd66671771184a4002f6d516492cc596d2ca003543"
name = "github.com/hashicorp/go-plugin"
packages = [
".",
"internal/plugin",
]
pruneopts = "NUT"
revision = "3f118e8ee104b6f22aeb12453fab56aed1356186"
revision = "a1bc61569a26c0f65865932c0d55743b0567c494"
[[projects]]
branch = "master"
@@ -294,12 +277,20 @@
version = "v1.3.0"
[[projects]]
digest = "1:8e36686e8b139f8fe240c1d5cf3a145bc675c22ff8e707857cdd3ae17b00d728"
digest = "1:0243cffa4a3410f161ee613dfdd903a636d07e838a42d341da95d81f42cd1d41"
name = "github.com/json-iterator/go"
packages = ["."]
pruneopts = "NUT"
revision = "1624edc4454b8682399def8740d46db5e4362ba4"
version = "v1.1.5"
revision = "ab8a2e0c74be9d3be70b3184d9acc634935ded82"
version = "1.1.4"
[[projects]]
branch = "master"
digest = "1:e94e69261097d7067fa28052bcf209be4c47b12c665b7e88116c96f905a77364"
name = "github.com/liggitt/tabwriter"
packages = ["."]
pruneopts = "NUT"
revision = "89fcab3d43de07060e4fd4c1547430ed57e87f24"
[[projects]]
digest = "1:13ada91f079028d1b4ca88e10a16439dcfa6541d26ed2e61e770f56d06301933"
@@ -349,22 +340,6 @@
revision = "4dadeb3030eda0273a12382bb2348ffc7c9d1a39"
version = "v1.0.0"
[[projects]]
branch = "master"
digest = "1:3bf17a6e6eaa6ad24152148a631d18662f7212e21637c2699bff3369b7f00fa2"
name = "github.com/petar/GoLLRB"
packages = ["llrb"]
pruneopts = "NUT"
revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4"
[[projects]]
digest = "1:6c6d91dc326ed6778783cff869c49fb2f61303cdd2ebbcf90abe53505793f3b6"
name = "github.com/peterbourgon/diskv"
packages = ["."]
pruneopts = "NUT"
revision = "5f041e8faa004a95c88a202771f4cc3e991971e6"
version = "v2.0.1"
[[projects]]
digest = "1:5cf3f025cbee5951a4ee961de067c8a89fc95a5adabead774f82822efabab121"
name = "github.com/pkg/errors"
@@ -569,18 +544,12 @@
[[projects]]
branch = "master"
digest = "1:0f6792185947c44cd78bc6a2f4399c44c7e85d406b3229a27d41f6cd0a8e982b"
digest = "1:97337ef8cb438f9e3a99ea91a300e916ed9a96fbf3ad50f9a020d30ea9f8692f"
name = "golang.org/x/text"
packages = [
"encoding",
"encoding/internal",
"encoding/internal/identifier",
"encoding/unicode",
"internal/gen",
"internal/triegen",
"internal/ucd",
"internal/utf8internal",
"runes",
"secure/bidirule",
"transform",
"unicode/bidi",
@@ -709,15 +678,14 @@
revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f"
[[projects]]
digest = "1:93e9a6515f47aaaf7f1c84617fc8c82db9216f7290c4d4149afeaf6936d9aa5e"
digest = "1:a937ed4322409fa22924f02124fd0727c19662f73cf15406646d19bdce972df2"
name = "k8s.io/api"
packages = [
"admission/v1beta1",
"admissionregistration/v1alpha1",
"admissionregistration/v1beta1",
"apps/v1",
"apps/v1beta1",
"apps/v1beta2",
"auditregistration/v1alpha1",
"authentication/v1",
"authentication/v1beta1",
"authorization/v1",
@@ -729,16 +697,20 @@
"batch/v1beta1",
"batch/v2alpha1",
"certificates/v1beta1",
"coordination/v1",
"coordination/v1beta1",
"core/v1",
"events/v1beta1",
"extensions/v1beta1",
"imagepolicy/v1alpha1",
"networking/v1",
"networking/v1beta1",
"node/v1alpha1",
"node/v1beta1",
"policy/v1beta1",
"rbac/v1",
"rbac/v1alpha1",
"rbac/v1beta1",
"scheduling/v1",
"scheduling/v1alpha1",
"scheduling/v1beta1",
"settings/v1alpha1",
@@ -747,22 +719,22 @@
"storage/v1beta1",
]
pruneopts = "NUT"
revision = "fd83cbc87e7632ccd8bbab63d2b673d4e0c631cc"
version = "kubernetes-1.12.0"
revision = "40a48860b5abbba9aa891b02b32da429b08d96a0"
version = "kubernetes-1.14.0"
[[projects]]
digest = "1:b8a1dcc5f4e559b7af185ba12dd341cb8c175ea3d36227a02699b251ae5fde05"
digest = "1:1d6160800196e00fc394f13ca8c1c0cdc360a170c1b6a9db0f0a1f9f1c4e9342"
name = "k8s.io/apiextensions-apiserver"
packages = [
"pkg/apis/apiextensions",
"pkg/apis/apiextensions/v1beta1",
]
pruneopts = "NUT"
revision = "1748dfb29e8a4432b78514bc88a1b07937a9805a"
version = "kubernetes-1.12.0"
revision = "53c4693659ed354d76121458fb819202dd1635fa"
version = "kubernetes-1.14.0"
[[projects]]
digest = "1:ca279c0bb7a72618aff5b77440d5a5e2f92857fdb7e0e4c7a1a77a7895929c49"
digest = "1:f249ae79e492647bb0640d656ccf70fd272359a75a35ac5b9748bd19ac42c1f0"
name = "k8s.io/apimachinery"
packages = [
"pkg/api/equality",
@@ -772,7 +744,6 @@
"pkg/apis/meta/internalversion",
"pkg/apis/meta/v1",
"pkg/apis/meta/v1/unstructured",
"pkg/apis/meta/v1/unstructured/unstructuredscheme",
"pkg/apis/meta/v1beta1",
"pkg/conversion",
"pkg/conversion/queryparams",
@@ -816,36 +787,26 @@
"third_party/forked/golang/reflect",
]
pruneopts = "NUT"
revision = "6dd46049f39503a1fc8d65de4bd566829e95faff"
version = "kubernetes-1.12.0"
revision = "d7deff9243b165ee192f5551710ea4285dcfd615"
version = "kubernetes-1.14.0"
[[projects]]
branch = "release-1.12"
digest = "1:7991e5074de01462e0cf6ef77060895b50e9026d16152a6e925cb99b67a1f8ae"
name = "k8s.io/cli-runtime"
packages = [
"pkg/genericclioptions",
"pkg/genericclioptions/printers",
"pkg/genericclioptions/resource",
]
pruneopts = "NUT"
revision = "11047e25a94a7eaa541b92a8bbfd3e1243607219"
[[projects]]
digest = "1:5d9f76731330e62bede1e4eb9d519b282a26621a5368e5db1a18a8eb1ccda1ff"
digest = "1:c225d3ffade76b27498a24472eeaa3e7c516f0c0bbaaacfaa2a1824e1ead7952"
name = "k8s.io/client-go"
packages = [
"discovery",
"discovery/fake",
"dynamic",
"dynamic/fake",
"informers",
"informers/admissionregistration",
"informers/admissionregistration/v1alpha1",
"informers/admissionregistration/v1beta1",
"informers/apps",
"informers/apps/v1",
"informers/apps/v1beta1",
"informers/apps/v1beta2",
"informers/auditregistration",
"informers/auditregistration/v1alpha1",
"informers/autoscaling",
"informers/autoscaling/v1",
"informers/autoscaling/v2beta1",
@@ -857,6 +818,7 @@
"informers/certificates",
"informers/certificates/v1beta1",
"informers/coordination",
"informers/coordination/v1",
"informers/coordination/v1beta1",
"informers/core",
"informers/core/v1",
@@ -867,6 +829,10 @@
"informers/internalinterfaces",
"informers/networking",
"informers/networking/v1",
"informers/networking/v1beta1",
"informers/node",
"informers/node/v1alpha1",
"informers/node/v1beta1",
"informers/policy",
"informers/policy/v1beta1",
"informers/rbac",
@@ -874,6 +840,7 @@
"informers/rbac/v1alpha1",
"informers/rbac/v1beta1",
"informers/scheduling",
"informers/scheduling/v1",
"informers/scheduling/v1alpha1",
"informers/scheduling/v1beta1",
"informers/settings",
@@ -883,43 +850,85 @@
"informers/storage/v1alpha1",
"informers/storage/v1beta1",
"kubernetes",
"kubernetes/fake",
"kubernetes/scheme",
"kubernetes/typed/admissionregistration/v1alpha1",
"kubernetes/typed/admissionregistration/v1beta1",
"kubernetes/typed/admissionregistration/v1beta1/fake",
"kubernetes/typed/apps/v1",
"kubernetes/typed/apps/v1/fake",
"kubernetes/typed/apps/v1beta1",
"kubernetes/typed/apps/v1beta1/fake",
"kubernetes/typed/apps/v1beta2",
"kubernetes/typed/apps/v1beta2/fake",
"kubernetes/typed/auditregistration/v1alpha1",
"kubernetes/typed/auditregistration/v1alpha1/fake",
"kubernetes/typed/authentication/v1",
"kubernetes/typed/authentication/v1/fake",
"kubernetes/typed/authentication/v1beta1",
"kubernetes/typed/authentication/v1beta1/fake",
"kubernetes/typed/authorization/v1",
"kubernetes/typed/authorization/v1/fake",
"kubernetes/typed/authorization/v1beta1",
"kubernetes/typed/authorization/v1beta1/fake",
"kubernetes/typed/autoscaling/v1",
"kubernetes/typed/autoscaling/v1/fake",
"kubernetes/typed/autoscaling/v2beta1",
"kubernetes/typed/autoscaling/v2beta1/fake",
"kubernetes/typed/autoscaling/v2beta2",
"kubernetes/typed/autoscaling/v2beta2/fake",
"kubernetes/typed/batch/v1",
"kubernetes/typed/batch/v1/fake",
"kubernetes/typed/batch/v1beta1",
"kubernetes/typed/batch/v1beta1/fake",
"kubernetes/typed/batch/v2alpha1",
"kubernetes/typed/batch/v2alpha1/fake",
"kubernetes/typed/certificates/v1beta1",
"kubernetes/typed/certificates/v1beta1/fake",
"kubernetes/typed/coordination/v1",
"kubernetes/typed/coordination/v1/fake",
"kubernetes/typed/coordination/v1beta1",
"kubernetes/typed/coordination/v1beta1/fake",
"kubernetes/typed/core/v1",
"kubernetes/typed/core/v1/fake",
"kubernetes/typed/events/v1beta1",
"kubernetes/typed/events/v1beta1/fake",
"kubernetes/typed/extensions/v1beta1",
"kubernetes/typed/extensions/v1beta1/fake",
"kubernetes/typed/networking/v1",
"kubernetes/typed/networking/v1/fake",
"kubernetes/typed/networking/v1beta1",
"kubernetes/typed/networking/v1beta1/fake",
"kubernetes/typed/node/v1alpha1",
"kubernetes/typed/node/v1alpha1/fake",
"kubernetes/typed/node/v1beta1",
"kubernetes/typed/node/v1beta1/fake",
"kubernetes/typed/policy/v1beta1",
"kubernetes/typed/policy/v1beta1/fake",
"kubernetes/typed/rbac/v1",
"kubernetes/typed/rbac/v1/fake",
"kubernetes/typed/rbac/v1alpha1",
"kubernetes/typed/rbac/v1alpha1/fake",
"kubernetes/typed/rbac/v1beta1",
"kubernetes/typed/rbac/v1beta1/fake",
"kubernetes/typed/scheduling/v1",
"kubernetes/typed/scheduling/v1/fake",
"kubernetes/typed/scheduling/v1alpha1",
"kubernetes/typed/scheduling/v1alpha1/fake",
"kubernetes/typed/scheduling/v1beta1",
"kubernetes/typed/scheduling/v1beta1/fake",
"kubernetes/typed/settings/v1alpha1",
"kubernetes/typed/settings/v1alpha1/fake",
"kubernetes/typed/storage/v1",
"kubernetes/typed/storage/v1/fake",
"kubernetes/typed/storage/v1alpha1",
"kubernetes/typed/storage/v1alpha1/fake",
"kubernetes/typed/storage/v1beta1",
"listers/admissionregistration/v1alpha1",
"kubernetes/typed/storage/v1beta1/fake",
"listers/admissionregistration/v1beta1",
"listers/apps/v1",
"listers/apps/v1beta1",
"listers/apps/v1beta2",
"listers/auditregistration/v1alpha1",
"listers/autoscaling/v1",
"listers/autoscaling/v2beta1",
"listers/autoscaling/v2beta2",
@@ -927,15 +936,20 @@
"listers/batch/v1beta1",
"listers/batch/v2alpha1",
"listers/certificates/v1beta1",
"listers/coordination/v1",
"listers/coordination/v1beta1",
"listers/core/v1",
"listers/events/v1beta1",
"listers/extensions/v1beta1",
"listers/networking/v1",
"listers/networking/v1beta1",
"listers/node/v1alpha1",
"listers/node/v1beta1",
"listers/policy/v1beta1",
"listers/rbac/v1",
"listers/rbac/v1alpha1",
"listers/rbac/v1beta1",
"listers/scheduling/v1",
"listers/scheduling/v1alpha1",
"listers/scheduling/v1beta1",
"listers/settings/v1alpha1",
@@ -967,20 +981,27 @@
"tools/remotecommand",
"transport",
"transport/spdy",
"util/buffer",
"util/cert",
"util/connrotation",
"util/exec",
"util/flowcontrol",
"util/homedir",
"util/integer",
"util/jsonpath",
"util/keyutil",
"util/retry",
"util/workqueue",
]
pruneopts = "NUT"
revision = "1638f8970cefaa404ff3a62950f88b08292b2696"
version = "v9.0.0"
revision = "6ee68ca5fd8355d024d02f9db0b3b667e8357a0f"
version = "kubernetes-1.14.0"
[[projects]]
digest = "1:2c16dda1c44c2564a7818fbacb701323c16d77c21b969987c1bec08d3ee0b050"
name = "k8s.io/klog"
packages = ["."]
pruneopts = "NUT"
revision = "e531227889390a39d9533dde61f590fe9f4b0035"
version = "v0.3.0"
[[projects]]
branch = "master"
@@ -991,15 +1012,32 @@
revision = "d83b052f768a50a309c692a9c271da3f3276ff88"
[[projects]]
digest = "1:8a9b1e755afd7ea778cd451a955977eb3fe0abcc4e32079644b6b7afc42d7ff8"
digest = "1:c2ad4e18f35cf651af430e4115e9d26bdd266e61b4076cb76d23a15078c5d58e"
name = "k8s.io/kubernetes"
packages = ["pkg/printers"]
pruneopts = "NUT"
revision = "b7394102d6ef778017f2ca4046abbaa23b88c290"
version = "v1.14.1"
[[projects]]
branch = "master"
digest = "1:14e8a3b53e6d8cb5f44783056b71bb2ca1ac7e333939cc97f3e50b579c920845"
name = "k8s.io/utils"
packages = [
"pkg/kubectl/scheme",
"pkg/printers",
"buffer",
"integer",
"trace",
]
pruneopts = "NUT"
revision = "51dd616cdd25d6ee22c83a858773b607328a18ec"
version = "v1.12.5"
revision = "21c4ce38f2a793ec01e925ddc31216500183b773"
[[projects]]
digest = "1:8730e0150dfb2b7e173890c8b9868e7a273082ef8e39f4940e3506a481cf895c"
name = "sigs.k8s.io/yaml"
packages = ["."]
pruneopts = "NUT"
revision = "fd68e9863619f6ec2fdd8625fe1f02e7c877e480"
version = "v1.1.0"
[solve-meta]
analyzer-name = "dep"
@@ -1024,7 +1062,7 @@
"github.com/aws/aws-sdk-go/service/s3",
"github.com/aws/aws-sdk-go/service/s3/s3manager",
"github.com/evanphx/json-patch",
"github.com/golang/glog",
"github.com/gobwas/glob",
"github.com/golang/protobuf/proto",
"github.com/hashicorp/go-hclog",
"github.com/hashicorp/go-plugin",
@@ -1069,6 +1107,7 @@
"k8s.io/apimachinery/pkg/runtime/serializer",
"k8s.io/apimachinery/pkg/types",
"k8s.io/apimachinery/pkg/util/clock",
"k8s.io/apimachinery/pkg/util/diff",
"k8s.io/apimachinery/pkg/util/duration",
"k8s.io/apimachinery/pkg/util/errors",
"k8s.io/apimachinery/pkg/util/runtime",
@@ -1079,9 +1118,11 @@
"k8s.io/client-go/discovery",
"k8s.io/client-go/discovery/fake",
"k8s.io/client-go/dynamic",
"k8s.io/client-go/dynamic/fake",
"k8s.io/client-go/informers",
"k8s.io/client-go/informers/core/v1",
"k8s.io/client-go/kubernetes",
"k8s.io/client-go/kubernetes/fake",
"k8s.io/client-go/kubernetes/scheme",
"k8s.io/client-go/kubernetes/typed/core/v1",
"k8s.io/client-go/kubernetes/typed/rbac/v1",
@@ -1098,6 +1139,7 @@
"k8s.io/client-go/tools/remotecommand",
"k8s.io/client-go/util/flowcontrol",
"k8s.io/client-go/util/workqueue",
"k8s.io/klog",
"k8s.io/kubernetes/pkg/printers",
]
solver-name = "gps-cdcl"

View File

@@ -31,28 +31,28 @@
[[constraint]]
name = "k8s.io/kubernetes"
version = "~1.12"
version = "~1.14"
[[constraint]]
name = "k8s.io/client-go"
version = "~9.0"
version = "kubernetes-1.14.0"
[[constraint]]
name = "k8s.io/apimachinery"
version = "kubernetes-1.12.0"
version = "kubernetes-1.14.0"
[[constraint]]
name = "k8s.io/api"
version = "kubernetes-1.12.0"
version = "kubernetes-1.14.0"
[[constraint]]
name = "k8s.io/apiextensions-apiserver"
version = "kubernetes-1.12.0"
version = "kubernetes-1.14.0"
# k8s.io/client-go v9.0 uses f2b4162afba35581b6d4a50d3b8f34e33c144682 (released in v1.1.4)
# k8s.io/client-go kubernetes-1.14.0 uses v1.1.4
[[override]]
name = "github.com/json-iterator/go"
version = "~1.1.4"
version = "=1.1.4"
#
# Cloud provider packages
@@ -63,12 +63,12 @@
[[constraint]]
name = "github.com/Azure/azure-sdk-for-go"
version = "~19.0.0"
version = "~21.4.0"
# k8s.io/client-go v9.0 uses bca49d5b51a50dc5bb17bbf6204c711c6dbded06 (v10.14.0)
# k8s.io/client-go kubernetes-1.14.0 uses v11.1.2
[[constraint]]
name = "github.com/Azure/go-autorest"
version = "~10.14.0"
version = "11.1.2"
[[constraint]]
name = "cloud.google.com/go"
@@ -85,10 +85,6 @@
#
# Third party packages
#
[[constraint]]
name = "github.com/golang/glog"
branch = "master"
[[constraint]]
name = "github.com/robfig/cron"
revision = "df38d32658d8788cd446ba74db4bb5375c4b0cb3"
@@ -115,7 +111,7 @@
[[constraint]]
name = "github.com/hashicorp/go-plugin"
revision = "3f118e8ee104b6f22aeb12453fab56aed1356186"
revision = "a1bc61569a26c0f65865932c0d55743b0567c494"
[[constraint]]
name = "github.com/golang/protobuf"
@@ -129,6 +125,10 @@
name = "github.com/joho/godotenv"
version = "~v1.3.0"
[[constraint]]
name = "github.com/gobwas/glob"
version = "~v0.2.3"
[[override]]
name = "golang.org/x/sys"
branch = "master"

View File

@@ -37,8 +37,8 @@ TAG_LATEST ?= false
### These variables should not need tweaking.
###
CLI_PLATFORMS := linux-amd64 linux-arm linux-arm64 darwin-amd64 windows-amd64
CONTAINER_PLATFORMS := linux-amd64 linux-arm linux-arm64
CLI_PLATFORMS := linux-amd64 linux-arm linux-arm64 darwin-amd64 windows-amd64 linux-ppc64le
CONTAINER_PLATFORMS := linux-amd64 linux-arm linux-arm64 linux-ppc64le
platform_temp = $(subst -, ,$(ARCH))
GOOS = $(word 1, $(platform_temp))
@@ -55,6 +55,9 @@ endif
#ifeq ($(GOARCH),arm64)
# DOCKERFILE ?= Dockerfile.arm64 #aarch64/busybox
#endif
ifeq ($(GOARCH),ppc64le)
DOCKERFILE ?= Dockerfile-$(BIN)-ppc64le
endif
IMAGE = $(REGISTRY)/$(BIN)
@@ -214,3 +217,37 @@ changelog:
release:
hack/goreleaser.sh
serve-docs:
docker run \
--rm \
-v "$$(pwd)/site:/srv/jekyll" \
-it -p 4000:4000 \
jekyll/jekyll \
jekyll serve --livereload --incremental
# gen-docs generates a new versioned docs directory under site/docs. It follows
# the following process:
# 1. Copies the contents of the most recently tagged docs directory into the new
# directory, to establish a useful baseline to diff against.
# 2. Adds all copied content from step 1 to git's staging area via 'git add'.
# 3. Replaces the contents of the new docs directory with the contents of the
# 'master' docs directory, updating any version-specific links (e.g. to a
# specific branch of the GitHub repository) to use the new version
# 4. Copies the previous version's ToC file and runs 'git add' to establish
# a useful baseline to diff against.
# 5. Replaces the content of the new ToC file with the master ToC.
# 6. Update site/_config.yml and site/_data/toc-mapping.yml to include entries
# for the new version.
#
# The unstaged changes in the working directory can now easily be diff'ed against the
# staged changes using 'git diff' to review all docs changes made since the previous
# tagged version. Once the unstaged changes are ready, they can be added to the
# staging area using 'git add' and then committed.
#
# To run gen-docs: "NEW_DOCS_VERSION=v1.1.0 make gen-docs"
#
# **NOTE**: there are additional manual steps required to finalize the process of generating
# a new versioned docs site. The full process is documented in site/README-JEKYLL.md.
gen-docs:
@hack/gen-docs.sh

View File

@@ -7,8 +7,8 @@
Velero (formerly Heptio Ark) gives you tools to back up and restore your Kubernetes cluster resources and persistent volumes. Velero lets you:
* Take backups of your cluster and restore in case of loss.
* Copy cluster resources to other clusters.
* Replicate your production environment for development and testing environments.
* Migrate cluster resources to other clusters.
* Replicate your production cluster to development and testing clusters.
Velero consists of:
@@ -19,8 +19,8 @@ You can run Velero in clusters on a cloud provider or on-premises. For detailed
## Installation
We strongly recommend that you use an [official release][6] of Velero. The tarballs for each release contain the
`velero` command-line client. Follow the instructions under the **Install** section of [our documentation][29] to get started.
We strongly recommend that you use an [official release][6] of Velero. The tarballs for each release contain the
`velero` command-line client. Follow the [installation instructions][28] to get started.
_The code and sample YAML files in the master branch of the Velero repository are under active development and are not guaranteed to be stable. Use them at your own risk!_
@@ -70,11 +70,11 @@ See [the list of releases][6] to find out about feature changes.
[24]: https://groups.google.com/forum/#!forum/projectvelero
[25]: https://kubernetes.slack.com/messages/velero
[26]: https://velero.io/docs/v1.0.0/zenhub
[26]: https://velero.io/docs/zenhub
[28]: https://velero.io/docs/install-overview
[29]: https://velero.io/docs/
[30]: https://velero.io/docs/troubleshooting
[29]: https://velero.io/docs/v1.0.0/
[30]: https://velero.io/docs/v1.0.0/troubleshooting
[99]: https://velero.io/docs/v1.0.0/support-matrix
[99]: https://velero.io/docs/support-matrix
[100]: /site/docs/master/img/velero.png

View File

@@ -0,0 +1,50 @@
## v1.1.0-beta.1
#### 2019-08-07
### Download
- https://github.com/heptio/velero/releases/tag/v1.1.0-beta.1
### Container Image
`gcr.io/heptio-images/velero:v1.1.0-beta.1`
### Documentation
https://velero.io/docs/v1.1.0-beta.1/
### All Changes
* adds the ability to define custom tags to be added to snapshots by specifying custom labels on the Backup CR with the `velero backup create --labels` flag (#1729, @prydonius)
* Restore restic volumes from PodVolumeBackups CRs (#1723, @carlisia)
* properly restore PVs backed up with restic and a reclaim policy of "Retain" (#1713, @skriss)
* Make `--secret-file` flag on `velero install` optional, add `--no-secret` flag for explicit confirmation (#1699, @nrb)
* Add low cpu/memory limits to the restic init container. This allows for restoration into namespaces with quotas defined. (#1677, @nrb)
* Adds configurable CPU/memory requests and limits to the restic DaemonSet generated by velero install. (#1710, @prydonius)
* remove any stale locks from restic repositories every 5m (#1708, @skriss)
* error if backup storage location's Bucket field also contains a prefix, and gracefully handle leading/trailing slashes on Bucket and Prefix fields. (#1694, @skriss)
* enhancement: allow option to choose JSON log output (#1654, @carthewd)
* Adds configurable CPU/memory requests and limits to the Velero Deployment generated by velero install. (#1678, @prydonius)
* Store restic PodVolumeBackups in obj storage & use that as source of truth like regular backups. (#1577, @carlisia)
* Update Velero Deployment to use apps/v1 API group. `velero install` and `velero plugin add/remove` commands will now require Kubernetes 1.9+ (#1673, @nrb)
* Respect the --kubecontext and --kubeconfig arugments for `velero install`. (#1656, @nrb)
* add plugin for updating PV & PVC storage classes on restore based on a config map (#1621, @skriss)
* Add restic support for CSI volumes (#1615, @nrb)
* bug fix: Fixed namespace usage with cli command 'version' (#1630, @jwmatthews)
* enhancement: allow users to specify additional Velero/Restic pod annotations on the command line with the pod-annotations flag. (#1626, @tlkamp)
* adds validation for pod volumes hostPath mount on restic server startup (#1616, @prydonius)
* enable support for ppc64le architecture (#1605, @prajyot)
* bug fix: only restore additional items returned from restore item actions if they match the restore's namespace/resource selectors (#1612, @skriss)
* add startTimestamp and completionTimestamp to PodVolumeBackup and PodVolumeRestore status fields (#1609, @prydonius)
* bug fix: respect namespace selector when determining which restore item actions to run (#1607, @skriss)
* ensure correct backup item actions run with namespace selector (#1601, @prydonius)
* allows excluding resources from backups with the `velero.io/exclude-from-backup=true` label (#1588, @prydonius)
* ensures backup item action modifications to an item's namespace/name are saved in the file path in the tarball (#1587, @prydonius)
* Hides `velero server` and `velero restic server` commands from the list of available commands as these are not intended for use by the velero CLI user. (#1561, @prydonius)
* remove dependency on glog, update to klog (#1559, @skriss)
* move issue-template-gen from docs/ to hack/ (#1558, @skriss)
* fix panic when processing DeleteBackupRequest objects without labels (#1556, @prydonius)
* support for multiple AWS profiles (#1548, @pranavgaikwad)
* Add CLI command to list (get) all Velero plugins (#1535, @carlisia)
* Added author as a tag on blog post. Should fix 404 error when trying to follow link as specified in issue #1522. (#1522, @coonsd)
* Allow individual backup storage locations to be read-only (#1517, @skriss)
* Stop returning an error when a restic volume is empty since it is a valid scenario. (#1480, @carlisia)
* add ability to use wildcard in includes/excludes (#1428, @guilhem)

View File

@@ -0,0 +1 @@
add ability to use wildcard in includes/excludes

View File

@@ -0,0 +1 @@
Stop returning an error when a restic volume is empty since it is a valid scenario.

View File

@@ -0,0 +1 @@
Allow individual backup storage locations to be read-only

View File

@@ -0,0 +1 @@
Added author as a tag on blog post. Should fix 404 error when trying to follow link as specified in issue #1522.

View File

@@ -0,0 +1 @@
Add CLI command to list (get) all Velero plugins

View File

@@ -0,0 +1 @@
support for multiple AWS profiles

View File

@@ -0,0 +1 @@
fix panic when processing DeleteBackupRequest objects without labels

View File

@@ -0,0 +1 @@
move issue-template-gen from docs/ to hack/

View File

@@ -0,0 +1 @@
remove dependency on glog, update to klog

View File

@@ -0,0 +1 @@
Hides `velero server` and `velero restic server` commands from the list of available commands as these are not intended for use by the velero CLI user.

View File

@@ -0,0 +1 @@
Store restic PodVolumeBackups in obj storage & use that as source of truth like regular backups.

View File

@@ -0,0 +1 @@
ensures backup item action modifications to an item's namespace/name are saved in the file path in the tarball

View File

@@ -0,0 +1 @@
allows excluding resources from backups with the velero.io/exclude-from-backup=true label

View File

@@ -0,0 +1 @@
ensure correct backup item actions run with namespace selector

View File

@@ -0,0 +1 @@
enable support for ppc64le architecture

View File

@@ -0,0 +1 @@
bug fix: respect namespace selector when determining which restore item actions to run

View File

@@ -0,0 +1 @@
add startTimestamp and completionTimestamp to PodVolumeBackup and PodVolumeRestore status fields

View File

@@ -0,0 +1 @@
bug fix: only restore additional items returned from restore item actions if they match the restore's namespace/resource selectors

View File

@@ -0,0 +1 @@
Add restic support for CSI volumes

View File

@@ -0,0 +1 @@
adds validation for pod volumes hostPath mount on restic server startup

View File

@@ -0,0 +1 @@
add plugin for updating PV & PVC storage classes on restore based on a config map

View File

@@ -0,0 +1 @@
enhancement: allow users to specify additional Velero/Restic pod annotations on the command line with the pod-annotations flag.

View File

@@ -0,0 +1 @@
bug fix: Fixed namespace usage with cli command 'version'

View File

@@ -0,0 +1 @@
enhancement: allow option to choose JSON log output

View File

@@ -0,0 +1 @@
Respect the --kubecontext and --kubeconfig arugments for `velero install`.

View File

@@ -0,0 +1 @@
Update Velero Deployment to use apps/v1 API group. `velero install` and `velero plugin add/remove` commands will now require Kubernetes 1.9+

View File

@@ -0,0 +1 @@
Add low cpu/memory limits to the restic init container. This allows for restoration into namespaces with quotas defined.

View File

@@ -0,0 +1 @@
Adds configurable CPU/memory requests and limits to the Velero Deployment generated by velero install.

View File

@@ -0,0 +1 @@
error if backup storage location's Bucket field also contains a prefix, and gracefully handle leading/trailing slashes on Bucket and Prefix fields.

View File

@@ -0,0 +1 @@
Make --secret-file argument on `velero install` optional, add --no-secret flag for explicit confirmation

View File

@@ -0,0 +1 @@
remove any stale locks from restic repositories every 5m

View File

@@ -0,0 +1 @@
Adds configurable CPU/memory requests and limits to the restic DaemonSet generated by velero install.

View File

@@ -0,0 +1 @@
properly restore PVs backed up with restic and a reclaim policy of "Retain"

View File

@@ -0,0 +1 @@
Restore restic volumes from PodVolumeBackups CRs

View File

@@ -0,0 +1 @@
adds the ability to define custom tags to be added to snapshots by specifying custom labels on the Backup CR with the velero backup create --labels flag

View File

@@ -1,5 +1,5 @@
/*
Copyright 2017 the Velero contributors.
Copyright 2017, 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -20,14 +20,14 @@ import (
"os"
"path/filepath"
"github.com/golang/glog"
"k8s.io/klog"
"github.com/heptio/velero/pkg/cmd"
"github.com/heptio/velero/pkg/cmd/velero"
)
func main() {
defer glog.Flush()
defer klog.Flush()
baseName := filepath.Base(os.Args[0])

48
design/_template.md Normal file
View File

@@ -0,0 +1,48 @@
# Design proposal template (replace with your proposal's title)
Status: {Draft,Accepted,Declined}
One to two sentences that describes the goal of this proposal.
The reader should be able to tell by the title, and the opening paragraph, if this document is relevant to them.
_Note_: The preferred style for design documents is one sentence per line.
*Do not wrap lines*.
This aids in review of the document as changes to a line are not obscured by the reflowing those changes caused and has a side effect of avoiding debate about one or two space after a period.
## Goals
- A short list of things which will be accomplished by implementing this proposal.
- Two things is ok.
- Three is pushing it.
- More than three goals suggests that the proposal's scope is too large.
## Non Goals
- A short list of items which are:
- a. out of scope
- b. follow on items which are deliberately excluded from this proposal.
## Background
One to two paragraphs of exposition to set the context for this proposal.
## High-Level Design
One to two paragraphs that describe the high level changes that will be made to implement this proposal.
## Detailed Design
A detailed design describing how the changes to the product should be made.
The names of types, fields, interfaces, and methods should be agreed on here, not debated in code review.
The same applies to changes in CRDs, YAML examples, and so on.
Ideally the changes should be made in sequence so that the work required to implement this design can be done incrementally, possibly in parallel.
## Alternatives Considered
If there are alternative high level or detailed designs that were not pursued they should be called out here with a brief explanation of why they were not pursued.
## Security Considerations
If this proposal has an impact to the security of the product, its users, or data stored or transmitted via the product, they must be addressed here.

View File

@@ -0,0 +1,96 @@
# Expose list of backed up resources in backup details
Status: Accepted
To increase the visibility of what a backup might contain, this document proposes storing metadata about backed up resources in object storage and adding a new section to the detailed backup description output to list them.
## Goals
- Include a list of backed up resources as metadata in the bucket
- Enable users to get a view of what resources are included in a backup using the Velero CLI
## Non Goals
- Expose the full manifests of the backed up resources
## Background
As reported in [#396](https://github.com/heptio/velero/issues/396), the information reported in a `velero backup describe <name> --details` command is fairly limited, and does not easily describe what resources a backup contains.
In order to see what a backup might contain, a user would have to download the backup tarball and extract it.
This makes it difficult to keep track of different backups in a cluster.
## High-Level Design
After performing a backup, a new file will be created that contains the list of the resources that have been included in the backup.
This file will be persisted in object storage alongside the backup contents and existing metadata.
A section will be added to the output of `velero backup describe <name> --details` command to view this metadata.
## Detailed Design
### Metadata file
This metadata will be in JSON (or YAML) format so that it can be easily inspected from the bucket outside of Velero tooling, and will contain the API resource and group, namespaces and names of the resources:
```
apps/v1/Deployment:
- default/database
- default/wordpress
v1/Service:
- default/database
- default/wordpress
v1/Secret:
- default/database-root-password
- default/database-user-password
v1/ConfigMap:
- default/database
v1/PersistentVolume:
- my-pv
```
The filename for this metadata will be `<backup name>-resource-list.json.gz`.
The top-level key is the string form of the `schema.GroupResource` type that we currently keep track of in the backup controller code path.
### Changes in Backup controller
The Backupper currently initialises a map to track the `backedUpItems` (https://github.com/heptio/velero/blob/1594bdc8d0132f548e18ffcc1db8c4cd2b042726/pkg/backup/backup.go#L269), this is passed down through GroupBackupper, ResourceBackupper and ItemBackupper where ItemBackupper records each backed up item.
This property will be moved to the [Backup request struct](https://github.com/heptio/velero/blob/16910a6215cbd8f0bde385dba9879629ebcbcc28/pkg/backup/request.go#L11), allowing the BackupController to access it after a successful backup.
`backedUpItems` currently uses the `schema.GroupResource` as a key for the resource.
In order to record the API group, version and kind for the resource, this key will be constructed from the object's `schema.GroupVersionKind` in the format `{group}/{version}/{kind}` (e.g. `apps/v1/Deployment`).
The `backedUpItems` map is kept as a flat structure internally for quick lookup.
When the backup is ready to upload, `backedUpItems` will be converted to a nested structure representing the metadata file above, grouped by `schema.GroupVersionKind`.
After converting to the right format, it can be passed to the `persistBackup` function to persist the file in object storage.
### Changes to DownloadRequest CRD and processing
A new `DownloadTargetKind` "BackupResourceList" will be added to the DownloadRequest CR.
The `GetDownloadURL` function in the `persistence` package will be updated to handle this new DownloadTargetKind to enable the Velero client to fetch the metadata from the bucket.
### Changes to `velero backup describe <name> --details`
This command will need to be updated to fetch the metadata from the bucket using the `Stream` method used in other commands.
The file will be read in memory and displayed in the output of the command.
Depending on the format the metadata is stored in, it may need processing to print in a more human-readable format.
If we choose to store the metadata in YAML, it can likely be directly printed out.
If the metadata file does not exist, this is an older backup and we cannot display the list of resources that were backed up.
## Open Questions
## Alternatives Considered
### Fetch backup contents archive and walkthrough to list contents
Instead of recording new metadata about what resources have been backed up, we could simply download the backup contents archive and walkthrough it to list the contents everytime `velero backup describe <name> --details` is run.
The advantage of this approach is that we don't need to change any backup procedures as we already have this content, and we will also be able to list resources for older backups.
Additionally, if we wanted to expose more information about the backed up resources, we can do so without having to update what we store in the metadata.
The disadvantages are:
- downloading the whole backup archive will be larger than just downloading a smaller file with metadata
- reduces the metadata available in the bucket that users might want to inspect outside of Velero tooling (though this is not an explicit requirement)
## Security Considerations

View File

@@ -19,7 +19,7 @@ metadata:
name: velero
---
apiVersion: apps/v1beta1
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: velero
@@ -29,6 +29,9 @@ metadata:
spec:
strategy:
type: Recreate
selector:
matchLabels:
component: minio
template:
metadata:
labels:

View File

@@ -21,13 +21,16 @@ metadata:
app: nginx
---
apiVersion: apps/v1beta1
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: nginx-example
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:

View File

@@ -29,7 +29,7 @@ metadata:
labels:
app: nginx
spec:
storageClassName: <YOUR_STORAGE_CLASS_NAME>
# storageClassName: <YOUR_STORAGE_CLASS_NAME>
accessModes:
- ReadWriteOnce
resources:
@@ -37,13 +37,16 @@ spec:
storage: 50Mi
---
apiVersion: apps/v1beta1
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: nginx-example
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:

View File

@@ -17,8 +17,8 @@ FROM golang:1.12
RUN mkdir -p /go/src/k8s.io && \
cd /go/src/k8s.io && \
git config --global advice.detachedHead false && \
git clone -b kubernetes-1.12.0 https://github.com/kubernetes/code-generator && \
git clone -b kubernetes-1.12.0 https://github.com/kubernetes/apimachinery && \
git clone -b kubernetes-1.14.0 https://github.com/kubernetes/code-generator && \
git clone -b kubernetes-1.14.0 https://github.com/kubernetes/apimachinery && \
go get golang.org/x/tools/cmd/goimports && \
cd /go/src/golang.org/x/tools && \
git checkout 40a48ad93fbe707101afb2099b738471f70594ec && \

17
hack/ci-check.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
# If we're doing push build, as opposed to a PR, always run make ci
if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then
make ci
# Exit script early, returning make ci's error
exit $?
fi
# Only run `make ci` if files outside of the site directory changed in the branch
# In a PR build, $TRAVIS_BRANCH is the destination branch.
if [[ $(git diff --name-only $TRAVIS_BRANCH | grep --invert-match site/) ]]; then
make ci
else
echo "Skipping make ci since nothing outside of site directory changed."
exit 0
fi

108
hack/gen-docs.sh Executable file
View File

@@ -0,0 +1,108 @@
#!/bin/bash
# Copyright 2019 the Velero contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# gen-docs.sh is used for the "make gen-docs" target. See additional
# documentation in the Makefile.
set -o errexit
set -o nounset
set -o pipefail
# don't run if there's already a directory for the target docs version
if [[ -d site/docs/$NEW_DOCS_VERSION ]]; then
echo "ERROR: site/docs/$NEW_DOCS_VERSION already exists"
exit 1
fi
# get the alphabetically last item in site/docs to use as PREVIOUS_DOCS_VERSION
# if not explicitly specified by the user
if [[ -z "${PREVIOUS_DOCS_VERSION:-}" ]]; then
echo "PREVIOUS_DOCS_VERSION was not specified, getting the latest version"
PREVIOUS_DOCS_VERSION=$(ls -1 site/docs/ | tail -n 1)
fi
# make a copy of the previous versioned docs dir
echo "Creating copy of docs directory site/docs/$PREVIOUS_DOCS_VERSION in site/docs/$NEW_DOCS_VERSION"
cp -r site/docs/${PREVIOUS_DOCS_VERSION}/ site/docs/${NEW_DOCS_VERSION}/
# 'git add' the previous version's docs as-is so we get a useful diff when we copy the master docs in
echo "Running 'git add' for previous version's doc contents to use as a base for diff"
git add site/docs/${NEW_DOCS_VERSION}
# now copy the contents of site/docs/master into the same directory so we can get a nice
# git diff of what changed since previous version
echo "Copying site/docs/master/ to site/docs/${NEW_DOCS_VERSION}/"
rm -rf site/docs/${NEW_DOCS_VERSION}/ && cp -r site/docs/master/ site/docs/${NEW_DOCS_VERSION}/
# make a copy of the previous versioned ToC
NEW_DOCS_TOC="$(echo ${NEW_DOCS_VERSION} | tr . -)-toc"
PREVIOUS_DOCS_TOC="$(echo ${PREVIOUS_DOCS_VERSION} | tr . -)-toc"
echo "Creating copy of site/_data/$PREVIOUS_DOCS_TOC.yml at site/_data/$NEW_DOCS_TOC.yml"
cp site/_data/$PREVIOUS_DOCS_TOC.yml site/_data/$NEW_DOCS_TOC.yml
# 'git add' the previous version's ToC content as-is so we get a useful diff when we copy the master ToC in
echo "Running 'git add' for previous version's ToC to use as a base for diff"
git add site/_data/$NEW_DOCS_TOC.yml
# now copy the master ToC so we can get a nice git diff of what changed since previous version
echo "Copying site/_data/master-toc.yml to site/_data/$NEW_DOCS_TOC.yml"
rm site/_data/$NEW_DOCS_TOC.yml && cp site/_data/master-toc.yml site/_data/$NEW_DOCS_TOC.yml
# replace known version-specific links -- the sed syntax is slightly different in OS X and Linux,
# so check which OS we're running on.
if [[ $(uname) == "Darwin" ]]; then
echo "[OS X] updating version-specific links"
find site/docs/${NEW_DOCS_VERSION} -type f -name "*.md" | xargs sed -i '' "s|https://velero.io/docs/master|https://velero.io/docs/$NEW_DOCS_VERSION|g"
find site/docs/${NEW_DOCS_VERSION} -type f -name "*.md" | xargs sed -i '' "s|https://github.com/heptio/velero/blob/master|https://github.com/heptio/velero/blob/$NEW_DOCS_VERSION|g"
echo "[OS X] Updating latest version in _config.yml"
sed -i '' "s/latest: ${PREVIOUS_DOCS_VERSION}/latest: ${NEW_DOCS_VERSION}/" site/_config.yml
# newlines and lack of indentation are requirements for this sed syntax
# which is doing an append
echo "[OS X] Adding latest version to versions list in _config.yml"
sed -i '' "/- master/a\\
- ${NEW_DOCS_VERSION}
" site/_config.yml
echo "[OS X] Adding ToC mapping entry"
sed -i '' "/master: master-toc/a\\
${NEW_DOCS_VERSION}: ${NEW_DOCS_TOC}
" site/_data/toc-mapping.yml
else
echo "[Linux] updating version-specific links"
find site/docs/${NEW_DOCS_VERSION} -type f -name "*.md" | xargs sed -i'' "s|https://velero.io/docs/master|https://velero.io/docs/$NEW_DOCS_VERSION|g"
find site/docs/${NEW_DOCS_VERSION} -type f -name "*.md" | xargs sed -i'' "s|https://github.com/heptio/velero/blob/master|https://github.com/heptio/velero/blob/$NEW_DOCS_VERSION|g"
echo "[Linux] Updating latest version in _config.yml"
sed -i'' "s/latest: ${PREVIOUS_DOCS_VERSION}/latest: ${NEW_DOCS_VERSION}/" site/_config.yml
echo "[Linux] Adding latest version to versions list in _config.yml"
sed -i'' "/- master/a - ${NEW_DOCS_VERSION}" site/_config.yml
echo "[Linux] Adding ToC mapping entry"
sed -i'' "/master: master-toc/a ${NEW_DOCS_VERSION}: ${NEW_DOCS_TOC}" site/_data/toc-mapping.yml
fi
echo "Success! site/docs/$NEW_DOCS_VERSION has been created."
echo ""
echo "The next steps are:"
echo " 1. Consult site/README-JEKYLL.md for further manual steps required to finalize the new versioned docs generation."
echo " 2. Run a 'git diff' to review all changes made to the docs since the previous version."
echo " 3. Make any manual changes/corrections necessary."
echo " 4. Run 'git add' to stage all unstaged changes, then 'git commit'."

View File

@@ -1,6 +1,6 @@
#!/bin/bash -e
#
# Copyright 2018 the Velero contributors.
# Copyright 2018, 2019 the Velero contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ BIN=${VELERO_ROOT}/_output/bin
mkdir -p ${BIN}
echo "Updating generated Github issue template"
go build -o ${BIN}/issue-tmpl-gen ./docs/issue-template-gen/main.go
go build -o ${BIN}/issue-tmpl-gen ./hack/issue-template-gen/main.go
if [[ $# -gt 1 ]]; then
echo "usage: ${BASH_SOURCE} [OUTPUT_FILE]"

View File

@@ -66,6 +66,9 @@ type BackupStorageLocationSpec struct {
Config map[string]string `json:"config"`
StorageType `json:",inline"`
// AccessMode defines the permissions for the backup storage location.
AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"`
}
// BackupStorageLocationPhase is the lifecyle phase of a Velero BackupStorageLocation.
@@ -90,10 +93,17 @@ const (
BackupStorageLocationAccessModeReadWrite BackupStorageLocationAccessMode = "ReadWrite"
)
// TODO(2.0): remove the AccessMode field from BackupStorageLocationStatus.
// BackupStorageLocationStatus describes the current status of a Velero BackupStorageLocation.
type BackupStorageLocationStatus struct {
Phase BackupStorageLocationPhase `json:"phase,omitempty"`
AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"`
LastSyncedRevision types.UID `json:"lastSyncedRevision,omitempty"`
LastSyncedTime metav1.Time `json:"lastSyncedTime,omitempty"`
Phase BackupStorageLocationPhase `json:"phase,omitempty"`
LastSyncedRevision types.UID `json:"lastSyncedRevision,omitempty"`
LastSyncedTime metav1.Time `json:"lastSyncedTime,omitempty"`
// AccessMode is an unused field.
//
// Deprecated: there is now an AccessMode field on the Spec and this field
// will be removed entirely as of v2.0.
AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"`
}

View File

@@ -31,6 +31,7 @@ const (
DownloadTargetKindBackupLog DownloadTargetKind = "BackupLog"
DownloadTargetKindBackupContents DownloadTargetKind = "BackupContents"
DownloadTargetKindBackupVolumeSnapshots DownloadTargetKind = "BackupVolumeSnapshots"
DownloadTargetKindBackupResourceList DownloadTargetKind = "BackupResourceList"
DownloadTargetKindRestoreLog DownloadTargetKind = "RestoreLog"
DownloadTargetKindRestoreResults DownloadTargetKind = "RestoreResults"
)

View File

@@ -68,6 +68,18 @@ type PodVolumeBackupStatus struct {
// Message is a message about the pod volume backup's status.
Message string `json:"message"`
// 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
StartTimestamp metav1.Time `json:"startTimestamp"`
// 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
CompletionTimestamp metav1.Time `json:"completionTimestamp"`
}
// +genclient

View File

@@ -57,6 +57,15 @@ type PodVolumeRestoreStatus struct {
// Message is a message about the pod volume restore's status.
Message string `json:"message"`
// StartTimestamp records the time a restore was started.
// The server's time is used for StartTimestamps
StartTimestamp metav1.Time `json:"startTimestamp"`
// CompletionTimestamp records the time a restore was completed.
// Completion time is recorded even on failed restores.
// The server's time is used for CompletionTimestamps
CompletionTimestamp metav1.Time `json:"completionTimestamp"`
}
// +genclient

View File

@@ -16,7 +16,9 @@ limitations under the License.
package v1
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@@ -45,6 +47,12 @@ const (
ServerStatusRequestPhaseProcessed ServerStatusRequestPhase = "Processed"
)
// PluginInfo contains attributes of a Velero plugin
type PluginInfo struct {
Name string `json:"name"`
Kind string `json:"kind"`
}
// ServerStatusRequestStatus is the current status of a ServerStatusRequest.
type ServerStatusRequestStatus struct {
// Phase is the current lifecycle phase of the ServerStatusRequest.
@@ -56,6 +64,9 @@ type ServerStatusRequestStatus struct {
// ServerVersion is the Velero server version.
ServerVersion string `json:"serverVersion"`
// Plugins list information about the plugins running on the Velero server
Plugins []PluginInfo `json:"plugins"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

View File

@@ -616,13 +616,29 @@ func (in *ObjectStorageLocation) DeepCopy() *ObjectStorageLocation {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PluginInfo) DeepCopyInto(out *PluginInfo) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PluginInfo.
func (in *PluginInfo) DeepCopy() *PluginInfo {
if in == nil {
return nil
}
out := new(PluginInfo)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PodVolumeBackup) DeepCopyInto(out *PodVolumeBackup) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
in.Status.DeepCopyInto(&out.Status)
return
}
@@ -704,6 +720,8 @@ func (in *PodVolumeBackupSpec) DeepCopy() *PodVolumeBackupSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PodVolumeBackupStatus) DeepCopyInto(out *PodVolumeBackupStatus) {
*out = *in
in.StartTimestamp.DeepCopyInto(&out.StartTimestamp)
in.CompletionTimestamp.DeepCopyInto(&out.CompletionTimestamp)
return
}
@@ -723,7 +741,7 @@ func (in *PodVolumeRestore) DeepCopyInto(out *PodVolumeRestore) {
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
out.Status = in.Status
in.Status.DeepCopyInto(&out.Status)
return
}
@@ -798,6 +816,8 @@ func (in *PodVolumeRestoreSpec) DeepCopy() *PodVolumeRestoreSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PodVolumeRestoreStatus) DeepCopyInto(out *PodVolumeRestoreStatus) {
*out = *in
in.StartTimestamp.DeepCopyInto(&out.StartTimestamp)
in.CompletionTimestamp.DeepCopyInto(&out.CompletionTimestamp)
return
}
@@ -1227,6 +1247,11 @@ func (in *ServerStatusRequestSpec) DeepCopy() *ServerStatusRequestSpec {
func (in *ServerStatusRequestStatus) DeepCopyInto(out *ServerStatusRequestStatus) {
*out = *in
in.ProcessedTimestamp.DeepCopyInto(&out.ProcessedTimestamp)
if in.Plugins != nil {
in, out := &in.Plugins, &out.Plugins
*out = make([]PluginInfo, len(*in))
copy(*out, *in)
}
return
}

View File

@@ -60,12 +60,6 @@ type kubernetesBackupper struct {
resticTimeout time.Duration
}
type itemKey struct {
resource string
namespace string
name string
}
type resolvedAction struct {
velero.BackupItemAction
@@ -240,6 +234,8 @@ func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Req
return err
}
backupRequest.BackedUpItems = map[itemKey]struct{}{}
podVolumeTimeout := kb.resticTimeout
if val := backupRequest.Annotations[api.PodVolumeOperationTimeoutAnnotation]; val != "" {
parsed, err := time.ParseDuration(val)
@@ -266,7 +262,6 @@ func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Req
backupRequest,
kb.dynamicFactory,
kb.discoveryHelper,
make(map[itemKey]struct{}),
cohabitatingResources(),
kb.podCommandExecutor,
tw,

File diff suppressed because it is too large Load Diff

View File

@@ -37,7 +37,6 @@ type groupBackupperFactory interface {
backupRequest *Request,
dynamicFactory client.DynamicFactory,
discoveryHelper discovery.Helper,
backedUpItems map[itemKey]struct{},
cohabitatingResources map[string]*cohabitatingResource,
podCommandExecutor podexec.PodCommandExecutor,
tarWriter tarWriter,
@@ -54,7 +53,6 @@ func (f *defaultGroupBackupperFactory) newGroupBackupper(
backupRequest *Request,
dynamicFactory client.DynamicFactory,
discoveryHelper discovery.Helper,
backedUpItems map[itemKey]struct{},
cohabitatingResources map[string]*cohabitatingResource,
podCommandExecutor podexec.PodCommandExecutor,
tarWriter tarWriter,
@@ -67,7 +65,6 @@ func (f *defaultGroupBackupperFactory) newGroupBackupper(
backupRequest: backupRequest,
dynamicFactory: dynamicFactory,
discoveryHelper: discoveryHelper,
backedUpItems: backedUpItems,
cohabitatingResources: cohabitatingResources,
podCommandExecutor: podCommandExecutor,
tarWriter: tarWriter,
@@ -88,7 +85,6 @@ type defaultGroupBackupper struct {
backupRequest *Request
dynamicFactory client.DynamicFactory
discoveryHelper discovery.Helper
backedUpItems map[itemKey]struct{}
cohabitatingResources map[string]*cohabitatingResource
podCommandExecutor podexec.PodCommandExecutor
tarWriter tarWriter
@@ -120,7 +116,6 @@ func (gb *defaultGroupBackupper) backupGroup(group *metav1.APIResourceList) erro
gb.backupRequest,
gb.dynamicFactory,
gb.discoveryHelper,
gb.backedUpItems,
gb.cohabitatingResources,
gb.podCommandExecutor,
gb.tarWriter,

View File

@@ -1,5 +1,5 @@
/*
Copyright 2017 the Velero contributors.
Copyright 2017, 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -19,111 +19,10 @@ package backup
import (
"testing"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/heptio/velero/pkg/client"
"github.com/heptio/velero/pkg/discovery"
"github.com/heptio/velero/pkg/podexec"
"github.com/heptio/velero/pkg/restic"
velerotest "github.com/heptio/velero/pkg/util/test"
)
func TestBackupGroupBacksUpCorrectResourcesInCorrectOrder(t *testing.T) {
resourceBackupperFactory := new(mockResourceBackupperFactory)
resourceBackupper := new(mockResourceBackupper)
defer resourceBackupperFactory.AssertExpectations(t)
defer resourceBackupper.AssertExpectations(t)
resourceBackupperFactory.On("newResourceBackupper",
mock.Anything,
mock.Anything,
mock.Anything,
mock.Anything,
mock.Anything,
mock.Anything,
mock.Anything,
mock.Anything,
mock.Anything,
mock.Anything,
mock.Anything,
).Return(resourceBackupper)
gb := &defaultGroupBackupper{
log: velerotest.NewLogger(),
resourceBackupperFactory: resourceBackupperFactory,
}
group := &metav1.APIResourceList{
GroupVersion: "v1",
APIResources: []metav1.APIResource{
{Name: "persistentvolumes"},
{Name: "pods"},
{Name: "persistentvolumeclaims"},
},
}
var actualOrder []string
runFunc := func(args mock.Arguments) {
actualOrder = append(actualOrder, args.Get(1).(metav1.APIResource).Name)
}
resourceBackupper.On("backupResource", group, metav1.APIResource{Name: "pods"}).Return(nil).Run(runFunc)
resourceBackupper.On("backupResource", group, metav1.APIResource{Name: "persistentvolumeclaims"}).Return(nil).Run(runFunc)
resourceBackupper.On("backupResource", group, metav1.APIResource{Name: "persistentvolumes"}).Return(nil).Run(runFunc)
require.NoError(t, gb.backupGroup(group))
// make sure PVs were last
assert.Equal(t, []string{"pods", "persistentvolumeclaims", "persistentvolumes"}, actualOrder)
}
type mockResourceBackupperFactory struct {
mock.Mock
}
func (rbf *mockResourceBackupperFactory) newResourceBackupper(
log logrus.FieldLogger,
backup *Request,
dynamicFactory client.DynamicFactory,
discoveryHelper discovery.Helper,
backedUpItems map[itemKey]struct{},
cohabitatingResources map[string]*cohabitatingResource,
podCommandExecutor podexec.PodCommandExecutor,
tarWriter tarWriter,
resticBackupper restic.Backupper,
resticSnapshotTracker *pvcSnapshotTracker,
volumeSnapshotterGetter VolumeSnapshotterGetter,
) resourceBackupper {
args := rbf.Called(
log,
backup,
dynamicFactory,
discoveryHelper,
backedUpItems,
cohabitatingResources,
podCommandExecutor,
tarWriter,
resticBackupper,
resticSnapshotTracker,
volumeSnapshotterGetter,
)
return args.Get(0).(resourceBackupper)
}
type mockResourceBackupper struct {
mock.Mock
}
func (rb *mockResourceBackupper) backupResource(group *metav1.APIResourceList, resource metav1.APIResource) error {
args := rb.Called(group, resource)
return args.Error(0)
}
func TestSortCoreGroup(t *testing.T) {
group := &metav1.APIResourceList{
GroupVersion: "v1",

View File

@@ -19,6 +19,7 @@ package backup
import (
"archive/tar"
"encoding/json"
"fmt"
"path/filepath"
"time"
@@ -33,6 +34,7 @@ import (
kubeerrs "k8s.io/apimachinery/pkg/util/errors"
api "github.com/heptio/velero/pkg/apis/velero/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
"github.com/heptio/velero/pkg/client"
"github.com/heptio/velero/pkg/discovery"
"github.com/heptio/velero/pkg/kuberesource"
@@ -45,7 +47,6 @@ import (
type itemBackupperFactory interface {
newItemBackupper(
backup *Request,
backedUpItems map[itemKey]struct{},
podCommandExecutor podexec.PodCommandExecutor,
tarWriter tarWriter,
dynamicFactory client.DynamicFactory,
@@ -60,7 +61,6 @@ type defaultItemBackupperFactory struct{}
func (f *defaultItemBackupperFactory) newItemBackupper(
backupRequest *Request,
backedUpItems map[itemKey]struct{},
podCommandExecutor podexec.PodCommandExecutor,
tarWriter tarWriter,
dynamicFactory client.DynamicFactory,
@@ -71,7 +71,6 @@ func (f *defaultItemBackupperFactory) newItemBackupper(
) ItemBackupper {
ib := &defaultItemBackupper{
backupRequest: backupRequest,
backedUpItems: backedUpItems,
tarWriter: tarWriter,
dynamicFactory: dynamicFactory,
discoveryHelper: discoveryHelper,
@@ -96,7 +95,6 @@ type ItemBackupper interface {
type defaultItemBackupper struct {
backupRequest *Request
backedUpItems map[itemKey]struct{}
tarWriter tarWriter
dynamicFactory client.DynamicFactory
discoveryHelper discovery.Helper
@@ -148,17 +146,18 @@ func (ib *defaultItemBackupper) backupItem(logger logrus.FieldLogger, obj runtim
log.Info("Skipping item because it's being deleted.")
return nil
}
key := itemKey{
resource: groupResource.String(),
resource: resourceKey(obj),
namespace: namespace,
name: name,
}
if _, exists := ib.backedUpItems[key]; exists {
if _, exists := ib.backupRequest.BackedUpItems[key]; exists {
log.Info("Skipping item because it's already been backed up.")
return nil
}
ib.backedUpItems[key] = struct{}{}
ib.backupRequest.BackedUpItems[key] = struct{}{}
log.Info("Backing up item")
@@ -206,6 +205,9 @@ func (ib *defaultItemBackupper) backupItem(logger logrus.FieldLogger, obj runtim
if metadata, err = meta.Accessor(obj); err != nil {
return errors.WithStack(err)
}
// update name and namespace in case they were modified in an action
name = metadata.GetName()
namespace = metadata.GetNamespace()
if groupResource == kuberesource.PersistentVolumes {
if err := ib.takePVSnapshot(obj, log); err != nil {
@@ -214,15 +216,10 @@ func (ib *defaultItemBackupper) backupItem(logger logrus.FieldLogger, obj runtim
}
if groupResource == kuberesource.Pods && pod != nil {
// this function will return partial results, so process volumeSnapshots
// this function will return partial results, so process podVolumeBackups
// even if there are errors.
volumeSnapshots, errs := ib.backupPodVolumes(log, pod, resticVolumesToBackup)
// annotate the pod with the successful volume snapshots
for volume, snapshot := range volumeSnapshots {
restic.SetPodSnapshotAnnotation(metadata, volume, snapshot)
}
podVolumeBackups, errs := ib.backupPodVolumes(log, pod, resticVolumesToBackup)
ib.backupRequest.PodVolumeBackups = podVolumeBackups
backupErrs = append(backupErrs, errs...)
}
@@ -266,9 +263,9 @@ func (ib *defaultItemBackupper) backupItem(logger logrus.FieldLogger, obj runtim
return nil
}
// backupPodVolumes triggers restic backups of the specified pod volumes, and returns a map of volume name -> snapshot ID
// backupPodVolumes triggers restic backups of the specified pod volumes, and returns a list of PodVolumeBackups
// for volumes that were successfully backed up, and a slice of any errors that were encountered.
func (ib *defaultItemBackupper) backupPodVolumes(log logrus.FieldLogger, pod *corev1api.Pod, volumes []string) (map[string]string, []error) {
func (ib *defaultItemBackupper) backupPodVolumes(log logrus.FieldLogger, pod *corev1api.Pod, volumes []string) ([]*velerov1api.PodVolumeBackup, []error) {
if len(volumes) == 0 {
return nil, nil
}
@@ -299,6 +296,11 @@ func (ib *defaultItemBackupper) executeActions(
continue
}
if namespace == "" && !action.namespaceIncludesExcludes.IncludeEverything() {
log.Debug("Skipping action because resource is cluster-scoped and action only applies to specific namespaces")
continue
}
if !action.selector.Matches(labels.Set(metadata.GetLabels())) {
log.Debug("Skipping action because label selector does not match")
continue
@@ -433,10 +435,12 @@ func (ib *defaultItemBackupper) takePVSnapshot(obj runtime.Unstructured, log log
log = log.WithField("volumeID", volumeID)
tags := map[string]string{
"velero.io/backup": ib.backupRequest.Name,
"velero.io/pv": pv.Name,
tags := ib.backupRequest.GetLabels()
if tags == nil {
tags = map[string]string{}
}
tags["velero.io/backup"] = ib.backupRequest.Name
tags["velero.io/pv"] = pv.Name
log.Info("Getting volume information")
volumeType, iops, err := volumeSnapshotter.GetVolumeInfo(volumeID, pvFailureDomainZone)
@@ -479,3 +483,10 @@ func volumeSnapshot(backup *api.Backup, volumeName, volumeID, volumeType, az, lo
},
}
}
// resourceKey returns a string representing the object's GroupVersionKind (e.g.
// apps/v1/Deployment).
func resourceKey(obj runtime.Unstructured) string {
gvk := obj.GetObjectKind().GroupVersionKind()
return fmt.Sprintf("%s/%s", gvk.GroupVersion().String(), gvk.Kind)
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2017, 2019 the Velero contributors.
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,853 +17,31 @@ limitations under the License.
package backup
import (
"archive/tar"
"encoding/json"
"fmt"
"reflect"
"testing"
"time"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
corev1api "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
v1 "github.com/heptio/velero/pkg/apis/velero/v1"
"github.com/heptio/velero/pkg/plugin/velero"
resticmocks "github.com/heptio/velero/pkg/restic/mocks"
"github.com/heptio/velero/pkg/util/collections"
velerotest "github.com/heptio/velero/pkg/util/test"
"github.com/heptio/velero/pkg/builder"
)
func TestBackupItemSkips(t *testing.T) {
func Test_resourceKey(t *testing.T) {
tests := []struct {
testName string
namespace string
name string
namespaces *collections.IncludesExcludes
groupResource schema.GroupResource
resources *collections.IncludesExcludes
terminating bool
backedUpItems map[itemKey]struct{}
resource metav1.Object
want string
}{
{
testName: "namespace not in includes list",
namespace: "ns",
name: "foo",
namespaces: collections.NewIncludesExcludes().Includes("a"),
},
{
testName: "namespace in excludes list",
namespace: "ns",
name: "foo",
namespaces: collections.NewIncludesExcludes().Excludes("ns"),
},
{
testName: "resource not in includes list",
namespace: "ns",
name: "foo",
groupResource: schema.GroupResource{Group: "foo", Resource: "bar"},
namespaces: collections.NewIncludesExcludes(),
resources: collections.NewIncludesExcludes().Includes("a.b"),
},
{
testName: "resource in excludes list",
namespace: "ns",
name: "foo",
groupResource: schema.GroupResource{Group: "foo", Resource: "bar"},
namespaces: collections.NewIncludesExcludes(),
resources: collections.NewIncludesExcludes().Excludes("bar.foo"),
},
{
testName: "resource already backed up",
namespace: "ns",
name: "foo",
groupResource: schema.GroupResource{Group: "foo", Resource: "bar"},
namespaces: collections.NewIncludesExcludes(),
resources: collections.NewIncludesExcludes(),
backedUpItems: map[itemKey]struct{}{
{resource: "bar.foo", namespace: "ns", name: "foo"}: {},
},
},
{
testName: "terminating resource",
namespace: "ns",
name: "foo",
groupResource: schema.GroupResource{Group: "foo", Resource: "bar"},
namespaces: collections.NewIncludesExcludes(),
resources: collections.NewIncludesExcludes(),
terminating: true,
},
{resource: builder.ForPod("default", "test").Result(), want: "v1/Pod"},
{resource: builder.ForDeployment("default", "test").Result(), want: "apps/v1/Deployment"},
{resource: builder.ForPersistentVolume("test").Result(), want: "v1/PersistentVolume"},
{resource: builder.ForRole("default", "test").Result(), want: "rbac.authorization.k8s.io/v1/Role"},
}
for _, test := range tests {
t.Run(test.testName, func(t *testing.T) {
req := &Request{
NamespaceIncludesExcludes: test.namespaces,
ResourceIncludesExcludes: test.resources,
}
ib := &defaultItemBackupper{
backupRequest: req,
backedUpItems: test.backedUpItems,
}
pod := &corev1api.Pod{
TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Pod"},
ObjectMeta: metav1.ObjectMeta{Namespace: test.namespace, Name: test.name},
}
if test.terminating {
pod.ObjectMeta.DeletionTimestamp = &metav1.Time{Time: time.Now()}
}
unstructuredObj, unmarshalErr := runtime.DefaultUnstructuredConverter.ToUnstructured(pod)
require.NoError(t, unmarshalErr)
u := &unstructured.Unstructured{Object: unstructuredObj}
err := ib.backupItem(velerotest.NewLogger(), u, test.groupResource)
assert.NoError(t, err)
for _, tt := range tests {
t.Run(tt.want, func(t *testing.T) {
content, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(tt.resource)
unstructured := &unstructured.Unstructured{Object: content}
assert.Equal(t, tt.want, resourceKey(unstructured))
})
}
}
func TestBackupItemSkipsClusterScopedResourceWhenIncludeClusterResourcesFalse(t *testing.T) {
f := false
ib := &defaultItemBackupper{
backupRequest: &Request{
Backup: &v1.Backup{
Spec: v1.BackupSpec{
IncludeClusterResources: &f,
},
},
NamespaceIncludesExcludes: collections.NewIncludesExcludes(),
ResourceIncludesExcludes: collections.NewIncludesExcludes(),
},
}
u := velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Foo","metadata":{"name":"bar"}}`)
err := ib.backupItem(velerotest.NewLogger(), u, schema.GroupResource{Group: "foo", Resource: "bar"})
assert.NoError(t, err)
}
func TestBackupItemNoSkips(t *testing.T) {
tests := []struct {
name string
item string
namespaceIncludesExcludes *collections.IncludesExcludes
expectError bool
expectExcluded bool
expectedTarHeaderName string
tarWriteError bool
tarHeaderWriteError bool
customAction bool
expectedActionID string
customActionAdditionalItemIdentifiers []velero.ResourceIdentifier
customActionAdditionalItems []runtime.Unstructured
groupResource string
snapshottableVolumes map[string]velerotest.VolumeBackupInfo
snapshotError error
additionalItemError error
trackedPVCs sets.String
expectedTrackedPVCs sets.String
}{
{
name: "explicit namespace include",
item: `{"metadata":{"namespace":"foo","name":"bar"}}`,
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("foo"),
expectError: false,
expectExcluded: false,
expectedTarHeaderName: "resources/resource.group/namespaces/foo/bar.json",
},
{
name: "* namespace include",
item: `{"metadata":{"namespace":"foo","name":"bar"}}`,
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
expectError: false,
expectExcluded: false,
expectedTarHeaderName: "resources/resource.group/namespaces/foo/bar.json",
},
{
name: "cluster-scoped",
item: `{"metadata":{"name":"bar"}}`,
expectError: false,
expectExcluded: false,
expectedTarHeaderName: "resources/resource.group/cluster/bar.json",
},
{
name: "tar header write error",
item: `{"metadata":{"name":"bar"},"spec":{"color":"green"},"status":{"foo":"bar"}}`,
expectError: true,
tarHeaderWriteError: true,
},
{
name: "tar write error",
item: `{"metadata":{"name":"bar"},"spec":{"color":"green"},"status":{"foo":"bar"}}`,
expectError: true,
tarWriteError: true,
},
{
name: "action invoked - cluster-scoped",
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
item: `{"metadata":{"name":"bar"}}`,
expectError: false,
expectExcluded: false,
expectedTarHeaderName: "resources/resource.group/cluster/bar.json",
customAction: true,
expectedActionID: "bar",
},
{
name: "action invoked - namespaced",
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
item: `{"metadata":{"namespace": "myns", "name":"bar"}}`,
expectError: false,
expectExcluded: false,
expectedTarHeaderName: "resources/resource.group/namespaces/myns/bar.json",
customAction: true,
expectedActionID: "myns/bar",
},
{
name: "action invoked - additional items",
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
item: `{"metadata":{"namespace": "myns", "name":"bar"}}`,
expectError: false,
expectExcluded: false,
expectedTarHeaderName: "resources/resource.group/namespaces/myns/bar.json",
customAction: true,
expectedActionID: "myns/bar",
customActionAdditionalItemIdentifiers: []velero.ResourceIdentifier{
{
GroupResource: schema.GroupResource{Group: "g1", Resource: "r1"},
Namespace: "ns1",
Name: "n1",
},
{
GroupResource: schema.GroupResource{Group: "g2", Resource: "r2"},
Namespace: "ns2",
Name: "n2",
},
},
customActionAdditionalItems: []runtime.Unstructured{
velerotest.UnstructuredOrDie(`{"apiVersion":"g1/v1","kind":"r1","metadata":{"namespace":"ns1","name":"n1"}}`),
velerotest.UnstructuredOrDie(`{"apiVersion":"g2/v1","kind":"r1","metadata":{"namespace":"ns2","name":"n2"}}`),
},
},
{
name: "action invoked - additional items - error",
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
item: `{"metadata":{"namespace": "myns", "name":"bar"}}`,
expectError: true,
expectExcluded: false,
expectedTarHeaderName: "resources/resource.group/namespaces/myns/bar.json",
customAction: true,
expectedActionID: "myns/bar",
customActionAdditionalItemIdentifiers: []velero.ResourceIdentifier{
{
GroupResource: schema.GroupResource{Group: "g1", Resource: "r1"},
Namespace: "ns1",
Name: "n1",
},
{
GroupResource: schema.GroupResource{Group: "g2", Resource: "r2"},
Namespace: "ns2",
Name: "n2",
},
},
customActionAdditionalItems: []runtime.Unstructured{
velerotest.UnstructuredOrDie(`{"apiVersion":"g1/v1","kind":"r1","metadata":{"namespace":"ns1","name":"n1"}}`),
velerotest.UnstructuredOrDie(`{"apiVersion":"g2/v1","kind":"r1","metadata":{"namespace":"ns2","name":"n2"}}`),
},
additionalItemError: errors.New("foo"),
},
{
name: "takePVSnapshot is not invoked for PVs when volumeSnapshotter == nil",
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
item: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/zone": "us-east-1c"}}, "spec": {"awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`,
expectError: false,
expectExcluded: false,
expectedTarHeaderName: "resources/persistentvolumes/cluster/mypv.json",
groupResource: "persistentvolumes",
},
{
name: "takePVSnapshot is invoked for PVs when volumeSnapshotter != nil",
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
item: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/zone": "us-east-1c"}}, "spec": {"awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`,
expectError: false,
expectExcluded: false,
expectedTarHeaderName: "resources/persistentvolumes/cluster/mypv.json",
groupResource: "persistentvolumes",
snapshottableVolumes: map[string]velerotest.VolumeBackupInfo{
"vol-abc123": {SnapshotID: "snapshot-1", AvailabilityZone: "us-east-1c"},
},
},
{
name: "takePVSnapshot is not invoked for PVs when their claim is tracked in the restic PVC tracker",
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
item: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/zone": "us-east-1c"}}, "spec": {"claimRef": {"namespace": "pvc-ns", "name": "pvc"}, "awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`,
expectError: false,
expectExcluded: false,
expectedTarHeaderName: "resources/persistentvolumes/cluster/mypv.json",
groupResource: "persistentvolumes",
// empty snapshottableVolumes causes a volumeSnapshotter to be created, but no
// snapshots are expected to be taken.
snapshottableVolumes: map[string]velerotest.VolumeBackupInfo{},
trackedPVCs: sets.NewString(key("pvc-ns", "pvc"), key("another-pvc-ns", "another-pvc")),
},
{
name: "takePVSnapshot is invoked for PVs when their claim is not tracked in the restic PVC tracker",
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
item: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/zone": "us-east-1c"}}, "spec": {"claimRef": {"namespace": "pvc-ns", "name": "pvc"}, "awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`,
expectError: false,
expectExcluded: false,
expectedTarHeaderName: "resources/persistentvolumes/cluster/mypv.json",
groupResource: "persistentvolumes",
snapshottableVolumes: map[string]velerotest.VolumeBackupInfo{
"vol-abc123": {SnapshotID: "snapshot-1", AvailabilityZone: "us-east-1c"},
},
trackedPVCs: sets.NewString(key("another-pvc-ns", "another-pvc")),
},
{
name: "backup fails when takePVSnapshot fails",
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
item: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/zone": "us-east-1c"}}, "spec": {"awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`,
expectError: true,
groupResource: "persistentvolumes",
snapshottableVolumes: map[string]velerotest.VolumeBackupInfo{
"vol-abc123": {SnapshotID: "snapshot-1", AvailabilityZone: "us-east-1c"},
},
snapshotError: fmt.Errorf("failure"),
},
{
name: "pod's restic PVC volume backups (only) are tracked",
item: `{"apiVersion": "v1", "kind": "Pod", "spec": {"volumes": [{"name": "volume-1", "persistentVolumeClaim": {"claimName": "bar"}},{"name": "volume-2", "persistentVolumeClaim": {"claimName": "baz"}},{"name": "volume-1", "emptyDir": {}}]}, "metadata":{"namespace":"foo","name":"bar", "annotations": {"backup.velero.io/backup-volumes": "volume-1,volume-2"}}}`,
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
groupResource: "pods",
expectError: false,
expectExcluded: false,
expectedTarHeaderName: "resources/pods/namespaces/foo/bar.json",
expectedTrackedPVCs: sets.NewString(key("foo", "bar"), key("foo", "baz")),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var (
action *fakeAction
backup = new(Request)
groupResource = schema.ParseGroupResource("resource.group")
backedUpItems = make(map[itemKey]struct{})
w = &fakeTarWriter{}
)
backup.Backup = new(v1.Backup)
backup.NamespaceIncludesExcludes = collections.NewIncludesExcludes()
backup.ResourceIncludesExcludes = collections.NewIncludesExcludes()
backup.SnapshotLocations = []*v1.VolumeSnapshotLocation{
new(v1.VolumeSnapshotLocation),
}
if test.groupResource != "" {
groupResource = schema.ParseGroupResource(test.groupResource)
}
item, err := velerotest.GetAsMap(test.item)
if err != nil {
t.Fatal(err)
}
namespaces := test.namespaceIncludesExcludes
if namespaces == nil {
namespaces = collections.NewIncludesExcludes()
}
if test.tarHeaderWriteError {
w.writeHeaderError = errors.New("error")
}
if test.tarWriteError {
w.writeError = errors.New("error")
}
if test.customAction {
action = &fakeAction{
additionalItems: test.customActionAdditionalItemIdentifiers,
}
backup.ResolvedActions = []resolvedAction{
{
BackupItemAction: action,
namespaceIncludesExcludes: collections.NewIncludesExcludes(),
resourceIncludesExcludes: collections.NewIncludesExcludes().Includes(groupResource.String()),
selector: labels.Everything(),
},
}
}
podCommandExecutor := &velerotest.MockPodCommandExecutor{}
defer podCommandExecutor.AssertExpectations(t)
dynamicFactory := &velerotest.FakeDynamicFactory{}
defer dynamicFactory.AssertExpectations(t)
discoveryHelper := velerotest.NewFakeDiscoveryHelper(true, nil)
volumeSnapshotterGetter := &volumeSnapshotterGetter{}
b := (&defaultItemBackupperFactory{}).newItemBackupper(
backup,
backedUpItems,
podCommandExecutor,
w,
dynamicFactory,
discoveryHelper,
nil, // restic backupper
newPVCSnapshotTracker(),
volumeSnapshotterGetter,
).(*defaultItemBackupper)
var volumeSnapshotter *velerotest.FakeVolumeSnapshotter
if test.snapshottableVolumes != nil {
volumeSnapshotter = &velerotest.FakeVolumeSnapshotter{
SnapshottableVolumes: test.snapshottableVolumes,
VolumeID: "vol-abc123",
Error: test.snapshotError,
}
volumeSnapshotterGetter.volumeSnapshotter = volumeSnapshotter
}
if test.trackedPVCs != nil {
b.resticSnapshotTracker.pvcs = test.trackedPVCs
}
// make sure the podCommandExecutor was set correctly in the real hook handler
assert.Equal(t, podCommandExecutor, b.itemHookHandler.(*defaultItemHookHandler).podCommandExecutor)
itemHookHandler := &mockItemHookHandler{}
defer itemHookHandler.AssertExpectations(t)
b.itemHookHandler = itemHookHandler
additionalItemBackupper := &mockItemBackupper{}
defer additionalItemBackupper.AssertExpectations(t)
b.additionalItemBackupper = additionalItemBackupper
obj := &unstructured.Unstructured{Object: item}
itemHookHandler.On("handleHooks", mock.Anything, groupResource, obj, backup.ResourceHooks, hookPhasePre).Return(nil)
itemHookHandler.On("handleHooks", mock.Anything, groupResource, obj, backup.ResourceHooks, hookPhasePost).Return(nil)
for i, item := range test.customActionAdditionalItemIdentifiers {
if test.additionalItemError != nil && i > 0 {
break
}
itemClient := &velerotest.FakeDynamicClient{}
defer itemClient.AssertExpectations(t)
dynamicFactory.On("ClientForGroupVersionResource", item.GroupResource.WithVersion("").GroupVersion(), metav1.APIResource{Name: item.Resource}, item.Namespace).Return(itemClient, nil)
itemClient.On("Get", item.Name, metav1.GetOptions{}).Return(test.customActionAdditionalItems[i], nil)
additionalItemBackupper.On("backupItem", mock.AnythingOfType("*logrus.Entry"), test.customActionAdditionalItems[i], item.GroupResource).Return(test.additionalItemError)
}
err = b.backupItem(velerotest.NewLogger(), obj, groupResource)
gotError := err != nil
if e, a := test.expectError, gotError; e != a {
t.Fatalf("error: expected %t, got %t: %v", e, a, err)
}
if test.expectError {
return
}
if test.expectExcluded {
if len(w.headers) > 0 {
t.Errorf("unexpected header write")
}
if len(w.data) > 0 {
t.Errorf("unexpected data write")
}
return
}
// Convert to JSON for comparing number of bytes to the tar header
itemJSON, err := json.Marshal(&item)
if err != nil {
t.Fatal(err)
}
require.Equal(t, 1, len(w.headers), "headers")
assert.Equal(t, test.expectedTarHeaderName, w.headers[0].Name, "header.name")
assert.Equal(t, int64(len(itemJSON)), w.headers[0].Size, "header.size")
assert.Equal(t, byte(tar.TypeReg), w.headers[0].Typeflag, "header.typeflag")
assert.Equal(t, int64(0755), w.headers[0].Mode, "header.mode")
assert.False(t, w.headers[0].ModTime.IsZero(), "header.modTime set")
assert.Equal(t, 1, len(w.data), "# of data")
actual, err := velerotest.GetAsMap(string(w.data[0]))
if err != nil {
t.Fatal(err)
}
if e, a := item, actual; !reflect.DeepEqual(e, a) {
t.Errorf("data: expected %s, got %s", e, a)
}
if test.customAction {
if len(action.ids) != 1 {
t.Errorf("unexpected custom action ids: %v", action.ids)
} else if e, a := test.expectedActionID, action.ids[0]; e != a {
t.Errorf("action.ids[0]: expected %s, got %s", e, a)
}
require.Equal(t, 1, len(action.backups), "unexpected custom action backups: %#v", action.backups)
assert.Equal(t, backup.Backup, &(action.backups[0]), "backup")
}
if test.snapshottableVolumes != nil {
require.Equal(t, len(test.snapshottableVolumes), len(volumeSnapshotter.SnapshotsTaken))
}
if len(test.snapshottableVolumes) > 0 {
require.Len(t, backup.VolumeSnapshots, 1)
snapshot := backup.VolumeSnapshots[0]
assert.Equal(t, test.snapshottableVolumes["vol-abc123"].SnapshotID, snapshot.Status.ProviderSnapshotID)
assert.Equal(t, test.snapshottableVolumes["vol-abc123"].Type, snapshot.Spec.VolumeType)
assert.Equal(t, test.snapshottableVolumes["vol-abc123"].Iops, snapshot.Spec.VolumeIOPS)
assert.Equal(t, test.snapshottableVolumes["vol-abc123"].AvailabilityZone, snapshot.Spec.VolumeAZ)
}
if test.expectedTrackedPVCs != nil {
require.Equal(t, len(test.expectedTrackedPVCs), len(b.resticSnapshotTracker.pvcs))
for key := range test.expectedTrackedPVCs {
assert.True(t, b.resticSnapshotTracker.pvcs.Has(key))
}
}
})
}
}
type volumeSnapshotterGetter struct {
volumeSnapshotter velero.VolumeSnapshotter
}
func (b *volumeSnapshotterGetter) GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) {
if b.volumeSnapshotter != nil {
return b.volumeSnapshotter, nil
}
return nil, errors.New("plugin not found")
}
type addAnnotationAction struct{}
func (a *addAnnotationAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) {
// since item actions run out-of-proc, do a deep-copy here to simulate passing data
// across a process boundary.
copy := item.(*unstructured.Unstructured).DeepCopy()
metadata, err := meta.Accessor(copy)
if err != nil {
return copy, nil, nil
}
annotations := metadata.GetAnnotations()
if annotations == nil {
annotations = make(map[string]string)
}
annotations["foo"] = "bar"
metadata.SetAnnotations(annotations)
return copy, nil, nil
}
func (a *addAnnotationAction) AppliesTo() (velero.ResourceSelector, error) {
panic("not implemented")
}
func TestItemActionModificationsToItemPersist(t *testing.T) {
var (
w = &fakeTarWriter{}
obj = &unstructured.Unstructured{
Object: map[string]interface{}{
"metadata": map[string]interface{}{
"namespace": "myns",
"name": "bar",
},
},
}
req = &Request{
NamespaceIncludesExcludes: collections.NewIncludesExcludes(),
ResourceIncludesExcludes: collections.NewIncludesExcludes(),
ResolvedActions: []resolvedAction{
{
BackupItemAction: &addAnnotationAction{},
namespaceIncludesExcludes: collections.NewIncludesExcludes(),
resourceIncludesExcludes: collections.NewIncludesExcludes(),
selector: labels.Everything(),
},
},
}
b = (&defaultItemBackupperFactory{}).newItemBackupper(
req,
make(map[itemKey]struct{}),
nil,
w,
&velerotest.FakeDynamicFactory{},
velerotest.NewFakeDiscoveryHelper(true, nil),
nil,
newPVCSnapshotTracker(),
nil,
).(*defaultItemBackupper)
)
// our expected backed-up object is the passed-in object plus the annotation
// that the backup item action adds.
expected := obj.DeepCopy()
expected.SetAnnotations(map[string]string{"foo": "bar"})
// method under test
require.NoError(t, b.backupItem(velerotest.NewLogger(), obj, schema.ParseGroupResource("resource.group")))
// get the actual backed-up item
require.Len(t, w.data, 1)
actual, err := velerotest.GetAsMap(string(w.data[0]))
require.NoError(t, err)
assert.EqualValues(t, expected.Object, actual)
}
func TestResticAnnotationsPersist(t *testing.T) {
var (
w = &fakeTarWriter{}
obj = &unstructured.Unstructured{
Object: map[string]interface{}{
"metadata": map[string]interface{}{
"namespace": "myns",
"name": "bar",
"annotations": map[string]interface{}{
"backup.velero.io/backup-volumes": "volume-1,volume-2",
},
},
},
}
req = &Request{
NamespaceIncludesExcludes: collections.NewIncludesExcludes(),
ResourceIncludesExcludes: collections.NewIncludesExcludes(),
ResolvedActions: []resolvedAction{
{
BackupItemAction: &addAnnotationAction{},
namespaceIncludesExcludes: collections.NewIncludesExcludes(),
resourceIncludesExcludes: collections.NewIncludesExcludes(),
selector: labels.Everything(),
},
},
}
resticBackupper = &resticmocks.Backupper{}
b = (&defaultItemBackupperFactory{}).newItemBackupper(
req,
make(map[itemKey]struct{}),
nil,
w,
&velerotest.FakeDynamicFactory{},
velerotest.NewFakeDiscoveryHelper(true, nil),
resticBackupper,
newPVCSnapshotTracker(),
nil,
).(*defaultItemBackupper)
)
resticBackupper.
On("BackupPodVolumes", mock.Anything, mock.Anything, mock.Anything).
Return(map[string]string{"volume-1": "snapshot-1", "volume-2": "snapshot-2"}, nil)
// our expected backed-up object is the passed-in object, plus the annotation
// that the backup item action adds, plus the annotations that the restic
// backupper adds
expected := obj.DeepCopy()
annotations := expected.GetAnnotations()
if annotations == nil {
annotations = make(map[string]string)
}
annotations["foo"] = "bar"
annotations["snapshot.velero.io/volume-1"] = "snapshot-1"
annotations["snapshot.velero.io/volume-2"] = "snapshot-2"
expected.SetAnnotations(annotations)
// method under test
require.NoError(t, b.backupItem(velerotest.NewLogger(), obj, schema.ParseGroupResource("pods")))
// get the actual backed-up item
require.Len(t, w.data, 1)
actual, err := velerotest.GetAsMap(string(w.data[0]))
require.NoError(t, err)
assert.EqualValues(t, expected.Object, actual)
}
func TestTakePVSnapshot(t *testing.T) {
iops := int64(1000)
tests := []struct {
name string
snapshotEnabled bool
pv string
ttl time.Duration
expectError bool
expectedVolumeID string
expectedSnapshotsTaken int
volumeInfo map[string]velerotest.VolumeBackupInfo
}{
{
name: "snapshot disabled",
pv: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv"}}`,
snapshotEnabled: false,
},
{
name: "unsupported PV source type",
snapshotEnabled: true,
pv: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv"}, "spec": {"unsupportedPVSource": {}}}`,
expectError: false,
},
{
name: "without iops",
snapshotEnabled: true,
pv: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/zone": "us-east-1c"}}, "spec": {"awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`,
expectError: false,
expectedSnapshotsTaken: 1,
expectedVolumeID: "vol-abc123",
ttl: 5 * time.Minute,
volumeInfo: map[string]velerotest.VolumeBackupInfo{
"vol-abc123": {Type: "gp", SnapshotID: "snap-1", AvailabilityZone: "us-east-1c"},
},
},
{
name: "with iops",
snapshotEnabled: true,
pv: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/zone": "us-east-1c"}}, "spec": {"awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`,
expectError: false,
expectedSnapshotsTaken: 1,
expectedVolumeID: "vol-abc123",
ttl: 5 * time.Minute,
volumeInfo: map[string]velerotest.VolumeBackupInfo{
"vol-abc123": {Type: "io1", Iops: &iops, SnapshotID: "snap-1", AvailabilityZone: "us-east-1c"},
},
},
{
name: "create snapshot error",
snapshotEnabled: true,
pv: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv"}, "spec": {"gcePersistentDisk": {"pdName": "pd-abc123"}}}`,
expectedVolumeID: "pd-abc123",
expectError: true,
},
{
name: "PV with label metadata but no failureDomainZone",
snapshotEnabled: true,
pv: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/region": "us-east-1"}}, "spec": {"awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`,
expectError: false,
expectedSnapshotsTaken: 1,
expectedVolumeID: "vol-abc123",
ttl: 5 * time.Minute,
volumeInfo: map[string]velerotest.VolumeBackupInfo{
"vol-abc123": {Type: "gp", SnapshotID: "snap-1"},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
backup := &v1.Backup{
ObjectMeta: metav1.ObjectMeta{
Namespace: v1.DefaultNamespace,
Name: "mybackup",
},
Spec: v1.BackupSpec{
SnapshotVolumes: &test.snapshotEnabled,
TTL: metav1.Duration{Duration: test.ttl},
},
}
volumeSnapshotter := &velerotest.FakeVolumeSnapshotter{
SnapshottableVolumes: test.volumeInfo,
VolumeID: test.expectedVolumeID,
}
ib := &defaultItemBackupper{
backupRequest: &Request{
Backup: backup,
SnapshotLocations: []*v1.VolumeSnapshotLocation{new(v1.VolumeSnapshotLocation)},
},
volumeSnapshotterGetter: &volumeSnapshotterGetter{volumeSnapshotter: volumeSnapshotter},
}
pv, err := velerotest.GetAsMap(test.pv)
if err != nil {
t.Fatal(err)
}
// method under test
err = ib.takePVSnapshot(&unstructured.Unstructured{Object: pv}, velerotest.NewLogger())
gotErr := err != nil
if e, a := test.expectError, gotErr; e != a {
t.Errorf("error: expected %v, got %v", e, a)
}
if test.expectError {
return
}
if !test.snapshotEnabled {
// don't need to check anything else if snapshots are disabled
return
}
// we should have exactly one snapshot taken
require.Equal(t, test.expectedSnapshotsTaken, volumeSnapshotter.SnapshotsTaken.Len())
if test.expectedSnapshotsTaken > 0 {
require.Len(t, ib.backupRequest.VolumeSnapshots, 1)
snapshot := ib.backupRequest.VolumeSnapshots[0]
snapshotID, _ := volumeSnapshotter.SnapshotsTaken.PopAny()
assert.Equal(t, snapshotID, snapshot.Status.ProviderSnapshotID)
assert.Equal(t, test.volumeInfo[test.expectedVolumeID].Type, snapshot.Spec.VolumeType)
assert.Equal(t, test.volumeInfo[test.expectedVolumeID].Iops, snapshot.Spec.VolumeIOPS)
assert.Equal(t, test.volumeInfo[test.expectedVolumeID].AvailabilityZone, snapshot.Spec.VolumeAZ)
}
})
}
}
type fakeTarWriter struct {
closeCalled bool
headers []*tar.Header
data [][]byte
writeHeaderError error
writeError error
}
func (w *fakeTarWriter) Close() error { return nil }
func (w *fakeTarWriter) Write(data []byte) (int, error) {
w.data = append(w.data, data)
return 0, w.writeError
}
func (w *fakeTarWriter) WriteHeader(header *tar.Header) error {
w.headers = append(w.headers, header)
return w.writeHeaderError
}
type mockItemBackupper struct {
mock.Mock
}
func (ib *mockItemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstructured, groupResource schema.GroupResource) error {
args := ib.Called(logger, obj, groupResource)
return args.Error(0)
}

View File

@@ -694,3 +694,11 @@ func TestResourceHookApplicableTo(t *testing.T) {
})
}
}
func parseLabelSelectorOrDie(s string) labels.Selector {
ret, err := labels.Parse(s)
if err != nil {
panic(err)
}
return ret
}

View File

@@ -1,11 +1,35 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package backup
import (
"fmt"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
"github.com/heptio/velero/pkg/util/collections"
"github.com/heptio/velero/pkg/volume"
)
type itemKey struct {
resource string
namespace string
name string
}
// Request is a request for a backup, with all references to other objects
// materialized (e.g. backup/snapshot locations, includes/excludes, etc.)
type Request struct {
@@ -18,5 +42,21 @@ type Request struct {
ResourceHooks []resourceHook
ResolvedActions []resolvedAction
VolumeSnapshots []*volume.Snapshot
VolumeSnapshots []*volume.Snapshot
PodVolumeBackups []*velerov1api.PodVolumeBackup
BackedUpItems map[itemKey]struct{}
}
// BackupResourceList returns the list of backed up resources grouped by the API
// Version and Kind
func (r *Request) BackupResourceList() map[string][]string {
resources := map[string][]string{}
for i := range r.BackedUpItems {
entry := i.name
if i.namespace != "" {
entry = fmt.Sprintf("%s/%s", i.namespace, i.name)
}
resources[i.resource] = append(resources[i.resource], entry)
}
return resources
}

View File

@@ -0,0 +1,58 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package backup
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestRequest_BackupResourceList(t *testing.T) {
items := []itemKey{
{
resource: "apps/v1/Deployment",
name: "my-deploy",
namespace: "default",
},
{
resource: "v1/Pod",
name: "pod1",
namespace: "ns1",
},
{
resource: "v1/Pod",
name: "pod2",
namespace: "ns2",
},
{
resource: "v1/PersistentVolume",
name: "my-pv",
},
}
backedUpItems := map[itemKey]struct{}{}
for _, it := range items {
backedUpItems[it] = struct{}{}
}
req := Request{BackedUpItems: backedUpItems}
assert.Equal(t, req.BackupResourceList(), map[string][]string{
"apps/v1/Deployment": {"default/my-deploy"},
"v1/Pod": {"ns1/pod1", "ns2/pod2"},
"v1/PersistentVolume": {"my-pv"},
})
}

View File

@@ -40,7 +40,6 @@ type resourceBackupperFactory interface {
backupRequest *Request,
dynamicFactory client.DynamicFactory,
discoveryHelper discovery.Helper,
backedUpItems map[itemKey]struct{},
cohabitatingResources map[string]*cohabitatingResource,
podCommandExecutor podexec.PodCommandExecutor,
tarWriter tarWriter,
@@ -57,7 +56,6 @@ func (f *defaultResourceBackupperFactory) newResourceBackupper(
backupRequest *Request,
dynamicFactory client.DynamicFactory,
discoveryHelper discovery.Helper,
backedUpItems map[itemKey]struct{},
cohabitatingResources map[string]*cohabitatingResource,
podCommandExecutor podexec.PodCommandExecutor,
tarWriter tarWriter,
@@ -70,7 +68,6 @@ func (f *defaultResourceBackupperFactory) newResourceBackupper(
backupRequest: backupRequest,
dynamicFactory: dynamicFactory,
discoveryHelper: discoveryHelper,
backedUpItems: backedUpItems,
cohabitatingResources: cohabitatingResources,
podCommandExecutor: podCommandExecutor,
tarWriter: tarWriter,
@@ -91,7 +88,6 @@ type defaultResourceBackupper struct {
backupRequest *Request
dynamicFactory client.DynamicFactory
discoveryHelper discovery.Helper
backedUpItems map[itemKey]struct{}
cohabitatingResources map[string]*cohabitatingResource
podCommandExecutor podexec.PodCommandExecutor
tarWriter tarWriter
@@ -156,7 +152,6 @@ func (rb *defaultResourceBackupper) backupResource(group *metav1.APIResourceList
itemBackupper := rb.itemBackupperFactory.newItemBackupper(
rb.backupRequest,
rb.backedUpItems,
rb.podCommandExecutor,
rb.tarWriter,
rb.dynamicFactory,
@@ -221,9 +216,9 @@ func (rb *defaultResourceBackupper) backupResource(group *metav1.APIResourceList
continue
}
var labelSelector string
labelSelector := "velero.io/exclude-from-backup!=true"
if selector := rb.backupRequest.Spec.LabelSelector; selector != nil {
labelSelector = metav1.FormatLabelSelector(selector)
labelSelector = labelSelector + "," + metav1.FormatLabelSelector(selector)
}
log.Info("Listing items")

View File

@@ -1,665 +0,0 @@
/*
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 backup
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
v1 "github.com/heptio/velero/pkg/apis/velero/v1"
"github.com/heptio/velero/pkg/client"
"github.com/heptio/velero/pkg/discovery"
"github.com/heptio/velero/pkg/kuberesource"
"github.com/heptio/velero/pkg/podexec"
"github.com/heptio/velero/pkg/restic"
"github.com/heptio/velero/pkg/util/collections"
velerotest "github.com/heptio/velero/pkg/util/test"
)
func TestBackupResource(t *testing.T) {
var (
trueVal = true
falseVal = false
truePointer = &trueVal
falsePointer = &falseVal
)
tests := []struct {
name string
namespaces *collections.IncludesExcludes
resources *collections.IncludesExcludes
expectSkip bool
expectedListedNamespaces []string
apiGroup *metav1.APIResourceList
apiResource metav1.APIResource
groupVersion schema.GroupVersion
groupResource schema.GroupResource
listResponses [][]*unstructured.Unstructured
getResponses []*unstructured.Unstructured
includeClusterResources *bool
}{
{
name: "resource not included",
apiGroup: v1Group,
apiResource: podsResource,
resources: collections.NewIncludesExcludes().Excludes("pods"),
expectSkip: true,
},
{
name: "list all namespaces",
namespaces: collections.NewIncludesExcludes(),
resources: collections.NewIncludesExcludes(),
expectedListedNamespaces: []string{""},
apiGroup: v1Group,
apiResource: podsResource,
groupVersion: schema.GroupVersion{Group: "", Version: "v1"},
groupResource: schema.GroupResource{Group: "", Resource: "pods"},
listResponses: [][]*unstructured.Unstructured{
{
velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Pod","metadata":{"namespace":"myns","name":"myname1"}}`),
velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Pod","metadata":{"namespace":"myns","name":"myname2"}}`),
},
},
},
{
name: "list selected namespaces",
namespaces: collections.NewIncludesExcludes().Includes("a", "b"),
resources: collections.NewIncludesExcludes(),
expectedListedNamespaces: []string{"a", "b"},
apiGroup: v1Group,
apiResource: podsResource,
groupVersion: schema.GroupVersion{Group: "", Version: "v1"},
groupResource: schema.GroupResource{Group: "", Resource: "pods"},
listResponses: [][]*unstructured.Unstructured{
{
velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Pod","metadata":{"namespace":"a","name":"myname1"}}`),
velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Pod","metadata":{"namespace":"a","name":"myname2"}}`),
},
{
velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Pod","metadata":{"namespace":"b","name":"myname3"}}`),
velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Pod","metadata":{"namespace":"b","name":"myname4"}}`),
},
},
},
{
name: "list all namespaces - cluster scoped",
namespaces: collections.NewIncludesExcludes(),
resources: collections.NewIncludesExcludes(),
expectedListedNamespaces: []string{""},
apiGroup: certificatesGroup,
apiResource: certificateSigningRequestsResource,
groupVersion: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1beta1"},
groupResource: schema.GroupResource{Group: "certificates.k8s.io", Resource: "certificatesigningrequests"},
listResponses: [][]*unstructured.Unstructured{
{
velerotest.UnstructuredOrDie(`{"apiVersion":"certificates.k8s.io/v1beta1","kind":"CertificateSigningRequest","metadata":{"name":"myname1"}}`),
velerotest.UnstructuredOrDie(`{"apiVersion":"certificates.k8s.io/v1beta1","kind":"CertificateSigningRequest","metadata":{"name":"myname2"}}`),
},
},
},
{
name: "should include cluster-scoped resource if backing up subset of namespaces and --include-cluster-resources=true",
namespaces: collections.NewIncludesExcludes().Includes("ns-1"),
resources: collections.NewIncludesExcludes(),
includeClusterResources: truePointer,
expectedListedNamespaces: []string{""},
apiGroup: certificatesGroup,
apiResource: certificateSigningRequestsResource,
groupVersion: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1beta1"},
groupResource: schema.GroupResource{Group: "certificates.k8s.io", Resource: "certificatesigningrequests"},
listResponses: [][]*unstructured.Unstructured{
{
velerotest.UnstructuredOrDie(`{"apiVersion":"certificates.k8s.io/v1beta1","kind":"CertificateSigningRequest","metadata":{"name":"myname1"}}`),
velerotest.UnstructuredOrDie(`{"apiVersion":"certificates.k8s.io/v1beta1","kind":"CertificateSigningRequest","metadata":{"name":"myname2"}}`),
},
},
},
{
name: "should not include cluster-scoped resource if backing up subset of namespaces and --include-cluster-resources=false",
namespaces: collections.NewIncludesExcludes().Includes("ns-1"),
resources: collections.NewIncludesExcludes(),
includeClusterResources: falsePointer,
apiGroup: certificatesGroup,
apiResource: certificateSigningRequestsResource,
groupVersion: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1beta1"},
groupResource: schema.GroupResource{Group: "certificates.k8s.io", Resource: "certificatesigningrequests"},
expectSkip: true,
},
{
name: "should not include cluster-scoped resource if backing up subset of namespaces and --include-cluster-resources=nil",
namespaces: collections.NewIncludesExcludes().Includes("ns-1"),
resources: collections.NewIncludesExcludes(),
includeClusterResources: nil,
apiGroup: certificatesGroup,
apiResource: certificateSigningRequestsResource,
groupVersion: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1beta1"},
groupResource: schema.GroupResource{Group: "certificates.k8s.io", Resource: "certificatesigningrequests"},
expectSkip: true,
},
{
name: "should include cluster-scoped resource if backing up all namespaces and --include-cluster-resources=true",
namespaces: collections.NewIncludesExcludes(),
resources: collections.NewIncludesExcludes(),
includeClusterResources: truePointer,
expectedListedNamespaces: []string{""},
apiGroup: certificatesGroup,
apiResource: certificateSigningRequestsResource,
groupVersion: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1beta1"},
groupResource: schema.GroupResource{Group: "certificates.k8s.io", Resource: "certificatesigningrequests"},
listResponses: [][]*unstructured.Unstructured{
{
velerotest.UnstructuredOrDie(`{"apiVersion":"certificates.k8s.io/v1beta1","kind":"CertificateSigningRequest","metadata":{"name":"myname1"}}`),
velerotest.UnstructuredOrDie(`{"apiVersion":"certificates.k8s.io/v1beta1","kind":"CertificateSigningRequest","metadata":{"name":"myname2"}}`),
},
},
},
{
name: "should not include cluster-scoped resource if backing up all namespaces and --include-cluster-resources=false",
namespaces: collections.NewIncludesExcludes(),
resources: collections.NewIncludesExcludes(),
includeClusterResources: falsePointer,
apiGroup: certificatesGroup,
apiResource: certificateSigningRequestsResource,
groupVersion: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1beta1"},
groupResource: schema.GroupResource{Group: "certificates.k8s.io", Resource: "certificatesigningrequests"},
expectSkip: true,
},
{
name: "should include cluster-scoped resource if backing up all namespaces and --include-cluster-resources=nil",
namespaces: collections.NewIncludesExcludes(),
resources: collections.NewIncludesExcludes(),
includeClusterResources: nil,
expectedListedNamespaces: []string{""},
apiGroup: certificatesGroup,
apiResource: certificateSigningRequestsResource,
groupVersion: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1beta1"},
groupResource: schema.GroupResource{Group: "certificates.k8s.io", Resource: "certificatesigningrequests"},
listResponses: [][]*unstructured.Unstructured{
{
velerotest.UnstructuredOrDie(`{"apiVersion":"certificates.k8s.io/v1beta1","kind":"CertificateSigningRequest","metadata":{"name":"myname1"}}`),
velerotest.UnstructuredOrDie(`{"apiVersion":"certificates.k8s.io/v1beta1","kind":"CertificateSigningRequest","metadata":{"name":"myname2"}}`),
},
},
},
{
name: "should include specified namespaces if backing up subset of namespaces and --include-cluster-resources=nil",
namespaces: collections.NewIncludesExcludes().Includes("ns-1", "ns-2"),
resources: collections.NewIncludesExcludes(),
includeClusterResources: nil,
expectedListedNamespaces: []string{"ns-1", "ns-2"},
apiGroup: v1Group,
apiResource: namespacesResource,
groupVersion: schema.GroupVersion{Group: "", Version: "v1"},
groupResource: schema.GroupResource{Group: "", Resource: "namespaces"},
expectSkip: false,
getResponses: []*unstructured.Unstructured{
velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"ns-1"}}`),
velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"ns-2"}}`),
},
},
}
for _, test := range tests {
req := &Request{
Backup: &v1.Backup{
Spec: v1.BackupSpec{
IncludeClusterResources: test.includeClusterResources,
},
},
ResolvedActions: []resolvedAction{
{
BackupItemAction: newFakeAction("pods"),
resourceIncludesExcludes: collections.NewIncludesExcludes().Includes("pods"),
},
},
ResourceHooks: []resourceHook{
{name: "myhook"},
},
ResourceIncludesExcludes: test.resources,
NamespaceIncludesExcludes: test.namespaces,
}
dynamicFactory := &velerotest.FakeDynamicFactory{}
defer dynamicFactory.AssertExpectations(t)
discoveryHelper := velerotest.NewFakeDiscoveryHelper(true, nil)
backedUpItems := map[itemKey]struct{}{
{resource: "foo", namespace: "ns", name: "name"}: {},
}
cohabitatingResources := map[string]*cohabitatingResource{
"deployments": newCohabitatingResource("deployments", "extensions", "apps"),
"networkpolicies": newCohabitatingResource("networkpolicies", "extensions", "networking.k8s.io"),
}
podCommandExecutor := &velerotest.MockPodCommandExecutor{}
defer podCommandExecutor.AssertExpectations(t)
tarWriter := &fakeTarWriter{}
t.Run(test.name, func(t *testing.T) {
rb := (&defaultResourceBackupperFactory{}).newResourceBackupper(
velerotest.NewLogger(),
req,
dynamicFactory,
discoveryHelper,
backedUpItems,
cohabitatingResources,
podCommandExecutor,
tarWriter,
nil, // restic backupper
newPVCSnapshotTracker(),
nil,
).(*defaultResourceBackupper)
itemBackupperFactory := &mockItemBackupperFactory{}
defer itemBackupperFactory.AssertExpectations(t)
rb.itemBackupperFactory = itemBackupperFactory
if !test.expectSkip {
itemBackupper := &mockItemBackupper{}
defer itemBackupper.AssertExpectations(t)
itemBackupperFactory.On("newItemBackupper",
req,
backedUpItems,
podCommandExecutor,
tarWriter,
dynamicFactory,
discoveryHelper,
mock.Anything,
mock.Anything,
mock.Anything,
).Return(itemBackupper)
if len(test.listResponses) > 0 {
for i, namespace := range test.expectedListedNamespaces {
client := &velerotest.FakeDynamicClient{}
defer client.AssertExpectations(t)
dynamicFactory.On("ClientForGroupVersionResource", test.groupVersion, test.apiResource, namespace).Return(client, nil)
list := &unstructured.UnstructuredList{
Items: []unstructured.Unstructured{},
}
for _, item := range test.listResponses[i] {
list.Items = append(list.Items, *item)
itemBackupper.On("backupItem", mock.AnythingOfType("*logrus.Entry"), item, test.groupResource).Return(nil)
}
client.On("List", metav1.ListOptions{}).Return(list, nil)
}
}
if len(test.getResponses) > 0 {
client := &velerotest.FakeDynamicClient{}
defer client.AssertExpectations(t)
dynamicFactory.On("ClientForGroupVersionResource", test.groupVersion, test.apiResource, "").Return(client, nil)
for i, namespace := range test.expectedListedNamespaces {
item := test.getResponses[i]
client.On("Get", namespace, metav1.GetOptions{}).Return(item, nil)
itemBackupper.On("backupItem", mock.AnythingOfType("*logrus.Entry"), item, test.groupResource).Return(nil)
}
}
}
err := rb.backupResource(test.apiGroup, test.apiResource)
require.NoError(t, err)
})
}
}
func TestBackupResourceCohabitation(t *testing.T) {
tests := []struct {
name string
apiResource metav1.APIResource
apiGroup1 *metav1.APIResourceList
groupVersion1 schema.GroupVersion
apiGroup2 *metav1.APIResourceList
groupVersion2 schema.GroupVersion
}{
{
name: "deployments - extensions first",
apiResource: deploymentsResource,
apiGroup1: extensionsGroup,
groupVersion1: extensionsGroupVersion,
apiGroup2: appsGroup,
groupVersion2: appsGroupVersion,
},
{
name: "deployments - apps first",
apiResource: deploymentsResource,
apiGroup1: appsGroup,
groupVersion1: appsGroupVersion,
apiGroup2: extensionsGroup,
groupVersion2: extensionsGroupVersion,
},
{
name: "networkpolicies - extensions first",
apiResource: networkPoliciesResource,
apiGroup1: extensionsGroup,
groupVersion1: extensionsGroupVersion,
apiGroup2: networkingGroup,
groupVersion2: networkingGroupVersion,
},
{
name: "networkpolicies - networking first",
apiResource: networkPoliciesResource,
apiGroup1: networkingGroup,
groupVersion1: networkingGroupVersion,
apiGroup2: extensionsGroup,
groupVersion2: extensionsGroupVersion,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := &Request{
Backup: &v1.Backup{
Spec: v1.BackupSpec{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"foo": "bar",
},
},
},
},
NamespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
ResourceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
ResolvedActions: []resolvedAction{
{
BackupItemAction: newFakeAction("pods"),
resourceIncludesExcludes: collections.NewIncludesExcludes().Includes("pods"),
},
},
ResourceHooks: []resourceHook{
{name: "myhook"},
},
}
dynamicFactory := &velerotest.FakeDynamicFactory{}
defer dynamicFactory.AssertExpectations(t)
discoveryHelper := velerotest.NewFakeDiscoveryHelper(true, nil)
backedUpItems := map[itemKey]struct{}{
{resource: "foo", namespace: "ns", name: "name"}: {},
}
cohabitatingResources := map[string]*cohabitatingResource{
"deployments": newCohabitatingResource("deployments", "extensions", "apps"),
"networkpolicies": newCohabitatingResource("networkpolicies", "extensions", "networking.k8s.io"),
}
podCommandExecutor := &velerotest.MockPodCommandExecutor{}
defer podCommandExecutor.AssertExpectations(t)
tarWriter := &fakeTarWriter{}
rb := (&defaultResourceBackupperFactory{}).newResourceBackupper(
velerotest.NewLogger(),
req,
dynamicFactory,
discoveryHelper,
backedUpItems,
cohabitatingResources,
podCommandExecutor,
tarWriter,
nil, // restic backupper
newPVCSnapshotTracker(),
nil,
).(*defaultResourceBackupper)
itemBackupperFactory := &mockItemBackupperFactory{}
defer itemBackupperFactory.AssertExpectations(t)
rb.itemBackupperFactory = itemBackupperFactory
itemBackupper := &mockItemBackupper{}
defer itemBackupper.AssertExpectations(t)
itemBackupperFactory.On("newItemBackupper",
req,
backedUpItems,
podCommandExecutor,
tarWriter,
dynamicFactory,
discoveryHelper,
mock.Anything, // restic backupper
mock.Anything, // pvc snapshot tracker
nil,
mock.Anything,
).Return(itemBackupper)
client := &velerotest.FakeDynamicClient{}
defer client.AssertExpectations(t)
// STEP 1: make sure the initial backup goes through
dynamicFactory.On("ClientForGroupVersionResource", test.groupVersion1, test.apiResource, "").Return(client, nil)
client.On("List", metav1.ListOptions{LabelSelector: metav1.FormatLabelSelector(req.Backup.Spec.LabelSelector)}).Return(&unstructured.UnstructuredList{}, nil)
// STEP 2: do the backup
err := rb.backupResource(test.apiGroup1, test.apiResource)
require.NoError(t, err)
// STEP 3: try to back up the cohabitating resource
err = rb.backupResource(test.apiGroup2, test.apiResource)
require.NoError(t, err)
})
}
}
func TestBackupResourceOnlyIncludesSpecifiedNamespaces(t *testing.T) {
req := &Request{
Backup: &v1.Backup{},
NamespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("ns-1"),
ResourceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
}
backedUpItems := map[itemKey]struct{}{}
dynamicFactory := &velerotest.FakeDynamicFactory{}
defer dynamicFactory.AssertExpectations(t)
discoveryHelper := velerotest.NewFakeDiscoveryHelper(true, nil)
cohabitatingResources := map[string]*cohabitatingResource{}
podCommandExecutor := &velerotest.MockPodCommandExecutor{}
defer podCommandExecutor.AssertExpectations(t)
tarWriter := &fakeTarWriter{}
rb := (&defaultResourceBackupperFactory{}).newResourceBackupper(
velerotest.NewLogger(),
req,
dynamicFactory,
discoveryHelper,
backedUpItems,
cohabitatingResources,
podCommandExecutor,
tarWriter,
nil, // restic backupper
newPVCSnapshotTracker(),
nil,
).(*defaultResourceBackupper)
itemBackupperFactory := &mockItemBackupperFactory{}
defer itemBackupperFactory.AssertExpectations(t)
rb.itemBackupperFactory = itemBackupperFactory
itemHookHandler := &mockItemHookHandler{}
defer itemHookHandler.AssertExpectations(t)
itemBackupper := &defaultItemBackupper{
backupRequest: req,
backedUpItems: backedUpItems,
tarWriter: tarWriter,
dynamicFactory: dynamicFactory,
discoveryHelper: discoveryHelper,
itemHookHandler: itemHookHandler,
}
itemBackupperFactory.On("newItemBackupper",
req,
backedUpItems,
podCommandExecutor,
tarWriter,
dynamicFactory,
discoveryHelper,
mock.Anything,
mock.Anything,
mock.Anything,
).Return(itemBackupper)
client := &velerotest.FakeDynamicClient{}
defer client.AssertExpectations(t)
coreV1Group := schema.GroupVersion{Group: "", Version: "v1"}
dynamicFactory.On("ClientForGroupVersionResource", coreV1Group, namespacesResource, "").Return(client, nil)
ns1 := velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"ns-1"}}`)
client.On("Get", "ns-1", metav1.GetOptions{}).Return(ns1, nil)
itemHookHandler.On("handleHooks", mock.Anything, schema.GroupResource{Group: "", Resource: "namespaces"}, ns1, req.ResourceHooks, hookPhasePre).Return(nil)
itemHookHandler.On("handleHooks", mock.Anything, schema.GroupResource{Group: "", Resource: "namespaces"}, ns1, req.ResourceHooks, hookPhasePost).Return(nil)
err := rb.backupResource(v1Group, namespacesResource)
require.NoError(t, err)
require.Len(t, tarWriter.headers, 1)
assert.Equal(t, "resources/namespaces/cluster/ns-1.json", tarWriter.headers[0].Name)
}
func TestBackupResourceListAllNamespacesExcludesCorrectly(t *testing.T) {
req := &Request{
Backup: &v1.Backup{
Spec: v1.BackupSpec{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"foo": "bar",
},
},
},
},
NamespaceIncludesExcludes: collections.NewIncludesExcludes().Excludes("ns-1"),
ResourceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
}
backedUpItems := map[itemKey]struct{}{}
dynamicFactory := &velerotest.FakeDynamicFactory{}
defer dynamicFactory.AssertExpectations(t)
discoveryHelper := velerotest.NewFakeDiscoveryHelper(true, nil)
cohabitatingResources := map[string]*cohabitatingResource{}
podCommandExecutor := &velerotest.MockPodCommandExecutor{}
defer podCommandExecutor.AssertExpectations(t)
tarWriter := &fakeTarWriter{}
rb := (&defaultResourceBackupperFactory{}).newResourceBackupper(
velerotest.NewLogger(),
req,
dynamicFactory,
discoveryHelper,
backedUpItems,
cohabitatingResources,
podCommandExecutor,
tarWriter,
nil, // restic backupper
newPVCSnapshotTracker(),
nil,
).(*defaultResourceBackupper)
itemBackupperFactory := &mockItemBackupperFactory{}
defer itemBackupperFactory.AssertExpectations(t)
rb.itemBackupperFactory = itemBackupperFactory
itemHookHandler := &mockItemHookHandler{}
defer itemHookHandler.AssertExpectations(t)
itemBackupper := &mockItemBackupper{}
defer itemBackupper.AssertExpectations(t)
itemBackupperFactory.On("newItemBackupper",
req,
backedUpItems,
podCommandExecutor,
tarWriter,
dynamicFactory,
discoveryHelper,
mock.Anything,
mock.Anything,
mock.Anything,
).Return(itemBackupper)
client := &velerotest.FakeDynamicClient{}
defer client.AssertExpectations(t)
coreV1Group := schema.GroupVersion{Group: "", Version: "v1"}
dynamicFactory.On("ClientForGroupVersionResource", coreV1Group, namespacesResource, "").Return(client, nil)
ns1 := velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"ns-1"}}`)
ns2 := velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"ns-2"}}`)
list := &unstructured.UnstructuredList{
Items: []unstructured.Unstructured{*ns1, *ns2},
}
client.On("List", metav1.ListOptions{LabelSelector: metav1.FormatLabelSelector(req.Backup.Spec.LabelSelector)}).Return(list, nil)
itemBackupper.On("backupItem", mock.AnythingOfType("*logrus.Entry"), ns2, kuberesource.Namespaces).Return(nil)
err := rb.backupResource(v1Group, namespacesResource)
require.NoError(t, err)
}
type mockItemBackupperFactory struct {
mock.Mock
}
func (ibf *mockItemBackupperFactory) newItemBackupper(
backup *Request,
backedUpItems map[itemKey]struct{},
podCommandExecutor podexec.PodCommandExecutor,
tarWriter tarWriter,
dynamicFactory client.DynamicFactory,
discoveryHelper discovery.Helper,
resticBackupper restic.Backupper,
resticSnapshotTracker *pvcSnapshotTracker,
volumeSnapshotterGetter VolumeSnapshotterGetter,
) ItemBackupper {
args := ibf.Called(
backup,
backedUpItems,
podCommandExecutor,
tarWriter,
dynamicFactory,
discoveryHelper,
resticBackupper,
resticSnapshotTracker,
volumeSnapshotterGetter,
)
return args.Get(0).(ItemBackupper)
}

View File

@@ -0,0 +1,164 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
)
/*
Example usage:
var backup = builder.ForBackup("velero", "backup-1").
ObjectMeta(
builder.WithLabels("foo", "bar"),
builder.WithClusterName("cluster-1"),
).
SnapshotVolumes(true).
Result()
*/
// BackupBuilder builds Backup objects.
type BackupBuilder struct {
object *velerov1api.Backup
}
// ForBackup is the constructor for a BackupBuilder.
func ForBackup(ns, name string) *BackupBuilder {
return &BackupBuilder{
object: &velerov1api.Backup{
TypeMeta: metav1.TypeMeta{
APIVersion: velerov1api.SchemeGroupVersion.String(),
Kind: "Backup",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built Backup.
func (b *BackupBuilder) Result() *velerov1api.Backup {
return b.object
}
// ObjectMeta applies functional options to the Backup's ObjectMeta.
func (b *BackupBuilder) ObjectMeta(opts ...ObjectMetaOpt) *BackupBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// IncludedNamespaces sets the Backup's included namespaces.
func (b *BackupBuilder) IncludedNamespaces(namespaces ...string) *BackupBuilder {
b.object.Spec.IncludedNamespaces = namespaces
return b
}
// ExcludedNamespaces sets the Backup's excluded namespaces.
func (b *BackupBuilder) ExcludedNamespaces(namespaces ...string) *BackupBuilder {
b.object.Spec.ExcludedNamespaces = namespaces
return b
}
// IncludedResources sets the Backup's included resources.
func (b *BackupBuilder) IncludedResources(resources ...string) *BackupBuilder {
b.object.Spec.IncludedResources = resources
return b
}
// ExcludedResources sets the Backup's excluded resources.
func (b *BackupBuilder) ExcludedResources(resources ...string) *BackupBuilder {
b.object.Spec.ExcludedResources = resources
return b
}
// IncludeClusterResources sets the Backup's "include cluster resources" flag.
func (b *BackupBuilder) IncludeClusterResources(val bool) *BackupBuilder {
b.object.Spec.IncludeClusterResources = &val
return b
}
// LabelSelector sets the Backup's label selector.
func (b *BackupBuilder) LabelSelector(selector *metav1.LabelSelector) *BackupBuilder {
b.object.Spec.LabelSelector = selector
return b
}
// SnapshotVolumes sets the Backup's "snapshot volumes" flag.
func (b *BackupBuilder) SnapshotVolumes(val bool) *BackupBuilder {
b.object.Spec.SnapshotVolumes = &val
return b
}
// Phase sets the Backup's phase.
func (b *BackupBuilder) Phase(phase velerov1api.BackupPhase) *BackupBuilder {
b.object.Status.Phase = phase
return b
}
// StorageLocation sets the Backup's storage location.
func (b *BackupBuilder) StorageLocation(location string) *BackupBuilder {
b.object.Spec.StorageLocation = location
return b
}
// VolumeSnapshotLocations sets the Backup's volume snapshot locations.
func (b *BackupBuilder) VolumeSnapshotLocations(locations ...string) *BackupBuilder {
b.object.Spec.VolumeSnapshotLocations = locations
return b
}
// TTL sets the Backup's TTL.
func (b *BackupBuilder) TTL(ttl time.Duration) *BackupBuilder {
b.object.Spec.TTL.Duration = ttl
return b
}
// Expiration sets the Backup's expiration.
func (b *BackupBuilder) Expiration(val time.Time) *BackupBuilder {
b.object.Status.Expiration.Time = val
return b
}
// StartTimestamp sets the Backup's start timestamp.
func (b *BackupBuilder) StartTimestamp(val time.Time) *BackupBuilder {
b.object.Status.StartTimestamp.Time = val
return b
}
// NoTypeMeta removes the type meta from the Backup.
func (b *BackupBuilder) NoTypeMeta() *BackupBuilder {
b.object.TypeMeta = metav1.TypeMeta{}
return b
}
// Hooks sets the Backup's hooks.
func (b *BackupBuilder) Hooks(hooks velerov1api.BackupHooks) *BackupBuilder {
b.object.Spec.Hooks = hooks
return b
}

View File

@@ -0,0 +1,88 @@
/*
Copyright 2017, 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
)
// BackupStorageLocationBuilder builds BackupStorageLocation objects.
type BackupStorageLocationBuilder struct {
object *velerov1api.BackupStorageLocation
}
// ForBackupStorageLocation is the constructor for a BackupStorageLocationBuilder.
func ForBackupStorageLocation(ns, name string) *BackupStorageLocationBuilder {
return &BackupStorageLocationBuilder{
object: &velerov1api.BackupStorageLocation{
TypeMeta: metav1.TypeMeta{
APIVersion: velerov1api.SchemeGroupVersion.String(),
Kind: "BackupStorageLocation",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built BackupStorageLocation.
func (b *BackupStorageLocationBuilder) Result() *velerov1api.BackupStorageLocation {
return b.object
}
// ObjectMeta applies functional options to the BackupStorageLocation's ObjectMeta.
func (b *BackupStorageLocationBuilder) ObjectMeta(opts ...ObjectMetaOpt) *BackupStorageLocationBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Provider sets the BackupStorageLocation's provider.
func (b *BackupStorageLocationBuilder) Provider(name string) *BackupStorageLocationBuilder {
b.object.Spec.Provider = name
return b
}
// Bucket sets the BackupStorageLocation's object storage bucket.
func (b *BackupStorageLocationBuilder) Bucket(val string) *BackupStorageLocationBuilder {
if b.object.Spec.StorageType.ObjectStorage == nil {
b.object.Spec.StorageType.ObjectStorage = new(velerov1api.ObjectStorageLocation)
}
b.object.Spec.ObjectStorage.Bucket = val
return b
}
// Prefix sets the BackupStorageLocation's object storage prefix.
func (b *BackupStorageLocationBuilder) Prefix(val string) *BackupStorageLocationBuilder {
if b.object.Spec.StorageType.ObjectStorage == nil {
b.object.Spec.StorageType.ObjectStorage = new(velerov1api.ObjectStorageLocation)
}
b.object.Spec.ObjectStorage.Prefix = val
return b
}
// AccessMode sets the BackupStorageLocation's access mode.
func (b *BackupStorageLocationBuilder) AccessMode(accessMode velerov1api.BackupStorageLocationAccessMode) *BackupStorageLocationBuilder {
b.object.Spec.AccessMode = accessMode
return b
}

View File

@@ -0,0 +1,63 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ConfigMapBuilder builds ConfigMap objects.
type ConfigMapBuilder struct {
object *corev1api.ConfigMap
}
// ForConfigMap is the constructor for a ConfigMapBuilder.
func ForConfigMap(ns, name string) *ConfigMapBuilder {
return &ConfigMapBuilder{
object: &corev1api.ConfigMap{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1api.SchemeGroupVersion.String(),
Kind: "ConfigMap",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built ConfigMap.
func (b *ConfigMapBuilder) Result() *corev1api.ConfigMap {
return b.object
}
// ObjectMeta applies functional options to the ConfigMap's ObjectMeta.
func (b *ConfigMapBuilder) ObjectMeta(opts ...ObjectMetaOpt) *ConfigMapBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Data set's the ConfigMap's data.
func (b *ConfigMapBuilder) Data(vals ...string) *ConfigMapBuilder {
b.object.Data = setMapEntries(b.object.Data, vals...)
return b
}

View File

@@ -0,0 +1,68 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
corev1api "k8s.io/api/core/v1"
)
// ContainerBuilder builds Container objects
type ContainerBuilder struct {
object *corev1api.Container
}
// ForContainer is the constructor for ContainerBuilder.
func ForContainer(name, image string) *ContainerBuilder {
return &ContainerBuilder{
object: &corev1api.Container{
Name: name,
Image: image,
},
}
}
// Result returns the built Container.
func (b *ContainerBuilder) Result() *corev1api.Container {
return b.object
}
// Args sets the container's Args.
func (b *ContainerBuilder) Args(args ...string) *ContainerBuilder {
b.object.Args = append(b.object.Args, args...)
return b
}
// VolumeMounts sets the container's VolumeMounts.
func (b *ContainerBuilder) VolumeMounts(volumeMounts ...*corev1api.VolumeMount) *ContainerBuilder {
for _, v := range volumeMounts {
b.object.VolumeMounts = append(b.object.VolumeMounts, *v)
}
return b
}
// Resources sets the container's Resources.
func (b *ContainerBuilder) Resources(resources *corev1api.ResourceRequirements) *ContainerBuilder {
b.object.Resources = *resources
return b
}
func (b *ContainerBuilder) Env(vars ...*corev1api.EnvVar) *ContainerBuilder {
for _, v := range vars {
b.object.Env = append(b.object.Env, *v)
}
return b
}

View File

@@ -0,0 +1,57 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
appsv1api "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// DeploymentBuilder builds Deployment objects.
type DeploymentBuilder struct {
object *appsv1api.Deployment
}
// ForDeployment is the constructor for a DeploymentBuilder.
func ForDeployment(ns, name string) *DeploymentBuilder {
return &DeploymentBuilder{
object: &appsv1api.Deployment{
TypeMeta: metav1.TypeMeta{
APIVersion: appsv1api.SchemeGroupVersion.String(),
Kind: "Deployment",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built Deployment.
func (b *DeploymentBuilder) Result() *appsv1api.Deployment {
return b.object
}
// ObjectMeta applies functional options to the Deployment's ObjectMeta.
func (b *DeploymentBuilder) ObjectMeta(opts ...ObjectMetaOpt) *DeploymentBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}

View File

@@ -0,0 +1,62 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// NamespaceBuilder builds Namespace objects.
type NamespaceBuilder struct {
object *corev1api.Namespace
}
// ForNamespace is the constructor for a NamespaceBuilder.
func ForNamespace(name string) *NamespaceBuilder {
return &NamespaceBuilder{
object: &corev1api.Namespace{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1api.SchemeGroupVersion.String(),
Kind: "Namespace",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
},
}
}
// Result returns the built Namespace.
func (b *NamespaceBuilder) Result() *corev1api.Namespace {
return b.object
}
// ObjectMeta applies functional options to the Namespace's ObjectMeta.
func (b *NamespaceBuilder) ObjectMeta(opts ...ObjectMetaOpt) *NamespaceBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Phase sets the namespace's phase
func (b *NamespaceBuilder) Phase(val corev1api.NamespacePhase) *NamespaceBuilder {
b.object.Status.Phase = val
return b
}

111
pkg/builder/object_meta.go Normal file
View File

@@ -0,0 +1,111 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
// ObjectMetaOpt is a functional option for ObjectMeta.
type ObjectMetaOpt func(metav1.Object)
// WithName is a functional option that applies the specified
// name to an object.
func WithName(val string) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetName(val)
}
}
// WithLabels is a functional option that applies the specified
// label keys/values to an object.
func WithLabels(vals ...string) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetLabels(setMapEntries(obj.GetLabels(), vals...))
}
}
// WithAnnotations is a functional option that applies the specified
// annotation keys/values to an object.
func WithAnnotations(vals ...string) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetAnnotations(setMapEntries(obj.GetAnnotations(), vals...))
}
}
func setMapEntries(m map[string]string, vals ...string) map[string]string {
if m == nil {
m = make(map[string]string)
}
// if we don't have a value for every key, add an empty
// string at the end to serve as the value for the last
// key.
if len(vals)%2 != 0 {
vals = append(vals, "")
}
for i := 0; i < len(vals); i += 2 {
key := vals[i]
val := vals[i+1]
m[key] = val
}
return m
}
// WithClusterName is a functional option that applies the specified
// cluster name to an object.
func WithClusterName(val string) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetClusterName(val)
}
}
// WithFinalizers is a functional option that applies the specified
// finalizers to an object.
func WithFinalizers(vals ...string) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetFinalizers(vals)
}
}
// WithDeletionTimestamp is a functional option that applies the specified
// deletion timestamp to an object.
func WithDeletionTimestamp(val time.Time) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetDeletionTimestamp(&metav1.Time{Time: val})
}
}
// WithUID is a functional option that applies the specified UID to an object.
func WithUID(val string) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetUID(types.UID(val))
}
}
// WithGenerateName is a functional option that applies the specified generate name to an object.
func WithGenerateName(val string) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetGenerateName(val)
}
}

View File

@@ -0,0 +1,96 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// PersistentVolumeBuilder builds PersistentVolume objects.
type PersistentVolumeBuilder struct {
object *corev1api.PersistentVolume
}
// ForPersistentVolume is the constructor for a PersistentVolumeBuilder.
func ForPersistentVolume(name string) *PersistentVolumeBuilder {
return &PersistentVolumeBuilder{
object: &corev1api.PersistentVolume{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1api.SchemeGroupVersion.String(),
Kind: "PersistentVolume",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
},
}
}
// Result returns the built PersistentVolume.
func (b *PersistentVolumeBuilder) Result() *corev1api.PersistentVolume {
return b.object
}
// ObjectMeta applies functional options to the PersistentVolume's ObjectMeta.
func (b *PersistentVolumeBuilder) ObjectMeta(opts ...ObjectMetaOpt) *PersistentVolumeBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// ReclaimPolicy sets the PersistentVolume's reclaim policy.
func (b *PersistentVolumeBuilder) ReclaimPolicy(policy corev1api.PersistentVolumeReclaimPolicy) *PersistentVolumeBuilder {
b.object.Spec.PersistentVolumeReclaimPolicy = policy
return b
}
// ClaimRef sets the PersistentVolume's claim ref.
func (b *PersistentVolumeBuilder) ClaimRef(ns, name string) *PersistentVolumeBuilder {
b.object.Spec.ClaimRef = &corev1api.ObjectReference{
Namespace: ns,
Name: name,
}
return b
}
// AWSEBSVolumeID sets the PersistentVolume's AWSElasticBlockStore volume ID.
func (b *PersistentVolumeBuilder) AWSEBSVolumeID(volumeID string) *PersistentVolumeBuilder {
if b.object.Spec.AWSElasticBlockStore == nil {
b.object.Spec.AWSElasticBlockStore = new(corev1api.AWSElasticBlockStoreVolumeSource)
}
b.object.Spec.AWSElasticBlockStore.VolumeID = volumeID
return b
}
// CSI sets the PersistentVolume's CSI.
func (b *PersistentVolumeBuilder) CSI(driver, volumeHandle string) *PersistentVolumeBuilder {
if b.object.Spec.CSI == nil {
b.object.Spec.CSI = new(corev1api.CSIPersistentVolumeSource)
}
b.object.Spec.CSI.Driver = driver
b.object.Spec.CSI.VolumeHandle = volumeHandle
return b
}
// StorageClass sets the PersistentVolume's storage class name.
func (b *PersistentVolumeBuilder) StorageClass(name string) *PersistentVolumeBuilder {
b.object.Spec.StorageClassName = name
return b
}

View File

@@ -0,0 +1,69 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// PersistentVolumeClaimBuilder builds PersistentVolumeClaim objects.
type PersistentVolumeClaimBuilder struct {
object *corev1api.PersistentVolumeClaim
}
// ForPersistentVolumeClaim is the constructor for a PersistentVolumeClaimBuilder.
func ForPersistentVolumeClaim(ns, name string) *PersistentVolumeClaimBuilder {
return &PersistentVolumeClaimBuilder{
object: &corev1api.PersistentVolumeClaim{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1api.SchemeGroupVersion.String(),
Kind: "PersistentVolumeClaim",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built PersistentVolumeClaim.
func (b *PersistentVolumeClaimBuilder) Result() *corev1api.PersistentVolumeClaim {
return b.object
}
// ObjectMeta applies functional options to the PersistentVolumeClaim's ObjectMeta.
func (b *PersistentVolumeClaimBuilder) ObjectMeta(opts ...ObjectMetaOpt) *PersistentVolumeClaimBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// VolumeName sets the PersistentVolumeClaim's volume name.
func (b *PersistentVolumeClaimBuilder) VolumeName(name string) *PersistentVolumeClaimBuilder {
b.object.Spec.VolumeName = name
return b
}
// StorageClass sets the PersistentVolumeClaim's storage class name.
func (b *PersistentVolumeClaimBuilder) StorageClass(name string) *PersistentVolumeClaimBuilder {
b.object.Spec.StorageClassName = &name
return b
}

View File

@@ -0,0 +1,78 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// PodBuilder builds Pod objects.
type PodBuilder struct {
object *corev1api.Pod
}
// ForPod is the constructor for a PodBuilder.
func ForPod(ns, name string) *PodBuilder {
return &PodBuilder{
object: &corev1api.Pod{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1api.SchemeGroupVersion.String(),
Kind: "Pod",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built Pod.
func (b *PodBuilder) Result() *corev1api.Pod {
return b.object
}
// ObjectMeta applies functional options to the Pod's ObjectMeta.
func (b *PodBuilder) ObjectMeta(opts ...ObjectMetaOpt) *PodBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Volumes appends to the pod's volumes
func (b *PodBuilder) Volumes(volumes ...*corev1api.Volume) *PodBuilder {
for _, v := range volumes {
b.object.Spec.Volumes = append(b.object.Spec.Volumes, *v)
}
return b
}
// NodeName sets the pod's node name
func (b *PodBuilder) NodeName(val string) *PodBuilder {
b.object.Spec.NodeName = val
return b
}
func (b *PodBuilder) InitContainers(containers ...*corev1api.Container) *PodBuilder {
for _, c := range containers {
b.object.Spec.InitContainers = append(b.object.Spec.InitContainers, *c)
}
return b
}

View File

@@ -0,0 +1,64 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
)
// PodVolumeBackupBuilder builds PodVolumeBackup objects
type PodVolumeBackupBuilder struct {
object *velerov1api.PodVolumeBackup
}
// ForPodVolumeBackup is the constructor for a PodVolumeBackupBuilder.
func ForPodVolumeBackup(ns, name string) *PodVolumeBackupBuilder {
return &PodVolumeBackupBuilder{
object: &velerov1api.PodVolumeBackup{
TypeMeta: metav1.TypeMeta{
APIVersion: velerov1api.SchemeGroupVersion.String(),
Kind: "PodVolumeBackup",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built PodVolumeBackup.
func (b *PodVolumeBackupBuilder) Result() *velerov1api.PodVolumeBackup {
return b.object
}
// ObjectMeta applies functional options to the PodVolumeBackup's ObjectMeta.
func (b *PodVolumeBackupBuilder) ObjectMeta(opts ...ObjectMetaOpt) *PodVolumeBackupBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Phase sets the PodVolumeBackup's phase.
func (b *PodVolumeBackupBuilder) Phase(phase velerov1api.PodVolumeBackupPhase) *PodVolumeBackupBuilder {
b.object.Status.Phase = phase
return b
}

View File

@@ -0,0 +1,135 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
)
// RestoreBuilder builds Restore objects.
type RestoreBuilder struct {
object *velerov1api.Restore
}
// ForRestore is the constructor for a RestoreBuilder.
func ForRestore(ns, name string) *RestoreBuilder {
return &RestoreBuilder{
object: &velerov1api.Restore{
TypeMeta: metav1.TypeMeta{
APIVersion: velerov1api.SchemeGroupVersion.String(),
Kind: "Restore",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built Restore.
func (b *RestoreBuilder) Result() *velerov1api.Restore {
return b.object
}
// ObjectMeta applies functional options to the Restore's ObjectMeta.
func (b *RestoreBuilder) ObjectMeta(opts ...ObjectMetaOpt) *RestoreBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Backup sets the Restore's backup name.
func (b *RestoreBuilder) Backup(name string) *RestoreBuilder {
b.object.Spec.BackupName = name
return b
}
// Schedule sets the Restore's schedule name.
func (b *RestoreBuilder) Schedule(name string) *RestoreBuilder {
b.object.Spec.ScheduleName = name
return b
}
// IncludedNamespaces appends to the Restore's included namespaces.
func (b *RestoreBuilder) IncludedNamespaces(namespaces ...string) *RestoreBuilder {
b.object.Spec.IncludedNamespaces = append(b.object.Spec.IncludedNamespaces, namespaces...)
return b
}
// ExcludedNamespaces appends to the Restore's excluded namespaces.
func (b *RestoreBuilder) ExcludedNamespaces(namespaces ...string) *RestoreBuilder {
b.object.Spec.ExcludedNamespaces = append(b.object.Spec.ExcludedNamespaces, namespaces...)
return b
}
// IncludedResources appends to the Restore's included resources.
func (b *RestoreBuilder) IncludedResources(resources ...string) *RestoreBuilder {
b.object.Spec.IncludedResources = append(b.object.Spec.IncludedResources, resources...)
return b
}
// ExcludedResources appends to the Restore's excluded resources.
func (b *RestoreBuilder) ExcludedResources(resources ...string) *RestoreBuilder {
b.object.Spec.ExcludedResources = append(b.object.Spec.ExcludedResources, resources...)
return b
}
// IncludeClusterResources sets the Restore's "include cluster resources" flag.
func (b *RestoreBuilder) IncludeClusterResources(val bool) *RestoreBuilder {
b.object.Spec.IncludeClusterResources = &val
return b
}
// LabelSelector sets the Restore's label selector.
func (b *RestoreBuilder) LabelSelector(selector *metav1.LabelSelector) *RestoreBuilder {
b.object.Spec.LabelSelector = selector
return b
}
// NamespaceMappings sets the Restore's namespace mappings.
func (b *RestoreBuilder) NamespaceMappings(mapping ...string) *RestoreBuilder {
if b.object.Spec.NamespaceMapping == nil {
b.object.Spec.NamespaceMapping = make(map[string]string)
}
if len(mapping)%2 != 0 {
panic("mapping must contain an even number of values")
}
for i := 0; i < len(mapping); i += 2 {
b.object.Spec.NamespaceMapping[mapping[i]] = mapping[i+1]
}
return b
}
// Phase sets the Restore's phase.
func (b *RestoreBuilder) Phase(phase velerov1api.RestorePhase) *RestoreBuilder {
b.object.Status.Phase = phase
return b
}
// RestorePVs sets the Restore's restore PVs.
func (b *RestoreBuilder) RestorePVs(val bool) *RestoreBuilder {
b.object.Spec.RestorePVs = &val
return b
}

View File

@@ -0,0 +1,57 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
rbacv1api "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// RoleBuilder builds Role objects.
type RoleBuilder struct {
object *rbacv1api.Role
}
// ForRole is the constructor for a RoleBuilder.
func ForRole(ns, name string) *RoleBuilder {
return &RoleBuilder{
object: &rbacv1api.Role{
TypeMeta: metav1.TypeMeta{
APIVersion: rbacv1api.SchemeGroupVersion.String(),
Kind: "Role",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built Role.
func (b *RoleBuilder) Result() *rbacv1api.Role {
return b.object
}
// ObjectMeta applies functional options to the Role's ObjectMeta.
func (b *RoleBuilder) ObjectMeta(opts ...ObjectMetaOpt) *RoleBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}

View File

@@ -0,0 +1,91 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
)
// ScheduleBuilder builds Schedule objects.
type ScheduleBuilder struct {
object *velerov1api.Schedule
}
// ForSchedule is the constructor for a ScheduleBuilder.
func ForSchedule(ns, name string) *ScheduleBuilder {
return &ScheduleBuilder{
object: &velerov1api.Schedule{
TypeMeta: metav1.TypeMeta{
APIVersion: velerov1api.SchemeGroupVersion.String(),
Kind: "Schedule",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built Schedule.
func (b *ScheduleBuilder) Result() *velerov1api.Schedule {
return b.object
}
// ObjectMeta applies functional options to the Schedule's ObjectMeta.
func (b *ScheduleBuilder) ObjectMeta(opts ...ObjectMetaOpt) *ScheduleBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Phase sets the Schedule's phase.
func (b *ScheduleBuilder) Phase(phase velerov1api.SchedulePhase) *ScheduleBuilder {
b.object.Status.Phase = phase
return b
}
// ValidationError appends to the Schedule's validation errors.
func (b *ScheduleBuilder) ValidationError(err string) *ScheduleBuilder {
b.object.Status.ValidationErrors = append(b.object.Status.ValidationErrors, err)
return b
}
// CronSchedule sets the Schedule's cron schedule.
func (b *ScheduleBuilder) CronSchedule(expression string) *ScheduleBuilder {
b.object.Spec.Schedule = expression
return b
}
// LastBackupTime sets the Schedule's last backup time.
func (b *ScheduleBuilder) LastBackupTime(val string) *ScheduleBuilder {
t, _ := time.Parse("2006-01-02 15:04:05", val)
b.object.Status.LastBackup.Time = t
return b
}
// Template sets the Schedule's template.
func (b *ScheduleBuilder) Template(spec velerov1api.BackupSpec) *ScheduleBuilder {
b.object.Spec.Template = spec
return b
}

View File

@@ -0,0 +1,57 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// SecretBuilder builds Secret objects.
type SecretBuilder struct {
object *corev1api.Secret
}
// ForSecret is the constructor for a SecretBuilder.
func ForSecret(ns, name string) *SecretBuilder {
return &SecretBuilder{
object: &corev1api.Secret{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1api.SchemeGroupVersion.String(),
Kind: "Secret",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built Secret.
func (b *SecretBuilder) Result() *corev1api.Secret {
return b.object
}
// ObjectMeta applies functional options to the Secret's ObjectMeta.
func (b *SecretBuilder) ObjectMeta(opts ...ObjectMetaOpt) *SecretBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}

View File

@@ -0,0 +1,84 @@
/*
Copyright 2018 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
)
// ServerStatusRequestBuilder builds ServerStatusRequest objects.
type ServerStatusRequestBuilder struct {
object *velerov1api.ServerStatusRequest
}
// ForServerStatusRequest is the constructor for for a ServerStatusRequestBuilder.
func ForServerStatusRequest(ns, name string) *ServerStatusRequestBuilder {
return &ServerStatusRequestBuilder{
object: &velerov1api.ServerStatusRequest{
TypeMeta: metav1.TypeMeta{
APIVersion: velerov1api.SchemeGroupVersion.String(),
Kind: "ServerStatusRequest",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built ServerStatusRequest.
func (b *ServerStatusRequestBuilder) Result() *velerov1api.ServerStatusRequest {
return b.object
}
// ObjectMeta applies functional options to the ServerStatusRequest's ObjectMeta.
func (b *ServerStatusRequestBuilder) ObjectMeta(opts ...ObjectMetaOpt) *ServerStatusRequestBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Phase sets the ServerStatusRequest's phase.
func (b *ServerStatusRequestBuilder) Phase(phase velerov1api.ServerStatusRequestPhase) *ServerStatusRequestBuilder {
b.object.Status.Phase = phase
return b
}
// ProcessedTimestamp sets the ServerStatusRequest's processed timestamp.
func (b *ServerStatusRequestBuilder) ProcessedTimestamp(time time.Time) *ServerStatusRequestBuilder {
b.object.Status.ProcessedTimestamp.Time = time
return b
}
// ServerVersion sets the ServerStatusRequest's server version.
func (b *ServerStatusRequestBuilder) ServerVersion(version string) *ServerStatusRequestBuilder {
b.object.Status.ServerVersion = version
return b
}
// Plugins sets the ServerStatusRequest's plugins.
func (b *ServerStatusRequestBuilder) Plugins(plugins []velerov1api.PluginInfo) *ServerStatusRequestBuilder {
b.object.Status.Plugins = plugins
return b
}

View File

@@ -0,0 +1,57 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ServiceAccountBuilder builds ServiceAccount objects.
type ServiceAccountBuilder struct {
object *corev1api.ServiceAccount
}
// ForServiceAccount is the constructor for a ServiceAccountBuilder.
func ForServiceAccount(ns, name string) *ServiceAccountBuilder {
return &ServiceAccountBuilder{
object: &corev1api.ServiceAccount{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1api.SchemeGroupVersion.String(),
Kind: "ServiceAccount",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built ServiceAccount.
func (b *ServiceAccountBuilder) Result() *corev1api.ServiceAccount {
return b.object
}
// ObjectMeta applies functional options to the ServiceAccount's ObjectMeta.
func (b *ServiceAccountBuilder) ObjectMeta(opts ...ObjectMetaOpt) *ServiceAccountBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}

View File

@@ -0,0 +1,56 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
storagev1api "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// StorageClassBuilder builds StorageClass objects.
type StorageClassBuilder struct {
object *storagev1api.StorageClass
}
// ForStorageClass is the constructor for a StorageClassBuilder.
func ForStorageClass(name string) *StorageClassBuilder {
return &StorageClassBuilder{
object: &storagev1api.StorageClass{
TypeMeta: metav1.TypeMeta{
APIVersion: storagev1api.SchemeGroupVersion.String(),
Kind: "StorageClass",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
},
}
}
// Result returns the built StorageClass.
func (b *StorageClassBuilder) Result() *storagev1api.StorageClass {
return b.object
}
// ObjectMeta applies functional options to the StorageClass's ObjectMeta.
func (b *StorageClassBuilder) ObjectMeta(opts ...ObjectMetaOpt) *StorageClassBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}

View File

@@ -0,0 +1,56 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
corev1api "k8s.io/api/core/v1"
)
// VolumeBuilder builds Volume objects.
type VolumeBuilder struct {
object *corev1api.Volume
}
// ForVolume is the constructor for a VolumeBuilder.
func ForVolume(name string) *VolumeBuilder {
return &VolumeBuilder{
object: &corev1api.Volume{
Name: name,
},
}
}
// Result returns the built Volume.
func (b *VolumeBuilder) Result() *corev1api.Volume {
return b.object
}
// PersistentVolumeClaimSource sets the Volume's persistent volume claim source.
func (b *VolumeBuilder) PersistentVolumeClaimSource(claimName string) *VolumeBuilder {
b.object.PersistentVolumeClaim = &corev1api.PersistentVolumeClaimVolumeSource{
ClaimName: claimName,
}
return b
}
// CSISource sets the Volume's CSI source.
func (b *VolumeBuilder) CSISource(driver string) *VolumeBuilder {
b.object.CSI = &corev1api.CSIVolumeSource{
Driver: driver,
}
return b
}

View File

@@ -0,0 +1,41 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
corev1api "k8s.io/api/core/v1"
)
// VolumeMountBuilder builds VolumeMount objects.
type VolumeMountBuilder struct {
object *corev1api.VolumeMount
}
// ForVolumeMount is the constructor for a VolumeMountBuilder.
func ForVolumeMount(name, mountPath string) *VolumeMountBuilder {
return &VolumeMountBuilder{
object: &corev1api.VolumeMount{
Name: name,
MountPath: mountPath,
},
}
}
// Result returns the built VolumeMount.
func (b *VolumeMountBuilder) Result() *corev1api.VolumeMount {
return b.object
}

View File

@@ -0,0 +1,64 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
)
// VolumeSnapshotLocationBuilder builds VolumeSnapshotLocation objects.
type VolumeSnapshotLocationBuilder struct {
object *velerov1api.VolumeSnapshotLocation
}
// ForVolumeSnapshotLocation is the constructor for a VolumeSnapshotLocationBuilder.
func ForVolumeSnapshotLocation(ns, name string) *VolumeSnapshotLocationBuilder {
return &VolumeSnapshotLocationBuilder{
object: &velerov1api.VolumeSnapshotLocation{
TypeMeta: metav1.TypeMeta{
APIVersion: velerov1api.SchemeGroupVersion.String(),
Kind: "VolumeSnapshotLocation",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built VolumeSnapshotLocation.
func (b *VolumeSnapshotLocationBuilder) Result() *velerov1api.VolumeSnapshotLocation {
return b.object
}
// ObjectMeta applies functional options to the VolumeSnapshotLocation's ObjectMeta.
func (b *VolumeSnapshotLocationBuilder) ObjectMeta(opts ...ObjectMetaOpt) *VolumeSnapshotLocationBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Provider sets the VolumeSnapshotLocation's provider.
func (b *VolumeSnapshotLocationBuilder) Provider(name string) *VolumeSnapshotLocationBuilder {
b.object.Spec.Provider = name
return b
}

View File

@@ -114,5 +114,5 @@ func (d *dynamicResourceClient) Get(name string, opts metav1.GetOptions) (*unstr
}
func (d *dynamicResourceClient) Patch(name string, data []byte) (*unstructured.Unstructured, error) {
return d.resourceClient.Patch(name, types.MergePatchType, data, metav1.UpdateOptions{})
return d.resourceClient.Patch(name, types.MergePatchType, data, metav1.PatchOptions{})
}

View File

@@ -22,6 +22,7 @@ import (
"github.com/pkg/errors"
"github.com/spf13/pflag"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
v1 "github.com/heptio/velero/pkg/apis/velero/v1"
@@ -38,6 +39,9 @@ type Factory interface {
// KubeClient returns a Kubernetes client. It uses the following priority to specify the cluster
// configuration: --kubeconfig flag, KUBECONFIG environment variable, in-cluster configuration.
KubeClient() (kubernetes.Interface, error)
// DynamicClient returns a Kubernetes dynamic client. It uses the following priority to specify the cluster
// configuration: --kubeconfig flag, KUBECONFIG environment variable, in-cluster configuration.
DynamicClient() (dynamic.Interface, error)
Namespace() string
}
@@ -103,6 +107,19 @@ func (f *factory) KubeClient() (kubernetes.Interface, error) {
return kubeClient, nil
}
func (f *factory) DynamicClient() (dynamic.Interface, error) {
clientConfig, err := Config(f.kubeconfig, f.kubecontext, f.baseName)
if err != nil {
return nil, err
}
dynamicClient, err := dynamic.NewForConfig(clientConfig)
if err != nil {
return nil, errors.WithStack(err)
}
return dynamicClient, nil
}
func (f *factory) Namespace() string {
return f.namespace
}

View File

@@ -36,12 +36,13 @@ import (
)
const (
s3URLKey = "s3Url"
publicURLKey = "publicUrl"
kmsKeyIDKey = "kmsKeyId"
s3ForcePathStyleKey = "s3ForcePathStyle"
bucketKey = "bucket"
signatureVersionKey = "signatureVersion"
s3URLKey = "s3Url"
publicURLKey = "publicUrl"
kmsKeyIDKey = "kmsKeyId"
s3ForcePathStyleKey = "s3ForcePathStyle"
bucketKey = "bucket"
signatureVersionKey = "signatureVersion"
credentialProfileKey = "profile"
)
type s3Interface interface {
@@ -81,6 +82,7 @@ func (o *ObjectStore) Init(config map[string]string) error {
kmsKeyIDKey,
s3ForcePathStyleKey,
signatureVersionKey,
credentialProfileKey,
); err != nil {
return err
}
@@ -92,6 +94,7 @@ func (o *ObjectStore) Init(config map[string]string) error {
kmsKeyID = config[kmsKeyIDKey]
s3ForcePathStyleVal = config[s3ForcePathStyleKey]
signatureVersion = config[signatureVersionKey]
credentialProfile = config[credentialProfileKey]
// note that bucket is automatically added to the config map
// by the server from the ObjectStorageProviderConfig so
@@ -124,7 +127,7 @@ func (o *ObjectStore) Init(config map[string]string) error {
return err
}
serverSession, err := getSession(serverConfig)
serverSession, err := getSession(serverConfig, credentialProfile)
if err != nil {
return err
}
@@ -145,7 +148,7 @@ func (o *ObjectStore) Init(config map[string]string) error {
if err != nil {
return err
}
publicSession, err := getSession(publicConfig)
publicSession, err := getSession(publicConfig, credentialProfile)
if err != nil {
return err
}

View File

@@ -48,8 +48,10 @@ type VolumeSnapshotter struct {
ec2 *ec2.EC2
}
func getSession(config *aws.Config) (*session.Session, error) {
sess, err := session.NewSession(config)
// takes AWS credential config & a profile to create a new session
func getSession(config *aws.Config, profile string) (*session.Session, error) {
sessionOptions := session.Options{Config: *config, Profile: profile}
sess, err := session.NewSessionWithOptions(sessionOptions)
if err != nil {
return nil, errors.WithStack(err)
}
@@ -66,18 +68,19 @@ func NewVolumeSnapshotter(logger logrus.FieldLogger) *VolumeSnapshotter {
}
func (b *VolumeSnapshotter) Init(config map[string]string) error {
if err := cloudprovider.ValidateVolumeSnapshotterConfigKeys(config, regionKey); err != nil {
if err := cloudprovider.ValidateVolumeSnapshotterConfigKeys(config, regionKey, credentialProfileKey); err != nil {
return err
}
region := config[regionKey]
credentialProfile := config[credentialProfileKey]
if region == "" {
return errors.Errorf("missing %s in aws configuration", regionKey)
}
awsConfig := aws.NewConfig().WithRegion(region)
sess, err := getSession(awsConfig)
sess, err := getSession(awsConfig, credentialProfile)
if err != nil {
return err
}

Some files were not shown because too many files have changed in this diff Show More