Skip to content

Commit 0c1b7a3

Browse files
committed
feat: configurable image via spec
1 parent e110bb7 commit 0c1b7a3

12 files changed

+252
-176
lines changed

api/v1alpha1/limitador_types.go

+67-6
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,20 @@ 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
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27-
28-
"github.com/kuadrant/limitador-operator/pkg/helpers"
2928
)
3029

3130
const (
32-
DefaultServiceHTTPPort int32 = 8080
33-
DefaultServiceGRPCPort int32 = 8081
31+
DefaultServiceHTTPPort int32 = 8080
32+
DefaultServiceGRPCPort int32 = 8081
33+
DefaultLimitadorRepository string = "quay.io/kuadrant/limitador"
3434

3535
// Status conditions
3636
StatusConditionReady string = "Ready"
37+
38+
ImageEnvVarKey = "RELATED_IMAGE_LIMITADOR"
3739
)
3840

3941
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
@@ -47,6 +49,7 @@ type LimitadorSpec struct {
4749
// +optional
4850
Replicas *int `json:"replicas,omitempty"`
4951

52+
// Deprecated: Use Image for specifying image version
5053
// +optional
5154
Version *string `json:"version,omitempty"`
5255

@@ -61,6 +64,9 @@ type LimitadorSpec struct {
6164

6265
// +optional
6366
Limits []RateLimit `json:"limits,omitempty"`
67+
68+
// +optional
69+
Image *Image `json:"image"`
6470
}
6571

6672
//+kubebuilder:object:root=true
@@ -103,6 +109,47 @@ func (l *Limitador) Limits() []RateLimit {
103109
return l.Spec.Limits
104110
}
105111

112+
func (l *Limitador) GetImage() string {
113+
image := fmt.Sprintf("%s:%s", l.GetImageRepository(), l.GetImageTag())
114+
return helpers.FetchEnv(ImageEnvVarKey, image)
115+
}
116+
117+
func (l *Limitador) GetImageRepository() string {
118+
if l.Spec.Image == nil || l.Spec.Image.Repository == nil {
119+
return DefaultLimitadorRepository
120+
}
121+
122+
return *l.Spec.Image.Repository
123+
}
124+
125+
func (l *Limitador) GetImageTag() string {
126+
versionSet := l.Spec.Version != nil
127+
imageTagSet := l.Spec.Image != nil && l.Spec.Image.Tag != nil
128+
129+
// Preference for tag in image struct over deprecated field
130+
if imageTagSet {
131+
return *l.Spec.Image.Tag
132+
}
133+
134+
// Maintain backwards compatability for deprecated field if set
135+
if versionSet {
136+
return *l.Spec.Version
137+
}
138+
139+
return "latest"
140+
}
141+
142+
func (l *Limitador) GetImagePullSecrets() []corev1.LocalObjectReference {
143+
var imagePullSecrets []corev1.LocalObjectReference
144+
if l.Spec.Image == nil || l.Spec.Image.PullSecret == nil {
145+
return nil
146+
}
147+
148+
imagePullSecrets = append(imagePullSecrets, *l.Spec.Image.PullSecret)
149+
150+
return imagePullSecrets
151+
}
152+
106153
//+kubebuilder:object:root=true
107154

108155
// LimitadorList contains a list of Limitador
@@ -177,7 +224,7 @@ func (s *Storage) Config(url string) []string {
177224
}
178225

179226
type Redis struct {
180-
// +ConfigSecretRef refers to the secret holding the URL for Redis.
227+
// ConfigSecretRef refers to the secret holding the URL for Redis.
181228
// +optional
182229
ConfigSecretRef *corev1.ObjectReference `json:"configSecretRef,omitempty"`
183230
}
@@ -201,7 +248,7 @@ type RedisCachedOptions struct {
201248
}
202249

203250
type RedisCached struct {
204-
// +ConfigSecretRef refers to the secret holding the URL for Redis.
251+
// ConfigSecretRef refers to the secret holding the URL for Redis.
205252
// +optional
206253
ConfigSecretRef *corev1.ObjectReference `json:"configSecretRef,omitempty"`
207254

@@ -285,6 +332,20 @@ func (s *LimitadorStatus) Equals(other *LimitadorStatus, logger logr.Logger) boo
285332
return true
286333
}
287334

335+
type Image struct {
336+
// Repository containing the image to be used
337+
// +optional
338+
Repository *string `json:"repository"`
339+
340+
// Tag of the image
341+
// +optional
342+
Tag *string `json:"tag"`
343+
344+
// PullSecret for pulling private images
345+
// +optional
346+
PullSecret *corev1.LocalObjectReference `json:"pullSecret"`
347+
}
348+
288349
func init() {
289350
SchemeBuilder.Register(&Limitador{}, &LimitadorList{})
290351
}

api/v1alpha1/limitador_types_test.go

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package v1alpha1
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"testing"
7+
8+
"gotest.tools/assert"
9+
)
10+
11+
func TestLimitadorGetImage(t *testing.T) {
12+
var (
13+
testRepo = "quay.io/test/limitador"
14+
testTag = "nightly"
15+
)
16+
17+
t.Run("test default image if not specified in spec", func(subT *testing.T) {
18+
l := &Limitador{}
19+
assert.Equal(subT, l.GetImage(), "quay.io/kuadrant/limitador:latest")
20+
})
21+
22+
t.Run("test image repo override", func(subT *testing.T) {
23+
l := &Limitador{Spec: LimitadorSpec{Image: &Image{Repository: &testRepo}}}
24+
assert.Equal(subT, l.GetImage(), fmt.Sprintf("%s:latest", testRepo))
25+
})
26+
27+
t.Run("test image tag override", func(subT *testing.T) {
28+
l := &Limitador{Spec: LimitadorSpec{Image: &Image{Tag: &testTag}}}
29+
assert.Equal(subT, l.GetImage(), fmt.Sprintf("quay.io/kuadrant/limitador:%s", testTag))
30+
})
31+
32+
t.Run("test image repo & tag override", func(subT *testing.T) {
33+
l := &Limitador{Spec: LimitadorSpec{Image: &Image{Repository: &testRepo, Tag: &testTag}}}
34+
assert.Equal(subT, l.GetImage(), fmt.Sprintf("%s:%s", testRepo, testTag))
35+
})
36+
37+
t.Run("test preference for env var", func(subT *testing.T) {
38+
envVarImage := "quay.io/env/limitador:custom"
39+
if err := os.Setenv(ImageEnvVarKey, envVarImage); err != nil {
40+
t.Fatal(err)
41+
}
42+
l := &Limitador{Spec: LimitadorSpec{Image: &Image{Repository: &testRepo, Tag: &testTag}}}
43+
assert.Equal(subT, l.GetImage(), envVarImage)
44+
if err := os.Unsetenv(ImageEnvVarKey); err != nil {
45+
t.Fatal(err)
46+
}
47+
})
48+
}

