diff --git a/changelogs/unreleased/4864-sseago b/changelogs/unreleased/4864-sseago new file mode 100644 index 000000000..fdbe78491 --- /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 b47713497..3db023bff 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 b7199e742..efdbadcf4 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 505e1d994..836701b77 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 7cf271e8f..adfb9c252 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 1862045e0..af94471e3 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 2de6b2827..b0e5e2f09 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 851542661..f6b8ac368 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 932b2fa7d..ba252aefd 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 33cd1ca3e..462205bc2 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 14dcc2349..20e340df0 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 ff8d36b34..ce3ab1e1d 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 9f20e34fc..a2794bdfc 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 000000000..62675dd44 --- /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 28ac33222..e6758f8fa 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 d5dbc710a..fe74391f9 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 cdfd25305..dd1a33149 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.