Skip to content

Commit 611ad6d

Browse files
committed
feat: configurable image via spec
1 parent 3037c95 commit 611ad6d

14 files changed

+172
-62
lines changed

api/v1alpha1/limitador_types.go

+32-7
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,23 @@ import (
2222

2323
"github.com/go-logr/logr"
2424
"github.com/google/go-cmp/cmp"
25+
"github.com/kuadrant/limitador-operator/pkg/helpers"
2526
corev1 "k8s.io/api/core/v1"
2627
"k8s.io/apimachinery/pkg/api/resource"
2728
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2829
"k8s.io/apimachinery/pkg/util/intstr"
29-
30-
"github.com/kuadrant/limitador-operator/pkg/helpers"
30+
"k8s.io/utils/env"
3131
)
3232

3333
const (
34-
DefaultServiceHTTPPort int32 = 8080
35-
DefaultServiceGRPCPort int32 = 8081
34+
DefaultServiceHTTPPort int32 = 8080
35+
DefaultServiceGRPCPort int32 = 8081
36+
DefaultLimitadorImage string = "quay.io/kuadrant/limitador:latest"
3637

3738
// Status conditions
3839
StatusConditionReady string = "Ready"
40+
41+
ImageEnvVarKey = "RELATED_IMAGE_LIMITADOR"
3942
)
4043