api/v1alpha1/zz_generated.deepcopy.go

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

bundle/manifests/limitador.kuadrant.io_limitadors.yaml

+23-52
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,24 @@ spec:
3434
spec:
3535
description: LimitadorSpec defines the desired state of Limitador
3636
properties:
37+
image:
38+
properties:
39+
name:
40+
description: Name of the image
41+
type: string
42+
pullSecret:
43+
description: PullSecret for pulling private images
44+
properties:
45+
name:
46+
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
47+
TODO: Add other useful fields. apiVersion, kind, uid?'
48+
type: string
49+
type: object
50+
x-kubernetes-map-type: atomic
51+
tag:
52+
description: Tag of the image
53+
type: string
54+
type: object
3755
limits:
3856
items:
3957
description: RateLimit defines the desired Limitador limit
@@ -91,32 +109,8 @@ spec:
91109
redis:
92110
properties:
93111
configSecretRef:
94-
description: 'ObjectReference contains enough information
95-
to let you inspect or modify the referred object. --- New
96-
uses of this type are discouraged because of difficulty
97-
describing its usage when embedded in APIs. 1. Ignored fields. It
98-
includes many fields which are not generally honored. For
99-
instance, ResourceVersion and FieldPath are both very rarely
100-
valid in actual usage. 2. Invalid usage help. It is impossible
101-
to add specific help for individual usage. In most embedded
102-
usages, there are particular restrictions like, "must refer
103-
only to types A and B" or "UID not honored" or "name must
104-
be restricted". Those cannot be well described when embedded.
105-
3. Inconsistent validation. Because the usages are different,
106-
the validation rules are different by usage, which makes
107-
it hard for users to predict what will happen. 4. The fields
108-
are both imprecise and overly precise. Kind is not a precise
109-
mapping to a URL. This can produce ambiguity during interpretation
110-
and require a REST mapping. In most cases, the dependency
111-
is on the group,resource tuple and the version of the actual
112-
struct is irrelevant. 5. We cannot easily change it. Because
113-
this type is embedded in many locations, updates to this
114-
type will affect numerous schemas. Don''t make new APIs
115-
embed an underspecified API type they do not control. Instead
116-
of using this type, create a locally provided and used type
117-
that is well-focused on your reference. For example, ServiceReferences
118-
for admission registration: https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533
119-
.'
112+
description: ConfigSecretRef refers to the secret holding
113+
the URL for Redis.
120114
properties:
121115
apiVersion:
122116
description: API version of the referent.
@@ -157,32 +151,8 @@ spec:
157151
redis-cached:
158152
properties:
159153
configSecretRef:
160-
description: 'ObjectReference contains enough information
161-
to let you inspect or modify the referred object. --- New
162-
uses of this type are discouraged because of difficulty
163-
describing its usage when embedded in APIs. 1. Ignored fields. It
164-
includes many fields which are not generally honored. For
165-
instance, ResourceVersion and FieldPath are both very rarely
166-
valid in actual usage. 2. Invalid usage help. It is impossible
167-
to add specific help for individual usage. In most embedded
168-
usages, there are particular restrictions like, "must refer
169-
only to types A and B" or "UID not honored" or "name must
170-
be restricted". Those cannot be well described when embedded.
171-
3. Inconsistent validation. Because the usages are different,
172-
the validation rules are different by usage, which makes
173-
it hard for users to predict what will happen. 4. The fields
174-
are both imprecise and overly precise. Kind is not a precise
175-
mapping to a URL. This can produce ambiguity during interpretation
176-
and require a REST mapping. In most cases, the dependency
177-
is on the group,resource tuple and the version of the actual
178-
struct is irrelevant. 5. We cannot easily change it. Because
179-
this type is embedded in many locations, updates to this
180-
type will affect numerous schemas. Don''t make new APIs
181-
embed an underspecified API type they do not control. Instead
182-
of using this type, create a locally provided and used type
183-
that is well-focused on your reference. For example, ServiceReferences
184-
for admission registration: https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533
185-
.'
154+
description: ConfigSecretRef refers to the secret holding
155+
the URL for Redis.
186156
properties:
187157
apiVersion:
188158
description: API version of the referent.
@@ -241,6 +211,7 @@ spec:
241211
type: object
242212
type: object
243213
version:
214+
description: 'Deprecated: Use Image for specifying image version'
244215
type: string
245216
type: object
246217
status:

0 commit comments

Comments
 (0)