From 5db63d8a18a7054d1a4ff53b5b4f5dbf47feeb03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Utku=20=C3=96zdemir?= <uoz@protonmail.com> Date: Fri, 4 Feb 2022 19:56:08 +0100 Subject: [PATCH] fix: do not fail on pvc names longer than 63 characters (#150) Signed-off-by: Utku Ozdemir <uoz@protonmail.com> --- helm/pv-migrate/Chart.yaml | 4 +- helm/pv-migrate/templates/rsync/job.yaml | 16 ++--- .../pv-migrate/templates/sshd/deployment.yaml | 16 ++--- internal/integrationtest/app_test.go | 65 ++++++++++++++++++ internal/migrator/migrator.go | 2 +- internal/migrator/pv-migrate-0.2.0.tgz | Bin 4264 -> 0 bytes internal/migrator/pv-migrate-0.2.1.tgz | Bin 0 -> 4286 bytes 7 files changed, 84 insertions(+), 19 deletions(-) delete mode 100644 internal/migrator/pv-migrate-0.2.0.tgz create mode 100644 internal/migrator/pv-migrate-0.2.1.tgz diff --git a/helm/pv-migrate/Chart.yaml b/helm/pv-migrate/Chart.yaml index fb749294..0686ce66 100644 --- a/helm/pv-migrate/Chart.yaml +++ b/helm/pv-migrate/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: pv-migrate description: The helm chart of pv-migrate type: application -version: 0.2.0 -appVersion: 0.2.0 +version: 0.2.1 +appVersion: 0.2.1 home: https://github.com/utkuozdemir/pv-migrate keywords: - pv-migrate diff --git a/helm/pv-migrate/templates/rsync/job.yaml b/helm/pv-migrate/templates/rsync/job.yaml index b9999e51..0118a480 100644 --- a/helm/pv-migrate/templates/rsync/job.yaml +++ b/helm/pv-migrate/templates/rsync/job.yaml @@ -55,10 +55,10 @@ spec: resources: {{- toYaml .Values.rsync.resources | nindent 12 }} volumeMounts: - {{- range .Values.rsync.pvcMounts }} - - mountPath: {{ .mountPath }} - name: {{ .name }} - readOnly: {{ default false .readOnly }} + {{- range $index, $mount := .Values.rsync.pvcMounts }} + - mountPath: {{ $mount.mountPath }} + name: vol-{{ $index }} + readOnly: {{ default false $mount.readOnly }} {{- end }} {{- if .Values.rsync.privateKeyMount }} - mountPath: {{ .Values.rsync.privateKeyMountPath }} @@ -79,11 +79,11 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} volumes: - {{- range .Values.rsync.pvcMounts }} - - name: {{ .name }} + {{- range $index, $mount := .Values.rsync.pvcMounts }} + - name: vol-{{ $index }} persistentVolumeClaim: - claimName: {{ required ".Values.rsync.pvcMounts[*].pvcName is required!" .name }} - readOnly: {{ default false .readOnly }} + claimName: {{ required ".Values.rsync.pvcMounts[*].pvcName is required!" $mount.name }} + readOnly: {{ default false $mount.readOnly }} {{- end }} {{- if .Values.rsync.privateKeyMount }} - name: private-key diff --git a/helm/pv-migrate/templates/sshd/deployment.yaml b/helm/pv-migrate/templates/sshd/deployment.yaml index 6d85f5d4..0d47c620 100644 --- a/helm/pv-migrate/templates/sshd/deployment.yaml +++ b/helm/pv-migrate/templates/sshd/deployment.yaml @@ -40,10 +40,10 @@ spec: resources: {{- toYaml .Values.sshd.resources | nindent 12 }} volumeMounts: - {{- range .Values.sshd.pvcMounts }} - - mountPath: {{ .mountPath }} - name: {{ .name }} - readOnly: {{ default false .readOnly }} + {{- range $index, $mount := .Values.sshd.pvcMounts }} + - mountPath: {{ $mount.mountPath }} + name: vol-{{ $index }} + readOnly: {{ default false $mount.readOnly }} {{- end }} {{- if .Values.sshd.publicKeyMount }} - mountPath: {{ .Values.sshd.publicKeyMountPath }} @@ -69,11 +69,11 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} volumes: - {{- range .Values.sshd.pvcMounts }} - - name: {{ .name }} + {{- range $index, $mount := .Values.sshd.pvcMounts }} + - name: vol-{{ $index }} persistentVolumeClaim: - claimName: {{ required ".Values.sshd.pvcMounts[*].pvcName is required!" .name }} - readOnly: {{ default false .readOnly }} + claimName: {{ required ".Values.sshd.pvcMounts[*].pvcName is required!" $mount.name }} + readOnly: {{ default false $mount.readOnly }} {{- end }} {{- if or .Values.sshd.publicKeyMount .Values.sshd.privateKeyMount }} - name: keys diff --git a/internal/integrationtest/app_test.go b/internal/integrationtest/app_test.go index 81e00937..f277a0a4 100644 --- a/internal/integrationtest/app_test.go +++ b/internal/integrationtest/app_test.go @@ -37,6 +37,9 @@ const ( dataFilePath = "/volume/file.txt" extraDataFilePath = "/volume/extra_file.txt" generateDataContent = "DATA" + + longSourcePvcName = "source-source-source-source-source-source-source-source-source-source-source-source-source-source-source-source-source-source-source-source-source-source-source-source-source-source-source" + longDestPvcName = "dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest-dest" ) var ( @@ -299,6 +302,28 @@ func TestLocal(t *testing.T) { assert.NoError(t, err) } +func TestLongPVCNames(t *testing.T) { + _, err := execInPod(mainClusterCli, ns1, "long-dest", clearDataShellCommand) + assert.NoError(t, err) + + cmd := fmt.Sprintf("--log-level debug m -i -n %s -N %s %s %s", + ns1, ns1, longSourcePvcName, longDestPvcName) + assert.NoError(t, runCliApp(cmd)) + + stdout, err := execInPod(mainClusterCli, ns1, "long-dest", printDataUidGidContentShellCommand) + assert.NoError(t, err) + + parts := strings.Split(stdout, "\n") + assert.Equal(t, len(parts), 3) + if len(parts) < 3 { + return + } + + assert.Equal(t, dataFileUid, parts[0]) + assert.Equal(t, dataFileGid, parts[1]) + assert.Equal(t, generateDataContent, parts[2]) +} + func setup() error { homeDir, err := userHomeDir() if err != nil { @@ -342,6 +367,11 @@ func setup() error { return err } + err = setupPVCsWithLongName() + if err != nil { + return err + } + _, err = createPVC(mainClusterCli, ns1, "source") if err != nil { return err @@ -426,6 +456,41 @@ func setup() error { return err } +func setupPVCsWithLongName() error { + _, err := createPVC(mainClusterCli, ns1, longSourcePvcName) + if err != nil { + return err + } + + _, err = createPVC(mainClusterCli, ns1, longDestPvcName) + if err != nil { + return err + } + + _, err = createPod(mainClusterCli, ns1, "long-source", longSourcePvcName) + if err != nil { + return err + } + + _, err = createPod(mainClusterCli, ns1, "long-dest", longDestPvcName) + if err != nil { + return err + } + + err = waitUntilPodIsRunning(mainClusterCli, ns1, "long-source") + if err != nil { + return err + } + + err = waitUntilPodIsRunning(mainClusterCli, ns1, "long-dest") + if err != nil { + return err + } + + _, err = execInPod(mainClusterCli, ns1, "long-source", generateDataShellCommand) + return err +} + func teardown() error { var result *multierror.Error err := deleteNs(mainClusterCli, ns1) diff --git a/internal/migrator/migrator.go b/internal/migrator/migrator.go index 0e138883..09f49572 100644 --- a/internal/migrator/migrator.go +++ b/internal/migrator/migrator.go @@ -16,7 +16,7 @@ import ( "helm.sh/helm/v3/pkg/chart/loader" ) -//go:embed pv-migrate-0.2.0.tgz +//go:embed pv-migrate-0.2.1.tgz var chartBytes []byte type ( diff --git a/internal/migrator/pv-migrate-0.2.0.tgz b/internal/migrator/pv-migrate-0.2.0.tgz deleted file mode 100644 index 19a9a293ec80800ace2dca9f4eecafe881aeba03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4264 zcmb2|<`7{3f&ZEe+KC=P2FV`2W<Hgcrb)(O1}VX&nNh)(X8vJeX1?J$S&4Zml_7!o zwjQZDxeRY?GmCFqO#3@?|D&yQW_H=?zWT6r_1!mqr%qn^;#Yo4^!t*m(^I{iRa6w0 zF_<w}6-M8izp*~!{|zw)IfaUjN&TMbE&SU!_6Z+2%q+KH`Hx-uRf_vm<mTuKO#jCl z<#yjDN0P7o@Z`nHf9ow|cKvr>eEFmK;>8y~KD_wy;maRiW<PvUP-FCut&LyDJ=e3v z<<IPgdn<RI`s>HFXo{x_=i;PCj0$=ud02}k`qUm`OX++T;gj%`k>N|@BGq{gO6!Hb zMR!bZ;qUr*Sv*NF%YvcTym10+_zZ0)U5&{T?Z33gN)!}2H}hI%%$d`=IL?L1hjre8 z(-N%QcjSLN^B;X8`>e^ps`2&4_D2@=GQYSKjwn4)nYQcIbJjN2zG(s)j~UIHo3?LH z@S52=W#bGUw&F!M*>Y|wtS)cOw=<OM>b$=F<cjtKubV5?daAr#R=BhztxNnUG%<sT z^Y`2HhUt?6zpjy}oM86i{?kyV|KInwEoGnZVTHh%NypM_-bsphGaM9>OE|k~x6voH zW5N!FG7}GY)W+r=um5u5zG�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 diff --git a/internal/migrator/pv-migrate-0.2.1.tgz b/internal/migrator/pv-migrate-0.2.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..58f45d35b2fc9636ed2bd7977e1f1a8a0e8961b7 GIT binary patch literal 4286 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 IP{zOj0B)CnBme*a literal 0 HcmV?d00001 -- GitLab