Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 19 additions & 21 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
module github.com/replicatedhq/pvmigrate

go 1.24.0
go 1.25.0

require (
github.com/google/go-cmp v0.7.0
github.com/stretchr/testify v1.11.1
k8s.io/api v0.34.1
k8s.io/apimachinery v0.34.1
k8s.io/client-go v0.34.1
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
sigs.k8s.io/controller-runtime v0.22.3
k8s.io/api v0.35.0
k8s.io/apimachinery v0.35.0
k8s.io/client-go v0.35.0
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4
sigs.k8s.io/controller-runtime v0.23.1
)

require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
Expand All @@ -29,26 +28,25 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/spf13/pflag v1.0.9 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/time v0.9.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
google.golang.org/protobuf v1.36.8 // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
131 changes: 52 additions & 79 deletions go.sum

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions pkg/k8sutil/storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func TestPVCSForPVs(t *testing.T) {
},
} {
t.Run(tt.name, func(t *testing.T) {
kcli := fake.NewSimpleClientset(tt.objs...)
kcli := fake.NewClientset(tt.objs...)
result, err := PVCSForPVs(context.Background(), kcli, tt.input)
if err != nil {
if len(tt.err) == 0 {
Expand Down Expand Up @@ -227,7 +227,7 @@ func TestPVsByStorageClass(t *testing.T) {
},
} {
t.Run(tt.name, func(t *testing.T) {
kcli := fake.NewSimpleClientset(tt.objs...)
kcli := fake.NewClientset(tt.objs...)
result, err := PVsByStorageClass(context.Background(), kcli, tt.scname)
if err != nil {
if len(tt.err) == 0 {
Expand Down
66 changes: 55 additions & 11 deletions pkg/migrate/migrate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ func (tw testWriter) Write(p []byte) (n int, err error) {
return len(p), nil
}

// clearManagedFields removes ManagedFields from k8s objects so tests can compare
// against expected objects without worrying about server-side field tracking added
// by fake.NewClientset.
func clearManagedFields(obj any) {
type hasManagedFields interface {
GetManagedFields() []metav1.ManagedFieldsEntry
SetManagedFields([]metav1.ManagedFieldsEntry)
}
if m, ok := obj.(hasManagedFields); ok {
m.SetManagedFields(nil)
}
}

func TestScaleUpPods(t *testing.T) {
zeroInt := int32(0)
tests := []struct {
Expand Down Expand Up @@ -201,7 +214,7 @@ func TestScaleUpPods(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := require.New(t)
clientset := fake.NewSimpleClientset(test.resources...)
clientset := fake.NewClientset(test.resources...)
testlog := log.New(testWriter{t: t}, "", 0)
err := scaleUpPods(context.Background(), testlog, clientset, test.namespaces)
req.NoError(err)
Expand Down Expand Up @@ -260,7 +273,7 @@ func TestMutatePV(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := require.New(t)
clientset := fake.NewSimpleClientset(test.resources...)
clientset := fake.NewClientset(test.resources...)
testlog := log.New(testWriter{t: t}, "", 0)
err := mutatePV(context.Background(), testlog, clientset, test.pvname, test.ttmutator, test.ttchecker)
req.NoError(err)
Expand Down Expand Up @@ -342,7 +355,7 @@ func TestValidateStorageClasses(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := require.New(t)
clientset := fake.NewSimpleClientset(test.resources...)
clientset := fake.NewClientset(test.resources...)
testlog := log.New(testWriter{t: t}, "", 0)
err := validateStorageClasses(context.Background(), testlog, clientset, test.sourceSC, test.destSC, test.skipSourceValidation)
if !test.wantErr {
Expand Down Expand Up @@ -894,7 +907,7 @@ func TestGetPVCs(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := require.New(t)
clientset := fake.NewSimpleClientset(test.resources...)
clientset := fake.NewClientset(test.resources...)
testlog := log.New(testWriter{t: t}, "", 0)
originalPVCs, nses, err := getPVCs(context.Background(), testlog, clientset, test.sourceScName, test.destScName, test.namespace)
if !test.wantErr {
Expand Down Expand Up @@ -1261,7 +1274,7 @@ func Test_createMigrationPod(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := require.New(t)
clientset := fake.NewSimpleClientset()
clientset := fake.NewClientset()

if tt.setGlobalFunc != nil {
tt.setGlobalFunc()
Expand All @@ -1279,6 +1292,7 @@ func Test_createMigrationPod(t *testing.T) {
}

req.NoError(err)
clearManagedFields(got)
req.Equal(tt.want, got)
})
}
Expand Down Expand Up @@ -1813,7 +1827,7 @@ func Test_swapPVs(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := require.New(t)
clientset := fake.NewSimpleClientset(tt.resources...)
clientset := fake.NewClientset(tt.resources...)
testlog := log.New(testWriter{t: t}, "", 0)

if tt.backgroundFunc != nil {
Expand All @@ -1829,10 +1843,16 @@ func Test_swapPVs(t *testing.T) {

finalPVs, err := clientset.CoreV1().PersistentVolumes().List(context.Background(), metav1.ListOptions{})
req.NoError(err)
for i := range finalPVs.Items {
clearManagedFields(&finalPVs.Items[i])
}
req.Equal(tt.wantPVs, finalPVs.Items)

finalPVCs, err := clientset.CoreV1().PersistentVolumeClaims(tt.ns).List(context.Background(), metav1.ListOptions{})
req.NoError(err)
for i := range finalPVCs.Items {
clearManagedFields(&finalPVCs.Items[i])
}
req.Equal(tt.wantPVCs, finalPVCs.Items)
})
}
Expand Down Expand Up @@ -2008,7 +2028,7 @@ func Test_resetReclaimPolicy(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := require.New(t)
clientset := fake.NewSimpleClientset(tt.resources...)
clientset := fake.NewClientset(tt.resources...)
testlog := log.New(testWriter{t: t}, "", 0)
err := resetReclaimPolicy(context.Background(), testlog, clientset, tt.pv, tt.reclaim)
if tt.wantErr {
Expand All @@ -2019,6 +2039,9 @@ func Test_resetReclaimPolicy(t *testing.T) {

finalPVs, err := clientset.CoreV1().PersistentVolumes().List(context.Background(), metav1.ListOptions{})
req.NoError(err)
for i := range finalPVs.Items {
clearManagedFields(&finalPVs.Items[i])
}
req.Equal(tt.wantPVs, finalPVs.Items)
})
}
Expand Down Expand Up @@ -2765,7 +2788,7 @@ func Test_scaleDownPods(t *testing.T) {
req := require.New(t)
testCtx, cancelfunc := context.WithTimeout(context.Background(), time.Minute) // if your test takes more than 1m, there are issues
defer cancelfunc()
clientset := fake.NewSimpleClientset(tt.resources...)
clientset := fake.NewClientset(tt.resources...)
testlog := log.New(testWriter{t: t}, "", 0)
if tt.backgroundFunc != nil {
go tt.backgroundFunc(testCtx, testlog, clientset)
Expand All @@ -2784,23 +2807,41 @@ func Test_scaleDownPods(t *testing.T) {
for _, ns := range tt.nsList {
finalNsPods, err := clientset.CoreV1().Pods(ns).List(testCtx, metav1.ListOptions{})
req.NoError(err)
for i := range finalNsPods.Items {
clearManagedFields(&finalNsPods.Items[i])
}
actualPods[ns] = finalNsPods.Items

finalNsDeps, err := clientset.AppsV1().Deployments(ns).List(testCtx, metav1.ListOptions{})
req.NoError(err)
for i := range finalNsDeps.Items {
clearManagedFields(&finalNsDeps.Items[i])
}
actualDeployments[ns] = finalNsDeps.Items

finalNsSS, err := clientset.AppsV1().StatefulSets(ns).List(testCtx, metav1.ListOptions{})
req.NoError(err)
for i := range finalNsSS.Items {
clearManagedFields(&finalNsSS.Items[i])
}
actualSS[ns] = finalNsSS.Items
}
for ns, pvcs := range actualMatchingPVCs {
for i := range pvcs {
clearManagedFields(pvcs[i])
}
actualMatchingPVCs[ns] = pvcs
}
req.Equal(tt.wantPods, actualPods)
req.Equal(tt.wantDeployments, actualDeployments)
req.Equal(tt.wantSS, actualSS)
req.Equal(tt.wantMatchingPVCs, actualMatchingPVCs)

actualPVs, err := clientset.CoreV1().PersistentVolumes().List(testCtx, metav1.ListOptions{})
req.NoError(err)
for i := range actualPVs.Items {
clearManagedFields(&actualPVs.Items[i])
}
req.Equal(tt.wantPVs, actualPVs.Items)
})
}
Expand Down Expand Up @@ -3157,7 +3198,7 @@ func Test_swapDefaults(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := require.New(t)
clientset := fake.NewSimpleClientset(tt.resources...)
clientset := fake.NewClientset(tt.resources...)
testlog := log.New(testWriter{t: t}, "", 0)
err := swapDefaultStorageClasses(context.Background(), testlog, clientset, tt.oldDefaultSC, tt.newDefaultSC)
if tt.wantErr {
Expand All @@ -3169,6 +3210,9 @@ func Test_swapDefaults(t *testing.T) {

finalSCs, err := clientset.StorageV1().StorageClasses().List(context.Background(), metav1.ListOptions{})
req.NoError(err)
for i := range finalSCs.Items {
clearManagedFields(&finalSCs.Items[i])
}
req.Equal(tt.wantSCs, finalSCs.Items)
})
}
Expand Down Expand Up @@ -3197,7 +3241,7 @@ func Test_waitForDeletion(t *testing.T) {
req := require.New(t)
testCtx, cancelfunc := context.WithTimeout(context.Background(), time.Minute) // if your test takes more than 1m, there are issues
defer cancelfunc()
clientset := fake.NewSimpleClientset(
clientset := fake.NewClientset(
[]runtime.Object{
&corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Expand Down Expand Up @@ -3443,7 +3487,7 @@ func Test_copyAllPVCs(t *testing.T) {
req := require.New(t)
testCtx, cancelfunc := context.WithTimeout(context.Background(), time.Second*10) // if your test takes more than 10s, there are issues
defer cancelfunc()
clientset := fake.NewSimpleClientset(tt.resources...)
clientset := fake.NewClientset(tt.resources...)
testlog := log.New(testWriter{t: t}, "", 0)

// handle making the pods start/succeed/fail/etc
Expand Down
2 changes: 1 addition & 1 deletion pkg/preflight/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ func getPVCError(ctx context.Context, client k8sclient.Interface, pvc *corev1.Pe

// pvcsForStorageClass returns all PersistentVolumeClaims, filtered by namespace, for a given
// storage class
func pvcsForStorageClass(ctx context.Context, l *log.Logger, client k8sclient.Interface, srcSC, namespace string) (map[string]corev1.PersistentVolumeClaim, error) {
func pvcsForStorageClass(ctx context.Context, _ *log.Logger, client k8sclient.Interface, srcSC, namespace string) (map[string]corev1.PersistentVolumeClaim, error) {
srcPVs, err := k8sutil.PVsByStorageClass(ctx, client, srcSC)
if err != nil {
return nil, fmt.Errorf("failed to get PVs for storage class %s: %w", srcSC, err)
Expand Down
12 changes: 6 additions & 6 deletions pkg/preflight/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func Test_validateVolumeAccessModes(t *testing.T) {
} {
t.Run(tt.name, func(t *testing.T) {
req := require.New(t)
kcli := fake.NewSimpleClientset(tt.resources...)
kcli := fake.NewClientset(tt.resources...)
logger := log.New(io.Discard, "", 0)
result, err := validateVolumeAccessModes(context.Background(), logger, kcli, tt.dstSC, "eeacms/rsync:2.3", tt.podReadyTimeout, tt.deletePVTimeout, tt.input)
if err != nil {
Expand Down Expand Up @@ -291,7 +291,7 @@ func Test_getPvcError(t *testing.T) {
} {
t.Run(tt.name, func(t *testing.T) {
req := require.New(t)
kcli := fake.NewSimpleClientset(tt.resources...)
kcli := fake.NewClientset(tt.resources...)
result, err := getPVCError(t.Context(), kcli, tt.input)
if err != nil {
if tt.wantErr {
Expand Down Expand Up @@ -431,7 +431,7 @@ func Test_checkVolumeAccessModes(t *testing.T) {
req := require.New(t)
testCtx, cancelfunc := context.WithTimeout(context.Background(), time.Minute) // if your test takes more than 1m, there are issues
defer cancelfunc()
kcli := fake.NewSimpleClientset(tt.resources...)
kcli := fake.NewClientset(tt.resources...)
logger := log.New(io.Discard, "", 0)
if tt.backgroundFunc != nil {
go tt.backgroundFunc(testCtx, logger, kcli, tt.tmpPodName, "default", "pv-for-pf-pvc-testpvc")
Expand Down Expand Up @@ -891,7 +891,7 @@ func Test_pvcsForStorageClass(t *testing.T) {
} {
t.Run(tt.name, func(t *testing.T) {
req := require.New(t)
kcli := fake.NewSimpleClientset(tt.resources...)
kcli := fake.NewClientset(tt.resources...)
logger := log.New(io.Discard, "", 0)
result, err := pvcsForStorageClass(context.Background(), logger, kcli, tt.scname, tt.namespace)
if err != nil {
Expand Down Expand Up @@ -985,7 +985,7 @@ func Test_validateStorageClasses(t *testing.T) {
} {
t.Run(tt.name, func(t *testing.T) {
req := require.New(t)
clientset := fake.NewSimpleClientset(tt.resources...)
clientset := fake.NewClientset(tt.resources...)
logger := log.New(io.Discard, "", 0)
result, err := validateStorageClasses(context.Background(), logger, clientset, tt.sourceSC, tt.destSC, tt.skipSourceSCValidation)
if !tt.wantErr {
Expand Down Expand Up @@ -1198,7 +1198,7 @@ func Test_deleteTmpPVCs(t *testing.T) {
} {
t.Run(tt.name, func(t *testing.T) {
req := require.New(t)
clientset := fake.NewSimpleClientset(tt.resources...)
clientset := fake.NewClientset(tt.resources...)
logger := log.New(io.Discard, "", 0)

if tt.backgroundFunc != nil {
Expand Down
2 changes: 1 addition & 1 deletion testing/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

FROM golang:1.24-alpine AS build
FROM golang:1.25-alpine AS build

RUN apk add --no-cache make git bash

Expand Down
Loading