Skip to content
Open
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
3 changes: 3 additions & 0 deletions charts/dragonfly-operator/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ spec:
- name: manager
args:
- --leader-elect
{{- if .Values.dragonflyImage }}
- --dragonfly-image={{ .Values.dragonflyImage }}
{{- end }}
{{- if .Values.rbacProxy.enabled }}
- --metrics-bind-address=127.0.0.1:8080
{{- end }}
Expand Down
3 changes: 3 additions & 0 deletions charts/dragonfly-operator/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ crds:
nameOverride: ""
fullnameOverride: ""

# -- Default dragonfly image to use
dragonflyImage: ""

# -- Additional labels to add to all resources
additionalLabels: {}
# app: dragonfly-operator
Expand Down
16 changes: 10 additions & 6 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,10 @@ func main() {
var probeAddr string
var versionFlag bool
var watchCurrentNamespace bool
var dragonflyImage string
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.StringVar(&dragonflyImage, "dragonfly-image", "", "The default dragonfly image to use.")
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
Expand Down Expand Up @@ -145,9 +147,10 @@ func main() {

if err = (&controller.DragonflyReconciler{
Reconciler: controller.Reconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
EventRecorder: eventRecorder,
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
EventRecorder: eventRecorder,
DefaultDragonflyImage: dragonflyImage,
},
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Dragonfly")
Expand All @@ -156,9 +159,10 @@ func main() {

if err = (&controller.DfPodLifeCycleReconciler{
Reconciler: controller.Reconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
EventRecorder: eventRecorder,
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
EventRecorder: eventRecorder,
DefaultDragonflyImage: dragonflyImage,
},
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Health")
Expand Down
19 changes: 11 additions & 8 deletions internal/controller/base_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package controller

import (
"context"

dfv1alpha1 "github.com/dragonflydb/dragonfly-operator/api/v1alpha1"
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -27,9 +28,10 @@ import (
)

type Reconciler struct {
Client client.Client
Scheme *runtime.Scheme
EventRecorder record.EventRecorder
Client client.Client
Scheme *runtime.Scheme
EventRecorder record.EventRecorder
DefaultDragonflyImage string
}

func (r *Reconciler) getDragonflyInstance(ctx context.Context, namespacedName types.NamespacedName, log logr.Logger) (*DragonflyInstance, error) {
Expand All @@ -41,10 +43,11 @@ func (r *Reconciler) getDragonflyInstance(ctx context.Context, namespacedName ty
}

return &DragonflyInstance{
df: &df,
client: r.Client,
log: log,
scheme: r.Scheme,
eventRecorder: r.EventRecorder,
df: &df,
client: r.Client,
log: log,
scheme: r.Scheme,
eventRecorder: r.EventRecorder,
defaultDragonflyImage: r.DefaultDragonflyImage,
}, nil
}
11 changes: 6 additions & 5 deletions internal/controller/dragonfly_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ type DragonflyInstance struct {
// Dragonfly is the relevant Dragonfly CRD that it performs actions over
df *dfv1alpha1.Dragonfly

client client.Client
log logr.Logger
scheme *runtime.Scheme
eventRecorder record.EventRecorder
client client.Client
log logr.Logger
scheme *runtime.Scheme
eventRecorder record.EventRecorder
defaultDragonflyImage string
}

// configureReplication configures the given pod as a master and other pods as replicas
Expand Down Expand Up @@ -487,7 +488,7 @@ func (dfi *DragonflyInstance) replicaOfNoOne(ctx context.Context, pod *corev1.Po

// reconcileResources creates or updates the dragonfly resources
func (dfi *DragonflyInstance) reconcileResources(ctx context.Context) error {
dfResources, err := resources.GenerateDragonflyResources(dfi.df)
dfResources, err := resources.GenerateDragonflyResources(dfi.df, dfi.defaultDragonflyImage)
if err != nil {
return fmt.Errorf("failed to generate dragonfly resources")
}
Expand Down
62 changes: 62 additions & 0 deletions internal/resources/image_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package resources

import (
"fmt"
"testing"

resourcesv1 "github.com/dragonflydb/dragonfly-operator/api/v1alpha1"
"github.com/stretchr/testify/assert"
appsv1 "k8s.io/api/apps/v1"
)

func TestGenerateDragonflyResources_ImageResolution(t *testing.T) {
tests := []struct {
name string
crdImage string
defaultImage string
expectedImage string
}{
{
name: "CRD image takes precedence",
crdImage: "crd-image:v1",
defaultImage: "default-image:v1",
expectedImage: "crd-image:v1",
},
{
name: "Default image used when CRD image is empty",
crdImage: "",
defaultImage: "default-image:v1",
expectedImage: "default-image:v1",
},
{
name: "Hardcoded default used when both are empty",
crdImage: "",
defaultImage: "",
expectedImage: fmt.Sprintf("%s:%s", DragonflyImage, Version),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
df := &resourcesv1.Dragonfly{
Spec: resourcesv1.DragonflySpec{
Image: tt.crdImage,
},
}

objs, err := GenerateDragonflyResources(df, tt.defaultImage)
assert.NoError(t, err)

var sts *appsv1.StatefulSet
for _, obj := range objs {
if s, ok := obj.(*appsv1.StatefulSet); ok {
sts = s
break
}
}

assert.NotNil(t, sts)
assert.Equal(t, tt.expectedImage, sts.Spec.Template.Spec.Containers[0].Image)
})
}
}
8 changes: 6 additions & 2 deletions internal/resources/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,16 @@ var (

// GenerateDragonflyResources returns the resources required for a Dragonfly
// Instance
func GenerateDragonflyResources(df *resourcesv1.Dragonfly) ([]client.Object, error) {
func GenerateDragonflyResources(df *resourcesv1.Dragonfly, defaultDragonflyImage string) ([]client.Object, error) {
var resources []client.Object

image := df.Spec.Image
if image == "" {
image = fmt.Sprintf("%s:%s", DragonflyImage, Version)
if defaultDragonflyImage != "" {
image = defaultDragonflyImage
} else {
image = fmt.Sprintf("%s:%s", DragonflyImage, Version)
}
}

// Create a StatefulSet, Headless Service
Expand Down