Skip to content

Commit

Permalink
test: Make Code Testable (#697)
Browse files Browse the repository at this point in the history
* testing-client

Signed-off-by: Shubham Gupta <[email protected]>

* client_test.go

Signed-off-by: Shubham Gupta <[email protected]>

* fix

Signed-off-by: Shubham Gupta <[email protected]>

* push

Signed-off-by: Shubham Gupta <[email protected]>

* fix Client

Signed-off-by: Shubham Gupta <[email protected]>

* fix Dockerfile

Signed-off-by: Shubham Gupta <[email protected]>

* try fixing lint

Signed-off-by: Shubham Gupta <[email protected]>

* fix ci

Signed-off-by: Shubham Gupta <[email protected]>

* fix CI

Signed-off-by: Shubham Gupta <[email protected]>

* fix CI

Signed-off-by: Shubham Gupta <[email protected]>

* ignoree

Signed-off-by: Shubham Gupta <[email protected]>

* change location

Signed-off-by: Shubham Gupta <[email protected]>

---------

Signed-off-by: Shubham Gupta <[email protected]>
  • Loading branch information
shubham-cmyk authored Nov 10, 2023
1 parent 56996ef commit 9167791
Show file tree
Hide file tree
Showing 17 changed files with 399 additions and 70 deletions.
16 changes: 13 additions & 3 deletions .github/workflows/operator-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,22 +64,32 @@ jobs:
verbose: true

code_quality_golang_ci_lint:
needs: [gofmt, govet, gotest]
needs: [gofmt, govet]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: ${{ env.GOLANG_VERSION }}

- name: Download Go modules
run: go mod download

- name: Check disk space
run: df -h

- name: List Go module cache
run: ls -la $(go env GOPATH)/pkg/mod

- name: Run GolangCI-Lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.54.0
args: --timeout=5m0s ./...
args: --timeout=5m0s ./... --verbose --out-format=github-actions

container_quality_dockerfile_lint:
needs: [gofmt, govet, gotest]
needs: [gofmt, govet]
runs-on: ubuntu-latest
steps:
- name: Checkout code
Expand Down
12 changes: 7 additions & 5 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ linters-settings:
gofmt:
simplify: true

# linters:
# enable-all: true
# disable:
# - errcheck
# - tagliatelle
linters:
disable:
- errcheck
- tagliatelle
- ineffassign
- staticcheck

# Exclude the files that are causing the errors
issues:
exclude-rules:
Expand Down
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ COPY main.go main.go
COPY api/ api/
COPY controllers/ controllers/
COPY k8sutils/ k8sutils/
COPY mocks/ mocks/

# Build
ARG LDFLAGS="-s -w"
Expand Down
10 changes: 7 additions & 3 deletions controllers/redis_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,19 @@ import (
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
)

// RedisReconciler reconciles a Redis object
type RedisReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
K8sClient kubernetes.Interface
Dk8sClinet dynamic.Interface
Log logr.Logger
Scheme *runtime.Scheme
}

