Skip to content

Commit

Permalink
feat: configurable image via spec
Browse files Browse the repository at this point in the history
  • Loading branch information
KevFan committed Sep 19, 2023
1 parent fdb0c52 commit 766cb00
Show file tree
Hide file tree
Showing 13 changed files with 171 additions and 61 deletions.
39 changes: 32 additions & 7 deletions api/v1alpha1/limitador_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,23 @@ import (

"github.com/go-logr/logr"
"github.com/google/go-cmp/cmp"
"github.com/kuadrant/limitador-operator/pkg/helpers"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"

"github.com/kuadrant/limitador-operator/pkg/helpers"
"k8s.io/utils/env"
)

const (
DefaultServiceHTTPPort int32 = 8080
DefaultServiceGRPCPort int32 = 8081
DefaultServiceHTTPPort int32 = 8080
DefaultServiceGRPCPort int32 = 8081
DefaultLimitadorImage string = "quay.io/kuadrant/limitador:latest"

// Status conditions
StatusConditionReady string = "Ready"

ImageEnvVarKey = "RELATED_IMAGE_LIMITADOR"
)

var (
Expand Down Expand Up @@ -65,9 +68,6 @@ type LimitadorSpec struct {
// +optional
Replicas *int `json:"replicas,omitempty"`

// +optional
Version *string `json:"version,omitempty"`

// +optional
Listener *Listener `json:"listener,omitempty"`

Expand All @@ -85,6 +85,12 @@ type LimitadorSpec struct {

// +optional
ResourceRequirements *corev1.ResourceRequirements `json:"resourceRequirements,omitempty"`

// +optional
Image *string `json:"image"`

// +optional
ImagePullSecret *corev1.LocalObjectReference `json:"imagePullSecret"`
}

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

func (l *Limitador) GetImage() string {
if l.Spec.Image == nil {
return env.GetString(ImageEnvVarKey, DefaultLimitadorImage)
}

return *l.Spec.Image
}

func (l *Limitador) GetImagePullSecrets() []corev1.LocalObjectReference {
var imagePullSecrets []corev1.LocalObjectReference
if l.Spec.ImagePullSecret == nil {
return nil
}

imagePullSecrets = append(imagePullSecrets, *l.Spec.ImagePullSecret)

return imagePullSecrets
}

//+kubebuilder:object:root=true

// LimitadorList contains a list of Limitador
Expand Down
51 changes: 51 additions & 0 deletions api/v1alpha1/limitador_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package v1alpha1

import (
"fmt"
"os"
"strconv"
"testing"

Expand Down Expand Up @@ -210,3 +211,53 @@ func TestLimitadorStatusEquals(t *testing.T) {
assert.Equal(subT, l.Equals(status, logr.Logger{}), true)
})
}

func TestLimitadorGetImage(t *testing.T) {
var (
testImage = "quay.io/test/limitador:nightly"
)
const envVarImage = "quay.io/env/limitador:custom"

setEnvVarImage := func(t *testing.T) {
if err := os.Setenv(ImageEnvVarKey, envVarImage); err != nil {
t.Fatal(err)
}
}
unSetEnvVarImage := func(t *testing.T) {
if err := os.Unsetenv(ImageEnvVarKey); err != nil {
t.Fatal(err)
}
}

t.Run("test default image if not specified in spec", func(subT *testing.T) {
l := &Limitador{}
assert.Equal(subT, l.GetImage(), DefaultLimitadorImage)
})

t.Run("test image override via env var", func(subT *testing.T) {
defer unSetEnvVarImage(subT)
setEnvVarImage(subT)
l := &Limitador{}
assert.Equal(subT, l.GetImage(), envVarImage)
})

t.Run("test image preference via spec over env var and default", func(subT *testing.T) {
defer unSetEnvVarImage(subT)
setEnvVarImage(subT)
l := &Limitador{Spec: LimitadorSpec{Image: &testImage}}
assert.Equal(subT, l.GetImage(), testImage)
})
}

func TestLimitadorGetImagePullSecrets(t *testing.T) {
t.Run("test nil returned if image not specified in spec", func(subT *testing.T) {
l := &Limitador{}
assert.DeepEqual(subT, l.GetImagePullSecrets(), []corev1.LocalObjectReference(nil))
})

t.Run("test pull secret returned if specified in spec", func(subT *testing.T) {
pullSecret := corev1.LocalObjectReference{Name: "pullSecret"}
l := &Limitador{Spec: LimitadorSpec{ImagePullSecret: &pullSecret}}
assert.DeepEqual(subT, l.GetImagePullSecrets(), []corev1.LocalObjectReference{pullSecret})
})
}
15 changes: 10 additions & 5 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions bundle/manifests/limitador.kuadrant.io_limitadors.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,18 @@ spec:
type: array
type: object
type: object
image:
type: string
imagePullSecret:
description: LocalObjectReference contains enough information to let
you locate the referenced object inside the same namespace.
properties:
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
type: object
x-kubernetes-map-type: atomic
limits:
items:
description: RateLimit defines the desired Limitador limit
Expand Down Expand Up @@ -1138,8 +1150,6 @@ spec:
type: object
type: object
type: object
version:
type: string
type: object
status:
description: LimitadorStatus defines the observed state of Limitador
Expand Down
14 changes: 12 additions & 2 deletions config/crd/bases/limitador.kuadrant.io_limitadors.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,18 @@ spec:
type: array
type: object
type: object
image:
type: string
imagePullSecret:
description: LocalObjectReference contains enough information to let
you locate the referenced object inside the same namespace.
properties:
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
type: object
x-kubernetes-map-type: atomic
limits:
items:
description: RateLimit defines the desired Limitador limit
Expand Down Expand Up @@ -1139,8 +1151,6 @@ spec:
type: object
type: object
type: object
version:
type: string
type: object
status:
description: LimitadorStatus defines the observed state of Limitador
Expand Down
1 change: 1 addition & 0 deletions controllers/limitador_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ func (r *LimitadorReconciler) reconcileDeployment(ctx context.Context, limitador
reconcilers.DeploymentCommandMutator,
reconcilers.DeploymentAffinityMutator,
reconcilers.DeploymentResourcesMutator,
reconcilers.DeploymentPullSecretMutator,
)

deployment := limitador.Deployment(limitadorObj, storageConfigSecret)
Expand Down
23 changes: 12 additions & 11 deletions controllers/limitador_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,11 @@ var _ = Describe("Limitador controller", func() {
const (
LimitadorNamespace = "default"
LimitadorReplicas = 2
LimitadorImage = "quay.io/kuadrant/limitador"
LimitadorVersion = "0.3.0"
LimitadorImage = "quay.io/kuadrant/limitador:0.3.0"
LimitadorHTTPPort = 8000
LimitadorGRPCPort = 8001
LimitadorMaxUnavailable = 1
LimitdaorUpdatedMaxUnavailable = 3
LimitadorUpdatedMaxUnavailable = 3

timeout = time.Second * 10
interval = time.Millisecond * 250
Expand All @@ -47,11 +46,11 @@ var _ = Describe("Limitador controller", func() {
}
updatedMaxUnavailable := &intstr.IntOrString{
Type: 0,
IntVal: LimitdaorUpdatedMaxUnavailable,
IntVal: LimitadorUpdatedMaxUnavailable,
}

replicas := LimitadorReplicas
version := LimitadorVersion
image := LimitadorImage
httpPort := &limitadorv1alpha1.TransportProtocol{Port: &httpPortNumber}
grpcPort := &limitadorv1alpha1.TransportProtocol{Port: &grpcPortNumber}
affinity := &v1.Affinity{
Expand Down Expand Up @@ -104,7 +103,7 @@ var _ = Describe("Limitador controller", func() {
},
Spec: limitadorv1alpha1.LimitadorSpec{
Replicas: &replicas,
Version: &version,
Image: &image,
Affinity: affinity,
Listener: &limitadorv1alpha1.Listener{
HTTP: httpPort,
Expand Down Expand Up @@ -186,7 +185,7 @@ var _ = Describe("Limitador controller", func() {
Equal((int32)(LimitadorReplicas)),
)
Expect(createdLimitadorDeployment.Spec.Template.Spec.Containers[0].Image).Should(
Equal(LimitadorImage + ":" + LimitadorVersion),
Equal(LimitadorImage),
)
// It should contain at least the limits file
Expect(len(createdLimitadorDeployment.Spec.Template.Spec.Containers[0].Command) > 1).Should(BeTrue())
Expand Down Expand Up @@ -308,7 +307,7 @@ var _ = Describe("Limitador controller", func() {
It("Should modify the limitador deployment", func() {
updatedLimitador := limitadorv1alpha1.Limitador{}
replicas = LimitadorReplicas + 1
version = "latest"
image := "quay.io/test/limitador:nightly"
resourceRequirements := &v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("200m"),
Expand All @@ -335,7 +334,8 @@ var _ = Describe("Limitador controller", func() {
}

updatedLimitador.Spec.Replicas = &replicas
updatedLimitador.Spec.Version = &version
updatedLimitador.Spec.Image = &image
updatedLimitador.Spec.ImagePullSecret = &v1.LocalObjectReference{Name: "pullSecret"}
updatedLimitador.Spec.ResourceRequirements = resourceRequirements
affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].Weight = 99
updatedLimitador.Spec.Affinity = affinity
Expand All @@ -358,11 +358,12 @@ var _ = Describe("Limitador controller", func() {
}

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

return correctReplicas && correctImage && correctResources && correctAffinity
return correctReplicas && correctImage && correctResources && correctAffinity && correctImagePullSecret
}, timeout, interval).Should(BeTrue())
})

Expand Down
15 changes: 0 additions & 15 deletions pkg/limitador/image.go

This file was deleted.

11 changes: 0 additions & 11 deletions pkg/limitador/image_test.go

This file was deleted.

8 changes: 2 additions & 6 deletions pkg/limitador/k8s_objects.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,6 @@ func Deployment(limitador *limitadorv1alpha1.Limitador, storageConfigSecret *v1.
replicas = int32(*limitador.Spec.Replicas)
}

image := GetLimitadorImageVersion()
if limitador.Spec.Version != nil {
image = fmt.Sprintf("%s:%s", LimitadorRepository, *limitador.Spec.Version)
}

return &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
Expand All @@ -90,7 +85,7 @@ func Deployment(limitador *limitadorv1alpha1.Limitador, storageConfigSecret *v1.
Containers: []v1.Container{
{
Name: "limitador",
Image: image,
Image: limitador.GetImage(),
Command: deploymentContainerCommand(limitador.Spec.Storage, storageConfigSecret, limitador.Spec.RateLimitHeaders),
Ports: []v1.ContainerPort{
{
Expand Down Expand Up @@ -142,6 +137,7 @@ func Deployment(limitador *limitadorv1alpha1.Limitador, storageConfigSecret *v1.
ImagePullPolicy: v1.PullIfNotPresent,
},
},
ImagePullSecrets: limitador.GetImagePullSecrets(),
Volumes: []v1.Volume{
{
Name: "config-file",
Expand Down
4 changes: 2 additions & 2 deletions pkg/limitador/k8s_objects_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestConstants(t *testing.T) {
func newTestLimitadorObj(name, namespace string, limits []limitadorv1alpha1.RateLimit) *limitadorv1alpha1.Limitador {
var (
replicas = 1
version = "1.0"
image = "quay.io/kuadrant/limitador:1.0"
httpPort = int32(8000)
grpcPort = int32(8001)
)
Expand All @@ -43,7 +43,7 @@ func newTestLimitadorObj(name, namespace string, limits []limitadorv1alpha1.Rate
},
Spec: limitadorv1alpha1.LimitadorSpec{
Replicas: &replicas,
Version: &version,
Image: &image,
Listener: &limitadorv1alpha1.Listener{
HTTP: &limitadorv1alpha1.TransportProtocol{Port: &httpPort},
GRPC: &limitadorv1alpha1.TransportProtocol{Port: &grpcPort},
Expand Down
Loading

0 comments on commit 766cb00

Please sign in to comment.