From e9cd5838b21a2c50ae53bc7ea0fb2273e370b4c2 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 8 Jan 2025 17:46:49 +0800 Subject: [PATCH] data mover restore for Windows Signed-off-by: Lyndon-Li --- changelogs/unreleased/8594-Lyndon-Li | 1 + .../bases/velero.io_datadownloads.yaml | 3 + .../v2alpha1/bases/velero.io_datauploads.yaml | 6 +- config/crd/v2alpha1/crds/crds.go | 4 +- .../velero/v2alpha1/data_download_types.go | 4 + pkg/apis/velero/v2alpha1/data_upload_types.go | 19 +++- pkg/builder/data_download_builder.go | 6 ++ pkg/builder/data_upload_builder.go | 6 ++ pkg/cmd/cli/datamover/restore.go | 19 +++- pkg/controller/data_download_controller.go | 57 +++++++---- .../data_download_controller_test.go | 2 +- pkg/controller/data_upload_controller.go | 12 +++ pkg/exposer/csi_snapshot.go | 6 ++ pkg/exposer/generic_restore.go | 59 ++++++++---- pkg/exposer/generic_restore_test.go | 95 +++++++++---------- pkg/exposer/types.go | 1 + pkg/restore/actions/csi/pvc_action.go | 1 + .../actions/dataupload_retrieve_action.go | 1 + 18 files changed, 214 insertions(+), 88 deletions(-) create mode 100644 changelogs/unreleased/8594-Lyndon-Li diff --git a/changelogs/unreleased/8594-Lyndon-Li b/changelogs/unreleased/8594-Lyndon-Li new file mode 100644 index 0000000000..0241e34826 --- /dev/null +++ b/changelogs/unreleased/8594-Lyndon-Li @@ -0,0 +1 @@ +Data mover restore for Windows \ No newline at end of file diff --git a/config/crd/v2alpha1/bases/velero.io_datadownloads.yaml b/config/crd/v2alpha1/bases/velero.io_datadownloads.yaml index c322a23859..12bfec76c7 100644 --- a/config/crd/v2alpha1/bases/velero.io_datadownloads.yaml +++ b/config/crd/v2alpha1/bases/velero.io_datadownloads.yaml @@ -92,6 +92,9 @@ spec: DataMover specifies the data mover to be used by the backup. If DataMover is "" or "velero", the built-in data mover will be used. type: string + nodeOS: + description: Node is OS of the node where the DataUpload is processed. + type: string operationTimeout: description: |- OperationTimeout specifies the time used to wait internal operations, diff --git a/config/crd/v2alpha1/bases/velero.io_datauploads.yaml b/config/crd/v2alpha1/bases/velero.io_datauploads.yaml index 005b11e5ff..9caec5b3f3 100644 --- a/config/crd/v2alpha1/bases/velero.io_datauploads.yaml +++ b/config/crd/v2alpha1/bases/velero.io_datauploads.yaml @@ -144,7 +144,8 @@ spec: description: DataUploadStatus is the current status of a DataUpload. properties: acceptedByNode: - description: Node is name of the node where the DataUpload is prepared. + description: AcceptedByNode is name of the node where the DataUpload + is prepared. type: string acceptedTimestamp: description: |- @@ -175,6 +176,9 @@ spec: node: description: Node is name of the node where the DataUpload is processed. type: string + nodeOS: + description: Node is OS of the node where the DataUpload is processed. + type: string path: description: Path is the full path of the snapshot volume being backed up. diff --git a/config/crd/v2alpha1/crds/crds.go b/config/crd/v2alpha1/crds/crds.go index d67770b45a..8263171eed 100644 --- a/config/crd/v2alpha1/crds/crds.go +++ b/config/crd/v2alpha1/crds/crds.go @@ -29,8 +29,8 @@ import ( ) var rawCRDs = [][]byte{ - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcYKs\xe3\xb8\x11\xbe\xfbWtM\x0es\x19\xc9;\xc9V*\xa5\xdbXN\xaa\\ٙ\xa8֎\xef Ѣ\xb0\x06\x01\x04\x0f9\xca㿧\x1a )\x90\x84,K\xbb\x19\xdd\x044>|\xe8n\xf4\x03\\,\x167̈g\xb4Nh\xb5\x02f\x04\xfeӣ\xa2\x7fn\xf9\xf2'\xb7\x14\xfav\xff\xf9\xe6E(\xbe\x82up^\xb7?\xa3\xd3\xc1\xd6x\x8f[\xa1\x84\x17Zݴ\xe8\x19g\x9e\xadn\x00\x98R\xda3\x1av\xf4\x17\xa0\xd6\xca[-%\xdaE\x83j\xf9\x12*\xac\x82\x90\x1cm\x04\xef\xb7\xde\xff\xb0\xfc\xfc\xe3\xf2\x87\x1b\x00\xc5Z\\\x01\xe1q\xfd\xaa\xa4f\xdc-\xf7(\xd1\xea\xa5\xd07\xce`M\xc0\x8d\xd5\xc1\xac\xe08\x91\x16v\x9b&\xc2\xf7̳\xfb\x0e#\x0eK\xe1\xfc_gS?\t\xe7㴑\xc129\xd9;\xce8\xa1\x9a \x99\x1d\xcf\xdd\x00\xb8Z\x1b\\\xc17\xdaڰ\x1ai\xac;S\xa4\xb2\x00\xc6y\xd4\x12\x93\x1b+\x94G\xbb\xd62\xb4\xbdv\x16\xc0\xd1\xd5V\x18\x1f\xb5\x90\xd3\x02\xe7\x99\x0f\x0e\\\xa8w\xc0\x1c|\xc3\xd7\xdb\a\xb5\xb1\xba\xb1\xe8\x12-\x80_\x9cV\x1b\xe6w+X&\xf1\xa5\xd91\x87\xddlR\xe5c\x9c\xe8\x86\xfc\x81\xf8:o\x85jJ\f\x9eD\x8b\xc0\x83\x8d&\xa4s\xd7\b~'ܘ\xda+sD\xcfz\xe4'\x89\xc4y\x82s\x9e\xb5f\xca([\x9a(q\xe6\xb1Dh\xad[#\xd1#\x87\xea\xe0\xb1?\xc6Vۖ\xf9\x15\b\xe5\xff\xf8\xe3i]t\xcaZƥ\xf7Z\x8d\x15sG\xa3\x90\r'&d\xa5\x06mQ;\xda3\xf9k\x88x\x02\xb8\xcb\xd6'&\t7\x1f?K\x85\\\x0e\xf4\x16\xfc\x0e\xe1\x8e\xd5/\xc1\xc0\xa3ז5\b?\xe9:\x99\xefu\x87\x16\xa3D\x95$\xc8{A\x90\xed\xb4-\x9a\xce`\xbdL\xb2\x1dX\x8f5\xb1\xdfx\xa3\xdfܷj\x8b\xac\xe8[}\xa8YF\t\xa1U\xd9\xc1\xbe4\xf8.\xe7ʕ\xa84\xc7Lc#N\u0081\xb1\xbaF\xe7\xdepx\x02\x18\xb1\xf8v\x1c\x98\xa9&I\xec\x7fϤٱ\xcf)\xc8\xd4;l٪[\xa1\r\xaa/\x9b\x87\xe7?<\x8e\x86ፀ\xc1j\xef(R\x10}c\xb5\u05f5\x96P\xa1\x7fET\xc9\xf4\xadޣ\xa58\xd7\b\xe5\x06D\x8a\xda<\x178\xc6l\xf2\xef\x88G\xb3i\xd2b\xf4\x1e\"hs\xeb\x03\xedi\xd0z\xd1G\xe1\x0e\xfb\x98`\xb2\xd1\xc99\xfe\xb3\x18\xcd\x01\xd0\xd1\xd3*\xe0\x94i0\x1d\xab\x8b\xad\xc8;m%\xe3\t\a\x16\x8dE\x87*\xe5\x1e\x1af\nt\xf5\v\xd6~9\x81~DK0\xe0v:HN\x87ݣ\xf5`\xb1֍\x12\xff\x1a\xb0\x1dx\x1d7\x95̣\xf3\xf12Z\xc5$\xec\x99\f\xf8\x89\x946An\xd9\x01,Ҟ\x10T\x86\x17\x17\xb8)\x8f\xaf\xa4E\xa1\xb6z\x05;\xef\x8d[\xdd\xde6\xc2\xf7i\xb7\xd6m\x1b\x94\xf0\x87\xdbh\rQ\x05\xaf\xad\xbb\xe5\xb8Gy\xebD\xb3`\xb6\xde\t\x8f\xb5\x0f\x16o\x99\x11\x8bx\x10\x15S\xef\xb2忳]\xa2v\xa3mg\x8e\x98~1a^`\x1eʢt+X\a\x95\x8ex\xb4\x02\r\x91\xea~\xfe\xf3\xe3\x13\xf4L\x92\xa5\x92Q\x8e\xa23\xbd\xf4\xf6!m\n\xb5E\x9b\xd6m\xadn#&*n\xb4P>\xfe\xa9\xa5@\xe5\xc1\x85\xaa\x15\x9e\xdc\xe0\x1f\x01\x9d'\xd3Maױ4\x81\n!\x18\x8a\a|*\xf0\xa0`\xcdZ\x94k\xe6\xf0;ۊ\xac\xe2\x16d\x84wY+/\xb8\xa6\xc2I\xbd\xd9D_1\x9d0m\x1eA\x1e\r\xd6dUR,-\x13[\xd1e\x12\n\x03l$;\xd6P\xf9\xeaӯ\x98M\xa6B\xe7܍~w%\xa0\x9e\xad\xca\x02y\x97\xeb\\\x97\xa4\xe48I\xe5\xbfY~\xb4h\xb4\x13^\xdb\xc31KN]\xe1\xa4U\xe8W3U\xa3\xbc\xe6x\xeb\xb8\x12\x84\xe2\xa4s\x1c\\\x99\x82PB\x8dD\xb5j4]\xae\x91)\xe0\xc1\x93\f\xf9\xb6C_>\xa8*f5\xa1\xe0XSB^;N\x8f[i-\x91M\xb5H^\xf8\x95\xd2\xc2Z\xab\xadh\xe6\a\xcf\xcb\xdfS.rF\xa7\x05\x87Ͷ\xa4S\x90w\x12\x93E\xccP\x8b\xdeu)\xb4oE\x13\xec)\xfbo\x05J>\x8b?'oR\x7f\xe0\xb8\xcb56\x1e\xa8\xf7\xb7\xab\xcbjY\xea\xf5:F(\x17\xeb\xdd\xcc5\xe7$\x01\x1e\xb6\x19\xa2p\xf0\xe1\x03h\v\x1fRO\xf4\xe1SZ\x1d\x84\xf4\v1\xca\xff\xafB\xca~\x97\x8b\xbc{H\xf9Tu\xe9\xe0\xaf\xd1\xc1\xdf&\x18\x13Ux\xaa\x11\xe3\xf1\xbd\x86W&\xb2\xb4;\xec\xee>\x15p+\xdcR\x8c\xb6\xe8\x83UtC\xd0Z\nZ.B\xea0+\x03\xde<\xa9S̸\x9d\xf6\x0f\xf7g\xce\xf88\b\xf6\xa1\xe8\xe1\xbe\x0fD\xcf\xd1\x10C<\xea$\xc1\xeb\"\xfd\xbe\xb0\xe21\xd3]\xc66\xa6ס\t\xbd\xc6,\x8fc\x88\xfe0ڊF\x90\xf2\xd50s\f\x9a{\xead\xa3(\x1d\x119\x04s\x82;P\x84\xa2|^!p\xb1ݢ\xa5\xa4\x1d3z\xdax\xf3\xbc\xfe\xe8\xb2M\xc46\xffC\xc1\xb0e\xc6 \xa7\xf6\x81\x8c\xdb\xe9\xea\"-yf\x1b\xf4ϑ\xf4\x19\x15=e\xa2\xbd*(\xfbS\xafו\x97\xd1Y\xa3\x18l\x9eׅb\x90~\x9b\xe79\xc3ө\x12\xba\xbe\xe0\x84\x11g,g\xd6\xea\xf8\f\x18E\x887#-\x80ٿc\xe7\xcds)\xf1\x0e\xea\x00\xbfc\x9e$\xba>\x0e\xaaC\x11\x13\xfa+ҙ\xf3:\xbe\xf5\xbb\b\xaf\xdfd\xbc\x9eR>\xc1\xb7:\xfcjʔׅE>g\xbdx\xc3r\v0\xfb\xe2`\xfd\xfe\xecU\xdeyQ.\xd1&2\xd3\xd0?\x99>\xc6\xcb\xe9\xc48\xaeLf\xf3+\xf9\xaeZ6v\xda\xef\xadf\xd3\xfbYg\xf6:\xd8\x18t\xbaW5j\x10\xaf\xaagY]\xa3\xf1\xc8\xef\x0e\xd4ޟ\x89#$B\x04\xd4\xdb\xef\f\x7f7\xc7W\x064\xecҢ\xb3\xa74\xbc\x85\\\x93\x00\xbeLAbCly\x96\x96\xe7tS\xb5r\x9a4\xc0\x13\xb5\x12\xb1\xa1\xfb\x9821-\x8b\xf9\x9d\x8a\xb6٦3\x84\xfe}\x8d:\xb6\x05\xad\x9fI\xa8 %\xab$\xae\xc0\xdbp\xaaz-\x17\xeb\xe9i1\x7fE\xba\xaar\x9f\xc3\xccudžw\x93\xf8\xbe\xd5?j\x96Tv\xc4\x1b\x14\x96\xe0\x90\x03\xeeQ\x01\xf5cLH\xe4=f\xa1\x84=\xa7\xf9\x02i\xf7]\x95ߢs\xac9w\x81\xbe&\xa9\xf4\xd4\xd0-\x01VQ\xdd8mg>\xba\xeen_tw\xd4ov\x89\x8b\x8f\x85\x17q\x89\xed\xd7\x192\x1b\x92)Ŵ\x81\xda\xe9\xa0F?T\xa1-e\x9eo\xf8Z\x18\xed\xefgaj\xd3]\xfa\xc2\xd4\xec+E>\x99\xfa\xdcRb\xec犘\xc3g\x80\xc2\xdc_\xe2e\xb8H\xd3\x1d\xbfk\xae\xfb\xd0-\xef\xb4\xecox|\xbeW\xa1\xadВ\x19\xe2\a\x82\xde\x1eC\xdd\xcf\x14ϭV*\xfe\x06\x84\xa1\x17\x88PKx\xdaQi\x92Z\xfc\xbe;\xe2\xc2\x19\xc9\x0e\xc3a\xf2\n\xb5\x00~\xbc5\xb3\x17\xdcK\x8b\xd4\xe1sJ\xb9\xf2*}\x13\x19\xff\xe6_7&\xf3\xc3g\x92\xff\xcf\x0eo4\xf8\xe3\xcfVW\xb5R#\x84s\xa9\xa0\xfb\x8cvy\x04\x1fo\xf3=\x83wQ{\xb3\xc1Ȝg\xd8݃\\>\x12\xaa\xe1\x95z\x05\xff\xfe\xef\xcd\xff\x02\x00\x00\xff\xff\x84s\xba\x82\x91\x1e\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcYIs\xe3\xb8\x15\xbe\xfbW\xbc\xea\x1c\xe6b\xc9\xd3\xc9T*\xa5[[N\xaa\\\x99vT-\xc7w\x88|\"1\x06\x01\x06\x8b\x14g\xf9\xef\xa9\a\x80\x14HBk\xa6\x87\a\x97\x85\xe5\xe1m\xf8ނ\xd9lv\xc7Z\xfe\x86\xdap%\x17\xc0Z\x8e\xff\xb4(闙\xbf\xff\xc9̹z\xd8}\xbe{\xe7\xb2\\\xc0\xd2\x19\xab\x9aoh\x94\xd3\x05>\xe1\x96Kn\xb9\x92w\rZV2\xcb\x16w\x00LJe\x19\r\x1b\xfa\tP(i\xb5\x12\x02\xf5\xacB9\x7fw\x1b\xdc8.JԞxw\xf4\xee\xc7\xf9\xe7\x9f\xe6?\xde\x01H\xd6\xe0\x02\x88\x9ek\x85b\xa5\x99\xefP\xa0Vs\xae\xeeL\x8b\x05\x91\xad\xb4r\xed\x02\x0e\x13a[<2\xb0\xfb\xc4,\xfb\xbb\xa7\xe0\a\x057\xf6\xaf\xa3\x89\x9f\xb9\xb1~\xb2\x15N318Տ\x1b.+'\x98Ng\xee\x00L\xa1Z\\\xc0\v\x1dٲ\x02i,J\xe2Y\x98\x01+K\xaf\x1b&V\x9aK\x8bz\xa9\x84k:\x9d̠DSh\xdeZ/\xfb\x81!0\x96Yg\xc0\xb8\xa2\x06f\xe0\x05\xf7\x0f\xcfr\xa5U\xa5\xd1\x04\x96\x00~1J\xae\x98\xad\x170\x0f\xcb\xe7m\xcd\f\xc6٠\xbe\xb5\x9f\x88C\xf6\x83\xb85VsY\xe5\xce\x7f\xe5\rB\xe9\xb47\x1b\xc9\\ ؚ\x9b\x94\xb1=3Ĝ\xb6X\x1ee\xc3\xcf\x131cYӎ\xf9I\xb6\x06\x86Jf1\xc7\xceR5\xad@\x8b%l>,vBl\x95n\x98]\x00\x97\xf6\x8f?\x1d\xd7DT\xd5\xdco}Rr\xa8\x96G\x1a\x85d8pB\x16\xaaPgu\xa3,\x13\xff\x0f#\x96\b<&\xfb\x03'\x81n:~\x96\x15r7P[\xb05\xc2#+\xde]\vk\xab4\xab\x10~VE0\u07beF\x1d\x8d\xb7\tKL\xad\x9c(a\xd3I\f`\xac\xd2Y+\xb6X\xccîH\xb7#;2\xe5\xf0\xcc_\xd9\xc9\n\x8d,\xebd\x1d\xca\xcc\xfd\n\xaed\xdeӾTx\x91\x97\xa5ڔ\xaa\xc4^u\x98r\xc4\r\xb4Z\x15h\xcc\t\xbf\xa7\xed\x03\x1e^\x0e\x03\x13\xb5\x84\x15\xbb\xdf3\xd1\xd6\xecs@\x99\xa2Ɔ-\xe2\x0eբ\xfc\xb2z~\xfb\xc3z0\fG1\x83\x15\xd6\x10X\x10\xeb\xadVV\x15J\xc0\x06\xed\x1eQz܂F\xedP\x13\xc8U\\\x1a`\xb2\xeciB\xba\xe0\x00\xd5\xe4\xe4\x9e\x1e͆\xc9\xe8N\xaaE\x9d\x9a\x1d\xe8\xc8\x16\xb5\xe5\x1d\xfa\x86/\t+\xc9\xe8H\x88\xff\xcc\x06s\x00$w\xd8\x05%\xc5\x17\fREl\xc52\xaa*؍\x1b\xd0\xd8j4(Cġa&Am~\xc1\xc2\xceG\xa4ר\x89Lw\x1f\n%w\xa8-h,T%\xf9\xbfz\xda\x06\xac\xf2\x87\nf\xd1X\x7f!\xb5d\x02vL8\xbc\x1fi\x8f\xbe\x86}\x80F:\x13\x9cL\xe8\xf9\rf\xcc\xc7W\xa5\x11\xb8ܪ\x05\xd4ֶf\xf1\xf0Pq\xdb\x05\xdbB5\x8d\x93\xdc~\xe0Λ\xf2w:\x86g38v\xe2\x85\xe1\xf3\x81\xf2\n\xf3P\xfc\xa4+\xc1\"\xa9 \xe2\xc1\n4D\xaa\xfb\xf6\xe7\xf5+t\x9c\x04K\x05\xa3\x1c\x96N\xf4\xd2ه\xb4\xc9\xe5\x16uطժ\xf14Q\x96\xad\xe2\xd2\xfa\x1f\x85\xe0(-\x18\xb7i\xb8%7\xf8\x87Cc\xc9tc\xb2K\x9f\x90\xc0\x06\xc1\xb5\x04\x05\xe5x\xc1\xb3\x84%kP,\x99\xc1\xdf\xd8Vd\x153##\\d\xad4\xcd\x1a/\x0e\xeaM&\xbaL\xe9\x88i\x0f\xf0\xb1n\xb1 \x9b\x92Zi\x13\xdf\xf2\x18K\b\x03X\xb2r\xa8\x9d\xfc\xb5\xa7/\x1bBƋι\x1a}\x8f9B\x1d\xaf2\xc1\xef.\xd4\xc5\xc8$\x86\x91)\xfd\x0e \x1f\xf7hl\x95\xe1V\xe9\x0f\"\x1cB\xe3\xd8\r\x8eZ\x84\xbe\x82\xc9\x02\xc5-\xe2-\xfdN\xe0\xb2$\x8dc\xef\xc6\x04@\x81\xaagT\xc9J\xd1\xc5J\f\x01ϖV\x90W\x1b\xb4y1e&\x94q\t\x87l\x12Ҭq,\xeaF)\x81l\xac\xc1\xc2\xf0\xb5d\xad\xa9\x95=#\xf0\xf3\x16\xba\x95\xaf\x1f-\xd2\xe1\xcb\xf5\xf3=\xfd\xe9\xc6Ƀv\xbc\x8c\x10O\xb7\x8c\xf2\xaa\xbc٢\x9d\x97\xebg0q\xfb\xd4H\xd2\t\xc16\x02\x17`\xb5\x9b\nv\xdca\xe9\xeb\xc8.\x053\xd9\x05#\x01\xd7\xe9\xfa\x9cOv\x04\xa1\xf0+l\xcdr\x86\xf2\x1a\xa7\bG\xe5A\xb2\x89\xf7\x89\x10칭\xb3;O8%\xc44\x8fUx\xb1@\xc9\xf2\xac<\xf1r\x05q\xd4\xf6\x840\xab\xb7\xa5\x97\xf7\x9cd\x84\xed\xb7H\x16H\x1e\xf7ĉlo\x83\r9\xe9F\\\x1e\x13Nѕ#\xe4\xc0\x12\\{=\xeftù\xc6r\xca\xf3l`\xaf\xcc\xf4P\xe8#\xd7v\x12\x06 fx_)\x87[*\xb9\xe5\xd5\xf4\xec\xb4X=uGN\x8a6\t/ɑ\xa4q\x8a&\xc4\xc9̧\x93\xb3.\xd4P\"\xb6\xe5\x95\xd3Ǯ\xfe\x96\xa3('\xd9\xc2\xd9\xdb~F\x1f\x9e\x89[@\xbb\x97\xac\v\x96\x11\xbf\x924:x\x893\xbe\x80Mb\xcdT\x06 \x9c}\x02\xa5\xe1Shl|\xba\x0f\xbb\x1d\x17v\xc6\a\xb9\xfc\x9e\vѝrU\xb8\xea\xf3w\xaa\x9e\x94;\x87\xe3Y\x1d\xfcmDc\xa4\nK\x95\x9e\x17\xdf*\xd83\x9e\xe4\xd0\xfd\xe9\xe6>Cw\x83[J\xb84Z\xa7%\x85<Ԛr\x10\xe3I*\x97\xc1\xfc\x13\x92\x9a$\xfe\x9c\x91r\x1c\xaa\xbc\x14\xf4\xff\x18\xcbS\x00\xc8\b\x90\xb3\xf1)\x0e}~\xdcw\x91n1\xc5zH\xa2c^i^qR\xb8\xecg\x0e\x99Oĺ\xd8\"\xf0H\xe6\xa18\xeb\x9f=Z\x1aB\xcb\x039\xba\xce\xe1pB{&K\x1f\x9c\xfb\xf92^\xbd\xcc\xc5=\xab\x90\xd5\xdb\xf2\x9c\xbd\xfa\x833PN\xc3\xfb\x9a\x17\xf5\xd0t|\n\xaa\x00\x96\xbd\xa3Ot\xaf`3\x8f\xe1\xb3|\xda;Z3\xbe}\xa3\xe9\xd4e\xc7SCCggWoˋJ\x03ߵ\xb8\xac8\b\xedȨ\xe5\xc2i\xedˮ0J\xd5\xf6\r\xe5\x01+\nl-\x96\x8f\x1f/\xaa<\xe7\xf4\xb4\x84\x8e\x97\x97wk\xb0e\xd7\xe6\xf0\x1dK}G閫\xf8eL\xc4\xf7\x16t\x99\x80\xe2\x94\xdd\x00(Ǚ\x06x%'\xf6\xb5\xf1\x0f\x01\ai\x9bGW\xba\x82\x93C'\x14\xbav%\x15\xbf3\xda\x7f[$\xcd\xd7>\xa1S\x9b\xf6\xe2n*\x84\xa6d\xa6\xbac]\xc5曄]\x8b8\xa7\xb1\x03\xb9^_\x81\x1a\x96\x80;\x94@\xb5-\xe3\x82\xe2\xb3'\x99\x01\xa9\xd3Tb\xa0\n\xef\x01]ӣk\x90e\xbbO\xe7-\x99Q\xc2\x14\xb1\xbe\xa71\xfb4\xf1\x1b\x1a'2\x89\xc1wL\x13Ñ\xa1\xfc6\xd94\xf1t}\xc8\f0ЁHD\x89c\xc0t\xb1\x92\xb2\xb9c\x83ư\xea\x1cj}\r\xabB\xab,n\x01\xb6\xa1Ti\xc8\xda\x0f&\x82\xe9Up%\xbf\vn\xc6.\xf7U\x9c\xb4\xcc\xd6g8Y1[w\x01d\xeb\x84\xf0{&\x89U\xccI6H\xb7\xe9\xd7ʯ|\x83\xe3\x1c{\xb4&\x17\xe0\xf0\x12GB\xe9\x9a\\-\xf7\x82\xfb\xcch\a֙\xa9U\x8c\x00\x99\xa9\xc9\xfb_:\x19zH\xb9\x8b\xd6\xcdei\xf6Ol\x99\xb9\xbfxh\xbcJϑ\xbf[\xb0\xbf\xefF\xd5Jtp\xef\x9fƤk6\xa8\xc9\b\xfe\xf1mT\xa8S\u0099X,C8\xd9\xdfg\xb9\x9e\xd2\x1c^kn\xba\xfeYW\xa7\x94ܴ\x82}\xf4\xb2\x9c\x03\x9c\xfe2\x8f\xdfE\xa6Nr\xba\xf1\xd4?T\xe6\xfb\x18\xb9\xd7\xc6\xe17}7\x1c\xcd\xf7\x0f\x90\xdf\xe7\x84\x13h\xd9]\xef\xe7\xa7\v\v\xb0\xe7\xa7\xee*\xf2\x12\xa5\xa5\x9a\xf2\xf0\x16uH\xe5}o3\xa7\xcbqO\xf7\xba\xeac\xf0|}S56\xa0p&\x87\x89\xaf\xe9\xb9LaM`@\x10\xe4_?\x96\xe3\xf7\xce\xfb\xfe\xf9\x94\xd9\xf8\x04S\xd4LV\x98+q\x94\xa4\xc0\xe8\x03\xeb\xf5I\xc9P\xa0\xdf2\x1f\xc9z\xd5d\xd0s^&\xb4c\x13-\x1dq\x9b\xfeMl\x01\xff\xfe\xef\xdd\xff\x02\x00\x00\xff\xff*b\xfd\xb1\xf5\"\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcY_\x93\xe3\xa8\x11\x7f\x9fOѵyؗ\x95\xe76\xb9J\xa5\xfc\xb6\xe3I\xaa\xa6r\xbb\xeb:O\xe6\x1d\x89\xb6\xcc-\x02\x02\xc8\x13\xe7\xcfwO5H2\x92\xb0=\xf6ݭ\xdeD7͏\xee\xa6\xff@Q\x14ẅ\x17\xb4Nh\xb5\x04f\x04\xfeˣ\xa2?\xb7\xf8\xf6\x17\xb7\x10\xfa~\xff\xf1\xee\x9bP|\t\xab\xd6y\xdd\xfc\x8cN\xb7\xb6\xc2G\xdc\n%\xbc\xd0\xea\xaeA\xcf8\xf3ly\a\xc0\x94ҞѰ\xa3_\x80J+o\xb5\x94h\x8b\x1a\xd5\xe2[[b\xd9\n\xc9\xd1\x06\xe1\xfd\xd2\xfb\x1f\x16\x1f\x7f\\\xfcp\a\xa0X\x83K y\\\xbf*\xa9\x19w\x8b=J\xb4z!\xf4\x9d3X\x91\xe0\xda\xea\xd6,\xe1H\x88\x13\xbbE#\xe0G\xe6\xd9c'#\fK\xe1\xfc\xdfg\xa4\x9f\x84\xf3\x81ldk\x99\x9c\xac\x1d(N\xa8\xba\x95̎iw\x00\xae\xd2\x06\x97\xf0\x85\x966\xacB\x1a\xeb\xf6\x14\xa0\x14\xc08\x0fZbrm\x85\xf2hWZ\xb6M\xaf\x9d\x028\xba\xca\n\xe3\x83\x16RX\xe0<\xf3\xad\x03\xd7V;`\x0e\xbe\xe0\xeb\xfd\x93Z[][t\x11\x16\xc0/N\xab5\xf3\xbb%,\"\xfb\xc2\xec\x98Î\x1aU\xb9\t\x84n\xc8\x1f\b\xaf\xf3V\xa8:\x87\xe0Y4\b\xbc\xb5\xc1\x84\xb4\xef\n\xc1\xef\x84\x1bC{e\x8e\xe0Y\x8f\xfc$\x90@'qγ\xc6L\x11%S#$\xce<\xe6\x00\xadtc$z\xe4P\x1e<\xf6\xdb\xd8j\xdb0\xbf\x04\xa1\xfc\x9f\x7f<\xad\x8bNY\x8b0\xf5Q\xab\xb1b\x1eh\x14\x92ሄ\xacT\xa3\xcdjG{&\x7f\r\x10O\x02\x1e\x92\xf9\x11I\x94\x9b\x8e_\x84B.\az\v~\x87\xf0\xc0\xaao\xad\x81\x8dז\xd5\b?\xe9*\x9a\xefu\x87\x16\x03G\x199\xc8{A\x90\xed\xb4͚\xce`\xb5\x88\xbc\x9d\xb0^\xd6\xc4~\xe3\x85~sߪ,\xb2\xaco\xf5\xa1f\x118\x84Vy\a\xfbT㛜+U\xa2\xd2\x1c\x13\x8d\x8d0\t\a\xc6\xea\n\x9d;\xe3\xf0$`\x84\xe2\xcbq`\xa6\x9aȱ\xff#\x93f\xc7>\xc6 S\xed\xb0a\xcbn\x866\xa8>\xad\x9f^\xfe\xb4\x19\rÙ\x80\xc1*\xef(R\x10|c\xb5ו\x96P\xa2\x7fET\xd1\xf4\x8dޣ\xa58W\v\xe5\x06\x89\x14\xb5y\xcap\x8c\xd9\xe4\xdfA\x1eQ#\xd1b\xf0\x1e\x02hS\xeb\x03\xadi\xd0z\xd1G\xe1N\xf61\xc1$\xa3\x93}\xfc\xb7\x18\xd1\x00h\xebq\x16p\xca4\x18\xb7\xd5\xc5V䝶\xa2\xf1\x84\x03\x8bƢC\x15s\x0f\r3\x05\xba\xfc\x05+\xbf\x98\x88ޠ%1\xe0v\xba\x95\x9c6\xbbG\xeb\xc1b\xa5k%\xfe=\xc8v\xe0uXT2\x8f·\xc3h\x15\x93\xb0g\xb2\xc5\x0f\xa4\xb4\x89\xe4\x86\x1d\xc0\"\xad\t\xadJ\xe4\x85\tn\x8a\xe33iQ\xa8\xad^\xc2\xce{\xe3\x96\xf7\xf7\xb5\xf0}ڭtӴJ\xf8\xc3}\xb0\x86([\xaf\xad\xbb\xe7\xb8Gy\xefD]0[\xed\x84\xc7ʷ\x16\xef\x99\x11E؈\n\xa9w\xd1\xf0?\xd8.Q\xbbѲ3G\x8c_H\x98W\x98\x87\xb2(\x9d\n։\x8a[(>=\xf6\x16x\t.1DƎ\x13\xbc\xce\xc2\xefK<\x1er\xeeuhC\xa2\x1f\xda\xe1[̲\x19\x8b\xe87\xa3\xad\xa8\x05)_\r\x94\xa3K\xed\xa9\xa7\x0e\xac\xb4E\xe4К\x13\u0601b%U\x16%\x02\x17\xdb-Z*\x1fBm\x11\x17^\xbf\xac\u07bbd\x11\xb1M\x7f(,7\xcc\x18\xe4\xd4Ȑq;]]\xa5%\xcfl\x8d\xfe%\x80\xbe\xa0\xa2焵W\x05\xd5!\xd4uv\x85np\xd6\xc0\x06\xeb\x97U\xa6,\xa5o\xfd2Gx:iCס\x9c0\xe2\f\xe5\xccZ\x1d\x9eAFV\xc4٘\x0f`\xf6oXy\xfd\x92+\x01\x06u\x80\xdf1O\x1c]G\t\xe5!+\x13\xfa#ҙ\xf36\xbc՛\x00\xaf\xce\"^M!\x9f\xc0[\x1e~5d\xaa0\x84E>G]\x9c\xb1\\\x01f\x9f\x1d\xacޞG\xf3+\x17\xf9bq\xc23\r\xfd\x13\xf21^N\t\xe3\xb82\xa1\xa6G\xf2MUu\xe8\xf9\xdfZWǛ\xbc\xce\xecUkC\xd0\xe9\xee\xf7\xa8U\xbd\xa9\xb2fU\x85\xc6#\x7f8P\xca}cVV\xe7o<Ҽ\x8c\x86][\xfe\xf6\x90\x86[\x99[\x12\xc0\xa7\xa9\x90К[\x9e\xa4\xe59\xdcX7\x9d\x06\r\xf0LMMh-\xdf\xc7LL\xd3B~\xa7\xf2q\xb6\xe8LB\x7f\xd3G\xbdcA\xf3g\x1c\xaa\x95\x92\x95\x12\x97\xe0m{\xaa\x8eη\r\xf1\x923\xbdϺ\xa9\x87\x98\x8b\x99\xeb\x8e\r78\u19ad\xbf^ͩ\xec(oPX\x14\x87\x1cp\x8f\n\xa83dB\"\xefef\x8a\xe9K\x9aπv\xdfU\xf9\r:\xc7\xeaK\a\xe8s䊗\x1e\xdd\x14`%Ս\xd3\xc6\xea\xbd\xeb\xce\xf6\xd5\xc5\xf5os\x88\xb3זWa\t\x8d\xe0\x050k\xe2\xc9Ŵ\x01\xda\xe9\xa0F\x1f\xaa\xb6\xc9e\x9e/\xf8\x9a\x19\xed\xcfg\x86\xb4\xee\x0e}\x864{/I\x89\xb1\xe3\xce%ƞ\x96\x959iUi4\x81%\x80_\x8c\x92O\xcc\xd6+X\x86\xe5˶f\x06\xe3lP\xdf\xc6O\xc4!\xfbN\xdc\x1a\xab\xb9\xacr\xe7?\xf3\x06\xa1tڛ\x8dd.\x10l\xcdM\xca\xd8\x1b3Ĝ\xb6X\x1ee\xc3\xcf\x131cYӎ\xf9I\xb6\x06\x86Jf1\xc7\xceZ5\xad@\x8b%l\xdf-vB\xec\x94n\x98]\x01\x97\xf6\xcf?\x1d\xd7DT\xd5\xd2o\xbdWr\xa8\x96;\x1a\x85d8pB\x16\xaaPgu\xa3,\x13\xbf\x86\x11K\x04\xee\x92\xfd\x81\x93@7\x1d\x9fe\x85\xdc\r\xd4\x0el\x8dpNJW\xd7\xc2\xc6*\xcd*\x84\x9fU\x11\x8c\xf7V\xa3\x8e\xc6ۆ%\xa6VN\x94\xb0\xed$\x060V\xe9\xac\x15[,\x96aW\xa4ۑ\x1d\x99rx\xe6o\xecd\x85F\x96u\xb2\x0ee\x96~\x05W2\xefi\x9f+<\xcb\xcbRmJUb\xaf:L9\xe2\x06Z\xad\n4\xe6\x84\xdf\xd3\xf6\x01\x0f\x8f\x87\x81\x89Z\u008a\xfd\x1f\x99hk\xf6)\xa0LQc\xc3Vq\x87jQ~~zx\xf9\xd3f0\fG1\x83\x15\xd6\x10X\x10\xeb\xadVV\x15J\xc0\x16\xed\x1b\xa2\xf4\xb8\x05\x8dڣ&\x90\xab\xb84\xc0d\xd9ӄt\xc1\x01\xaa\xc9\xc9==\x9a\r\x93ѝT\x8b:5;Б-j\xcb;\xf4\r_\x12V\x92ё\x10\xff]\f\xe6\x00H\xee\xb0\vJ\x8a/\x18\xa4\x8a؊eTU\xb0\x1b7\xa0\xb1\xd5hP\x86\x88C\xc3L\x82\xda\xfe\x82\x85]\x8eHoP\x13\x99\xee>\x14J\xeeQ[\xd0X\xa8J\xf2\x7f\xf7\xb4\rX\xe5\x0f\x15̢\xb1\xfeBj\xc9\x04\xec\x99p\xf8q\xa4=\xfa\x1a\xf6\x0e\x1a\xe9Lp2\xa1\xe77\x981\x1f_\x94F\xe0r\xa7VP[ۚ\xd5\xedm\xc5m\x17l\v\xd54Nr\xfb~\xeb\x8d\xc1\xb7\xce*mnKܣ\xb85\xbcZ0]\xd4\xdcba\x9d\xc6[\xd6\xf2\x85\x17D\xfa\x80\xbbl\xca?\xe8\x18\x9e\xcd\xe0؉\x17\x86\xcf\a\xca\v\xccC\xf1\x93\xae\x04\x8b\xa4\x82\x88\a+\xd0\x10\xa9\xee\xdb_7\xcf\xd0q\x12,\x15\x8crX:\xd1Kg\x1f\xd2&\x97;\xd4a\xdfN\xab\xc6\xd3DY\xb6\x8aK\xeb\x7f\x14\x82\xa3\xb4`ܶ\xe1\x96\xdc\xe0\x9f\x0e\x8d%Ӎɮ}B\x02[\x04\xd7\x12\x14\x94\xe3\x05\x0f\x12֬A\xb1f\x06\x7fg[\x91Û\x8cp\x96\xb5\xd24k\xbc8\xa87\x99\xe82\xa5#\xa6=\xc0ǦłlJj\xa5M|\xc7c,!\f`\xc9ʡv\xf2מ\xbel\b\x19/\x9as5\xfa\xeer\x84:^e\x82\xdf]\xa8\x8b\x91I\f#S\xfa\x1d@>\xee\xd1\xd8*í\xd2\xefD8\x84Ʊ\x1b\x1c\xb5\b}\x05\x93\x05\x8ak\xc4[\xfb\x9d\xc0eI\x1a\xc7ލ\t\x80\x02UϨ\x92\x95\xa2\x8b\x95\x18\x02\x1e,\xad \xaf6h\xf3b\xcaL(\xe3\x12\x0e\xd9$\xa4Y\xe3XԭR\x02\xd9X\x83\x85\xe1\x1b\xc9ZS+;#\xf0\xc3\x0e\xba\x95\xcf\xef-\xd2\xe1\xeb\xcd\xc3G\xfaӍ\x93\a\xedy\x19!\x9en\x19\xe5Uy\xb3E;\xaf7\x0f`\xe2\xf6\xa9\x91\xa4\x13\x82m\x05\xae\xc0j7\x15\xec\xb8\xc3\xd2ב]\vf\xb2\vF\x02n\xd2\xf59\x9f\xec\bB\xe1Wؚ\xe5\f\xe55N\x11\x8eʃd\x13\xef\x13!x\xe3\xb6\xce\xee<\xe1\x94\x10\xd3|\x00\xa5\xe1Chl|\xf8\x18v;.\xec\x82\x0fr\xf97.Dw\xcaE\xe1\xaa\xcfߩzRn\x0edz:\xf8:\xa21R\x85\xa5Jϋo\x15\xbc1\x9e\xe4\xd0\xfd\xe9\xe6c\x86\xee\x16w\x94pi\xb4NK\ny\xa85\xe5 ƓT.\x83\xf9'$5I\xfc\x99\x91r\x1c\xaa\xbc\x14\xf4\xff\x18\xcbS\x00\xc8\b\x90\xb3\xf1)\x0e}~\xdcw\x91\xae1\xc5fH\xa2c^i^qR\xb8\xecg\x0e\x99Oĺ\xd8\"\xf0H\xe6\xa18\xeb\x9f=Z\x1aB\xcb\x039\xba\xce\xe1pB{&K\x1f\x9c\xfb\xf92^\xbd\xccŝU\xc8\xd3\xcbz\xce^\xfd\xc1\x19(\xa7ᷚ\x17\xf5\xd0t|\n\xaa\x00\x96\xbd\xa2Ot/`3\x8f\xe1\x8b|\xda;Z3\xbe}\xa3\xe9\xd4e\xc7SCCgg\x9f^\xd6g\x95\x06\xbekq^q\x10ڑQ˅\xd3ڗ]a\x94\xaa\xed+\xca\x03V\x14\xd8Z,\xef\xde\x1fU9\xe7\xf4\x9f\a\x8b\x89\x11yN\xdf&cj\xdf\xc9\xc1\x96]\x9a\xdfw\xec\xf6ݦk\xae\xe9\xe71\x11\xdfw\xd0e\x02\x98\xd3l=\x80\xcdq\xa6\x01\x9e\xc9\xc1}\xdd\xfcC\xc0H\xda摗\xae\xe7\xe4\xd0\t\x85\xae\x95I\x85\xf1\x82\xf6_\x17e\xf3uQ\xe8\xe2\xa6}\xba\xab\x8a\xa4)\x99\xa9\xeeXW\xcd\xf9\x06b\xd7>\xcei\xec@\xae\xd7W\xa0\x86%\xe0\x1e%P\xdd˸\xa0\xd8\xedIf\x00\xec4\x95\x18\xc4\xc2[A\xd7\x10\xe9\x9ag\xd9\xceԼ%3J\x98\xa2\xd9\xf74f\x9fB~C\xe3D&i\xf8\x8e)d82\x94\xe6&\x9bB\x9e\xae\x1d\x99\x01\x06:\x10\x89\xb8q\f\xb4\xceVR6\xafl\xd0\x18V\xcd!ڗ\xb0*\xb4\xd1\xe2\x16`[J\xa3\x86\xac\xfd`\"\xd0^\x04Wr\x1eS/B\xd2A\a\xfcbN\xben\xce\xe4\xe5\xeb\xe6;r\xd22[\xcf\xf0\xf1\xc4l݅\xb9\x9d\x13\xc2\uf664\x7f1s\xda\"\xdd\xeb\xdf*\v\xf4m\x989\xf6hM.\f\xe39.\x8d\xd25\xb9\x8a\xf3\x11\xdf2\xa3]\xd8\xc8L=\xc5X\x94\x99\x9a\xbcR\xa6\x93\xa1ӕ\xbb\xf2\xdd\\\x96f\xff\x10\x98\x99\xfb\x9b\a\xe9\x8b\xf4\x1c\xf9\xbb&\n\xf5=\xb3Z\x89.\xf0\xf8\a<\xe9\x9a-j2\x82\x7f\"\x1c\xb5\x13(-N,\x96!\x9c\xec\xefsqOi\t\xcf57]\x97\xaf\xab\xa6JnZ\xc1\xde{Y栯\x87\x95\xf1\xeb\xcd\xd4IN\xb7\xc7\xfa\xe7\xd4|\xb7%\xf7&:\xfc\xa6\xaf\x9b\xa3\xf9\xfe\x99\xf4\xfb\x9cp\x02\xb7\xbb\xeb\xfdp\x7ff\x99\xf8p\xdf]E^\xa2\xb4T\xf9\x1e^\xcc\x0e\x05\x87\xef\xc0\xe6t9\xee<_V#\r\x1eٯ\xaa\x19\a\x14f\xb2\xa9\xf8\xe6\x9f\xcbY6\x04\x06\x04A\xfe\x8df=~\x95\xfd\xd8?\xf22\x1b\x1f\x8a\x8a\x9a\xc9\ns\x85\x98\x92\x14\xa2}\x88\xbf<=\x1a\n\xf4{fFY\xaf\x9a\fz\xce˄vl\xf5\xa5#nۿܭ\xe0?\xff\xbb\xf9\x7f\x00\x00\x00\xff\xff\xff\x99\xc7\x13\x9b#\x00\x00"), } var CRDs = crds() diff --git a/pkg/apis/velero/v2alpha1/data_download_types.go b/pkg/apis/velero/v2alpha1/data_download_types.go index 3a700661a0..625dbc4237 100644 --- a/pkg/apis/velero/v2alpha1/data_download_types.go +++ b/pkg/apis/velero/v2alpha1/data_download_types.go @@ -54,6 +54,10 @@ type DataDownloadSpec struct { // OperationTimeout specifies the time used to wait internal operations, // before returning error as timeout. OperationTimeout metav1.Duration `json:"operationTimeout"` + + // NodeOS is OS of the node where the DataUpload is processed. + // +optional + NodeOS NodeOS `json:"nodeOS,omitempty"` } // TargetVolumeSpec is the specification for a target PVC. diff --git a/pkg/apis/velero/v2alpha1/data_upload_types.go b/pkg/apis/velero/v2alpha1/data_upload_types.go index 546caa05e9..558b20f574 100644 --- a/pkg/apis/velero/v2alpha1/data_upload_types.go +++ b/pkg/apis/velero/v2alpha1/data_upload_types.go @@ -96,6 +96,14 @@ const ( DataUploadPhaseFailed DataUploadPhase = "Failed" ) +type NodeOS string + +const ( + NodeOSLinux NodeOS = "linux" + NodeOSWindows NodeOS = "windows" + NodeOSAuto NodeOS = "auto" +) + // DataUploadStatus is the current status of a DataUpload. type DataUploadStatus struct { // Phase is the current state of the DataUpload. @@ -144,7 +152,12 @@ type DataUploadStatus struct { // Node is name of the node where the DataUpload is processed. // +optional Node string `json:"node,omitempty"` - // Node is name of the node where the DataUpload is prepared. + + // NodeOS is OS of the node where the DataUpload is processed. + // +optional + NodeOS NodeOS `json:"nodeOS,omitempty"` + + // AcceptedByNode is name of the node where the DataUpload is prepared. // +optional AcceptedByNode string `json:"acceptedByNode,omitempty"` @@ -221,4 +234,8 @@ type DataUploadResult struct { // +optional // +nullable DataMoverResult *map[string]string `json:"dataMoverResult,omitempty"` + + // NodeOS is OS of the node where the DataUpload is processed. + // +optional + NodeOS NodeOS `json:"nodeOS,omitempty"` } diff --git a/pkg/builder/data_download_builder.go b/pkg/builder/data_download_builder.go index 9364022bd5..5b23a9dcc4 100644 --- a/pkg/builder/data_download_builder.go +++ b/pkg/builder/data_download_builder.go @@ -148,6 +148,12 @@ func (d *DataDownloadBuilder) Node(node string) *DataDownloadBuilder { return d } +// NodeOS sets the DataDownload's Node OS. +func (d *DataDownloadBuilder) NodeOS(nodeOS velerov2alpha1api.NodeOS) *DataDownloadBuilder { + d.object.Spec.NodeOS = nodeOS + return d +} + // AcceptedByNode sets the DataDownload's AcceptedByNode. func (d *DataDownloadBuilder) AcceptedByNode(node string) *DataDownloadBuilder { d.object.Status.AcceptedByNode = node diff --git a/pkg/builder/data_upload_builder.go b/pkg/builder/data_upload_builder.go index b4fa72e438..b77566bf66 100644 --- a/pkg/builder/data_upload_builder.go +++ b/pkg/builder/data_upload_builder.go @@ -151,6 +151,12 @@ func (d *DataUploadBuilder) Node(node string) *DataUploadBuilder { return d } +// NodeOS sets the DataUpload's Node OS. +func (d *DataUploadBuilder) NodeOS(nodeOS velerov2alpha1api.NodeOS) *DataUploadBuilder { + d.object.Status.NodeOS = nodeOS + return d +} + // AcceptedByNode sets the DataUpload's AcceptedByNode. func (d *DataUploadBuilder) AcceptedByNode(node string) *DataUploadBuilder { d.object.Status.AcceptedByNode = node diff --git a/pkg/cmd/cli/datamover/restore.go b/pkg/cmd/cli/datamover/restore.go index 244060cc9a..4730cf9035 100644 --- a/pkg/cmd/cli/datamover/restore.go +++ b/pkg/cmd/cli/datamover/restore.go @@ -160,7 +160,24 @@ func newdataMoverRestore(logger logrus.FieldLogger, factory client.Factory, conf return nil, errors.Wrap(err, "error to create client") } - cache, err := ctlcache.New(clientConfig, cacheOption) + var cache ctlcache.Cache + retry := 10 + for { + cache, err = ctlcache.New(clientConfig, cacheOption) + if err == nil { + break + } + + retry-- + if retry == 0 { + break + } + + logger.WithError(err).Warn("Failed to create client cache, need retry") + + time.Sleep(time.Second) + } + if err != nil { cancelFunc() return nil, errors.Wrap(err, "error to create client cache") diff --git a/pkg/controller/data_download_controller.go b/pkg/controller/data_download_controller.go index bbd45b97e8..eecdd7617b 100644 --- a/pkg/controller/data_download_controller.go +++ b/pkg/controller/data_download_controller.go @@ -183,28 +183,15 @@ func (r *DataDownloadReconciler) Reconcile(ctx context.Context, req ctrl.Request return ctrl.Result{}, nil } - hostingPodLabels := map[string]string{velerov1api.DataDownloadLabel: dd.Name} - for _, k := range util.ThirdPartyLabels { - if v, err := nodeagent.GetLabelValue(ctx, r.kubeClient, dd.Namespace, k, kube.NodeOSLinux); err != nil { - if err != nodeagent.ErrNodeAgentLabelNotFound { - log.WithError(err).Warnf("Failed to check node-agent label, skip adding host pod label %s", k) - } - } else { - hostingPodLabels[k] = v - } + exposeParam, err := r.setupExposeParam(dd) + if err != nil { + return r.errorOut(ctx, dd, err, "failed to set exposer parameters", log) } // Expose() will trigger to create one pod whose volume is restored by a given volume snapshot, // but the pod maybe is not in the same node of the current controller, so we need to return it here. // And then only the controller who is in the same node could do the rest work. - err = r.restoreExposer.Expose(ctx, getDataDownloadOwnerObject(dd), exposer.GenericRestoreExposeParam{ - TargetPVCName: dd.Spec.TargetVolume.PVC, - SourceNamespace: dd.Spec.TargetVolume.Namespace, - HostingPodLabels: hostingPodLabels, - Resources: r.podResources, - ExposeTimeout: dd.Spec.OperationTimeout.Duration, - RestorePVCConfig: r.restorePVCConfig, - }) + err = r.restoreExposer.Expose(ctx, getDataDownloadOwnerObject(dd), exposeParam) if err != nil { if err := r.client.Get(ctx, req.NamespacedName, dd); err != nil { if !apierrors.IsNotFound(err) { @@ -243,7 +230,7 @@ func (r *DataDownloadReconciler) Reconcile(ctx context.Context, req ctrl.Request log.Debugf("Data download is been canceled %s in Phase %s", dd.GetName(), dd.Status.Phase) r.tryCancelAcceptedDataDownload(ctx, dd, "") } else if peekErr := r.restoreExposer.PeekExposed(ctx, getDataDownloadOwnerObject(dd)); peekErr != nil { - r.tryCancelAcceptedDataDownload(ctx, dd, fmt.Sprintf("found a dataupload %s/%s with expose error: %s. mark it as cancel", dd.Namespace, dd.Name, peekErr)) + r.tryCancelAcceptedDataDownload(ctx, dd, fmt.Sprintf("found a datadownload %s/%s with expose error: %s. mark it as cancel", dd.Namespace, dd.Name, peekErr)) log.Errorf("Cancel dd %s/%s because of expose error %s", dd.Namespace, dd.Name, peekErr) } else if dd.Status.AcceptedTimestamp != nil { if time.Since(dd.Status.AcceptedTimestamp.Time) >= r.preparingTimeout { @@ -737,6 +724,40 @@ func (r *DataDownloadReconciler) closeDataPath(ctx context.Context, ddName strin r.dataPathMgr.RemoveAsyncBR(ddName) } +func (r *DataDownloadReconciler) setupExposeParam(dd *velerov2alpha1api.DataDownload) (exposer.GenericRestoreExposeParam, error) { + nodeOS := string(dd.Spec.NodeOS) + if nodeOS == "" { + r.logger.Info("nodeOS is empty in DD, fallback to linux") + nodeOS = kube.NodeOSLinux + } + + if err := kube.HasNodeWithOS(context.Background(), string(dd.Spec.NodeOS), r.kubeClient.CoreV1()); err != nil { + return exposer.GenericRestoreExposeParam{}, errors.Wrapf(err, "no appropriate node to run datadownload %s/%s", dd.Namespace, dd.Name) + } + + hostingPodLabels := map[string]string{velerov1api.DataDownloadLabel: dd.Name} + for _, k := range util.ThirdPartyLabels { + if v, err := nodeagent.GetLabelValue(context.Background(), r.kubeClient, dd.Namespace, k, nodeOS); err != nil { + if err != nodeagent.ErrNodeAgentLabelNotFound { + r.logger.WithError(err).Warnf("Failed to check node-agent label, skip adding host pod label %s", k) + } + } else { + hostingPodLabels[k] = v + } + } + + return exposer.GenericRestoreExposeParam{ + TargetPVCName: dd.Spec.TargetVolume.PVC, + TargetNamespace: dd.Spec.TargetVolume.Namespace, + HostingPodLabels: hostingPodLabels, + Resources: r.podResources, + OperationTimeout: dd.Spec.OperationTimeout.Duration, + ExposeTimeout: r.preparingTimeout, + NodeOS: nodeOS, + RestorePVCConfig: r.restorePVCConfig, + }, nil +} + func getDataDownloadOwnerObject(dd *velerov2alpha1api.DataDownload) v1.ObjectReference { return v1.ObjectReference{ Kind: dd.Kind, diff --git a/pkg/controller/data_download_controller_test.go b/pkg/controller/data_download_controller_test.go index d3d9488958..909ac134f9 100644 --- a/pkg/controller/data_download_controller_test.go +++ b/pkg/controller/data_download_controller_test.go @@ -67,7 +67,7 @@ func dataDownloadBuilder() *builder.DataDownloadBuilder { PV: "test-pv", PVC: "test-pvc", Namespace: "test-ns", - }) + }).NodeOS(velerov2alpha1api.NodeOS("test-node-os")) } func initDataDownloadReconciler(objects []runtime.Object, needError ...bool) (*DataDownloadReconciler, error) { diff --git a/pkg/controller/data_upload_controller.go b/pkg/controller/data_upload_controller.go index 66f5b67f76..c1569a8210 100644 --- a/pkg/controller/data_upload_controller.go +++ b/pkg/controller/data_upload_controller.go @@ -285,6 +285,17 @@ func (r *DataUploadReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, nil } + var nodeOS velerov2alpha1api.NodeOS + if res.ByPod.NodeOS == nil { + nodeOS = velerov2alpha1api.NodeOSAuto + } else if *res.ByPod.NodeOS == "linux" { + nodeOS = velerov2alpha1api.NodeOSLinux + } else if *res.ByPod.NodeOS == "windows" { + nodeOS = velerov2alpha1api.NodeOSWindows + } else { + return r.errorOut(ctx, du, errors.Errorf("invalid node OS %s", *res.ByPod.NodeOS), "invalid expose result", log) + } + log.Info("Exposed snapshot is ready and creating data path routine") // Need to first create file system BR and get data path instance then update data upload status @@ -317,6 +328,7 @@ func (r *DataUploadReconciler) Reconcile(ctx context.Context, req ctrl.Request) original := du.DeepCopy() du.Status.Phase = velerov2alpha1api.DataUploadPhaseInProgress du.Status.StartTimestamp = &metav1.Time{Time: r.Clock.Now()} + du.Status.NodeOS = nodeOS if err := r.client.Patch(ctx, du, client.MergeFrom(original)); err != nil { log.WithError(err).Warnf("Failed to update dataupload %s to InProgress, will data path close and retry", du.Name) diff --git a/pkg/exposer/csi_snapshot.go b/pkg/exposer/csi_snapshot.go index 043462792d..b4543ee530 100644 --- a/pkg/exposer/csi_snapshot.go +++ b/pkg/exposer/csi_snapshot.go @@ -281,10 +281,16 @@ func (e *csiSnapshotExposer) GetExposed(ctx context.Context, ownerObject corev1. curLog.WithField("pod", pod.Name).Infof("Backup volume is found in pod at index %v", i) + var nodeOS *string + if os, found := pod.Spec.NodeSelector[kube.NodeOSLabel]; found { + nodeOS = &os + } + return &ExposeResult{ByPod: ExposeByPod{ HostingPod: pod, HostingContainer: containerName, VolumeName: volumeName, + NodeOS: nodeOS, }}, nil } diff --git a/pkg/exposer/generic_restore.go b/pkg/exposer/generic_restore.go index f2c540d6ed..b332c611a3 100644 --- a/pkg/exposer/generic_restore.go +++ b/pkg/exposer/generic_restore.go @@ -40,8 +40,8 @@ type GenericRestoreExposeParam struct { // TargetPVCName is the target volume name to be restored TargetPVCName string - // SourceNamespace is the original namespace of the volume that the snapshot is taken for - SourceNamespace string + // TargetNamespace is the namespace of the volume to be restored + TargetNamespace string // HostingPodLabels is the labels that are going to apply to the hosting pod HostingPodLabels map[string]string @@ -52,6 +52,12 @@ type GenericRestoreExposeParam struct { // ExposeTimeout specifies the timeout for the entire expose process ExposeTimeout time.Duration + // OperationTimeout specifies the time wait for resources operations in Expose + OperationTimeout time.Duration + + // NodeOS specifies the OS of node that the volume should be attached + NodeOS string + // RestorePVCConfig is the config for restorePVC (intermediate PVC) of generic restore RestorePVCConfig nodeagent.RestorePVC } @@ -99,21 +105,21 @@ func (e *genericRestoreExposer) Expose(ctx context.Context, ownerObject corev1.O curLog := e.log.WithFields(logrus.Fields{ "owner": ownerObject.Name, "target PVC": param.TargetPVCName, - "source namespace": param.SourceNamespace, + "target namespace": param.TargetNamespace, }) - selectedNode, targetPVC, err := kube.WaitPVCConsumed(ctx, e.kubeClient.CoreV1(), param.TargetPVCName, param.SourceNamespace, e.kubeClient.StorageV1(), param.ExposeTimeout, param.RestorePVCConfig.IgnoreDelayBinding) + selectedNode, targetPVC, err := kube.WaitPVCConsumed(ctx, e.kubeClient.CoreV1(), param.TargetPVCName, param.TargetNamespace, e.kubeClient.StorageV1(), param.ExposeTimeout, param.RestorePVCConfig.IgnoreDelayBinding) if err != nil { - return errors.Wrapf(err, "error to wait target PVC consumed, %s/%s", param.SourceNamespace, param.TargetPVCName) + return errors.Wrapf(err, "error to wait target PVC consumed, %s/%s", param.TargetNamespace, param.TargetPVCName) } curLog.WithField("target PVC", param.TargetPVCName).WithField("selected node", selectedNode).Info("Target PVC is consumed") if kube.IsPVCBound(targetPVC) { - return errors.Errorf("Target PVC %s/%s has already been bound, abort", param.SourceNamespace, param.TargetPVCName) + return errors.Errorf("Target PVC %s/%s has already been bound, abort", param.TargetNamespace, param.TargetPVCName) } - restorePod, err := e.createRestorePod(ctx, ownerObject, targetPVC, param.ExposeTimeout, param.HostingPodLabels, selectedNode, param.Resources) + restorePod, err := e.createRestorePod(ctx, ownerObject, targetPVC, param.OperationTimeout, param.HostingPodLabels, selectedNode, param.Resources, param.NodeOS) if err != nil { return errors.Wrapf(err, "error to create restore pod") } @@ -274,19 +280,19 @@ func (e *genericRestoreExposer) CleanUp(ctx context.Context, ownerObject corev1. kube.DeletePVAndPVCIfAny(ctx, e.kubeClient.CoreV1(), restorePVCName, ownerObject.Namespace, 0, e.log) } -func (e *genericRestoreExposer) RebindVolume(ctx context.Context, ownerObject corev1.ObjectReference, targetPVCName string, sourceNamespace string, timeout time.Duration) error { +func (e *genericRestoreExposer) RebindVolume(ctx context.Context, ownerObject corev1.ObjectReference, targetPVCName string, targetNamespace string, timeout time.Duration) error { restorePodName := ownerObject.Name restorePVCName := ownerObject.Name curLog := e.log.WithFields(logrus.Fields{ "owner": ownerObject.Name, "target PVC": targetPVCName, - "source namespace": sourceNamespace, + "target namespace": targetNamespace, }) - targetPVC, err := e.kubeClient.CoreV1().PersistentVolumeClaims(sourceNamespace).Get(ctx, targetPVCName, metav1.GetOptions{}) + targetPVC, err := e.kubeClient.CoreV1().PersistentVolumeClaims(targetNamespace).Get(ctx, targetPVCName, metav1.GetOptions{}) if err != nil { - return errors.Wrapf(err, "error to get target PVC %s/%s", sourceNamespace, targetPVCName) + return errors.Wrapf(err, "error to get target PVC %s/%s", targetNamespace, targetPVCName) } restorePV, err := kube.WaitPVCBound(ctx, e.kubeClient.CoreV1(), e.kubeClient.CoreV1(), restorePVCName, ownerObject.Namespace, timeout) @@ -368,7 +374,7 @@ func (e *genericRestoreExposer) RebindVolume(ctx context.Context, ownerObject co } func (e *genericRestoreExposer) createRestorePod(ctx context.Context, ownerObject corev1.ObjectReference, targetPVC *corev1.PersistentVolumeClaim, - operationTimeout time.Duration, label map[string]string, selectedNode string, resources corev1.ResourceRequirements) (*corev1.Pod, error) { + operationTimeout time.Duration, label map[string]string, selectedNode string, resources corev1.ResourceRequirements, nodeType string) (*corev1.Pod, error) { restorePodName := ownerObject.Name restorePVCName := ownerObject.Name @@ -409,7 +415,28 @@ func (e *genericRestoreExposer) createRestorePod(ctx context.Context, ownerObjec args = append(args, podInfo.logFormatArgs...) args = append(args, podInfo.logLevelArgs...) - userID := int64(0) + var securityCtx *corev1.PodSecurityContext + nodeSelector := map[string]string{} + podOS := corev1.PodOS{} + if nodeType == kube.NodeOSWindows { + userID := "ContainerAdministrator" + securityCtx = &corev1.PodSecurityContext{ + WindowsOptions: &corev1.WindowsSecurityContextOptions{ + RunAsUserName: &userID, + }, + } + + nodeSelector[kube.NodeOSLabel] = kube.NodeOSWindows + podOS.Name = kube.NodeOSWindows + } else { + userID := int64(0) + securityCtx = &corev1.PodSecurityContext{ + RunAsUser: &userID, + } + + nodeSelector[kube.NodeOSLabel] = kube.NodeOSLinux + podOS.Name = kube.NodeOSLinux + } pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -427,6 +454,8 @@ func (e *genericRestoreExposer) createRestorePod(ctx context.Context, ownerObjec Labels: label, }, Spec: corev1.PodSpec{ + NodeSelector: nodeSelector, + OS: &podOS, Containers: []corev1.Container{ { Name: containerName, @@ -450,9 +479,7 @@ func (e *genericRestoreExposer) createRestorePod(ctx context.Context, ownerObjec Volumes: volumes, NodeName: selectedNode, RestartPolicy: corev1.RestartPolicyNever, - SecurityContext: &corev1.PodSecurityContext{ - RunAsUser: &userID, - }, + SecurityContext: securityCtx, }, } diff --git a/pkg/exposer/generic_restore_test.go b/pkg/exposer/generic_restore_test.go index 338d58b52b..15f8c1615b 100644 --- a/pkg/exposer/generic_restore_test.go +++ b/pkg/exposer/generic_restore_test.go @@ -31,7 +31,6 @@ import ( velerotest "github.com/vmware-tanzu/velero/pkg/test" appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" corev1api "k8s.io/api/core/v1" clientTesting "k8s.io/client-go/testing" ) @@ -76,9 +75,9 @@ func TestRestoreExpose(t *testing.T) { APIVersion: appsv1.SchemeGroupVersion.String(), }, Spec: appsv1.DaemonSetSpec{ - Template: corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ + Template: corev1api.PodTemplateSpec{ + Spec: corev1api.PodSpec{ + Containers: []corev1api.Container{ { Image: "fake-image", }, @@ -93,21 +92,21 @@ func TestRestoreExpose(t *testing.T) { kubeClientObj []runtime.Object ownerRestore *velerov1.Restore targetPVCName string - sourceNamespace string + targetNamespace string kubeReactors []reactor err string }{ { name: "wait target pvc consumed fail", targetPVCName: "fake-target-pvc", - sourceNamespace: "fake-ns", + targetNamespace: "fake-ns", ownerRestore: restore, err: "error to wait target PVC consumed, fake-ns/fake-target-pvc: error to wait for PVC: error to get pvc fake-ns/fake-target-pvc: persistentvolumeclaims \"fake-target-pvc\" not found", }, { name: "target pvc is already bound", targetPVCName: "fake-target-pvc", - sourceNamespace: "fake-ns", + targetNamespace: "fake-ns", ownerRestore: restore, kubeClientObj: []runtime.Object{ targetPVCObjBound, @@ -117,7 +116,7 @@ func TestRestoreExpose(t *testing.T) { { name: "create restore pod fail", targetPVCName: "fake-target-pvc", - sourceNamespace: "fake-ns", + targetNamespace: "fake-ns", ownerRestore: restore, kubeClientObj: []runtime.Object{ targetPVCObj, @@ -137,7 +136,7 @@ func TestRestoreExpose(t *testing.T) { { name: "create restore pvc fail", targetPVCName: "fake-target-pvc", - sourceNamespace: "fake-ns", + targetNamespace: "fake-ns", ownerRestore: restore, kubeClientObj: []runtime.Object{ targetPVCObj, @@ -182,9 +181,9 @@ func TestRestoreExpose(t *testing.T) { err := exposer.Expose(context.Background(), ownerObject, GenericRestoreExposeParam{ TargetPVCName: test.targetPVCName, - SourceNamespace: test.sourceNamespace, + TargetNamespace: test.targetNamespace, HostingPodLabels: map[string]string{}, - Resources: corev1.ResourceRequirements{}, + Resources: corev1api.ResourceRequirements{}, ExposeTimeout: time.Millisecond}) assert.EqualError(t, err, test.err) }) @@ -244,21 +243,21 @@ func TestRebindVolume(t *testing.T) { kubeClientObj []runtime.Object ownerRestore *velerov1.Restore targetPVCName string - sourceNamespace string + targetNamespace string kubeReactors []reactor err string }{ { name: "get target pvc fail", targetPVCName: "fake-target-pvc", - sourceNamespace: "fake-ns", + targetNamespace: "fake-ns", ownerRestore: restore, err: "error to get target PVC fake-ns/fake-target-pvc: persistentvolumeclaims \"fake-target-pvc\" not found", }, { name: "wait restore pvc bound fail", targetPVCName: "fake-target-pvc", - sourceNamespace: "fake-ns", + targetNamespace: "fake-ns", ownerRestore: restore, kubeClientObj: []runtime.Object{ targetPVCObj, @@ -268,7 +267,7 @@ func TestRebindVolume(t *testing.T) { { name: "retain target pv fail", targetPVCName: "fake-target-pvc", - sourceNamespace: "fake-ns", + targetNamespace: "fake-ns", ownerRestore: restore, kubeClientObj: []runtime.Object{ targetPVCObj, @@ -289,7 +288,7 @@ func TestRebindVolume(t *testing.T) { { name: "delete restore pod fail", targetPVCName: "fake-target-pvc", - sourceNamespace: "fake-ns", + targetNamespace: "fake-ns", ownerRestore: restore, kubeClientObj: []runtime.Object{ targetPVCObj, @@ -311,7 +310,7 @@ func TestRebindVolume(t *testing.T) { { name: "delete restore pvc fail", targetPVCName: "fake-target-pvc", - sourceNamespace: "fake-ns", + targetNamespace: "fake-ns", ownerRestore: restore, kubeClientObj: []runtime.Object{ targetPVCObj, @@ -333,7 +332,7 @@ func TestRebindVolume(t *testing.T) { { name: "rebind target pvc fail", targetPVCName: "fake-target-pvc", - sourceNamespace: "fake-ns", + targetNamespace: "fake-ns", ownerRestore: restore, kubeClientObj: []runtime.Object{ targetPVCObj, @@ -355,7 +354,7 @@ func TestRebindVolume(t *testing.T) { { name: "reset pv binding fail", targetPVCName: "fake-target-pvc", - sourceNamespace: "fake-ns", + targetNamespace: "fake-ns", ownerRestore: restore, kubeClientObj: []runtime.Object{ targetPVCObj, @@ -382,7 +381,7 @@ func TestRebindVolume(t *testing.T) { { name: "wait restore PV bound fail", targetPVCName: "fake-target-pvc", - sourceNamespace: "fake-ns", + targetNamespace: "fake-ns", ownerRestore: restore, kubeClientObj: []runtime.Object{ targetPVCObj, @@ -420,7 +419,7 @@ func TestRebindVolume(t *testing.T) { hookCount = 0 - err := exposer.RebindVolume(context.Background(), ownerObject, test.targetPVCName, test.sourceNamespace, time.Millisecond) + err := exposer.RebindVolume(context.Background(), ownerObject, test.targetPVCName, test.targetNamespace, time.Millisecond) assert.EqualError(t, err, test.err) }) } @@ -526,7 +525,7 @@ func Test_ReastoreDiagnoseExpose(t *testing.T) { }, } - restorePodWithoutNodeName := corev1.Pod{ + restorePodWithoutNodeName := corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1.DefaultNamespace, Name: "fake-restore", @@ -539,19 +538,19 @@ func Test_ReastoreDiagnoseExpose(t *testing.T) { }, }, }, - Status: corev1.PodStatus{ - Phase: corev1.PodPending, - Conditions: []corev1.PodCondition{ + Status: corev1api.PodStatus{ + Phase: corev1api.PodPending, + Conditions: []corev1api.PodCondition{ { - Type: corev1.PodInitialized, - Status: corev1.ConditionTrue, + Type: corev1api.PodInitialized, + Status: corev1api.ConditionTrue, Message: "fake-pod-message", }, }, }, } - restorePodWithNodeName := corev1.Pod{ + restorePodWithNodeName := corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1.DefaultNamespace, Name: "fake-restore", @@ -564,22 +563,22 @@ func Test_ReastoreDiagnoseExpose(t *testing.T) { }, }, }, - Spec: corev1.PodSpec{ + Spec: corev1api.PodSpec{ NodeName: "fake-node", }, - Status: corev1.PodStatus{ - Phase: corev1.PodPending, - Conditions: []corev1.PodCondition{ + Status: corev1api.PodStatus{ + Phase: corev1api.PodPending, + Conditions: []corev1api.PodCondition{ { - Type: corev1.PodInitialized, - Status: corev1.ConditionTrue, + Type: corev1api.PodInitialized, + Status: corev1api.ConditionTrue, Message: "fake-pod-message", }, }, }, } - restorePVCWithoutVolumeName := corev1.PersistentVolumeClaim{ + restorePVCWithoutVolumeName := corev1api.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1.DefaultNamespace, Name: "fake-restore", @@ -592,12 +591,12 @@ func Test_ReastoreDiagnoseExpose(t *testing.T) { }, }, }, - Status: corev1.PersistentVolumeClaimStatus{ - Phase: corev1.ClaimPending, + Status: corev1api.PersistentVolumeClaimStatus{ + Phase: corev1api.ClaimPending, }, } - restorePVCWithVolumeName := corev1.PersistentVolumeClaim{ + restorePVCWithVolumeName := corev1api.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1.DefaultNamespace, Name: "fake-restore", @@ -610,35 +609,35 @@ func Test_ReastoreDiagnoseExpose(t *testing.T) { }, }, }, - Spec: corev1.PersistentVolumeClaimSpec{ + Spec: corev1api.PersistentVolumeClaimSpec{ VolumeName: "fake-pv", }, - Status: corev1.PersistentVolumeClaimStatus{ - Phase: corev1.ClaimPending, + Status: corev1api.PersistentVolumeClaimStatus{ + Phase: corev1api.ClaimPending, }, } - restorePV := corev1.PersistentVolume{ + restorePV := corev1api.PersistentVolume{ ObjectMeta: metav1.ObjectMeta{ Name: "fake-pv", }, - Status: corev1.PersistentVolumeStatus{ - Phase: corev1.VolumePending, + Status: corev1api.PersistentVolumeStatus{ + Phase: corev1api.VolumePending, Message: "fake-pv-message", }, } - nodeAgentPod := corev1.Pod{ + nodeAgentPod := corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1.DefaultNamespace, Name: "node-agent-pod-1", Labels: map[string]string{"role": "node-agent"}, }, - Spec: corev1.PodSpec{ + Spec: corev1api.PodSpec{ NodeName: "fake-node", }, - Status: corev1.PodStatus{ - Phase: corev1.PodRunning, + Status: corev1api.PodStatus{ + Phase: corev1api.PodRunning, }, } diff --git a/pkg/exposer/types.go b/pkg/exposer/types.go index d4d8c87300..670c8b661a 100644 --- a/pkg/exposer/types.go +++ b/pkg/exposer/types.go @@ -38,4 +38,5 @@ type ExposeByPod struct { HostingPod *corev1.Pod HostingContainer string VolumeName string + NodeOS *string } diff --git a/pkg/restore/actions/csi/pvc_action.go b/pkg/restore/actions/csi/pvc_action.go index 19c687e8bf..0462bb74c9 100644 --- a/pkg/restore/actions/csi/pvc_action.go +++ b/pkg/restore/actions/csi/pvc_action.go @@ -478,6 +478,7 @@ func newDataDownload( SnapshotID: dataUploadResult.SnapshotID, SourceNamespace: dataUploadResult.SourceNamespace, OperationTimeout: backup.Spec.CSISnapshotTimeout, + NodeOS: dataUploadResult.NodeOS, }, } if restore.Spec.UploaderConfig != nil { diff --git a/pkg/restore/actions/dataupload_retrieve_action.go b/pkg/restore/actions/dataupload_retrieve_action.go index 653f5e3403..d5b922d6f0 100644 --- a/pkg/restore/actions/dataupload_retrieve_action.go +++ b/pkg/restore/actions/dataupload_retrieve_action.go @@ -78,6 +78,7 @@ func (d *DataUploadRetrieveAction) Execute(input *velero.RestoreItemActionExecut SnapshotID: dataUpload.Status.SnapshotID, SourceNamespace: dataUpload.Spec.SourceNamespace, DataMoverResult: dataUpload.Status.DataMoverResult, + NodeOS: dataUpload.Status.NodeOS, } jsonBytes, err := json.Marshal(dataUploadResult)