// Reconcile is part of the main kubernetes reconciliation loop which aims
Expand All @@ -53,7 +57,7 @@ func (r *RedisReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl
reqLogger.Info("Found annotations redis.opstreelabs.in/skip-reconcile, so skipping reconcile")
return ctrl.Result{RequeueAfter: time.Second * 10}, nil
}
if err = k8sutils.HandleRedisFinalizer(instance, r.Client); err != nil {
if err = k8sutils.HandleRedisFinalizer(r.Client, r.K8sClient, r.Log, instance); err != nil {
return ctrl.Result{}, err
}

Expand Down
34 changes: 14 additions & 20 deletions k8sutils/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,28 @@ import (
"k8s.io/client-go/tools/clientcmd"
)

// generateK8sClient create client for kubernetes
func generateK8sClient() *kubernetes.Clientset {
config, err := generateK8sConfig()
if err != nil {
panic(err.Error())
}
clientset, err := kubernetes.NewForConfig(config)
type K8sConfigProvider = func() (*rest.Config, error)

// GenerateK8sClient create client for kubernetes
func GenerateK8sClient(configProvider K8sConfigProvider) (kubernetes.Interface, error) {
config, err := configProvider()
if err != nil {
panic(err.Error())
return nil, err
}
return clientset
return kubernetes.NewForConfig(config)
}

// generateK8sClient create Dynamic client for kubernetes
func generateK8sDynamicClient() dynamic.Interface {
config, err := generateK8sConfig()
if err != nil {
panic(err.Error())
}
dynamicClientset, err := dynamic.NewForConfig(config)
// GenerateK8sClient create Dynamic client for kubernetes
func GenerateK8sDynamicClient(configProvider K8sConfigProvider) (dynamic.Interface, error) {
config, err := configProvider()
if err != nil {
panic(err.Error())
return nil, err
}
return dynamicClientset
return dynamic.NewForConfig(config)
}

// generateK8sConfig will load the kube config file
func generateK8sConfig() (*rest.Config, error) {
// GenerateK8sConfig will load the kube config file
func GenerateK8sConfig() (*rest.Config, error) {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
// if you want to change the loading rules (which files in which order), you can do so here
configOverrides := &clientcmd.ConfigOverrides{}
Expand Down
78 changes: 78 additions & 0 deletions k8sutils/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package k8sutils

import (
"errors"
"testing"

"github.com/stretchr/testify/assert"
"k8s.io/client-go/rest"
)

func mockK8sConfigProvider() (*rest.Config, error) {
return &rest.Config{}, nil
}
func mockInvalidK8sConfigProvider() (*rest.Config, error) {
return nil, errors.New("invalid configuration")
}

func TestGenerateK8sClient(t *testing.T) {
tests := []struct {
name string
configProvider func() (*rest.Config, error)
wantErr bool
}{
{
name: "valid config",
configProvider: mockK8sConfigProvider,
wantErr: false,
},
{
name: "invalid config",
configProvider: mockInvalidK8sConfigProvider,
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
client, err := GenerateK8sClient(tt.configProvider)
if tt.wantErr {
assert.Error(t, err, "GenerateK8sClient() should return an error for invalid config")
} else {
assert.NoError(t, err, "GenerateK8sClient() should not return an error for valid config")
assert.NotNil(t, client, "expected a non-nil Kubernetes client")
}
})
}
}

func TestGenerateK8sDynamicClient(t *testing.T) {
tests := []struct {
name string
configProvider func() (*rest.Config, error)
wantErr bool
}{
{
name: "valid config",
configProvider: mockK8sConfigProvider,
wantErr: false,
},
{
name: "invalid config",
configProvider: mockInvalidK8sConfigProvider,
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
client, err := GenerateK8sDynamicClient(tt.configProvider)
if tt.wantErr {
assert.Error(t, err, "GenerateK8sDynamicClient() should return an error for invalid config")
} else {
assert.NoError(t, err, "GenerateK8sDynamicClient() should not return an error for valid config")
assert.NotNil(t, client, "expected a non-nil Kubernetes client")
}
})
}
}
37 changes: 24 additions & 13 deletions k8sutils/finalizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package k8sutils

import (
"context"
"fmt"
"strconv"

redisv1beta2 "github.com/OT-CONTAINER-KIT/redis-operator/api/v1beta2"
mockLog "github.com/OT-CONTAINER-KIT/redis-operator/mocks/log"
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
Expand All @@ -26,16 +29,15 @@ func finalizerLogger(namespace string, name string) logr.Logger {
}

// HandleRedisFinalizer finalize resource if instance is marked to be deleted
func HandleRedisFinalizer(cr *redisv1beta2.Redis, cl client.Client) error {
logger := finalizerLogger(cr.Namespace, RedisFinalizer)
func HandleRedisFinalizer(ctrlclient client.Client, k8sClient kubernetes.Interface, logger logr.Logger, cr *redisv1beta2.Redis) error {
if cr.GetDeletionTimestamp() != nil {
if controllerutil.ContainsFinalizer(cr, RedisFinalizer) {
if err := finalizeRedisPVC(cr); err != nil {
if err := finalizeRedisPVC(k8sClient, logger, cr); err != nil {
return err
}
controllerutil.RemoveFinalizer(cr, RedisFinalizer)
if err := cl.Update(context.TODO(), cr); err != nil {
logger.Error(err, "Could not remove finalizer "+RedisFinalizer)
if err := ctrlclient.Update(context.TODO(), cr); err != nil {
logger.Error(err, "Could not remove finalizer", "finalizer", RedisFinalizer)
return err
}
}
Expand Down Expand Up @@ -134,12 +136,11 @@ func AddRedisSentinelFinalizer(cr *redisv1beta2.RedisSentinel, cl client.Client)
}

// finalizeRedisPVC delete PVC
func finalizeRedisPVC(cr *redisv1beta2.Redis) error {
logger := finalizerLogger(cr.Namespace, RedisFinalizer)
PVCName := cr.Name + "-" + cr.Name + "-0"
err := generateK8sClient().CoreV1().PersistentVolumeClaims(cr.Namespace).Delete(context.TODO(), PVCName, metav1.DeleteOptions{})
func finalizeRedisPVC(client kubernetes.Interface, logger mockLog.LoggerInterface, cr *redisv1beta2.Redis) error {
PVCName := fmt.Sprintf("%s-%s-0", cr.Name, cr.Name)
err := client.CoreV1().PersistentVolumeClaims(cr.Namespace).Delete(context.TODO(), PVCName, metav1.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
logger.Error(err, "Could not delete Persistent Volume Claim "+PVCName)
logger.Error(err, "Could not delete Persistent Volume Claim", "PVCName", PVCName)
return err
}
return nil
Expand All @@ -148,10 +149,15 @@ func finalizeRedisPVC(cr *redisv1beta2.Redis) error {
// finalizeRedisClusterPVC delete PVCs
func finalizeRedisClusterPVC(cr *redisv1beta2.RedisCluster) error {
logger := finalizerLogger(cr.Namespace, RedisClusterFinalizer)
client, err := GenerateK8sClient(GenerateK8sConfig)
if err != nil {
logger.Error(err, "Could not generate kubernetes client")
return err
}
for _, role := range []string{"leader", "follower"} {
for i := 0; i < int(cr.Spec.GetReplicaCounts(role)); i++ {
PVCName := cr.Name + "-" + cr.Name + "-" + role + "-" + strconv.Itoa(i)
err := generateK8sClient().CoreV1().PersistentVolumeClaims(cr.Namespace).Delete(context.TODO(), PVCName, metav1.DeleteOptions{})
err := client.CoreV1().PersistentVolumeClaims(cr.Namespace).Delete(context.TODO(), PVCName, metav1.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
logger.Error(err, "Could not delete Persistent Volume Claim "+PVCName)
return err
Expand All @@ -160,7 +166,7 @@ func finalizeRedisClusterPVC(cr *redisv1beta2.RedisCluster) error {
if cr.Spec.Storage.NodeConfVolume {
for i := 0; i < int(cr.Spec.GetReplicaCounts(role)); i++ {
PVCName := "node-conf" + cr.Name + "-" + role + "-" + strconv.Itoa(i)
err := generateK8sClient().CoreV1().PersistentVolumeClaims(cr.Namespace).Delete(context.TODO(), PVCName, metav1.DeleteOptions{})
err := client.CoreV1().PersistentVolumeClaims(cr.Namespace).Delete(context.TODO(), PVCName, metav1.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
logger.Error(err, "Could not delete Persistent Volume Claim "+PVCName)
return err
Expand All @@ -175,9 +181,14 @@ func finalizeRedisClusterPVC(cr *redisv1beta2.RedisCluster) error {
// finalizeRedisReplicationPVC delete PVCs
func finalizeRedisReplicationPVC(cr *redisv1beta2.RedisReplication) error {
logger := finalizerLogger(cr.Namespace, RedisReplicationFinalizer)
client, err := GenerateK8sClient(GenerateK8sConfig)
if err != nil {
logger.Error(err, "Could not generate kubernetes client")
return err
}
for i := 0; i < int(cr.Spec.GetReplicationCounts("replication")); i++ {
PVCName := cr.Name + "-" + cr.Name + "-" + strconv.Itoa(i)
err := generateK8sClient().CoreV1().PersistentVolumeClaims(cr.Namespace).Delete(context.TODO(), PVCName, metav1.DeleteOptions{})
err := client.CoreV1().PersistentVolumeClaims(cr.Namespace).Delete(context.TODO(), PVCName, metav1.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
logger.Error(err, "Could not delete Persistent Volume Claim "+PVCName)
return err
Expand Down
Loading

0 comments on commit 9167791

Please sign in to comment.