From 596114b4279d8b8472d9f3bc1f8967df17186efe Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Fri, 1 Apr 2022 13:29:52 -0400 Subject: [PATCH] Add credentials to volume snapshot locations. This is analogous to the BSL creds work that was done previously, but for VSLs instead. Signed-off-by: Scott Seago --- changelogs/unreleased/4864-sseago | 1 + .../velero.io_volumesnapshotlocations.yaml | 18 ++++++++ config/crd/v1/crds/crds.go | 2 +- .../v1/volume_snapshot_location_type.go | 9 +++- pkg/apis/velero/v1/zz_generated.deepcopy.go | 5 +++ .../volume_snapshot_location_builder.go | 10 ++++- pkg/cmd/cli/snapshotlocation/create.go | 35 +++++++++++---- pkg/cmd/cli/snapshotlocation/set.go | 24 +++++++--- pkg/cmd/server/server.go | 2 + pkg/controller/backup_controller.go | 13 ++++++ pkg/controller/backup_controller_test.go | 3 ++ pkg/restore/pv_restorer.go | 11 ++++- pkg/restore/restore.go | 11 +++-- pkg/volume/snapshotlocation.go | 44 +++++++++++++++++++ .../main/api-types/volumesnapshotlocation.md | 6 +++ site/content/docs/main/locations.md | 6 ++- site/content/docs/main/troubleshooting.md | 18 ++++++-- 17 files changed, 189 insertions(+), 29 deletions(-) create mode 100644 changelogs/unreleased/4864-sseago create mode 100644 pkg/volume/snapshotlocation.go diff --git a/changelogs/unreleased/4864-sseago b/changelogs/unreleased/4864-sseago new file mode 100644 index 0000000000..fdbe784915 --- /dev/null +++ b/changelogs/unreleased/4864-sseago @@ -0,0 +1 @@ +Add credentials to volume snapshot locations diff --git a/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml b/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml index b47713497a..3db023bff9 100644 --- a/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml +++ b/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml @@ -45,6 +45,24 @@ spec: type: string description: Config is for provider-specific configuration fields. type: object + credential: + description: Credential contains the credential information intended + to be used with this location + properties: + key: + description: The key of the secret to select from. Must be a + valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + required: + - key + type: object provider: description: Provider is the provider of the volume storage. type: string diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index b7199e7421..efdbadcf47 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -39,7 +39,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xdc\x18\x93Z\x98\x92\x9a\x01fAW֬#D{zSI\xa4\xbd\x16ĺv\xacV\x95\x1f{h\xe0:\x14\x8fS\x04V\xcc`\x0e*\x88}%Є\xb5r\xc7\xfeV\xb1\\\x8d\x82n\x90\xf7\xae\x86`+\x14`P`f\x95\x1eR2\x85\x9e\xfeIQ\x96#t\x8c\xa8;\xfc\xb7\x88M\x80\x04\x12\xf3\xe7-϶\xde\v \xd9tp Wh\x9c\xe6 Ou?\x86$\xcc\xf1>,2\xa5;\xdag\xe6L\x1d\u008b\xe9\x93\xf6Iз\xed3\xa3y\a\x9a%\xbc\x8f\x9a\xce\xf6\xf9\xf7$lmJN\x10ڻ\xc1\xd4\xd7\x15Z\x17U\x91\xb7\u007f\xb7\x06,J\xbb\xbf\x02n\xeb\xb7s\x10\x99\x10\x9d\xf5\xffƌ9^\xe2\xef\x0eg\xbe\xaa\xc4Ore\x0e\"q\xa5Y\xfeo\xc8\x14g,\x1e\x83\xadHfȯ\xddYW\xc0\xd7\rC\xf2+XsaQ\x1fp\xe6E\xe7\xe55\x88\x91b\xef\xe8)\x98Ͷ\uffd0gc\xda\fT\"]\x0e'{\x9f\xb8\x0e\x12\xfa\x86y\x06.\xb8\xf8\x95k,|\\\xfc\xc9Q\xb3}\xe3\x02\x8aw\x1f~\xc4|\x8a<\x90&y\x03D\xde\x1dl\xb6\xbbtp\xf4S\xd1\b\xaeO\x134\xf9\x8c\xc7\x150x½\xf7X\x98\x04b\x0e\xb3\xceߍ\x86OC\xe2\xb8ԋw\x8fq\xef\xc0\x84\\\xca\xec\xecTQ\xf0\xcf\x13F\xfc\xfd\xd8\xd3# \xed)D\xb8\x9e\x92\xf4\xc2\x11\xc2E\xde\xe9\xc4\x03\x97\x17\xabu\xd1\xb7]o\x87@\x9c\xd5ۡ\x05\x1aCwJj\xe6\xae\a\x80Le\xed8\xbb\xbd7Rs\x84\xe7\xb3B\nP1\xf7I/2\x9f\xc1\x8f\xf6\xbdK#e\xf0(vǻ.I\x9c\xedNH\xcf\x12}Y\xb4\xed\n\v\x97\x14\xd4;\\T\xf2I\xaag\xb9p1\xa5\x99\xcd\xd67\x8b\x9f\xac8\xbe\xa6\xd2\xe8\x8bW\xba\b\xd4\xf6\xf7\fJ!\x99\xcdG\x05\xc6SR0\xa7\x86|\x1b\xebȟ\xb3\xbb\x98Z\u007fbr\xa89\xde\xfa\xfe\xd3\xd4ަ\xbb\xf8\xac\x8e\xff\xf0\xbcE\xbbE]7\xb6.\\\x0foL\xad\xb6\xa5\xc9\xd6\x15n\x9a\x9dH~jo\xca7\xe1\x1d\xb4?\xc5}eY\tqE\x82\xcd*a}+\xaa\xae\"B\x94\xd4\x03\xb4RJ ;l\x8bM)\xa2ϕ\xce\xfb\xfd`M\xe9\xban\bS\xf5\"\x11\f=/}\xd7g\xb7.ۯ\x81\xbb\xecO\xbdӿ\xbcU,\xa1\xbc=SԞn\xa0\x9b\xa2\xd7Pl\xba\x14ke0\x8c\v\x9d\x95\xdf\x14\xf9f\n\xd1\xe3\xe5\xe7\x90lE\xcbvo\x97\xfd\u007f\xac\n\xc5h\x97Y\x88\xa0\xf2\xbcm\xf2\x04\xce\xf2ʜ\xefx^1ѓ\xc0\x0e\xcdZ҂\xd2 \xb9\x88ա\x88\xe6\xf5\xfc\x1e\x8d\xe1c\xe9\xf3\xd1G\x9f\xd5iw'\xadf}r\xa5\xba_\x89\x1e\xd1\xe0Ǧf\xd3[\xf2\xd2k\xd1\xd3\xc5\xe3c*Ї\xf5\xe5Q\xa0\xf3u\xe7\x14Ou\xa6\xc6|Be9\xb1\xab\xe8\xc5\t\xe8\x94\xda\xf1I\x15\xe3\xd9ƛ\xc4:q\xbf\x02<\r\xf2\x88\xeap\x12q\xe6+\xc1G\xd7\u007fC\xbdu\x12\x8f\xe4\xaao\xa4\x9e;\tx\xb4\xd6;Uŝ&y\xa4\u009b^\xbb\x9d\x04\xed\xea\xba\xf3\x15\xdb\xd7\xeb\xcbz\r\x17y\\\xd5\xccV]_\xe4B'\xd4U\x8f\xa9\xa6\xceR\xec\xc4\xcaiS\x19\x1dY\xf7\xd8zi\xbf\x1e:\x024\xa5J:R\x05\x1d\x818Y\x1bM\xad}\x8e\xc0\x9e1\xbb\x93R2\xf1g\xe3u\xff\xc6ʒ\xcb͐\xf3\xa9\xf21)\x1b\x83\xd2iw͞pt\x9d\xe3^X\x11[\xd2\u007f\x90\x18\tA\xea\xa4\x13\x97V-\xe1\x9d\xdc\x0f\xe0\xbaf\xe8\xa8\xcb\xdd\xffb\x85\xb6\xf5̅\xe8~\x95\xe1\xc0vA\x85\x0f\x9cL<\x10\xa6\x81c\x1f3E\x99\xa2t\xcfߝ\x8b8>\x1e\f鈴\xa6\xfd\xe7\x98\xeb\xcc\xed\xf6D\xff\xb9\xa8\x84\xe5e\xf4\x10\x97Z\xed\xb8K\x8amq\xdf\xd0\xf3\x0f徇X\xed\x1d\xa4\x8f\x0f\xcd\xf9Z\x1e\x84\x02,v*\x9eQ\b`f\x88~\xe6\xbf\t\xcc\xd4\xc2}\xe6C\x9c\xac\xe5!|;x\xe5\xce`,@\x95\xf5\xd7j\x05\x81q\xdf\x15\x9aH\x02`ԺL{\xb8\xde\x19w\xef\xfe\xacP\xefA\xed\\\x154\xb8<3\r\xc7^S\x98J\xb4\x1d\x1eA\x01\xfa/Q\x0f<\xffVc\xc0;\xe9mp\x14\xec\xc1\x1e\x1d\x1cRZm\xb4C\xfa\x99\x02\x99\x91\xa1Q\xa8R5\xb3\xe3\xf20ijR\xbbu\xcf\x1b\xfb\x1c\x1f\xfd\xcc\xfa\x1dg\x89\x80N\x8f\x81&@\xa6vߦe\xecg\xbbm\xcf\x15\v\xcdEC\xc9n`Z7\xed9\xbah\x8f\xe8\x9e=\"*:..J&SJ\x97\xecY\xa2\xa33\xc6G爐N\x8b\x91f@\x1et\xbf\xa6\xf4\xb5&\x15\x99\x92K\x14)E\xa5\xf9\xba\xe6t\xbfjB\x9fjB\xf1cn\xa7\t\xfd\xa8\xc7\xf5\xa1&\xd0\xf0L\xd1ә\xe2\xa7sDP獡f\xa3\xa8Yə\xfc\xfb\xe4\x1cy]M\xfd\xa0r\xbcW\xda\xce9\xfc\xf7\x87\xe3#\x15\xacN\x10\xa4D\x0e\xb2\x1e\x1aA\xca\xf9\xf2\xc1\x8f?\r\xa9x\xb1)\xac\u007f\xffy\x0e\x9f\x87f\xe04\"\xe4\x92\xd6\xf1Y\x04\x0f\x9a\xefp1\x92\x95f\xab,|\xb7\xe3,\\(\xa2\xaa<\x04!\xfa\xfbs`\xf9h\x99\xad\x12\x11\xf5c{\xb8\xf2l۩\xe7G\x10\xb2\x91\xb8N\x1e\x137\x8b;\xdf\xdfV\xe5\\\x9b\x1f;\xd5\x1f\xb86\xf6S\x99W\x8a\xe5\xad\xf1l\xad\xe6bW\xe5L5\xf5g\x00:\x91%^\xc1G\x1a\xaad\t\xa6g\x00~bv\xe8\x15\xb04\xb5\xa4b\xf9gŅAu-\xf3\xaa\b$ZA\x8a:Q\xbc4\x96\x14w\x86\x99J\x83܂ɰ=\x0e\x95\x9f\xb5\x14\x9f\x99ɮ`\xadm\xbbu\x991\x1d\xbe:\x129\x00\xbe\xca\x1c\b7m\x14\x17\xbb\xa1\xd1\xde\xc1\xb5\x92\x02\xf0k\xa9P\x13ʐZΊ\x1dCș6`x\x81\xc0\xfc\x80\xf0Ĵ\xc5a+\x15\x98\x8c\xeby\x9a\x10\x90\x0e\xb6\x0e\x9d\x0f\xfdj\x87P\xca\fztZ\xa0\x82T\xaf\x8f$\xb2\x03\xf3\xdd\x0e\x87\x81\xb9\xcf\xfb\xb7Nn\x92\f\vv\xe5[\xca\x12ŻϷ\x0f\u007f\xbd\xebTCO\x0e\xfc,\x81k`\xf0`\x85\x1a\x94_~`2f@!q\r\x85\xa1\x16\xa5\xc2U\xa0LZ\x83\x04\x90\nJT\\\xa6<\t\x14\xb5\x9du&\xab<\x85\r\x12q\xd7u\x87R\xc9\x12\x95\xe1aٸ\xd2R\x13\xad\xda\x1e\xc6ohR\xae\x95\x93\"\xd4Vp\xfcb\xc0\xd4\xd3\xc1\xc96\xd7\r\xfe\x96\xc0\x1d\xc0@\x8d\x98\x00\xb9\xf9\x19\x13\xb3\x86;T\x04&`\x9dH\xb1GE\x14H\xe4N\xf0\xff\u05305I\xac\xb1\x82dЯ\xe5\xa6\xd8\xc5'X\x0e{\x96Wx\x01L\xa4P\xb0\x03(\xa4Q\xa0\x12-x\xb6\x89^\xc3?\xa5B\xe0b+\xaf 3\xa6\xd4W\x97\x97;n\x82zLdQT\x82\x9bå\xd5t|S\x19\xa9\xf4e\x8a{\xcc/5߭\x98J2n01\x95\xc2KV\xf2\x95E]X\x15\xb9.\xd2?\x05\x8e\xea7\x1d\\\x8f֊+V\x89Mp\x80\xb4\x99\x13\x18\xd7\xd5͢!4U\x11u\xbe\xdc\xdcݷ\x85\x89\xeb>\xf5-\xdd[\x12ְ\x80\b\xc6\xc5\x16\xfdj\xdc*YX\x98(\xd2Rra\xec\x8f$\xe7(\xfa\xe4\xd7զ\xe0\x86\xf8\xfeK\x85\xda\x10\xaf\xd6pmm\x06\xc9aU\xd2\xeaI\xd7p+\xe0\x9a\x15\x98_3\x8d/\xce\x00\xa2\xb4^\x11a\xe3X\xd06w\xfdƎj\xad\x0f\xc14\x8d\xf0+\xac\xf1\xbb\x12\x93Β\xa1~|\xcb\x13\xbb0\xac\xe6\xabU@O\xfb\xb92\xbcj\xc1\xab\x1ejޯ\x9f\xd46\xb16\xe1\b&x\x15\xb3>\xfa2BM\xfb\t\x8b\x92\x96\xeb\f\x8a\xf7\xbe\x19\xa1H4Jk\x17$\x18ˠޤ\xd7jp\xa4T\xecp\x19\x12\xbd\xf6<\xf5Z㈚\xd3\x14\xa5\x92h~'X\xa93i\xc8.\xc8\xca\f\xb5\xeaM\xe0\xfa\xee\xb6\xd7)\xf0\xd9s\xddڽJcJSxb\xbc\xbf~B!y\xb8\xbe\xbb\x85\ar#0\xc0\x04g\xfd\xc0TJX5\xf8\x05Yz\xb8\x97?i\x84\xb4\xb2\xda ز\x8b\x11\xc0\x1b\xdc\xd2bSH0\xa8\x03*E\xb2\xa7-j\xb22kk\xa4Sܲ*7^\xb9p\ro\xbf\x83\x82\x8b\xca\xe01\xdfa\x9a\xf7\x8eH\x16\x9c\x9b\x8d\xbe\x97_P\x1b\x9eD\x10\xf4\xfd`\xc7\x16Q\x9f24\x19*\xd2t\xf6\x835\x1e\xa3s\xafIo\xd8#\xf9\x1f\x1b'Nd\x88\xf2\x1cJ\x99\xc2ލ\x04\x9bC@zj\xc2\x1b)sdC\"\x88_\x93\xbcJ1\xad}\xc6A!\xeb\xcd\xf6樓\xf5\xae\x19\x17\xb4dɗ%TE\xfdud\x9e\xd6\xf83\x85@Z\x97\v\a\x13\xb8\xf3\xf16#\xab\x97\n7X\x8c\xe09\xcbb\xb0^<\xdb\xe4x\x05FUC\x8a#\xc0`J\xb1\xc3\x04\xcdB\x04\xb2\x84du\x1fo\x1bs\x9e \x11\xab\xb6\x80\x96j\x964#\xf3\xfb?$X&\xe5c\f\x91\xfeA\xed\x1aK\x0f\x89\r\xf4`\x83\x19\xdbs\xa9t\xdf]į\x98T\x06\xc7\xd6\x113\x90\xf2\xed\x16\x15\xc1\xb2\xd1I\x1d\xccL\x11kZ\xdfRQӌ?\x9aW\xc3tb\x9e\xa5\xc6\xd8T\xac]\x1b\x85\n\x16qR\x87U\t\\\xa4|\xcfӊ\xe5\xc0\x856L$n~\xac\xc6ox~0'\x10G\xf8;k\x16fA\\\xea\xb8\tR \xf9\xf6\x85T\xc3\xc2\x11\xca1\x98q2l\x18i@9fۛ\xa2($\xf6\xa8\xa4\xd6\x1e5z\xe7\xa2\xe1\x94\xf3\xb0s\xb6\xc1\x1c4\xe6\x98\x18\xa9\xc6\xc9\x13#\x04\xae\xc4\xea\xcf\x11\xca\x0ehҮ!\x9eU\xa2M!K\x9d\xf1$s\xce0I\x99\x85\x05\xa9Dm5\x06+\xcb\xfc05i\x88\x91\f?\u061c\xd2hJ\x84\xfa\xe8\xc3\x1dS$M\x89\xd4\xc1M\x99\xd1\xc6]\xaa\xd7b\xf3J\xf4\x0e\x9a⛄\xfd\xf6\xa8\xfb\xf3\v;\x91\x9bS\xb0|\xbb\x05,Js\xb8\x00nBm\fTr\xb0\x1a<\xfe`\x8c;m\xb5\xdc\xf6{?\xfbjy\x16\xae\xd5h\xfcA\x98f\x8d՝\xb7U\x8b\x18\xf6\xa1\xdd\xf3\x02\xf8\xb6fXz\x01[\x9e\x1b\xb4\xbe\xd4\x1c\xa2-Gg\x96s\xcfI\xa0X\xdbK\xa5`&\xc9n\xea\xfd\x81\x88\x1e=Z\xf5\x018\xbf<\xc40\x96\a\x11 \xa1v*\xec\x96\x12WX\xb8\xad\xaa{\xbb>\x9a\x1a\xeb\x01\xbe\xfb\xf8\x1e\xd39\x92A\xbc\xa4\x1eM\xea]\xcf\xd3i\xa3`'\x18\x05\xb25)\xeb\xa6\xd51\x9eې\xbc\x00\x06\x8fxp\x9e\xd5`p9T\x88\xb5\xac\x06\xa9\xd0\xee\x8eZ5\xf2\x88\a\v\xcaowF\xc1[\"*\xae<\xe2!\xb6i\x8f\xa8\x84\x9f\xdf\xf0qԥ\n;\x8b\x98\xa5Ԕ\x9a\xa8~퀑q\x93\x85eJ)\x94@\xf1\x13\xa7]3\xac\xb3\xc7\xff\x88\x877ڱ\x8fVM\xc6\xcb\x05\x14 \x85\r\x1a\xed\n\v\x9b\xdb\x0f,\xe7i=\x98]'\v ފ\v\xf8(\r\xfds\xf3\x95kBQ\xa4\xf0^\xa2\xfe(\x8d\xadyQ\x12\xbbI\x9cH`\xd7\xd9.K\xe1\xcc\x02\xd1e\xd1\xf8\r\x0eք\x92\x88\xd6l\xe3\x1an\x05\xc5g\x8e>Kؔa@ΡUT\xdan\x8f\v)V\xd6L\x87\xd1\x16\x00m\xe3\xe5Y%U\x87S\x17\v!\x0e\xa2\xe8ѻ'k\xe5\xbe\x1c\x1d,L\x15\x85e\xce\x12L\xc3v\xa5=\xc5`\x06w<\x81\x02\xd5\x0e\xa1$\xbb\x11/T\v4\xb9+'Ha\xbck\x11\x8a7\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\x1cYL7\x8f\x9b\xa55\xef\xd6\x1f\x8a\xa2~\xfb||\x99eYȯc\x1f\xc4!\xe9\u070f\x82\xd9\xcd\xde_ɼZ\xf1\xfe-\xce\x1a2\xae\xf4\x1a\xde\xd9\xec\x80\x1c\xdb\xfd\xc3.ak\xa8(\x90\x84\t\xd7@r\xb2g9\xb9\x0f\xa4\xbc\x05`\xee\x9c\t\xb9=\xf2\xa0\xe2T\xccS&\xb5\xb3\xf9[\x8e\xb9=*<\u007f\xc4\xc3\xf9ő\xf6:\xbf\x15\xe7q0I\xe7\x1f)\xad\xdak\x91\"?\xc0\xb9\xfdvn\x1d\xb3%K\xe4\x04\xe7m\x81TG7\xb5g\xf9KB\x01\x8a\xb5\x83\xd7B\x9d\xeb\x13or\xe1\xe7f\x11-ӥ\xd4#\xc7N#h}\x96ڸ\r\xc0\x8e\xbb=\xb0C\x18\x13\xfd\xf9]C`[\x83\n\xb4\x91*\x9c.\x93\xda\xedm\x90\x13\xe7\xf5<\xef\x89\xd5\xf5n\xa4\x03LA\xe6y\xa3!\x9cN?w\xc7\xce\xf4\xf7<\xcc\xc4:K\x16v\xa9d\x82ZϋR\xa4\xe5\x98ٰ\xad7k\x99\v\u07b6Q\xaa9f+9\x94e\xae8\x91\xf6\x84\xc0\xe6\xe6kkߙ\xd4\x10\xfd\x8e\x11\xe5Sp\x04\x9b5V\x14\xac\x9f\xe9\x10\x8d\xee\xb5\xeb\x1d\x16\xa0\a\xe6\x02&\xb5\xab\xacRY\xe67{\x91\xfc\xbd9\x1e\x05\x17\xb7v x\xfbb\xce\n\x04U\x8e\xa7\x862ס\u007fÐ\xba\"6~\x85pn/\xedY\x8d\xc2\x0eg\x8fO2\xe29\x05\xe4L\viڛ5~\xa47\x1a\xb6\\i\xd3 \xbc\x00*\xd7\xf68\xf9ecLq\xa3\xd4\xc9!\xe6'\u05fb\xb5\xad\x98\xc9'\x9fe\xb2$\xb0\x0e\xc4\xcf\xd8\x1e\x81o\x81\x1b@\x91\xc8J\xd8\r/R\x174\xcc\x02\x88\x8e\x89ΘD\xda\xccVgQ\x15\xf1\x04YY\xe9\xe4bvw\xac\xdd\xe5\a\xc6\xe3v\xa7\xe04\xb6\x9a\xa9\x8c\x92\xa1\xd2M\x93\xf1\xa9%\xedt\xa2\x82}\xe5EU\x00+\x88-K\xe2ƭKJ\t\xb9G\x8e\xd7O\x8c\x1b\x9f\x8e\xe9\x0eV\x97i\xd3D\x16e\x8e\x06C\xbaI\"\x85\xe6)\xd6\xee\x83\xe7\xff`\xf2\xceXa\xb0e<\xaf\xd4\x02\x1d\xbd\x983K\xe36\xaf\x9e\x9e?\x18\x8bGde\x89\x19\xb9\xe9\xbe\xc0i\x9e\xb7\x1f\xa5Z\xe62\u007fV\xf8\xfc\xaei\xa98I\xa9\x9c\xf3NgaZ\xef\xb5\xeb\x9dz\xe1e\xe20\xe6\x9e\xceB\xb5\x98\xbc\xba\xa7uyuO_\xdd\xd3W\xf7\xb4W^\xdd\xd3W\xf7\xf4\xd5=\x1d.\xaf\xeei\xab\xbc\xba\xa7\xd1\xf6#\x06Õݹ\x9dh\x10\x85Ud\n\xc6\x1c\xda3c\xf9L\xa3\xeb\xbc\xd2\x06Ւ\f\xe9\xdb\xe1\x9e\x039\xf4\x89k\xb2\xb2\xd7\x1dǤ\xa6I]i\x8c^\x9d2MK2,&w\xb1%\xc2\v\x8fN\x83\x1e϶\x8fM\xa0\x9bK\x9b\xeb\xe6\x8e\xd7\xe9j\xee\xaf\x11\x82\x18\x19\x86\xf7\xdcs\x17\xa6\xda9W\xdd\xdc7\x1b\a\x04\x8c\u007f\x97y\xe5\x91im3\xc9lӉ\xf8c\x16>вw\xb8\xd0%\xa6\xea$~\xff\xaei\x19\x91m6\x9ec\xe6O-Ѱ\xfd\xdbu\xf7\x8b\x91>\xe3ldfO\xdcd\xee2\x17\x85\xaeb\xd7Nk\x0fr\xea/R\xf6i<\x02Q*\x10\x11\x90*E5{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xc8\xfa\x16|\x02?r\x91:ސ\x10\xb6\xfc\v\xfbj\xa1\xbd'S;>\xe3b\xd4x\x9b\xbdC#\x8d%#5j\x83\"{\xb8\xad\xd7pÒ\xacn8\x02ю\x9c1\r[\xa9\nf\xe0\xbc>\x8d\xbb\f=\xa9\xe6|\r\xf0\x83\xac\x0fB[\xaf܌\xc0ռ(\xf3\x03E?p\xde\x05\xf4m\xa23*~ڿ\x04\xe7\x1fD\x8b\x88\x80\xef\xba=\x06\x8e}\xc3shI.\xab\xb4\x1ea\x82\xddL\x1c\xe0\xf3\x83\xf5\xa6\xec#PI\xf3X\x96\xf7\x95B$\xdc{Kk\x04\xe4\u0603\x82\x8bH6~8\xac\x8dTl\x87\x1f\xa4{k1\x86f\xdd\x1e\x9d\xe76\xbd\xae\n\xa9\"\xfe\xeeר$\xbb\xb9\xf5\x016\x19d~\xb55g\xe9\x84\xed\x98\x12\x9bY\xe7\xc6\xe4\x11\x93\xbb\xbf\xff\xe0&dx\x81\xeb\xf7\x95;\xa8_\x95Li$J\x87\x89\xbaN\x9bq;\x97\xc9'ȥ\xa7\xc3\xf7\xfdy(\xb4\x19k6'\xe0\xa4\xd9\xec;O\x1f\x06\xd2ň\xfc\xc3p\xcfV \xdbb\xe2\xd4Ѿ\u070e\xc2bZ˄[]d\xb7\x84l\xa2\xd8\xcb=\x157eQ&TF\xa5\xf1ӓ@\xf5%,T}+\x1c\xa7f^\xe2\xfc\xe9\xa8c`\xf0\x90\xfa \xfd\xd7k>d\xf7\x84'\x90v\xafT\x86\xbd-\xae\xeb\xb7I\x8fI7\xb3\xfe\xc7\xd7\xfe\xb0\xef\xba\x1a~\x0etU\xbfPz\x16AY\xf7\ng̣\xaf\xee\xb9΄\x95\xa6R\u07bc&\x95\xb2\xef\xe6\x11\x10t\xcfʝ\xf6\xeck\xf3\x04\xf6\f/\x9bG\xb1\x9bh\u007f\xf6\t\xee\x01\xfe\xd5\x0fȎ\xbe\xa8ꬫ{\"{E\xf0Oc\xe7\xe0:\xb0\xef\f\xce\xcc\xf43\xb5\xa9\x93|=\xa1m\xc7\xf0>\xe1\xdd\x18\xea\xc3Y\x9b+\xf8\x88O\x03\xb57\x82&q|\xa6\xe6R31\xb5{\x04CO^ONq_\xf7\xb2y\xb1\x03ڢ\xab\xe6z\xcd{\t7,\xcf[\x10]\x0e\xec\x10[\xff̷n\x03'\xa19\xfd\xe5\xa8Ũ\xe2\x9aTZc\nkpI\x1dUjT{L[B\xe2mx\xbb\xa6\xda4\xcfE¯\xbf\x9d5\xab\x92%\t\x96\xc6'v\xb5\xffk\x80\xf3s\xfb#\xbc\xfco\u007f&R8G[_\xc1\xbf\xfe}\x06\xde\x00?\x84\xe7\xfd\xa9\xf2\xbf\x01\x00\x00\xff\xff\x9dJq\x1dHa\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VO\x8f\xeb4\x10\xbf\xe7S\x8c\x1e\x87w!\xe9{\xe2\x00\xca\r\x15\x0e+`\xb5\xda>\xed\x05qp\x9di;\xacc\x9b\xf1\xb8K\xf9\xf4\xc8v\xb2m\x93\x94]\x90\xf0-\xf6\xfc\xf9\xcdo\xfed\xaa\xba\xae+\xe5\xe9\t9\x90\xb3-(O\xf8\xa7\xa0M_\xa1y\xfe.4\xe4V\xc7\xcf\xd53ٮ\x85u\f\xe2\xfaG\f.\xb2\xc6\x1fpG\x96\x84\x9c\xadz\x14\xd5)Qm\x05\xa0\xacu\xa2\xd2uH\x9f\x00\xdaYag\fr\xbdG\xdb<\xc7-n#\x99\x0e9\x1b\x1f]\x1f?5\xdf6\x9f*\x00͘տP\x8fAT\xef[\xb0ј\n\xc0\xaa\x1e[\b\xc8II\x94\xc4\xc0\xf8G\xc4 \xa19\xa2Av\r\xb9*x\xd4\xc9\xf1\x9e]\xf4-\x9c\x1f\x8a\xfe\x00\xaa\x04\xb4ɦ6\xd9\xd4c1\x95_\r\x05\xf9\xe9\x96\xc4\xcf4Hy\x13Y\x99e@Y \x1c\x1c\xcb\xfd\xd9i\r!py!\xbb\x8fF\xf1\xa2r\x05\x10\xb4\xf3\xd8B\xd6\xf5JcW\x01\fLe[\xf5\xc0\xc5\xf1s1\xa7\x0fث\xe2\x04\xc0y\xb4\xdf?\xdc=}\xb3\xb9\xba\x06\xe80h&/\x99\xef\x85Ȁ\x02(\x18P\x808PZc\b\xa0#3Z\x81\x82\x12\xc8\xee\x1c\xf79G\xaf\xa6\x01\xd4\xd6E\x019 d\x05\xd9*\x03Ge\"~\r\xcavЫ\x130&/\x10텽,\x12\x1a\xf8\xc51f2[8\x88\xf8ЮV{\x92\xb1\xeb\xb4\xeb\xfbhIN\xab\xdc@\xb4\x8d\xe28\xac:<\xa2Y\x05\xda\u05ca\xf5\x81\x04\xb5Dƕ\xf2Tg\xe86w^\xd3w_\xf1Ч\xe1\xe3\x15V9\xa5\xca\n\xc2d\xf7\x17\x0f\xb9!\xfe!\x03\xa9\x1dJ}\x14\xd5\x12ř\xe8t\x95\xd8y\xfcq\xf3\x05F\xd79\x19S\xf63\xefg\xc5pNA\"\x8c\xec\x0e\xb9$qǮ\xcf6\xd1vޑ-ե\r\xa1\x9d\xd2\x1f\xe2\xb6'\tc\xed\xa6\\5\xb0Σ\b\xb6\b\xd1wJ\xb0k\xe0\xce\xc2Z\xf5h\xd6*\xe0\xff\x9e\x80\xc4t\xa8\x13\xb1\xefK\xc1\xe5\x14\x9d\n\x17\xd6.\x1e\xc61w#_\vݽ\xf1\xa8S\x06\x13\x89I\x9bv\xa4s{\xc0\xce1\xa8%\x95\xe6]H\xb2ƿ\xc42L\x92\x82f2_R\u007f\xbe\x8dfy\x9c䗃\n8\xbd\x9c`zH2S\xff\x86v\xa8O\xda`1Q\xa6\t\xbe\r%\x1d\xb4\xb1\x9f\xfb\xac\xe1\x1e_\x16n\x1fإɚ\xe7\xfa\xf5\xb9Q\x1bP\xfe7{\xb2\xb3p\xa7\x91\x15\xa9\xfc\x0f\xbb\x1c\xd5\x17\x03z0\x04\x1c\xadM};\x9b\x90\x19\xc8t\x92\xcfdH\xb0_@\xb3\x88\xe7\xce\xee\\\xde\x04Tr\xac\xa4\xf4\x13\x0e\xc9\x1e\xfc\x14\\\v\x06o纜\xf9\xf0z\x17\xa1\xe5\xe4?\xe9\u007fSN\xe3\x86\x18\x17}\xd7\x19\xd5\xe2C\xf2\xb8\xc4\xf8r\u007f\r(\xa31jk\xb0\x05\xe18\xd7.\xba\x8aY\x9d\xa6U3\x96\xday\x9fz\xa3\x80f\n\xa9O^\x0ehou\x03\xbc\xa8锿\xf2\f\xdb\xd3-\xd5\xf5\xebr8o\xa9R\xba-\xa4\xd9]\v-p\xf6.R\x16\xb3WJzq\xf3\x98\x11\xb2\xb9\x94\x1dg\xc6Uk\x8c\x8b\xc8<\x86\x9b\x10\x16\x93=\xbb\xcc滋\xf0\x828V\xfb1\xe0\xf3\xe8M\x9b\x9a\x17\xec\xee\xa7+\xee\x87\x0fW\xbbj\xfe\xd4\xcevT6t\xf8\xf5\xb7\xaaX\xc5\xeei\\0\xd3\xe5\xdf\x01\x00\x00\xff\xff\xfb\xb1p\x12\x1b\f\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdc6\x0f\xbe\xfbW\x10y\x0f\xb9\xbc\xf6$衅o\xc1\xb6\x87\xa0i\xb0Ȧ{)z\xd0H\xf4\f\xbb\xb2\xa4\x8aԴ\xdb__\xe8\xc3;\x1f\xeb\xd9&@\xeb\x9bi\xf2\xe1Ç\x1f3]\xdf\xf7\x9d\nt\x8f\x91ɻ\x11T \xfcS\xd0\xe57\x1e\x1e\xbe\xe3\x81\xfc\xe6\xf0\xb6{ gF\xb8I,~\xfe\x84\xecS\xd4\xf8=N\xe4HȻnFQF\x89\x1a;\x00\xe5\x9c\x17\x95͜_\x01\xb4w\x12\xbd\xb5\x18\xfb\x1d\xba\xe1!mq\x9b\xc8\x1a\x8c\x05|I}x3|;\xbc\xe9\x00t\xc4\x12\xfe\x99fdQs\x18\xc1%k;\x00\xa7f\x1c\xe1\xe0m\x9a\x91\x9d\n\xbc\xf7b\xbd\xaeɆ\x03Z\x8c~ \xdfq@\x9ds\xef\xa2Oa\x84\xe3\x87\n\xd1x՚\xee\v\xda]C\xfb\xd0Њ\x83%\x96\x1f_p\xfa@,\xc51\xd8\x14\x95\xbdʬ\xf8\xf0\xdeG\xf9x\xcc\xdeÁm\xfdBn\x97\xac\x8a\xd7\xe2;\x00\xd6>\xe0\b%<(\x8d\xa6\x03h\xc2\x15\xb8~\x91\xe6mE\xd4{\x9cU\xcd\x03\xe0\x03\xbaw\xb7\xef\ufff9;3\x03\x18d\x1d)H\x91\u007f\xbdD \x06\x05\v\x13\xf8c\x8f\x11\xe1\xbe\xe8\t,>\"7\xd2O\xa0\x00\v\u007f\x1e\x9e\x8c!\xfa\x80Qh)\xbe>'\x83wb\xbd\xe0\xf5:S\xaf^`\xf2\xc4!\x83\xecq)\x1fM\xab\x16\xfc\x04\xb2'\x86\x88!\"\xa3\x93c#\x8f\x8f\x9f@9\xf0\xdb\xdfP\xcb\x00w\x183L\xeeM\xb2&\x0f\xea\x01\xa3@D\xedw\x8e\xfez\xc2f\x10_\x92Z%\xd8z~|\xc8\tF\xa7,\x1c\x94M\xf8\u007fP\xce\xc0\xac\x1e!b\xce\x02ɝ\xe0\x15\x17\x1e\xe0'\x1f\x11\xc8M~\x84\xbdH\xe0q\xb3ّ,\v\xa7\xfd<'G\xf2\xb8)\xbbC\xdb$>\xf2\xc6\xe0\x01\xed\x86i\u05eb\xa8\xf7$\xa8%Eܨ@}\xa1\xee\xea\x1e\xcc\xe6\u007f\xb1\xad(\xbf>\xe3*\x8fy\x8aX\"\xb9\xddɇ\xb2\b/t \xef@\x1d\x84\x1aZ\xab8\n\x9dMY\x9dO?\xdc}\x86%uiƥ\xfaE\xf7c \x1f[\x90\x05#7a\xacM\x9c\xa2\x9f\v&:\x13<9)/\xda\x12\xbaK\xf99mg\x92\xdc\xf7\xdf\x13\xb2\xe4^\rpS\xae\x10l\x11R0J\xd0\f\xf0\xde\xc1\x8d\x9a\xd1\xde(\xc6\xff\xbc\x01Yi\uecf0_ւ\xd3\x03z\xe9\\U;]\xb0vޮ\xf4k}\x93\xef\x02\xea\xb3\x05\xca(4Q\xdb\xec\xc9\xc7\v]ղ\xe7\xebxÙ\xfb\xfa\x82C\xbd\xfe\x13\xed.\xad\x00ʘ\xf2ۡ\xec\xed\xd5\xd8\x17\x04[\xa9\xfb\xa6dʃ:\xf9\x98\x19\x1d\xc8`\xec\x97:\x1b\x93\x14[\xc1\x84\xd6\xf0\xf0\f\xf2\x8a\xe6\xad\xc8\x02\xf9\x9c\xe6\x19\x8f\xdb料d\xa1\x97\xb0z\xa1\xb0\x1d\xccr>\xd5\x0e\xaf1X\xa98O8E\xbc\xd8\xd5\xfe)\xc1\x17͎(I\xfc\xf5\xd3S\u009a\xe7\xb6M\x90N1\xa2\x93\x86\xb9ri\xff\x9d\t\n{\xc5\xf8\x0f\x9a\xafg\xb8͑K\x1b,M\xa8\x1f\xb5\xc5\n\b~Z\x99\xb6\xaf\xa2\x9c\x1fti~έ\x87w\aEVm-\xae|\xfb٩\xab_\xaf6\u007f\xb5\x9fό\x9cϩ\x19Ab\xaa\xd8mʚ\xe5\xd8}\xa55\x06A\xf3\xf1\xf2\xffЫWg\u007fiʫ\xf6\xae.+\x8f\xf0˯]EEs\xbf\xfc\x03\xc9ƿ\x03\x00\x00\xff\xffz{3\x1eK\n\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WO\x8f۶\x13\xbd\xfbS\f\x92\xc3^\"9\xc1\xef\xf0+t)\x82M\x0fA\xf3g\x11o\xf7R\xf4@\x93#\x8b]\x8aTgHm\xddO_\f)\xad\xbd\xb6\x9cl\x8aV\x17C\x149|\xf3\u07bc!\xbd\xaa\xaaj\xa5\x06{\x87\xc46\xf8\x06\xd4`\xf1ψ^\u07b8\xbe\xff\x81k\x1b\xd6\xe3\x9bս\xf5\xa6\x81\xeb\xc41\xf4_\x90C\"\x8dﰵ\xdeF\x1b\xfc\xaaǨ\x8c\x8a\xaaY\x01(\xefCT2\xcc\xf2\n\xa0\x83\x8f\x14\x9cC\xaav\xe8\xeb\xfb\xb4\xc5m\xb2\xce \xe5\xe0\xf3\xd6\xe3\xeb\xfa\xff\xf5\xeb\x15\x80&\xcc\xcbom\x8f\x1cU?4\xe0\x93s+\x00\xafzl`\f.\xf5\xc8^\r܅\xe8\x82.\x9b\xd5#:\xa4P۰\xe2\x01\xb5콣\x90\x86\x06\x0e\x1fJ\x88\tW\xc9\xe9.G\xdbL\xd1>L\xd1\xf2\x04g9\xfe\xfc\x95I\x1f,\xc7\xe8\xcdK\x9a,\xcaWO\xb0ƽT\x11G\xb2~w\xf4!\x1b\xe1+\n\x88\aJ!\x94\xa5%\x8b\x03\xd12$\xec|\xf9is\v\xf3\xd6Y\x8cS\xf63\uf1c5|\x90@\b\xb3\xbeE*\"\xb6\x14\xfa\x1c\x13\xbd\x19\x82\xf51\xbfhgџ\xd2\xcfi\xdb\xdb(\xba\xff\x91\x90\xa3hU\xc3u\xeeB\xb0EH\x83Q\x11M\r\xef=\\\xab\x1eݵb\xfc\xcf\x05\x10\xa6\xb9\x12b\x9f'\xc1q\x03=\x9d\\X;6\xd8\xd4\xde.\xe8\xb5\xec\xe4̀\xfa\x89\x81$\x8am\xed\xe4\xec6\xd0\t\xafj\xf6\xf9r\xbc\xfa\xc9\xf4e\x83C\xe9\xfe\xadݝ\x8e\x02(c\xf2١\xdc\xcdŵ_!l!\xef뼓\x14j\x1bH\x10\x8d\xd6 Us\x9e\x13\x92DS\xc2\x16\x9d\xe1\xfa,\xe4\x05\xces*\x84F4V\xee\x1c\xe8S$\x8f\x13\xf3᧬/\x94\x1f\x02\xe4ң~\xea\xb1>\xa27\xb9\xa9\x9f\xa1\t\xb9\x86\x19\r<\xd8\xd8\x15s\xb8\xe3C\xeay*\xc8s\x8f\xfb\xa5\xe1\x13\xec\xb7\x1d\xca\xcc\xd2N\x11\x185a\x14\x1c\x8cN\xcc+ά\x01>&\xce\xf6R\x8b\x11AZ\x845\xf3\xea{ܟ\x13\r\xdf\x12w:\xef\xbf\r\xf9J\xce\xc5\x190a\x8b\x84>.Z\\\xee\x1e\xe41bv\xb9\t\x9a\xc5\xe0\x1a\x87\xc8\xeb0\"\x8d\x16\x1f\xd6\x0f\x81\xee\xad\xdfUBxU\n\x81\xd7\xf9ް~\x99\u007f.\xa4|\xfb\xf9\xdd\xe7\x06\xde\x1a\x03!vH\xa2Z\x9b\xdc\\hG\xa7ݫ\xdcq_A\xb2\xe6ǫ\u007f\xc2K\x18\x8as\x9e\xc1\xcd&W\xff^N\xee\fJ(\xda\x14U\x02\x81\xf4M\x11\xbb\x9f\xd4,\xfda\xa9\x10gL\xdb\x10\x1c\xaa\xf3ғ\xeek\t\xcd9\xa4Jv\xf8\x1e\x9b\xcd\xce\xfd\x86\xc9n\xa6ibx\xc9j^6\x17B\xb9\x97\xe4[\x8a\xda\xe1%\xa3/p\xbc\x9cJ\xf5\xb8\xc1\xb3ZtT1\xf1\xf77\xe9\xbcl\x9a\xb9\x9d\x1a\xb5N$\x05=\xc5\\\xb8\xd0\xfc;\x8dz\xe8\x14/\xb8\xed\x19\xa8od\xe5,\x83\xb3-\xea\xbdvX\x02Bh\x17\xaa\xe9\xbb ˃>\xf5K\xa5\xf5vT֩\xadÅo\xbfxu\xf1\xebE\xf1\x17\xf5<\x1bd\xb9\xb5\x98\x06\"\xa5\x12{\xaa\xb2i䠾\xd2\xd2\\\xd0|:\xfd\xdb\xf1\xe2œ\u007f\x0e\xf9U\a_\xceDn\xe0\xd7\xdfV%*\x9a\xbb\xf9\xa2/\x83\u007f\a\x00\x00\xff\xff\xe4\xf3S\x85\xb2\r\x00\x00"), } var CRDs = crds() diff --git a/pkg/apis/velero/v1/volume_snapshot_location_type.go b/pkg/apis/velero/v1/volume_snapshot_location_type.go index 505e1d994a..836701b774 100644 --- a/pkg/apis/velero/v1/volume_snapshot_location_type.go +++ b/pkg/apis/velero/v1/volume_snapshot_location_type.go @@ -16,7 +16,10 @@ limitations under the License. package v1 -import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +import ( + corev1api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -61,6 +64,10 @@ type VolumeSnapshotLocationSpec struct { // Config is for provider-specific configuration fields. // +optional Config map[string]string `json:"config,omitempty"` + + // Credential contains the credential information intended to be used with this location + // +optional + Credential *corev1api.SecretKeySelector `json:"credential,omitempty"` } // VolumeSnapshotLocationPhase is the lifecycle phase of a Velero VolumeSnapshotLocation. diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index 7cf271e8f8..adfb9c2526 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -1656,6 +1656,11 @@ func (in *VolumeSnapshotLocationSpec) DeepCopyInto(out *VolumeSnapshotLocationSp (*out)[key] = val } } + if in.Credential != nil { + in, out := &in.Credential, &out.Credential + *out = new(corev1.SecretKeySelector) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotLocationSpec. diff --git a/pkg/builder/volume_snapshot_location_builder.go b/pkg/builder/volume_snapshot_location_builder.go index 1862045e0b..af94471e36 100644 --- a/pkg/builder/volume_snapshot_location_builder.go +++ b/pkg/builder/volume_snapshot_location_builder.go @@ -1,5 +1,5 @@ /* -Copyright 2019 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,6 +19,8 @@ package builder import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1api "k8s.io/api/core/v1" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" ) @@ -62,3 +64,9 @@ func (b *VolumeSnapshotLocationBuilder) Provider(name string) *VolumeSnapshotLoc b.object.Spec.Provider = name return b } + +// Credential sets the VolumeSnapshotLocation's credential selector. +func (b *VolumeSnapshotLocationBuilder) Credential(selector *corev1api.SecretKeySelector) *VolumeSnapshotLocationBuilder { + b.object.Spec.Credential = selector + return b +} diff --git a/pkg/cmd/cli/snapshotlocation/create.go b/pkg/cmd/cli/snapshotlocation/create.go index 2de6b28277..b0e5e2f09e 100644 --- a/pkg/cmd/cli/snapshotlocation/create.go +++ b/pkg/cmd/cli/snapshotlocation/create.go @@ -1,5 +1,5 @@ /* -Copyright 2020 the Velero contributors. +Copyright the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/cmd" "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" @@ -54,16 +55,18 @@ func NewCreateCommand(f client.Factory, use string) *cobra.Command { } type CreateOptions struct { - Name string - Provider string - Config flag.Map - Labels flag.Map + Name string + Provider string + Config flag.Map + Labels flag.Map + Credential flag.Map } func NewCreateOptions() *CreateOptions { return &CreateOptions{ - Config: flag.NewMap(), - Labels: flag.NewMap(), + Config: flag.NewMap(), + Labels: flag.NewMap(), + Credential: flag.NewMap(), } } @@ -71,6 +74,7 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { flags.StringVar(&o.Provider, "provider", o.Provider, "Name of the volume snapshot provider (e.g. aws, azure, gcp).") flags.Var(&o.Config, "config", "Configuration key-value pairs.") flags.Var(&o.Labels, "labels", "Labels to apply to the volume snapshot location.") + flags.Var(&o.Credential, "credential", "The credential to be used by this location as a key-value pair, where the key is the Kubernetes Secret name, and the value is the data key name within the Secret. Optional, one value only.") } func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Factory) error { @@ -82,6 +86,10 @@ func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Facto return errors.New("--provider is required") } + if len(o.Credential.Data()) > 1 { + return errors.New("--credential can only contain 1 key/value pair") + } + return nil } @@ -90,10 +98,10 @@ func (o *CreateOptions) Complete(args []string, f client.Factory) error { return nil } -func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { +func (o *CreateOptions) BuildVolumeSnapshotLocation(namespace string) *api.VolumeSnapshotLocation { volumeSnapshotLocation := &api.VolumeSnapshotLocation{ ObjectMeta: metav1.ObjectMeta{ - Namespace: f.Namespace(), + Namespace: namespace, Name: o.Name, Labels: o.Labels.Data(), }, @@ -102,6 +110,15 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { Config: o.Config.Data(), }, } + for secretName, secretKey := range o.Credential.Data() { + volumeSnapshotLocation.Spec.Credential = builder.ForSecretKeySelector(secretName, secretKey).Result() + break + } + return volumeSnapshotLocation +} + +func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { + volumeSnapshotLocation := o.BuildVolumeSnapshotLocation(f.Namespace()) if printed, err := output.PrintWithFormat(c, volumeSnapshotLocation); printed || err != nil { return err diff --git a/pkg/cmd/cli/snapshotlocation/set.go b/pkg/cmd/cli/snapshotlocation/set.go index 8515426610..f6b8ac3688 100644 --- a/pkg/cmd/cli/snapshotlocation/set.go +++ b/pkg/cmd/cli/snapshotlocation/set.go @@ -27,8 +27,10 @@ import ( kbclient "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/cmd" + "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" "github.com/vmware-tanzu/velero/pkg/cmd/util/output" ) @@ -39,9 +41,6 @@ func NewSetCommand(f client.Factory, use string) *cobra.Command { Use: use + " NAME", Short: "Set specific features for a snapshot location", Args: cobra.ExactArgs(1), - // Mark this command as hidden until more functionality is added - // as part of https://github.com/vmware-tanzu/velero/issues/2426 - Hidden: true, Run: func(c *cobra.Command, args []string) { cmd.CheckError(o.Complete(args, f)) cmd.CheckError(o.Validate(c, args, f)) @@ -54,14 +53,18 @@ func NewSetCommand(f client.Factory, use string) *cobra.Command { } type SetOptions struct { - Name string + Name string + Credential flag.Map } func NewSetOptions() *SetOptions { - return &SetOptions{} + return &SetOptions{ + Credential: flag.NewMap(), + } } -func (o *SetOptions) BindFlags(*pflag.FlagSet) { +func (o *SetOptions) BindFlags(flags *pflag.FlagSet) { + flags.Var(&o.Credential, "credential", "Sets the credential to be used by this location as a key-value pair, where the key is the Kubernetes Secret name, and the value is the data key name within the Secret. Optional, one value only.") } func (o *SetOptions) Validate(c *cobra.Command, args []string, f client.Factory) error { @@ -69,6 +72,10 @@ func (o *SetOptions) Validate(c *cobra.Command, args []string, f client.Factory) return err } + if len(o.Credential.Data()) > 1 { + return errors.New("--credential can only contain 1 key/value pair") + } + return nil } @@ -92,6 +99,11 @@ func (o *SetOptions) Run(c *cobra.Command, f client.Factory) error { return errors.WithStack(err) } + for name, key := range o.Credential.Data() { + location.Spec.Credential = builder.ForSecretKeySelector(name, key).Result() + break + } + if err := kbClient.Update(context.Background(), location, &kbclient.UpdateOptions{}); err != nil { return errors.WithStack(err) } diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 2253d9dbc6..4e39c7d77b 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -650,6 +650,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string csiVSCLister, csiVSClassLister, backupStoreGetter, + s.credentialFileStore, ) return controllerRunInfo{ @@ -672,6 +673,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.logger, podexec.NewPodCommandExecutor(s.kubeClientConfig, s.kubeClient.CoreV1().RESTClient()), s.kubeClient.CoreV1().RESTClient(), + s.credentialFileStore, ) cmd.CheckError(err) diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 33cd1ca3ed..462205bc2e 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -50,6 +50,7 @@ import ( snapshotterClientSet "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" snapshotv1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1" + "github.com/vmware-tanzu/velero/internal/credentials" "github.com/vmware-tanzu/velero/internal/storage" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" @@ -98,6 +99,7 @@ type backupController struct { volumeSnapshotClient *snapshotterClientSet.Clientset volumeSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister volumeSnapshotClassLister snapshotv1listers.VolumeSnapshotClassLister + credentialFileStore credentials.FileStore } func NewBackupController( @@ -123,6 +125,7 @@ func NewBackupController( volumeSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister, volumesnapshotClassLister snapshotv1listers.VolumeSnapshotClassLister, backupStoreGetter persistence.ObjectBackupStoreGetter, + credentialStore credentials.FileStore, ) Interface { c := &backupController{ genericController: newGenericController(Backup, logger), @@ -148,6 +151,7 @@ func NewBackupController( volumeSnapshotContentLister: volumeSnapshotContentLister, volumeSnapshotClassLister: volumesnapshotClassLister, backupStoreGetter: backupStoreGetter, + credentialFileStore: credentialStore, } c.syncHandler = c.processBackup @@ -566,6 +570,15 @@ func (c *backupController) validateAndGetSnapshotLocations(backup *velerov1api.B return nil, errors } + // add credential to config for each location + for _, location := range providerLocations { + err = volume.UpdateVolumeSnapshotLocationWithCredentialConfig(location, c.credentialFileStore, c.logger) + if err != nil { + errors = append(errors, fmt.Sprintf("error adding credentials to volume snapshot location named %s: %v", location.Name, err)) + continue + } + } + return providerLocations, nil } diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 14dcc2349d..20e340df06 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -1007,12 +1007,15 @@ func TestValidateAndGetSnapshotLocations(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + formatFlag := logging.FormatText var ( client = fake.NewSimpleClientset() sharedInformers = informers.NewSharedInformerFactory(client, 0) + logger = logging.DefaultLogger(logrus.DebugLevel, formatFlag) ) c := &backupController{ + genericController: newGenericController("backup-test", logger), snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), defaultSnapshotLocations: test.defaultLocations, } diff --git a/pkg/restore/pv_restorer.go b/pkg/restore/pv_restorer.go index ff8d36b347..ce3ab1e1d9 100644 --- a/pkg/restore/pv_restorer.go +++ b/pkg/restore/pv_restorer.go @@ -21,6 +21,7 @@ import ( "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "github.com/vmware-tanzu/velero/internal/credentials" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" "github.com/vmware-tanzu/velero/pkg/util/boolptr" @@ -39,6 +40,7 @@ type pvRestorer struct { volumeSnapshots []*volume.Snapshot volumeSnapshotterGetter VolumeSnapshotterGetter snapshotLocationLister listers.VolumeSnapshotLocationLister + credentialFileStore credentials.FileStore } func (r *pvRestorer) executePVAction(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { @@ -59,7 +61,7 @@ func (r *pvRestorer) executePVAction(obj *unstructured.Unstructured) (*unstructu log := r.logger.WithFields(logrus.Fields{"persistentVolume": pvName}) - snapshotInfo, err := getSnapshotInfo(pvName, r.backup, r.volumeSnapshots, r.snapshotLocationLister) + snapshotInfo, err := getSnapshotInfo(pvName, r.backup, r.volumeSnapshots, r.snapshotLocationLister, r.credentialFileStore, r.logger) if err != nil { return nil, err } @@ -103,7 +105,7 @@ type snapshotInfo struct { location *api.VolumeSnapshotLocation } -func getSnapshotInfo(pvName string, backup *api.Backup, volumeSnapshots []*volume.Snapshot, snapshotLocationLister listers.VolumeSnapshotLocationLister) (*snapshotInfo, error) { +func getSnapshotInfo(pvName string, backup *api.Backup, volumeSnapshots []*volume.Snapshot, snapshotLocationLister listers.VolumeSnapshotLocationLister, credentialStore credentials.FileStore, logger logrus.FieldLogger) (*snapshotInfo, error) { var pvSnapshot *volume.Snapshot for _, snapshot := range volumeSnapshots { if snapshot.Spec.PersistentVolumeName == pvName { @@ -120,6 +122,11 @@ func getSnapshotInfo(pvName string, backup *api.Backup, volumeSnapshots []*volum if err != nil { return nil, errors.WithStack(err) } + // add credential to config + err = volume.UpdateVolumeSnapshotLocationWithCredentialConfig(loc, credentialStore, logger) + if err != nil { + return nil, errors.WithStack(err) + } return &snapshotInfo{ providerSnapshotID: pvSnapshot.Status.ProviderSnapshotID, diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index c3a022c566..2228627bc3 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -46,6 +46,7 @@ import ( corev1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/tools/cache" + "github.com/vmware-tanzu/velero/internal/credentials" "github.com/vmware-tanzu/velero/internal/hook" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/archive" @@ -113,6 +114,7 @@ type kubernetesRestorer struct { logger logrus.FieldLogger podCommandExecutor podexec.PodCommandExecutor podGetter cache.Getter + credentialFileStore credentials.FileStore } // NewKubernetesRestorer creates a new kubernetesRestorer. @@ -128,6 +130,7 @@ func NewKubernetesRestorer( logger logrus.FieldLogger, podCommandExecutor podexec.PodCommandExecutor, podGetter cache.Getter, + credentialStore credentials.FileStore, ) (Restorer, error) { return &kubernetesRestorer{ restoreClient: restoreClient, @@ -147,9 +150,10 @@ func NewKubernetesRestorer( veleroCloneName := "velero-clone-" + veleroCloneUuid.String() return veleroCloneName, nil }, - fileSystem: filesystem.NewFileSystem(), - podCommandExecutor: podCommandExecutor, - podGetter: podGetter, + fileSystem: filesystem.NewFileSystem(), + podCommandExecutor: podCommandExecutor, + podGetter: podGetter, + credentialFileStore: credentialStore, }, nil } @@ -276,6 +280,7 @@ func (kr *kubernetesRestorer) RestoreWithResolvers( volumeSnapshots: req.VolumeSnapshots, volumeSnapshotterGetter: volumeSnapshotterGetter, snapshotLocationLister: snapshotLocationLister, + credentialFileStore: kr.credentialFileStore, } restoreCtx := &restoreContext{ diff --git a/pkg/volume/snapshotlocation.go b/pkg/volume/snapshotlocation.go new file mode 100644 index 0000000000..62675dd444 --- /dev/null +++ b/pkg/volume/snapshotlocation.go @@ -0,0 +1,44 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package volume + +import ( + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + "github.com/vmware-tanzu/velero/internal/credentials" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +// UpdateVolumeSnapshotLocationWithCredentialConfig adds the credentials file path to the config +// if the VSL specifies a credential +func UpdateVolumeSnapshotLocationWithCredentialConfig(location *velerov1api.VolumeSnapshotLocation, credentialStore credentials.FileStore, logger logrus.FieldLogger) error { + if location.Spec.Config == nil { + location.Spec.Config = make(map[string]string) + } + // If the VSL specifies a credential, fetch its path on disk and pass to + // plugin via the config. + if location.Spec.Credential != nil && credentialStore != nil { + credsFile, err := credentialStore.Path(location.Spec.Credential) + if err != nil { + return errors.Wrap(err, "unable to get credentials") + } + + location.Spec.Config["credentialsFile"] = credsFile + } + return nil +} diff --git a/site/content/docs/main/api-types/volumesnapshotlocation.md b/site/content/docs/main/api-types/volumesnapshotlocation.md index 28ac332222..e6758f8faf 100644 --- a/site/content/docs/main/api-types/volumesnapshotlocation.md +++ b/site/content/docs/main/api-types/volumesnapshotlocation.md @@ -21,6 +21,9 @@ metadata: namespace: velero spec: provider: aws + credential: + name: secret-name + key: key-in-secret config: region: us-west-2 profile: "default" @@ -37,4 +40,7 @@ The configurable parameters are as follows: | --- | --- | --- | --- | | `provider` | String | Required Field | The name for whichever storage provider will be used to create/store the volume snapshots. See [your volume snapshot provider's plugin documentation](../supported-providers) for the appropriate value to use. | | `config` | map string string | None (Optional) | Provider-specific configuration keys/values to be passed to the volume snapshotter plugin. See [your volume snapshot provider's plugin documentation](../supported-providers) for details. | +| `credential` | [corev1.SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#secretkeyselector-v1-core) | Optional Field | The credential information to be used with this location. | +| `credential/name` | String | Optional Field | The name of the secret within the Velero namespace which contains the credential information. | +| `credential/key` | String | Optional Field | The key to use within the secret. | {{< /table >}} diff --git a/site/content/docs/main/locations.md b/site/content/docs/main/locations.md index d5dbc710a6..fe74391f9e 100644 --- a/site/content/docs/main/locations.md +++ b/site/content/docs/main/locations.md @@ -26,8 +26,10 @@ This configuration design enables a number of different use cases, including: All [plugins maintained by the Velero team][5] support this feature. If you are using a plugin from another provider, please check their documentation to determine if this feature is supported. -- Velero only supports a single set of credentials for `VolumeSnapshotLocations`. - Velero will always use the credentials provided at install time (stored in the `cloud-credentials` secret) for volume snapshots. +- Velero supports multiple credentials for `VolumeSnapshotLocations`, allowing you to specify the credentials to use with any `VolumeSnapshotLocation`. + However, use of this feature requires support within the plugin for the object storage provider you wish to use. + All [plugins maintained by the Velero team][5] support this feature. + If you are using a plugin from another provider, please check their documentation to determine if this feature is supported. - Volume snapshots are still limited by where your provider allows you to create snapshots. For example, AWS and Azure do not allow you to create a volume snapshot in a different region than where the volume is. If you try to take a Velero backup using a volume snapshot location with a different region than where your cluster's volumes are, the backup will fail. diff --git a/site/content/docs/main/troubleshooting.md b/site/content/docs/main/troubleshooting.md index cdfd253059..dd1a331493 100644 --- a/site/content/docs/main/troubleshooting.md +++ b/site/content/docs/main/troubleshooting.md @@ -175,9 +175,9 @@ Follow the below troubleshooting steps to confirm that Velero is using the corre ``` -### Troubleshooting `BackupStorageLocation` credentials +### Troubleshooting `BackupStorageLocation` and `VolumeSnapshotLocation` credentials -Follow the below troubleshooting steps to confirm that Velero is using the correct credentials if using credentials specific to a [`BackupStorageLocation`][10]: +Follow the below troubleshooting steps to confirm that Velero is using the correct credentials if using credentials specific to a [`BackupStorageLocation` or `VolumeSnapshotLocation`][10]: 1. Confirm that the object storage provider plugin being used supports multiple credentials. If the logs from the Velero deployment contain the error message `"config has invalid keys credentialsFile"`, the version of your object storage plugin does not yet support multiple credentials. @@ -186,7 +186,7 @@ Follow the below troubleshooting steps to confirm that Velero is using the corre If you are using a plugin from a different provider, please contact them for further advice. -1. Confirm that the secret and key referenced by the `BackupStorageLocation` exists in the Velero namespace and has the correct content: +1. Confirm that the secret and key referenced by the `BackupStorageLocation` or `VolumeSnapshotLocation` exists in the Velero namespace and has the correct content: ```bash # Determine which secret and key the BackupStorageLocation is using BSL_SECRET=$(kubectl get backupstoragelocations.velero.io -n velero -o yaml -o jsonpath={.spec.credential.name}) @@ -197,11 +197,21 @@ Follow the below troubleshooting steps to confirm that Velero is using the corre # Print the content of the secret and ensure it is correct kubectl -n velero get secret $BSL_SECRET -ojsonpath={.data.$BSL_SECRET_KEY} | base64 --decode + + # Determine which secret and key the VolumeSnapshotLocation is using + VSL_SECRET=$(kubectl get volumesnapshotlocations.velero.io -n velero -o yaml -o jsonpath={.spec.credential.name}) + VSL_SECRET_KEY=$(kubectl get volumesnapshotlocations.velero.io -n velero -o yaml -o jsonpath={.spec.credential.key}) + + # Confirm that the secret exists + kubectl -n velero get secret $VSL_SECRET + + # Print the content of the secret and ensure it is correct + kubectl -n velero get secret $VSL_SECRET -ojsonpath={.data.$VSL_SECRET_KEY} | base64 --decode ``` If the secret can't be found, the secret does not exist within the Velero namespace and must be created. If no output is produced when printing the contents of the secret, the key within the secret may not exist or may have no content. - Ensure that the key exists within the secret's data by checking the output from `kubectl -n velero describe secret $BSL_SECRET`. + Ensure that the key exists within the secret's data by checking the output from `kubectl -n velero describe secret $BSL_SECRET` or `kubectl -n velero describe secret $VSL_SECRET`. If it does not exist, follow the instructions for [editing a Kubernetes secret][12] to add the base64 encoded credentials data.