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
13 changes: 11 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"flag"
"os"
"os/signal"
"regexp"

"github.com/super-phenix/volume-replicator/internal/k8s"
"github.com/super-phenix/volume-replicator/internal/replicator"
Expand All @@ -16,17 +17,25 @@ func main() {
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()

var kubeconfig, namespace string
var kubeconfig, namespace, exclusionRegexStr string
flag.StringVar(&kubeconfig, "kubeconfig", "", "path to kubeconfig file")
flag.StringVar(&namespace, "namespace", os.Getenv("NAMESPACE"), "deployment namespace")
flag.StringVar(&replicator.ExclusionRegex, "exclusion-regex", os.Getenv("EXCLUSION_REGEX"), "regex to exclude PVCs from replication")
flag.StringVar(&exclusionRegexStr, "exclusion-regex", os.Getenv("EXCLUSION_REGEX"), "regex to exclude PVCs from replication")
klog.InitFlags(nil)
flag.Parse()

if namespace == "" {
klog.Fatalf("must provide the namespace in which the controller is running through --namespace")
}

if exclusionRegexStr != "" {
var err error
replicator.ExclusionRegex, err = regexp.Compile(exclusionRegexStr)
if err != nil {
klog.Fatalf("failed to compile exclusion regex: %s", err.Error())
}
}

if err := k8s.Load(kubeconfig); err != nil {
klog.Fatalf("failed to load kubernetes configuration: %s", err.Error())
}
Expand Down
26 changes: 24 additions & 2 deletions internal/replicator/informers.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,18 @@ func (c *Controller) createPvcInformer(factory informers.SharedInformerFactory)
c.pvcUpdate(newObj.(*corev1.PersistentVolumeClaim))
},
DeleteFunc: func(obj any) {
c.pvcUpdate(obj.(*corev1.PersistentVolumeClaim))
pvc, ok := obj.(*corev1.PersistentVolumeClaim)
if !ok {
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
return
}
pvc, ok = tombstone.Obj.(*corev1.PersistentVolumeClaim)
if !ok {
return
}
}
c.pvcUpdate(pvc)
},
})
}
Expand All @@ -87,7 +98,18 @@ func (c *Controller) createVolumeReplicationInformer(factory dynamicinformer.Dyn
c.volumeReplicationUpdate(oldObj.(*unstructured.Unstructured), newObj.(*unstructured.Unstructured))
},
DeleteFunc: func(obj any) {
c.volumeReplicationCreateOrDelete(obj.(*unstructured.Unstructured))
vr, ok := obj.(*unstructured.Unstructured)
if !ok {
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
return
}
vr, ok = tombstone.Obj.(*unstructured.Unstructured)
if !ok {
return
}
}
c.volumeReplicationCreateOrDelete(vr)
},
})
}
13 changes: 3 additions & 10 deletions internal/replicator/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"k8s.io/klog/v2"
)

var ExclusionRegex string
var ExclusionRegex *regexp.Regexp

// isVolumeReplicationCorrect verifies if the definition of a VolumeReplication conforms to its originating PVC
func isVolumeReplicationCorrect(pvc *corev1.PersistentVolumeClaim, vr *unstructured.Unstructured) bool {
Expand Down Expand Up @@ -197,17 +197,10 @@ func isNamespacePaused(namespace string) bool {
// pvcNameMatchesExclusion returns whether a PVC has a name matching the exclusion regex
func pvcNameMatchesExclusion(pvc *corev1.PersistentVolumeClaim) bool {
// If no regex is provided, return that it doesn't match
// This is to avoid Go matching "" as "everything matches"
if ExclusionRegex == "" {
if ExclusionRegex == nil {
return false
}

// Match the user-provided regex
match, err := regexp.MatchString(ExclusionRegex, pvc.Name)
if err != nil {
klog.Errorf("failed to parse exclusion regex: %s", err.Error())
return false
}

return match
return ExclusionRegex.MatchString(pvc.Name)
}
15 changes: 14 additions & 1 deletion internal/replicator/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package replicator

import (
"fmt"
"regexp"
"testing"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -940,7 +941,19 @@ func TestPvcNameMatchesExclusion(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ExclusionRegex = tt.exclusionRegex
if tt.exclusionRegex != "" {
var err error
ExclusionRegex, err = regexp.Compile(tt.exclusionRegex)
if tt.name != "Invalid regex" {
require.NoError(t, err)
} else {
require.Error(t, err)
ExclusionRegex = nil // Simulate what happens when it fails (though main would exit)
}
} else {
ExclusionRegex = nil
}

pvc := &corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: tt.pvcName,
Expand Down
7 changes: 4 additions & 3 deletions internal/replicator/vrc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package replicator

import (
"fmt"
"regexp"
"testing"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -211,11 +212,11 @@ func TestGetVolumeReplicationClass(t *testing.T) {

// Set ExclusionRegex specifically for each test case
if tt.name == "Excluded by name regex" {
ExclusionRegex = "exclude-.*"
ExclusionRegex = regexp.MustCompile("exclude-.*")
} else {
ExclusionRegex = ""
ExclusionRegex = nil
}
defer func() { ExclusionRegex = "" }()
defer func() { ExclusionRegex = nil }()

if tt.namespace != nil {
err := NamespaceInformer.Informer().GetIndexer().Add(tt.namespace)
Expand Down
Loading