From 3905ccb0eaa5f472606e509d1702396d9a073e8f Mon Sep 17 00:00:00 2001 From: Xun Jiang/Bruce Jiang <59276555+blackpiglet@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:36:44 +0800 Subject: [PATCH] Backup workflow for block data mover. (#10067) Signed-off-by: Xun Jiang --- changelogs/unreleased/10067-blackpiglet | 1 + .../v2alpha1/bases/velero.io_datauploads.yaml | 7 + config/crd/v2alpha1/crds/crds.go | 2 +- pkg/apis/velero/shared/constants.go | 22 +++ pkg/apis/velero/v2alpha1/data_upload_types.go | 6 + pkg/backup/actions/csi/pvc_action.go | 12 ++ pkg/backup/actions/csi/pvc_action_test.go | 152 ++++++++++++++++-- pkg/datamover/backup_micro_service.go | 2 +- pkg/datamover/util.go | 7 +- pkg/datamover/util_test.go | 10 ++ pkg/util/datamover/datamover.go | 13 +- pkg/util/datamover/datamover_test.go | 68 ++++++++ 12 files changed, 285 insertions(+), 17 deletions(-) create mode 100644 changelogs/unreleased/10067-blackpiglet create mode 100644 pkg/apis/velero/shared/constants.go diff --git a/changelogs/unreleased/10067-blackpiglet b/changelogs/unreleased/10067-blackpiglet new file mode 100644 index 000000000..3a4b67c31 --- /dev/null +++ b/changelogs/unreleased/10067-blackpiglet @@ -0,0 +1 @@ +Backup workflow for block data mover. \ No newline at end of file diff --git a/config/crd/v2alpha1/bases/velero.io_datauploads.yaml b/config/crd/v2alpha1/bases/velero.io_datauploads.yaml index 15682739b..6aed785d3 100644 --- a/config/crd/v2alpha1/bases/velero.io_datauploads.yaml +++ b/config/crd/v2alpha1/bases/velero.io_datauploads.yaml @@ -131,6 +131,13 @@ spec: OperationTimeout specifies the time used to wait internal operations, before returning error as timeout. type: string + parentSnapshot: + description: |- + ParentSnapshot specifies the parent snapshot that current backup is based on. + If its value is "" or "auto", the data mover finds the recent backup of the same volume as parent. + If its value is "none", the data mover will do a full backup + If its value is a specific snapshotID, the data mover finds the specific snapshot as parent. + type: string snapshotType: description: SnapshotType is the type of the snapshot to be backed up. diff --git a/config/crd/v2alpha1/crds/crds.go b/config/crd/v2alpha1/crds/crds.go index 59af9e6f0..485fafa80 100644 --- a/config/crd/v2alpha1/crds/crds.go +++ b/config/crd/v2alpha1/crds/crds.go @@ -30,7 +30,7 @@ import ( var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcYK\x93\xe3\xb6\x11\xbeϯ\xe8\xda\x1c\xf6\xb2\xd2d\xf3p\xa5t\xdb\xd1\xc4US\xf1Ϊ\xac\xc9\xdcA\xb2E\xc1\v\x02\b\x1e\x92\xe5$\xff\xdd\xd5\x00I\x81$4z\xd8^\xdd\x044>|\xe8n\xf4\x03\x9c\xcdfwL\xf3W4\x96+\xb9\x00\xa69\xfe\xecP\xd2?;\xff\xfa\x0f;\xe7\xea~\xf7\xf1\xee+\x97\xd5\x02\x96\xde:\xd5\xfc\x88VyS\xe2#n\xb8\xe4\x8e+yנc\x15slq\a\xc0\xa4T\x8eѰ\xa5\xbf\x00\xa5\x92\xce(!\xd0\xccj\x94\xf3\xaf\xbe\xc0\xc2sQ\xa1\t\xe0\xddֻ?\xcf?~7\xff\xfb\x1d\x80d\r.\x80\xf0*\xb5\x97B\xb1\xca\xcew(Ш9WwVcI\xc0\xb5Q^/\xe08\x11\x17\xb6\x9bF\u008f̱\xc7\x16#\f\vnݿ&S?p\xeb´\x16\xde01\xda;\xccح2\xee\xf9\x88?\x83*\"Z.k/\x98\x19.\xba\x03\xb0\xa5Ҹ\x80\xb0F\xb3\x12i\xac=l\xc0\x98\x01\xab\xaa\xa0>&V\x86K\x87f\xa9\x84o\xe4q\a\xb4\xa5\xe1\xda\x05\xf5\xa4|\xc1:\xe6\xbc\x05\xeb\xcb-0\vϸ\xbf\x7f\x92+\xa3j\x836\xf2\x05\xf8\xc9*\xb9bn\xbb\x80y\x14\x9f\xeb-\xb3\xd8\xceF\x1d\xaf\xc3D;\xe4\x0e\xc4\xd7:\xc3e\x9dc\xf0\xc2\x1b\x84ʛ`[:w\x89\xe0\xb6\xdc\x0e\xa9\xed\x99%z\xc6au\x92H\x98'8\xebX\xa3nj\x92\xa5\x91R\xc5\x1c\xe6\b-U\xa3\x05:\xac\xa088쎱Q\xa6an\x01\\\xba\xef\xfevZ\x17\xad\xb2\xe6a飒C\xc5<\xd0($Ñ\tY\xa9F\x93ՎrL\xfc\x16\"\x8e\x00\x1e\x92\xf5\x91I\xc4M\xc7\xcfR!\x97\x03\xb5\x01\xb7Ex`\xe5W\xafa\xed\x94a5\xc2\x0f\xaa\x8c\xe6\xdbo\xd1`\x90(\xa2\x04y/p\xb2\x9d2Y\xd3i,\xe7Q\xb6\x05\xeb\xb0F\xf6\x1bn\xf4\xbb\xfbVi\x90e}\xab\x8bA\xf3 \xc1\x95\xcc;ا\x1a/r\xaeT\x89RU\x98hl\xc0\x89[\xd0F\x95h\xed\x1b\x0eO\x00\x03\x16\xcfǁ\x89j\xa2\xc4\xee/L\xe8-\xfb\x18\x83L\xb9ņ-\xda\x15J\xa3\xfc\xb4zz\xfd\xebz0\fo\x04\fV:K\x91\x82\xe8k\xa3\x9c*\x95\x80\x02\xdd\x1eQF\xd37j\x87\x86\x02`ͥ\xed\x11)\x9cW\xa9\xc01\x98\x93\x7f\a<\x9a\x8d\x93\x06\x83\xf7\x10A\x93Z\x1fhO\x8d\xc6\xf1.|\xb6\xd8\xc7̓\x8c\x8e\xce\xf1\xbf\xd9`\x0e\x80\x8e\x1eWAE)\b\xe3\xb1\xda؊U\xab\xadhn\xa1\x94@6\xd6\"y\xe1gJ\vK%7\xbc\x9e\x1e<-\x7fO\xb9\xc8\x19\x9df\x1c6ْNA\xdeILf!C\xcd:ץо\xe1\xb57\xa7\xec\xbf\xe1(\xaaI\xfc9y\x93\xba\x03\x87]n\xb1qO\xbd\xbb]mVKR\xafS!B\xd9P\xef&\xae9%\t\xf0\xb4I\x10\xb9\x85w\xef@\x19x\x17\x9b\xa5w\x1f\xe2jυ\x9b\xf1A\xfe\xdfs!\xba]\xae\xf2n\xaap\xbe\xacϜ\xfc9\b\x11\x9f/\xebkk\xab)\x1b\x94\xbe\x99n8\x03\xe6\x9d\xca\f\v.\xfdϙ\xf1=\x97\x95\xda\xdbk\x0e\xdb\xd77Tb*\xefn1\xf8\x97\x11\xc6\xc8\xee\x8e\n\xe2`k\xa7`\xcfxRc\xf4\xbb\xdb\x0f\x19\xdc\x027\x94\x90\f:o$\x85\x034\x86\"\xb4\r\x90\xcaOj\x9e7Oj%\xd3v\xab\xdc\xd3\xe3\x993\xae{\xc1.\xee>=v&~\r^\xd7\a\xdfV\x122V\"\xfa]\x15Y\x85\xb4~\x13\xdb5\xff\x05/\xe4K\xa2\x1dc\xa1j^2\x016\x8cɶ\tl\x0f\xd1aO\t\xe5\xfa\xbc1ݴ[K\xf8\x86ڧ\x7f!\xb8ō\xd6C\x88\xee(\xca\U0001a4f3\xc8~\xe6x\xc7vJ\xf8&\x88\x92I\xb0\x02\xafO\xe8\x1a(}P\xb1U T|\xb3AC\x15U(\xb7\xe2ƫ\xd7\xe5{\x9bl\xc27\xe9\x1f\xcaT\r\xd3\x1a+\xea\xed\xc8\x19[\xdb^eU\xc7L\x8d\xee5\x90>\xa3\xa2\x97D\xb4S\x05\x95fd\xa0\xb6\xf6\x0f\x97+\x88\xc1\xeau\x99\xa9\xd4\xe9\xb7z\x9d2<]\xc7\xd0oc_\xe8\x04\x99\x99\x11\xc5\xef\xd7$ؑ\xdbp\x81`\x0f\xd6a\x13T0b\x18-\x95\xb3˙\xb4\bG3\\\xc0i\xe2>\xed\xf6=\xc6-\x04\xf4\ue09dW\xaf\xb92\xad\xb7\x0f\xb8-s$\xd1v\xfdP\x1c\xb2\x98\xd0Řֿn\xe3[^Dx\xf9&\xe3\xe5\x98\xf2\t\xbe\xc5\xe17S\xa6*\x90\x1b\xacr9\xf0\xb4\xe5f\xa0w\xd9\xc1\xf2\xf2Z'\xbf\xf3,_Џdƹs4}L8\xe3\x89a\xa0\x1bͦ1\xe2\xa2\xce'\xbc\xcb\\\xda\xfb\xc4\xd7\xd6\xd6\xec\xa57!\n\xb6o\xb0jsc\xf7\xc3\xca\x12\xb5\xc3\xea\xe1@e\xd1\x05\x95\x13\x11\x90o\xbfJ\xfd[\x1f\xeb&\xd4\xec\xda\x16\xa5\xa3Կ\x9cݒ\x91>\x8dA\xc2\U000c9a52\xbafJ7ֶ\xa7I\x03\xbcP\x0e\x0e\xed\xff\xfbX\xcaвP Q\x89?\xd9\xf4d\x96\xa6\xfe~F\xeb'\x12\xd2\v\xc1\n\x81\vpƟ\xeau\xf2\xad]|\x88N\xdf\x1co\xea\xf3\xa60Sݱ\xfe\x95-\xbc\x86vO\xe09\x95\x1d\xf1z\x85E8\xac\x00w(\x81\xbaw\xc6\x05V\x1df\xa6\xe19\xa7\xf9\f\xe9i-\xfdG*\xbfAkY}\xee\x02}\x8eR\xf1a\xaa]\x02\xac\xa0\xc2{\xdcv\xbc\xb7\xedݾ\xba\x01\xfa}.\xf1\x85\xed\xcf\x1b\\B\xb3~\x86̊dr1\xad\xa7v:\xa8\xc1\x1b\xdd\xd73\xee3\xa3\xdd\xfd\xccL\xad\xdaK\x9f\x99\x9a|\xd3J'\xe3\xabH.1vsY\xcc\xfe\xa3Qf\xee\xfbp\x19\xae\xd2t\xcb\xef\x96\xeb\u07bf\xadl\x95\xe8nx\xf8\xd8#}S\xa0!3\x14\xb9\x0e$<\xc9'V\xcb\x15\x7f=B\xdfL\x05\xa89\xbcl\xa94\x89\x0fB]{Yq\xab\x05;\xf4\x87IK\xe6\f\xf8\xf1\xd6L\xde\xfb\xaf\xad\x9a\xfb\x8fo\xf9\xca\xeb\xed\xce\n\xcetWa\xbe\xff\xa8\xf6\xc7\xec\xf0\xc6s\xd0\xf0#\xe7M\xbd\xdd\x00\xe1\\*h?\xba^\x1f\xc1\x87\xdb|\xcb\xe0\x9d\xd5\xded00\xaf\x12\xec\xf6\xf96\x1d\xf1E\xffMc\x01\xff\xfd\xffݯ\x01\x00\x00\xff\xff];\x85{\xd8 \x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcZIs\xe36\x16\xbe\xfbW\xbc\xea9\xe4b\xc9\xe9YRS\xba\xb5\xe5I\x95j\xd2nW\xcb\xe3;D>\x89\x88A\x80\x83E\x8af\xf9\xef\xa9\ap\x01IH\x94\x94Nx\xe8jcyx\x1b\xbe\xb7@\xb3\xd9\xec\x8eU\xfc\r\xb5\xe1J.\x80U\x1c\x7f\xb1(\xe9/3\x7f\xff\xbb\x99s\xf5\xb0\xffx\xf7\xcee\xbe\x80\xa53V\x95_\xd1(\xa73|\xc2-\x97\xdcr%\xefJ\xb4,g\x96-\xee\x00\x98\x94\xca2\x1a6\xf4'@\xa6\xa4\xd5J\bԳ\x1d\xca\xf9\xbb\xdb\xe0\xc6q\x91\xa3\xf6ě\xa3\xf7\xdf\xcf?\xfe0\xff\xdb\x1d\x80d%.\x80\xe8\xb9J(\x96\x9b\xf9\x1e\x05j5\xe7\xea\xceT\x98\x11ٝV\xaeZ@7\x11\xb6\xd5G\x06v\x9f\x98e\xff\xf2\x14\xfc\xa0\xe0\xc6\xfes0\xf1\x137\xd6OV\xc2i&z\xa7\xfaqS(m\x9f;\xca3\xc8]\x98\xe0r\xe7\x04\xd3\xf1\x96;\x00\x93\xa9\n\x17\xe0wT,C\x1a\xabE\xf4\x14f\xc0\xf2\xdc+\x8d\x89\x17ͥE\xbdT\u0095\xb2\xa3\x8f&Ӽ\xb2^)\x1d\xa7`,\xb3\u0380qY\x01\xcc\xc03\x1e\x1eV\xf2E\xab\x9dF\x13x\x05\xf8\xd9(\xf9\xc2l\xb1\x80yX>\xaf\nf\xb0\x9e\rz]\xfb\x89z\xc8\x1e\x89[c5\x97\xbb\xd4\xf9\xaf\xbcDȝ\xf6\xf6$\x993\x04[p\x133v`\x86\x98\xd3\x16\xf3\x93l\xf8y\"f,+\xab!?\xd1\xd6\xc0P\xce,\xa6\xd8Y\xaa\xb2\x12h1\x87\xcd\xd1b#\xc4V\xe9\x92\xd9\x05pi\x7f\xf8\xebiMԪ\x9a\xfb\xadOJ\xf6\xd5\xf2H\xa3\x10\r\aN\xc8B;\xd4I\xdd(\xcb\xc4oa\xc4\x12\x81\xc7h\x7f\xe0$Ѝ\xc7'YY\xc9Lc\x89\xf26\x86x\xb7{\xccML:\x9e\xad4W\x9a\xdb\xe3\x02>~\x7f)\x9bt+@m\xc1\x16\b\x8f,{w\x15\xac\xad\xd2l\x87\xf0\x93ʂ\x8f\x1d\nԵ\x8fm\xc2\x12S('r\xd84\x86\x010V餳U\x98\xcdî\x9anCv\xe0q\xfd3\xbf\xf1]\xc84\xb2\xe4]hPr\xeeWp%\xd3\x17\xe2\xd3\x0e/\xba\f\xb16\xa5ʱU\x1d\xc6\x1cq\x03\x95V\x19\x1as\xe6z\xd2\xf6\x1e\x0f\xcf\xdd\xc0H-a\xc5\xfe\xcfLT\x05\xfb\x18\xc00+\xb0d\x8bz\x87\xaaP~zY\xbd\xfde\xdd\x1b\x86\x93\xd0\xc62k\bӈ\xf5J+\xab2%`\x83\xf6\x80(=\xbcB\xa9\xf6\xa8\t\xa4w\\\x1a`2oiB\xbc\xa0\v5\xe4\xfa\x9e\x1e͆\xc9ڝT\x85:6;\xb92\x8dY\xde`|\xf8\xa2\xb0\x18\x8d\x0e\x84\xf8߬7\a@r\x87]\x90S|\xc4 U\x1d\x020\xafU\x15\xec\xc6\rh\xac4\x1a\xba^ޫ\xd4\x16\x98\x04\xb5\xf9\x193;\x1f\x90^\xa3&2\xcd}Ȕܣ\xb6\xa01S;\xc9\xff\xd3\xd26`\x95?T0\x8b\xc6\xfa\v\xa9%\x13\xb0g\xc2\xe1\xfd@{\xf4\x95\xec\b\x1a\xe9Lp2\xa2\xe77\x98!\x1f\x9f\x95F\xe0r\xab\x16PX[\x99\xc5\xc3Î\xdb&Y\xc8TY:\xc9\xed\xf1\xc1\x1b\x83o\x9cU\xda<\xe4\xb8G\xf1`\xf8n\xc6tVp\x8b\x99u\x1a\x1fX\xc5g^\x10\xe9\x13\x86y\x99\xffI\xd7\xe9\x85\xe9\x1d;\xf2\xc2\xf0\xf9@\x7f\x85y(\xfeӕ`5\xa9 bg\x05\x1a\"\xd5}\xfd\xc7\xfa\x15\x1aN\x82\xa5\x82Q\xba\xa5#\xbd4\xf6!mr\xb9E\x1d\xf6m\xb5*=M\x94y\xa5\xb8\xb4\xfe\x8fLp\x94\x16\x8c۔ܒ\x1b\xfcۡ\xb1d\xba!٥O\xa8`\x83\xe0*\x82\x82|\xb8`%a\xc9J\x14Kf\xf0\x0f\xb6\x15Y\xc5\xcc\xc8\b\x17Y+N\x13\x87\x8b\x83z\xa3\x89&\xd3;a\xda\x0e>\xd6\x15fdSR+m\xe2[^\xc7\x12\xc2\x00\x16\xad\xeck'}\xed\xe9K\x86\x90\xe1\xa2)W\xa3\xef1E\xa8\xe1UF\xf8݄\xba:2\x89~d\x8a\xbf\x0e\xe4\xeb=\x1a+e\xb8U\xfaH\x84Ch\x1c\xba\xc1I\x8bЗ1\x99\xa1\xb8E\xbc\xa5\xdf\t\\\xe6\xa4qlݘ\x00(P\xf5\x8c*\xb9St\xb1\"C\xc0\xca\xd2\n\xf2j\x836-\xa6L\x842.\xa1Kz!Nn\x87\xa2n\x94\x12Ȇ\x1a\xcc\f_KV\x99B\xd9\t\x81W[hV\xbe\x1e+\xa4×\xeb\xd5=\xfdӌ\x93\a\xedy^C<\xdd2ʶ\xd2f\xab\xed\xbc\\\xaf\xc0\xd4\xdb\xc7F\x92N\b\xb6\x11\xb8\x00\xab\xddX\xb0\xd3\x0e\xeb\xb9\xd7|\x8f:53\xbc9~a\xe3\x85a\x1b8\xe3\x93j?\xf4F\x05\t6R.\x95\xb4(S6:\xebU\xf45\x92.\x053I\x9e\a\x9c\xad\xe3\xf5\xa9k\xd2\x10\x84̯\xb0\x05K\xf3\x05!\xe8z9\xbaM\xbc\xcd\xcd\xe0\xc0mq\x93D\xe1\x82^,P\xb4<)O}߃8j{F\x98\x97\xb7\xa5\x97wJ2\n7\xb7H\xb6\xef\x19\xfd\x02\xd9\xfa^\x92\x92n\xc0\xe5)\xe1\x14\xa1\x00\x81\x19\xe6\xe0\xaa\xeby'\xd0\xe1\x1a\xf31ϳ\x9e\xbd\x12\xd3}\xa1O \xc9(2A\x9dt~\xa6\xb4r\xa9\xe4\x96\xef\xc6g\xc7e\xfe\xb9k{V\xb4Qċ\x8e$\x8dS\x80#Nf>Ý5яr\xc3-\xdf9}\n\x8d\xb6\x1cE>J`&\x01hB\x1f\x9e\x89[\xe2H+Y\x13\xbfkH\x8d2\xfb\xe0%1J\x85\xf07\x96\x01\b\xba;\x8a\xdc\xc0\x87\x0f\xa04|\b\xbd\xa2\x0f\xf7a\xb7\xe3\xc2\xcex\xaf\xbc8p!\x9aS\xae\x8a\xa0mIA\x05\x9drS\xa1%\xa9\x83/\x03\x1a\x03UX*>\xbd\xf8V\xc1\x81\xf1(\xadoO7\xf7\t\xba\x1b\xdcR\x0e\xa8\xd1:-)\n\xa3֔\x16\x19OR\xb9D\x18:#\xa9\x89B℔\xc3\xe8饠\xff\x0f\xb1<\x06\x80\x84\x00)\x1b\x9f\xe3Ч\xec?\xae/\xe10Z\xdap\xb8\xe5\x02\xc1\x1c\x8dŲ\xcfm\xa8\x04\x02`\xdc\xc0P\xdb\x10\xbc\xc57\xd6}\x12\r\xafJ\xf3\x1d'\x0f\x90\xedL\x97\x1d\xd6\xe0[\xb7Q<\xb4\xfaؐ\xbc0-|\x1b\x82\xef\x8e\x1c\xe1K8\x9c\xc2\x0f\x93\xb9O`\xda\xf9\xbcƂ\x04\x92L*\xe4\xe5my\x91y\xe8\xe0Dl\xa1\xe1C\xc1\xb3\xa2\xefK|\x8c\xf2\x00\x96\xbd\xa3/\x06\xae`3\x1dTf\xe9\xd2`\xb0f\b\a\x83\xe9\xf8\x0e\r\xa7\xfa\x86Nξ\xbc-/*\x9f|g\xe7\xb2\x02*t\x96k-gNk_\x9a\x86Q\xb5\xbd\xa9\x84bY\x86\x95\xc5\xfc\xf1\xf8\xac\xf2)\xa7\xff\xd4[L\x8c\xc8Kz[\tS\xfbn\x17V\xec\xda\x1a\xa8a\xb7\xed\xc8\xddrM?\r\x89\xf8ތ\xce#\x04\x1fW4\x01\xfdN3\r\xf0J\x0e\xee{\v\xdf\x05Цm>\x14\xd0\xf5\x1c\x1d:\xa2\xd04\x81sfqF\xfbo\v\xfb\xe9\xda14\xe4\xe3^\xe6M\x85\xe4\x98\xccXw\xac\xa9x}\x93\xb5y\tHi\xac#\xd7\xea+P\xc3\x1cp\x8f\x12\x94\x84-を\tO2\x01`\xe7\xa9\xd4Q5<\xfb4M\xa3\xa6\xc1\x98\xec\xdeM[2\xa1\x841\x9a\xfd\x9e\xc6lsگh\x9cHd1\xbfcN\x1b\x8e\f\xed\v\x93\xcci\xcf\xd7\xd7\xcc\x00\x03\x1d\x88Ըq\n\xb4.VR2\xd1\x1d>\x96L\xb5\x11\x06ˡP\xa2vj\xe9\xca\rj\xe2\xd6?ـ\xc4\x03\xe5\xa9Y\xc1\xe4.\x99\t5O\x0e\b\x82\x19[\xbb\xdbI\x0f\x89\xdf|\x86\x92\xc5o4\xddW\xa21l7\x05֟ê\xd0E\xad\xb7\x00\xdbP\xca\xda\xd7\xfaw\xa6\x8e!W!\xb1\x9c\x0e\x17W\x05\x89\xde\x03\xc8՜|Y_\xc0˗5\x1d\xf2e\xfd[yA\xe9\xcaT\x11˜U\x89a\xc1\xa5\xfb%1~\xe02W\x871t\x9c\x11\xb5b\xb6\x98\x10\xf4\x85٢M\x92\x9d\x10~\xcf(\x97\xaf\xb3\xce\r\x12&~\xab\x94\u07b7\xf9\xa6أ5\xa9\x14\x06/\x81\x83S\x9a\x7f\xc6Cb\xb4\t\xb9\x89\xa9\x97:\x8e'\xa6F\x8f\xf5\xf1d褦ಙK\xd2l\xdf\xc3\x13s?\xfa\x00w\x95\x9ek\xfen\x89\xe0mO\xb6\xc37\xff\xbc=B\xb9~o\x88J\x8a\xc8b\t\xc2\xd1\xfe\xb6\x8e\xf1\x94\xe6\xf0Zp\xd3t\x91\x9b\xd28\xe7\xa6\x12\xec\xd8\xca2\x156Z\xdc\x1a\xbe\x0e\x8e\x9d\xe4|\xfb\xb5\xfdUA\xbauv\x1e\x95a\x02\x99\xfd\xbc:\x1dr\xbe\xc5\tgb^s\xbdWO\x17\xd6\xfc\xab\xa7\xe6*\xf2\x1c\xa5\xe5[\x1e\xbd\xc8vŚ\xef\xf0\xa7t9|ٸ\xae\xbe\xec\xfd\xd6\xe4\xa6z\xbbGa\"\x13\xad\x7f\xfa\x92\xca\xf7\xd6\x04\x06\x04A\xfe\rp9|\xf5\xbfo#:\xb3\xf5Cd\b\xfe\xa9\"VIJo|zt}j\xd9\x17\xe8\x8f\xcc*\x93^5\x1a\xf4\x9c\xe7\x11\xed\xbao\x1b\x8f\xb8M\xfb2\xbc\x80\xff\xfe\xff\xee\xd7\x00\x00\x00\xff\xffʖ\x89F\xbb&\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcZI\xb3\xdb6\x12\xbe\xbf_\xd1\xe59\xe4b\xe9ų\xa4\xa6t\xb3\xe5Iի\x89\xedW\x96\xe7\xdd!\xb2)\"&\x01\x0e\x16)\x9a忧\x1a\v\t\x92\xd0\x1a'<\xb8\xfc\xb04zC\xf7\xd7\r-\x16\x8b\a\xd6\xf1\x17T\x9aK\xb1\x02\xd6q\xfcŠ\xa0\xbf\xf4\xf2\xeb\xdf\xf5\x92\xcb\xc7\xfd\x9b\x87\xaf\\\x94+X[md\xfb\x19\xb5\xb4\xaa\xc0\xf7Xq\xc1\r\x97\xe2\xa1E\xc3Jf\xd8\xea\x01\x80\t!\r\xa3aM\x7f\x02\x14R\x18%\x9b\x06\xd5b\x87b\xf9\xd5nqkyS\xa2r\xc4\xe3\xd1\xfb\xef\x97o~X\xfe\xed\x01@\xb0\x16W@\xf4l\xd7HV\xea\xe5\x1e\x1bTr\xc9\xe5\x83\xee\xb0 \xb2;%m\xb7\x82a\xc2o\vGzv\xdf3\xc3\xfe\xe5(\xb8\xc1\x86k\xf3\xcf\xc9\xc4O\\\x1b7\xd95V\xb1ft\xaa\x1b\u05f5T\xe6\xe3@y\x01\xa5\xf5\x13\\\xecl\xc3T\xba\xe5\x01@\x17\xb2\xc3\x15\xb8\x1d\x1d+\x90Ƃ\x88\x8e\xc2\x02XY:\xa5\xb1\xe6YqaP\xadec[1\xd0G](\xde\x19\xa7\x94\x81SІ\x19\xabAۢ\x06\xa6\xe1#\x1e\x1e\x9fij\x92;\x85\xda\xf3\n\xf0\xb3\x96♙z\x05K\xbf|\xd9\xd5Lc\x98\xf5zݸ\x890d\x8eĭ6\x8a\x8b]\xee\xfc/\xbcE(\xadr\xf6$\x99\v\x04Ss\x9d2v`\x9a\x98S\x06˓l\xb8y\"\xa6\rk\xbb)?\xc9V\xcfP\xc9\f\xe6\xd8Y˶k\xd0`\tۣ\xc1(D%U\xcb\xcc\n\xb80?\xfc\xf5\xb4&\x82\xaa\x96n\xeb{)\xc6jyG\xa3\x90\f{N\xc8B;TY\xddHÚ\xdf\u0088!\x02\xef\x92\xfd\x9e\x13O7\x1d\xbf\xc8ʓ(\x14\xb6(\xeec\x88\x0f\xbb\xe7ܤ\xa4\xd3\xd9Nq\xa9\xb89\xae\xe0\xcd\xf7ײI\xb7\x02d\x05\xa6FxNJ\xaf\xb6\x83\x8d\x91\x8a\xed\x10~\x92\x85\xf7\xb1C\x8d*\xf8\xd8\xd6/ѵ\xb4M\t\xdbh\x18\x00m\xa4\xca:[\x87\xc5\xd2\xef\nt#ىǍ\xcf\xfc\xc6w\xa1PȲw!Fɥ[\xc1\xa5\xc8_\x88\xb7;\xbc\xea2\xa4\xda\x14\xb2\xc4^u\x98r\xc45tJ\x16\xa8\xf5\x99\xebI\xdbG<|\x1c\x06fj\xf1+\xf6\x7ffMW\xb37>\x18\x165\xb6l\x15v\xc8\x0e\xc5\xdb秗\xbflF\xc3p2\xb4\xb1\xc2h\x8ai\xc4z\xa7\xa4\x91\x85l`\x8b\xe6\x80(\\x\x85V\xeeQQ\x90\xdeq\xa1\x81\x89\xb2\xa7\t\xe9\x82!Ր\xeb;z4\xeb'\x83;\xc9\x0eUjvre\x1a3<\xc6x\xff%i1\x19\x9d\b\xf1\xbf\xc5h\x0e\x80\xe4\xf6\xbb\xa0\xa4\xfc\x88^\xaa\x90\x02\xb0\f\xaa\xf2v\xe3\x1a\x14v\n5]/\xe7U\xb2\x02&@n\x7f\xc6\xc2,'\xa47\xa8\x88L\xbc\x0f\x85\x14{T\x06\x14\x16r'\xf8\x7fz\xda\x1a\x8ct\x876̠6\xeeB*\xc1\x1aس\xc6\xe2\xeb\x89\xf6\xe8k\xd9\x11\x14ҙ`EB\xcfm\xd0S>>H\x85\xc0E%WP\x1b\xd3\xe9\xd5\xe3㎛\b\x16\nٶVps|t\xc6\xe0[k\xa4ҏ%\xee\xb1y\xd4|\xb7`\xaa\xa8\xb9\xc1\xc2X\x85\x8f\xac\xe3\v'\x88p\x80aٖ\x7fR\x01^\xe8ѱ3/\xf4\x9fK\xf47\x98\x87\xf2?]\t\x16Hy\x11\a+\xd0\x10\xa9\xee\xf3?6_ r\xe2-\xe5\x8d2,\x9d\xe9%ڇ\xb4\xc9E\x85\xca䀹l\x1dM\x14e'\xb90\ue3e2\xe1(\fh\xbbm\xb9!7\xf8\xb7Em\xc8tS\xb2k\a\xa8`\x8b`;\n\x05\xe5t\xc1\x93\x805k\xb1Y3\x8d\x7f\xb0\xad\xc8*zAF\xb8\xcaZ)L\x9c.\xf6\xeaM&\"\xd2;a\xda!|l:,Ȧ\xa4V\xda\xc4+\x1er\t\xc5\x00\x96\xac\x1ck'\x7f\xed\xe9˦\x90\xe9\xa2K\xaeF\u07fb\x1c\xa1ȫH\xe2wLu!35\xe3̔~C\x90\x0f{\x14vRs#Ց\b\xfb\xd48u\x83\x93\x16\xa1\xaf`\xa2\xc0\xe6\x1e\xf1\xd6n'pQ\x92Ʊwc\n@\x9e\xaacT\x8a\x9d\xa4\x8b\x95\x18\x02\x9e\f\xad \xaf\xd6h\xf2b\x8aL*\xe3\x02\x06\xd0\v)\xb8\x9d\x8a\xba\x95\xb2A6\xd5`\xa1\xf9F\xb0N\xd7\xd2\\\x10\xf8\xa9\x82\xb8\xf2˱C:|\xbdyzM\xff\xc4q\xf2\xa0=/C\x88\xa7[Fh+o\xb6`\xe7\xf5\xe6\tt\xd8>7\x92\xb0Mö\r\xae\xc0(;\x17\xec\xb4\xc3:\xee\x15ߣ\xca\xcdLo\x8e[\x18\xbd\xd0o\x03\xab\x1d\xa8vC/T\x90`\x94r-\x85A\x91\xb3\xd1Y\xaf\xa2/J\xban\x98\xce\xf2<\xe1l\x93\xae\xcf]\x93H\x10\n\xb7\xc2\xd4,\xcf\x17\xf8\xa4\xeb\xe4\x186\xf1\x1e\x9b\xc1\x81\x9b\xfa.\x89\xfc\x05\xbdZ\xa0dyV\x9ep߽8\xb2:#\xcc\xf3\xcb\xda\xc9{I2J7\xf7H\xb6\x1f\x19\xfd\n\xd9\xc6^\x92\x93n\xc2\xe5)\xe1$E\x01\nfX\x82\xedn睂\x0eWX\xcey^\x8c앙\x1e\v}\"\x92\xcc2\x13\x04\xd0\xf9\x81`\xe5Z\x8a\x8a\xef\xe6g\xa7e\xfe\xb9k{V\xb4Y\xc6K\x8e$\x8dS\x82#N\x16\x0e\xe1.b\xf6#lX\xf1\x9dU\xa7\xa2Qű)g\x00\xe6b\x00\xba\xa0\x0f\xc7\xc4=y\xa4\x97,\xe6\xef\x10R\x13d\xef\xbd$\x8dR>\xfd\xcde\x00\n\xdd\x03E\xae\xe1\xd5+\x90\n^\xf9^ѫ\xd7~\xb7\xe5\x8dY\xf0Qyq\xe0M\x13O\xb9)\x83\xf6%\x05\x15t\xd2^J-Y\x1d|\x9aИ\xa8\xc2P\xf1\xe9\xc47\x12\x0e\x8c'\xb0\xbe?]\xbf\xce\xd0\xddbE\x18P\xa1\xb1JP\x16F\xa5\b\x16iGR\xdaL\x1a:#i\xc7\x14\nse\n\xcd\xca\xf9<\xa20\x91ғ\x1f\xe2\x9a\vx\x85Un4\xe0\x1d\xd7\x18 EHq\xc2\xf8\x04\xa8=\xae\x1f\x8cϬ\x89\xa6O,^\x11ru\x83\n\x8b\xe4\x8c\x18\x9e)\x98\x85(\xc6t\xe0\xee\xaaC\x85\x148?\xce9X)\x81Ae\xc9\xd5\xdcaW\x90c=\xae\xedU\xf3\xf4\xfe\x8c0\xb3\xd5\xe7\xb8?cm\x9d\x00\xa0\v\xb6\x9eb%\xe7\xb3\xf4\xffi\xe6N\xc3}F\xf4܍>ǡ+\xd0~\xdc\\\xc3a\xb24rX\xf1\x06A\x1f\xb5\xc1v̭\xaf\xfb\xbc\xe9\xef`\xa8o\xff\xdesC6c\x12\x91W\xa9\xf8\x8e\xd3}\x17\xfd\xccP\v\x04'\rM3\x97H\x1d\x12\xc8:k\x9f\xac\x9d\x7f\x0f\xe4(\x9b\xf8\xc3\tl0Q:\xb8\xdaϗ!\xf2g\xf2\xc6E\x85<\xbf\xac\xaf2\x0f\x1d\x9cA\x124|\xa8yQ\x8f}\x89\xcfs:\x80a_ѕ~7\xb0\x99\x87\x10\x8b|!8Y3\r\xfe\x93\xe9\xf4\x0eM\xa7Ɔ\xce\xce>\xbf\xac\xaf*\x96]\x1f\xef\xbarٿ#\x04-\xc7\xe0\x1a^\x17duW\xc1̊\x02;\x83\xe5\xbb\xe3GY^r\xfa\xb7\xa3\xc5Ĉ\xb8\xa6\x93\x991\xb5\xebm\"\x05\xb6\xdb\xf2ud\xb7\xef\xbf\xdesM\xdfN\x89\xb8N\x9c*\x93|=\xaf_}\xf4;\xcd4\xc0\x17rp\xd7I\xfaΧh\xda\xe6\x12?]\xcf١3\n\xb1\xe5_2\x83\v\xda\x7f\x1f\xc8\xcbw\n\xfc\xf3Kڹ\xbe\xabm0'3\xd7\x1d\x8b\xb9ص\xd4\xe3\xbbONc\x03\xb9^_\x9e\x1a\x96\x80{\x14 \x05T\x8c7\x04\x1d\x1d\xc9L\x00;O%`(\xff\xc8\x17[\x84\x11*d{\xb5\x97-\x99Q\xc2<\x9a\xfd\x9e\xc6\xec+\x98Ϩm\x93\xc1r\xbfc\x05\xe3\x8f\xf4\xcd*\x9d\xad`\xcewS\x18a\"剄\xb8q*h]\xad\xa4lY3}\x1a\xbb\xd44\x9a,\x87Z6\xc1\xa9\x85m\xb7\xa8\x88[\xf7@\a\x02\x0f\x04L\x8b\x9a\x89]\x16\t\xc5\a&\x84\x86is\n,\xe6^\xf8\xa6\x92\xa5/r\xc3ע\xd6lw)X\x7f\xf0\xab<\n\r[\x80m\xa9@\x19k\xfd;\x1dr\xc8M\x91X\\N\x177%\x89\xd1s\xd7͜|\xda\\\xc1˧\r\x1d\xf2i\xf3[yAa\xdb\\˂*\x95\xccpÅ\xfd%3~\u0894\x87y\xe88[ę\xfa\x82\xa0\xcf\xcc\xd4=H\xa6Z\x85\xf6̰|@\x9d[\xa4\x98\xf8\xad \xbdk\xea^b\x8f\xd6\xe4 \f^\x13\x0eNi\xfe#\x1e2\xa31\xe5f\xa6\x9eC\x1e\xcfL\xcd~\x9a\x91N\xfa\xbey.\\ƹ,\xcd\xfe\xd7\x0f\x99\xb9\x1f]\x82\xbbIρ\xbf\xbb\x8a\xf8\u0601\x1f\xe2\x9b\xfb1\xc3,ʍ;\x81TR$\x16\xcb\x10N\xf6\xf7u\x8c\xa3\xb4\x84/5\xd7\xf1\xcd 6BJ\xae\xbb\x86\x1d{Y.\xa5\x8d>nM߂\xe7Nr\xbe\xd9\xde\xff\x86$\xdf(=\x1f\x95\xe1Bdv\xf3\xf2t\xca\xf9\x16'\x9c\xc9yC\x8b\xe1ʚ\xff\xe9}\xbc\x8a\xbcDaxœ\xf7\xf7\xa1Xs\xef99]N߱n\xab/G\xbf,\xba\xab\xde\x1eQ\xb8\x80D\xc3\x0f\x9drxoC\xc1\x80B\x90{\xf1]O\x7f\xe3\xf1\xba\xcf\xe8̄֎O\xfe\xb9\"V\n\x827\x0e\x1e\xdd\x0e-\xc7\x02\xfd\x91\xa82\xebU\xb3A\xc7y\x99\xd0\x0e]\xfat\xc4n\xfb\xdf\x01\xac\xe0\xbf\xff\x7f\xf85\x00\x00\xff\xff\x02\xf2+ܩ(\x00\x00"), } var CRDs = crds() diff --git a/pkg/apis/velero/shared/constants.go b/pkg/apis/velero/shared/constants.go new file mode 100644 index 000000000..12f8b51ee --- /dev/null +++ b/pkg/apis/velero/shared/constants.go @@ -0,0 +1,22 @@ +/* +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 shared + +const ( + DataUploadParentSnapshotNone = "none" + DataUploadParentSnapshotAuto = "auto" +) diff --git a/pkg/apis/velero/v2alpha1/data_upload_types.go b/pkg/apis/velero/v2alpha1/data_upload_types.go index ac57ad89d..56225f387 100644 --- a/pkg/apis/velero/v2alpha1/data_upload_types.go +++ b/pkg/apis/velero/v2alpha1/data_upload_types.go @@ -64,6 +64,12 @@ type DataUploadSpec struct { // SourceFSType is the file system type of the source volume. // +optional SourceFSType string `json:"sourceFSType,omitempty"` + + // ParentSnapshot specifies the parent snapshot that current backup is based on. + // If its value is "" or "auto", the data mover finds the recent backup of the same volume as parent. + // If its value is "none", the data mover will do a full backup + // If its value is a specific snapshotID, the data mover finds the specific snapshot as parent. + ParentSnapshot string `json:"parentSnapshot,omitempty"` } type SnapshotType string diff --git a/pkg/backup/actions/csi/pvc_action.go b/pkg/backup/actions/csi/pvc_action.go index 66c14b820..259ec5783 100644 --- a/pkg/backup/actions/csi/pvc_action.go +++ b/pkg/backup/actions/csi/pvc_action.go @@ -42,6 +42,7 @@ import ( crclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + veleroshared "github.com/vmware-tanzu/velero/pkg/apis/velero/shared" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" veleroclient "github.com/vmware-tanzu/velero/pkg/client" @@ -535,6 +536,16 @@ func newDataUpload( vsc *snapshotv1api.VolumeSnapshotContent, fsType string, ) *velerov2alpha1.DataUpload { + var parentSnapshot string + switch backup.Spec.BackupType { + case velerov1api.BackupTypeFull: + parentSnapshot = veleroshared.DataUploadParentSnapshotNone + case velerov1api.BackupTypeIncremental: + parentSnapshot = veleroshared.DataUploadParentSnapshotAuto + default: + parentSnapshot = veleroshared.DataUploadParentSnapshotAuto + } + dataUpload := &velerov2alpha1.DataUpload{ TypeMeta: metav1.TypeMeta{ APIVersion: velerov2alpha1.SchemeGroupVersion.String(), @@ -572,6 +583,7 @@ func newDataUpload( SourceNamespace: pvc.Namespace, OperationTimeout: backup.Spec.CSISnapshotTimeout, SourceFSType: fsType, + ParentSnapshot: parentSnapshot, }, } diff --git a/pkg/backup/actions/csi/pvc_action_test.go b/pkg/backup/actions/csi/pvc_action_test.go index e7320cd1a..9c8405efc 100644 --- a/pkg/backup/actions/csi/pvc_action_test.go +++ b/pkg/backup/actions/csi/pvc_action_test.go @@ -23,40 +23,39 @@ import ( "testing" "time" - "github.com/vmware-tanzu/velero/pkg/kuberesource" - - volumegroupsnapshotv1beta2 "github.com/kubernetes-csi/external-snapshotter/client/v8/apis/volumegroupsnapshot/v1beta2" - "github.com/stretchr/testify/assert" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "k8s.io/utils/ptr" - - "github.com/vmware-tanzu/velero/pkg/label" - + "github.com/cockroachdb/errors" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + volumegroupsnapshotv1beta2 "github.com/kubernetes-csi/external-snapshotter/client/v8/apis/volumegroupsnapshot/v1beta2" snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v8/apis/volumesnapshot/v1" - - "github.com/cockroachdb/errors" "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1api "k8s.io/api/core/v1" storagev1api "k8s.io/api/storage/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/utils/ptr" crclient "sigs.k8s.io/controller-runtime/pkg/client" "github.com/vmware-tanzu/velero/pkg/apis/velero/shared" + veleroshared "github.com/vmware-tanzu/velero/pkg/apis/velero/shared" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" "github.com/vmware-tanzu/velero/pkg/builder" factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks" + "github.com/vmware-tanzu/velero/pkg/kuberesource" + "github.com/vmware-tanzu/velero/pkg/label" "github.com/vmware-tanzu/velero/pkg/plugin/velero" velerotest "github.com/vmware-tanzu/velero/pkg/test" + uploaderUtil "github.com/vmware-tanzu/velero/pkg/uploader/util" + "github.com/vmware-tanzu/velero/pkg/util/boolptr" ) const testDriver = "csi.example.com" @@ -163,6 +162,7 @@ func TestExecute(t *testing.T) { SourcePVC: "testPVC", SourceNamespace: "velero", OperationTimeout: metav1.Duration{Duration: 1 * time.Minute}, + ParentSnapshot: veleroshared.DataUploadParentSnapshotAuto, }, }, }, @@ -2176,3 +2176,131 @@ func TestGetOrCreateVolumeHelper(t *testing.T) { // The pvcPodCache should be the same instance require.Same(t, cache1, action.pvcPodCache, "Expected same pvcPodCache instance on repeated calls") } + +func TestNewDataUpload(t *testing.T) { + tests := []struct { + name string + backupType velerov1api.BackupType + vsClassName *string + uploaderConfig *velerov1api.UploaderConfigForBackup + expectedParentSnap string + expectedDataMoverCfg map[string]string + }{ + { + name: "Full backup type, no uploader config, no vs class name", + backupType: velerov1api.BackupTypeFull, + vsClassName: nil, + uploaderConfig: nil, + expectedParentSnap: "none", + expectedDataMoverCfg: nil, + }, + { + name: "Incremental backup type, with uploader config, with vs class name", + backupType: velerov1api.BackupTypeIncremental, + vsClassName: ptr.To("test-vs-class"), + uploaderConfig: &velerov1api.UploaderConfigForBackup{ParallelFilesUpload: 10}, + expectedParentSnap: "auto", + expectedDataMoverCfg: map[string]string{ + uploaderUtil.ParallelFilesUpload: "10", + }, + }, + { + name: "Default backup type, uploader config with 0 parallel files", + backupType: "", + vsClassName: ptr.To("test-vs-class"), + uploaderConfig: &velerov1api.UploaderConfigForBackup{ParallelFilesUpload: 0}, + expectedParentSnap: "auto", + expectedDataMoverCfg: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + backup := &velerov1api.Backup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-backup", + Namespace: "velero", + UID: types.UID("backup-uid"), + }, + Spec: velerov1api.BackupSpec{ + BackupType: tc.backupType, + DataMover: "velero", + StorageLocation: "default", + CSISnapshotTimeout: metav1.Duration{Duration: 10 * time.Minute}, + UploaderConfig: tc.uploaderConfig, + }, + } + + vs := &snapshotv1api.VolumeSnapshot{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-vs", + }, + Spec: snapshotv1api.VolumeSnapshotSpec{ + VolumeSnapshotClassName: tc.vsClassName, + }, + } + + pvc := &corev1api.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-pvc", + Namespace: "test-ns", + UID: types.UID("pvc-uid"), + }, + Spec: corev1api.PersistentVolumeClaimSpec{ + StorageClassName: ptr.To("test-storage-class"), + }, + } + + vsc := &snapshotv1api.VolumeSnapshotContent{ + Spec: snapshotv1api.VolumeSnapshotContentSpec{ + Driver: "test-driver", + }, + } + + operationID := "test-op-id" + fsType := "ext4" + + du := newDataUpload(backup, vs, pvc, operationID, vsc, fsType) + + require.NotNil(t, du) + assert.Equal(t, velerov2alpha1.SchemeGroupVersion.String(), du.APIVersion) + assert.Equal(t, "DataUpload", du.Kind) + assert.Equal(t, backup.Namespace, du.Namespace) + assert.Equal(t, backup.Name+"-", du.GenerateName) + + require.Len(t, du.OwnerReferences, 1) + assert.Equal(t, velerov1api.SchemeGroupVersion.String(), du.OwnerReferences[0].APIVersion) + assert.Equal(t, "Backup", du.OwnerReferences[0].Kind) + assert.Equal(t, backup.Name, du.OwnerReferences[0].Name) + assert.Equal(t, backup.UID, du.OwnerReferences[0].UID) + assert.Equal(t, boolptr.True(), du.OwnerReferences[0].Controller) + + expectedLabels := map[string]string{ + velerov1api.BackupNameLabel: label.GetValidName(backup.Name), + velerov1api.BackupUIDLabel: string(backup.UID), + velerov1api.PVCUIDLabel: string(pvc.UID), + velerov1api.AsyncOperationIDLabel: operationID, + } + assert.Equal(t, expectedLabels, du.Labels) + + assert.Equal(t, velerov2alpha1.SnapshotTypeCSI, du.Spec.SnapshotType) + assert.Equal(t, vs.Name, du.Spec.CSISnapshot.VolumeSnapshot) + assert.Equal(t, *pvc.Spec.StorageClassName, du.Spec.CSISnapshot.StorageClass) + assert.Equal(t, vsc.Spec.Driver, du.Spec.CSISnapshot.Driver) + if tc.vsClassName != nil { + assert.Equal(t, *tc.vsClassName, du.Spec.CSISnapshot.SnapshotClass) + } else { + assert.Empty(t, du.Spec.CSISnapshot.SnapshotClass) + } + + assert.Equal(t, pvc.Name, du.Spec.SourcePVC) + assert.Equal(t, backup.Spec.DataMover, du.Spec.DataMover) + assert.Equal(t, backup.Spec.StorageLocation, du.Spec.BackupStorageLocation) + assert.Equal(t, pvc.Namespace, du.Spec.SourceNamespace) + assert.Equal(t, backup.Spec.CSISnapshotTimeout, du.Spec.OperationTimeout) + assert.Equal(t, fsType, du.Spec.SourceFSType) + assert.Equal(t, tc.expectedParentSnap, du.Spec.ParentSnapshot) + assert.Equal(t, tc.expectedDataMoverCfg, du.Spec.DataMoverConfig) + }) + } +} diff --git a/pkg/datamover/backup_micro_service.go b/pkg/datamover/backup_micro_service.go index 08a005217..cb5aeb3fe 100644 --- a/pkg/datamover/backup_micro_service.go +++ b/pkg/datamover/backup_micro_service.go @@ -204,7 +204,7 @@ func (r *BackupMicroService) RunCancelableDataPath(ctx context.Context) (string, if err := dp.StartBackup(r.sourceTargetPath, du.Spec.DataMoverConfig, &datapath.BackupStartParam{ RealSource: GetRealSource(du.Spec.SourceNamespace, du.Spec.SourcePVC), - ParentSnapshot: "", + ParentSnapshot: du.Spec.ParentSnapshot, ForceFull: false, Tags: tags, VolumeID: r.volumeID, diff --git a/pkg/datamover/util.go b/pkg/datamover/util.go index ed66d497a..c82184f31 100644 --- a/pkg/datamover/util.go +++ b/pkg/datamover/util.go @@ -19,12 +19,15 @@ package datamover import ( "fmt" + "github.com/vmware-tanzu/velero/pkg/uploader" datamoverutil "github.com/vmware-tanzu/velero/pkg/util/datamover" ) func GetUploaderType(dataMover string) string { - if datamoverutil.IsBuiltInDataMover(dataMover) { - return "kopia" + if datamoverutil.IsVeleroFSDataMover(dataMover) { + return uploader.KopiaType + } else if datamoverutil.IsVeleroBlockDataMover(dataMover) { + return uploader.BlockType } else { return dataMover } diff --git a/pkg/datamover/util_test.go b/pkg/datamover/util_test.go index d44f3c307..d29b3de12 100644 --- a/pkg/datamover/util_test.go +++ b/pkg/datamover/util_test.go @@ -22,6 +22,16 @@ func TestGetUploaderType(t *testing.T) { input: "velero", want: "kopia", }, + { + name: "velero-fs dataMover is kopia", + input: "velero-fs", + want: "kopia", + }, + { + name: "velero-block dataMover is velero-block", + input: "velero-block", + want: "velero-block", + }, { name: "kopia dataMover is kopia", input: "kopia", diff --git a/pkg/util/datamover/datamover.go b/pkg/util/datamover/datamover.go index 59dd1499b..b6d965d60 100644 --- a/pkg/util/datamover/datamover.go +++ b/pkg/util/datamover/datamover.go @@ -32,7 +32,18 @@ const ( // IsBuiltInDataMover reports whether the given data mover value refers to a // Velero built-in data mover (an empty value or the default "velero" alias). func IsBuiltInDataMover(dataMover string) bool { - return dataMover == "" || dataMover == DataMoverTypeVelero + return IsVeleroBlockDataMover(dataMover) || IsVeleroFSDataMover(dataMover) +} + +func IsVeleroFSDataMover(dataMover string) bool { + if dataMover == "" || dataMover == DataMoverTypeVelero { + dataMover = DataMoverTypeVeleroFs + } + return dataMover == DataMoverTypeVeleroFs +} + +func IsVeleroBlockDataMover(dataMover string) bool { + return dataMover == DataMoverTypeVeleroBlock } // GetDefaultBuiltInDataMover returns the data mover used when the default diff --git a/pkg/util/datamover/datamover_test.go b/pkg/util/datamover/datamover_test.go index 8576aed0e..94585e8f9 100644 --- a/pkg/util/datamover/datamover_test.go +++ b/pkg/util/datamover/datamover_test.go @@ -38,6 +38,16 @@ func TestIsBuiltInDataMover(t *testing.T) { dataMover: "velero", want: true, }, + { + name: "velero-fs dataMover is builtin", + dataMover: "velero-fs", + want: true, + }, + { + name: "velero-block dataMover is builtin", + dataMover: "velero-block", + want: true, + }, { name: "kopia dataMover is not builtin", dataMover: "kopia", @@ -54,3 +64,61 @@ func TestIsBuiltInDataMover(t *testing.T) { func TestGetDefaultBuiltInDataMover(t *testing.T) { assert.Equal(t, DataMoverTypeVeleroFs, GetDefaultBuiltInDataMover()) } + +func TestIsFSDataMover(t *testing.T) { + testcases := []struct { + name string + dataMover string + want bool + }{ + { + name: "empty dataMover is fs", + dataMover: "", + want: true, + }, + { + name: "velero dataMover is fs", + dataMover: "velero", + want: true, + }, + { + name: "velero-fs dataMover is fs", + dataMover: "velero-fs", + want: true, + }, + { + name: "velero-block dataMover is not fs", + dataMover: "velero-block", + want: false, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(tt *testing.T) { + assert.Equal(tt, tc.want, IsVeleroFSDataMover(tc.dataMover)) + }) + } +} + +func TestIsBlockDataMover(t *testing.T) { + testcases := []struct { + name string + dataMover string + want bool + }{ + { + name: "velero-block dataMover is block", + dataMover: "velero-block", + want: true, + }, + { + name: "velero-fs dataMover is not block", + dataMover: "velero-fs", + want: false, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(tt *testing.T) { + assert.Equal(tt, tc.want, IsVeleroBlockDataMover(tc.dataMover)) + }) + } +}