4144
var (
@@ -65,9 +68,6 @@ type LimitadorSpec struct {
6568
// +optional
6669
Replicas *int `json:"replicas,omitempty"`
6770

68-
// +optional
69-
Version *string `json:"version,omitempty"`
70-
7171
// +optional
7272
Listener *Listener `json:"listener,omitempty"`
7373

@@ -85,6 +85,12 @@ type LimitadorSpec struct {
8585

8686
// +optional
8787
ResourceRequirements *corev1.ResourceRequirements `json:"resourceRequirements,omitempty"`
88+
89+
// +optional
90+
Image *string `json:"image"`
91+
92+
// +optional
93+
ImagePullSecret *corev1.LocalObjectReference `json:"imagePullSecret"`
8894
}
8995

9096
//+kubebuilder:object:root=true
@@ -135,6 +141,25 @@ func (l *Limitador) GetResourceRequirements() *corev1.ResourceRequirements {
135141
return l.Spec.ResourceRequirements
136142
}
137143

144+
func (l *Limitador) GetImage() string {
145+
if l.Spec.Image == nil {
146+
return env.GetString(ImageEnvVarKey, DefaultLimitadorImage)
147+
}
148+
149+
return *l.Spec.Image
150+
}
151+
152+
func (l *Limitador) GetImagePullSecrets() []corev1.LocalObjectReference {
153+
var imagePullSecrets []corev1.LocalObjectReference
154+
if l.Spec.ImagePullSecret == nil {
155+
return nil
156+
}
157+
158+
imagePullSecrets = append(imagePullSecrets, *l.Spec.ImagePullSecret)
159+
160+
return imagePullSecrets
161+
}
162+
138163
//+kubebuilder:object:root=true
139164

140165
// LimitadorList contains a list of Limitador

api/v1alpha1/limitador_types_test.go

+51
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package v1alpha1
22

33
import (
44
"fmt"
5+
"os"
56
"strconv"
67
"testing"
78

@@ -210,3 +211,53 @@ func TestLimitadorStatusEquals(t *testing.T) {
210211
assert.Equal(subT, l.Equals(status, logr.Logger{}), true)
211212
})
212213
}
214+
215+
func TestLimitadorGetImage(t *testing.T) {
216+
var (
217+
testImage = "quay.io/test/limitador:nightly"
218+
)
219+
const envVarImage = "quay.io/env/limitador:custom"
220+
221+
setEnvVarImage := func(t *testing.T) {
222+
if err := os.Setenv(ImageEnvVarKey, envVarImage); err != nil {
223+
t.Fatal(err)
224+
}
225+
}
226+
unSetEnvVarImage := func(t *testing.T) {
227+
if err := os.Unsetenv(ImageEnvVarKey); err != nil {
228+
t.Fatal(err)
229+
}
230+
}
231+
232+
t.Run("test default image if not specified in spec", func(subT *testing.T) {
233+
l := &Limitador{}
234+
assert.Equal(subT, l.GetImage(), DefaultLimitadorImage)
235+
})
236+
237+
t.Run("test image override via env var", func(subT *testing.T) {
238+
defer unSetEnvVarImage(subT)
239+
setEnvVarImage(subT)
240+
l := &Limitador{}
241+
assert.Equal(subT, l.GetImage(), envVarImage)
242+
})
243+
244+
t.Run("test image preference via spec over env var and default", func(subT *testing.T) {
245+
defer unSetEnvVarImage(subT)
246+
setEnvVarImage(subT)
247+
l := &Limitador{Spec: LimitadorSpec{Image: &testImage}}
248+
assert.Equal(subT, l.GetImage(), testImage)
249+
})
250+
}
251+
252+
func TestLimitadorGetImagePullSecrets(t *testing.T) {
253+
t.Run("test nil returned if image not specified in spec", func(subT *testing.T) {
254+
l := &Limitador{}
255+
assert.DeepEqual(subT, l.GetImagePullSecrets(), []corev1.LocalObjectReference(nil))
256+
})
257+
258+
t.Run("test pull secret returned if specified in spec", func(subT *testing.T) {
259+
pullSecret := corev1.LocalObjectReference{Name: "pullSecret"}
260+
l := &Limitador{Spec: LimitadorSpec{ImagePullSecret: &pullSecret}}
261+
assert.DeepEqual(subT, l.GetImagePullSecrets(), []corev1.LocalObjectReference{pullSecret})
262+
})
263+
}

api/v1alpha1/zz_generated.deepcopy.go

+10-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bundle/manifests/limitador-operator.clusterserviceversion.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ metadata:
3636
capabilities: Basic Install
3737
categories: Integration & Delivery
3838
containerImage: quay.io/kuadrant/limitador-operator:latest
39-
createdAt: "2023-09-01T15:47:50Z"
39+
createdAt: "2023-09-05T16:08:01Z"
4040
operators.operatorframework.io/builder: operator-sdk-v1.28.1
4141
operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
4242
repository: https://github.com/Kuadrant/limitador-operator

bundle/manifests/limitador.kuadrant.io_limitadors.yaml

+12-2
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,18 @@ spec:
862862
type: array
863863
type: object
864864
type: object
865+
image:
866+
type: string
867+
imagePullSecret:
868+
description: LocalObjectReference contains enough information to let
869+
you locate the referenced object inside the same namespace.
870+
properties:
871+
name:
872+
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
873+
TODO: Add other useful fields. apiVersion, kind, uid?'
874+
type: string
875+
type: object
876+
x-kubernetes-map-type: atomic
865877
limits:
866878
items:
867879
description: RateLimit defines the desired Limitador limit
@@ -1138,8 +1150,6 @@ spec:
11381150
type: object
11391151
type: object
11401152
type: object
1141-
version:
1142-
type: string
11431153
type: object
11441154
status:
11451155
description: LimitadorStatus defines the observed state of Limitador

config/crd/bases/limitador.kuadrant.io_limitadors.yaml

+12-2
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,18 @@ spec:
863863
type: array
864864
type: object
865865
type: object
866+
image:
867+
type: string
868+
imagePullSecret:
869+
description: LocalObjectReference contains enough information to let
870+
you locate the referenced object inside the same namespace.
871+
properties:
872+
name:
873+
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
874+
TODO: Add other useful fields. apiVersion, kind, uid?'
875+
type: string
876+
type: object
877+
x-kubernetes-map-type: atomic
866878
limits:
867879
items:
868880
description: RateLimit defines the desired Limitador limit
@@ -1139,8 +1151,6 @@ spec:
11391151
type: object
11401152
type: object
11411153
type: object
1142-
version:
1143-
type: string
11441154
type: object
11451155
status:
11461156
description: LimitadorStatus defines the observed state of Limitador

controllers/limitador_controller.go

+1
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ func (r *LimitadorReconciler) reconcileDeployment(ctx context.Context, limitador
188188
reconcilers.DeploymentCommandMutator,
189189
reconcilers.DeploymentAffinityMutator,
190190
reconcilers.DeploymentResourcesMutator,
191+
reconcilers.DeploymentPullSecretMutator,
191192
)
192193

193194
deployment := limitador.Deployment(limitadorObj, storageConfigSecret)

controllers/limitador_controller_test.go

+12-11
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,11 @@ var _ = Describe("Limitador controller", func() {
2727
const (
2828
LimitadorNamespace = "default"
2929
LimitadorReplicas = 2
30-
LimitadorImage = "quay.io/kuadrant/limitador"
31-
LimitadorVersion = "0.3.0"
30+
LimitadorImage = "quay.io/kuadrant/limitador:0.3.0"
3231
LimitadorHTTPPort = 8000
3332
LimitadorGRPCPort = 8001
3433
LimitadorMaxUnavailable = 1
35-
LimitdaorUpdatedMaxUnavailable = 3
34+
LimitadorUpdatedMaxUnavailable = 3
3635

3736
timeout = time.Second * 10
3837
interval = time.Millisecond * 250
@@ -47,11 +46,11 @@ var _ = Describe("Limitador controller", func() {
4746
}
4847
updatedMaxUnavailable := &intstr.IntOrString{
4948
Type: 0,
50-
IntVal: LimitdaorUpdatedMaxUnavailable,
49+
IntVal: LimitadorUpdatedMaxUnavailable,
5150
}
5251

5352
replicas := LimitadorReplicas
54-
version := LimitadorVersion
53+
image := LimitadorImage
5554
httpPort := &limitadorv1alpha1.TransportProtocol{Port: &httpPortNumber}
5655
grpcPort := &limitadorv1alpha1.TransportProtocol{Port: &grpcPortNumber}
5756
affinity := &v1.Affinity{
@@ -104,7 +103,7 @@ var _ = Describe("Limitador controller", func() {
104103
},
105104
Spec: limitadorv1alpha1.LimitadorSpec{
106105
Replicas: &replicas,
107-
Version: &version,
106+
Image: &image,
108107
Affinity: affinity,
109108
Listener: &limitadorv1alpha1.Listener{
110109
HTTP: httpPort,
@@ -181,7 +180,7 @@ var _ = Describe("Limitador controller", func() {
181180
Equal((int32)(LimitadorReplicas)),
182181
)
183182
Expect(createdLimitadorDeployment.Spec.Template.Spec.Containers[0].Image).Should(
184-
Equal(LimitadorImage + ":" + LimitadorVersion),
183+
Equal(LimitadorImage),
185184
)
186185
// It should contain at least the limits file
187186
Expect(len(createdLimitadorDeployment.Spec.Template.Spec.Containers[0].Command) > 1).Should(BeTrue())
@@ -314,8 +313,9 @@ var _ = Describe("Limitador controller", func() {
314313

315314
replicas = LimitadorReplicas + 1
316315
updatedLimitador.Spec.Replicas = &replicas
317-
version = "latest"
318-
updatedLimitador.Spec.Version = &version
316+
image := "quay.io/test/limitador:nightly"
317+
updatedLimitador.Spec.Image = &image
318+
updatedLimitador.Spec.ImagePullSecret = &v1.LocalObjectReference{Name: "pullSecret"}
319319
resourceRequirements := &v1.ResourceRequirements{
320320
Requests: v1.ResourceList{
321321
v1.ResourceCPU: resource.MustParse("200m"),
@@ -346,11 +346,12 @@ var _ = Describe("Limitador controller", func() {
346346
}
347347

348348
correctReplicas := *updatedLimitadorDeployment.Spec.Replicas == LimitadorReplicas+1
349-
correctImage := updatedLimitadorDeployment.Spec.Template.Spec.Containers[0].Image == LimitadorImage+":latest"
349+
correctImage := updatedLimitadorDeployment.Spec.Template.Spec.Containers[0].Image == image
350350
correctResources := reflect.DeepEqual(updatedLimitadorDeployment.Spec.Template.Spec.Containers[0].Resources, *resourceRequirements)
351351
correctAffinity := updatedLimitadorDeployment.Spec.Template.Spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].Weight == 99
352+
correctImagePullSecret := len(updatedLimitadorDeployment.Spec.Template.Spec.ImagePullSecrets) == 1 && updatedLimitadorDeployment.Spec.Template.Spec.ImagePullSecrets[0].Name == updatedLimitador.Spec.ImagePullSecret.Name
352353

353-
return correctReplicas && correctImage && correctResources && correctAffinity
354+
return correctReplicas && correctImage && correctResources && correctAffinity && correctImagePullSecret
354355
}, timeout, interval).Should(BeTrue())
355356
})
356357

pkg/limitador/image.go

-15
This file was deleted.

pkg/limitador/image_test.go

-11
This file was deleted.

pkg/limitador/k8s_objects.go

+2-6
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,6 @@ func Deployment(limitador *limitadorv1alpha1.Limitador, storageConfigSecret *v1.
6161
replicas = int32(*limitador.Spec.Replicas)
6262
}
6363

64-
image := GetLimitadorImageVersion()
65-
if limitador.Spec.Version != nil {
66-
image = fmt.Sprintf("%s:%s", LimitadorRepository, *limitador.Spec.Version)
67-
}
68-
6964
return &appsv1.Deployment{
7065
TypeMeta: metav1.TypeMeta{
7166
Kind: "Deployment",
@@ -90,7 +85,7 @@ func Deployment(limitador *limitadorv1alpha1.Limitador, storageConfigSecret *v1.
9085
Containers: []v1.Container{
9186
{
9287
Name: "limitador",
93-
Image: image,
88+
Image: limitador.GetImage(),
9489
Command: deploymentContainerCommand(limitador.Spec.Storage, storageConfigSecret, limitador.Spec.RateLimitHeaders),
9590
Ports: []v1.ContainerPort{
9691
{
@@ -142,6 +137,7 @@ func Deployment(limitador *limitadorv1alpha1.Limitador, storageConfigSecret *v1.
142137
ImagePullPolicy: v1.PullIfNotPresent,
143138
},
144139
},
140+
ImagePullSecrets: limitador.GetImagePullSecrets(),
145141
Volumes: []v1.Volume{
146142
{
147143
Name: "config-file",

pkg/limitador/k8s_objects_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func TestConstants(t *testing.T) {
2828
func newTestLimitadorObj(name, namespace string, limits []limitadorv1alpha1.RateLimit) *limitadorv1alpha1.Limitador {
2929
var (
3030
replicas = 1
31-
version = "1.0"
31+
image = "quay.io/kuadrant/limitador:1.0"
3232
httpPort = int32(8000)
3333
grpcPort = int32(8001)
3434
)
@@ -43,7 +43,7 @@ func newTestLimitadorObj(name, namespace string, limits []limitadorv1alpha1.Rate
4343
},
4444
Spec: limitadorv1alpha1.LimitadorSpec{
4545
Replicas: &replicas,
46-
Version: &version,
46+
Image: &image,
4747
Listener: &limitadorv1alpha1.Listener{
4848
HTTP: &limitadorv1alpha1.TransportProtocol{Port: &httpPort},
4949
GRPC: &limitadorv1alpha1.TransportProtocol{Port: &grpcPort},

0 commit comments

Comments
 (0)