Skip to content

Commit

Permalink
kac: add new kac package and unit tests
Browse files Browse the repository at this point in the history
Adds the kac package with just the required functions to interface with the KlusterletAddonConfig resource provided by the klusterlet addon controller as well as unit tests for the new package.
  • Loading branch information
klaskosk committed Jun 17, 2024
1 parent 9652ba3 commit fb36156
Show file tree
Hide file tree
Showing 25 changed files with 2,587 additions and 0 deletions.
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ linters-settings:
- "open-cluster-management.io/governance-policy-propagator/api/v1beta1"
- "open-cluster-management.io/multicloud-operators-subscription/pkg/apis/apps/placementrule/v1"
- "open-cluster-management.io/api"
- "github.com/stolostron/klusterlet-addon-controller/pkg/apis"
- "sigs.k8s.io/controller-runtime"
- "github.com/stretchr/testify"
- $gostd
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,13 @@ require (
sigs.k8s.io/yaml v1.4.0 // indirect
)

require github.com/stolostron/klusterlet-addon-controller v0.0.0-20240606130554-01338045271a

require (
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/expr-lang/expr v1.16.5 // indirect
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
github.com/stolostron/cluster-lifecycle-api v0.0.0-20240109072430-f5fe6043d1f8 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
knative.dev/pkg v0.0.0-20240423132823-3c6badc82748 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1862,6 +1862,10 @@ github.com/stmcginnis/gofish v0.15.0/go.mod h1:BLDSFTp8pDlf/xDbLZa+F7f7eW0E/CHCb
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stolostron/cluster-lifecycle-api v0.0.0-20240109072430-f5fe6043d1f8 h1:DRFh4ML+WuDovJsrdgszqMQ4+qGznlYlX9/pItxWwQ8=
github.com/stolostron/cluster-lifecycle-api v0.0.0-20240109072430-f5fe6043d1f8/go.mod h1:ZNQ3Rttgk4HEreCHfocrhXavLDaUgHbZaUqk5dP8/As=
github.com/stolostron/klusterlet-addon-controller v0.0.0-20240606130554-01338045271a h1:eBHb7E/A7Ev2CnggwCKfGpV7nTltmQhK+QYiMEls+AY=
github.com/stolostron/klusterlet-addon-controller v0.0.0-20240606130554-01338045271a/go.mod h1:ptR774KOKeg3AW4G4jkf0d+Hn5iTyLMLWc6n5UcP+zw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
Expand Down
7 changes: 7 additions & 0 deletions pkg/clients/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ import (
lsoV1alpha1 "github.com/openshift/local-storage-operator/api/v1alpha1"
ocsoperatorv1 "github.com/red-hat-storage/ocs-operator/api/v1"
mcmV1Beta1 "github.com/rh-ecosystem-edge/kernel-module-management/api-hub/v1beta1"
kacv1 "github.com/stolostron/klusterlet-addon-controller/pkg/apis/agent/v1"
velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
veleroClient "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned"
veleroFakeClient "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake"
Expand Down Expand Up @@ -398,6 +399,10 @@ func SetScheme(crScheme *runtime.Scheme) error {
return err
}

if err := kacv1.SchemeBuilder.AddToScheme(crScheme); err != nil {
return err
}

return nil
}

Expand Down Expand Up @@ -547,6 +552,8 @@ func GetTestClients(tcp TestClientParams) *Settings {
genericClientObjects = append(genericClientObjects, v)
case *agentInstallV1Beta1.AgentServiceConfig:
genericClientObjects = append(genericClientObjects, v)
case *kacv1.KlusterletAddonConfig:
genericClientObjects = append(genericClientObjects, v)
// ArgoCD Client Objects
case *argocdOperatorv1alpha1.ArgoCD:
genericClientObjects = append(genericClientObjects, v)
Expand Down
257 changes: 257 additions & 0 deletions pkg/kac/kac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package kac

import (
"context"
"fmt"

"github.com/golang/glog"
"github.com/openshift-kni/eco-goinfra/pkg/clients"
"github.com/openshift-kni/eco-goinfra/pkg/msg"
kacv1 "github.com/stolostron/klusterlet-addon-controller/pkg/apis/agent/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
)

// Builder provides a struct for the KlusterletAddonConfig resource containing a connection to the cluster and the
// KlusterletAddonConfig definition.
type Builder struct {
// Definition of the KlusterletAddonConfig used to create the object.
Definition *kacv1.KlusterletAddonConfig
// Object of the KlusterletAddonConfig as it is on the cluster.
Object *kacv1.KlusterletAddonConfig
// apiClient used to interact with the cluster.
apiClient *clients.Settings
// errorMsg used to store latest error message from functions that do not return errors.
errorMsg string
}

// NewBuilder creates a new instance of a KlusterletAddonConfig builder.
func NewBuilder(apiClient *clients.Settings, name, nsname string) *Builder {
glog.V(100).Infof(
"Initializing new KlusterletAddonConfig structure with the following params: name: %s, nsname: %s", name, nsname)

builder := &Builder{
apiClient: apiClient,
Definition: &kacv1.KlusterletAddonConfig{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: nsname,
},
},
}

if apiClient == nil {
glog.V(100).Info("The apiClient for the KlusterletAddonConfig is nil")

builder.errorMsg = "klusterletAddonConfig 'apiClient' cannot be nil"

return builder
}

if name == "" {
glog.V(100).Info("The name of the KlusterletAddonConfig is empty")

builder.errorMsg = "klusterletAddonConfig 'name' cannot be empty"

return builder
}

if nsname == "" {
glog.V(100).Info("The namespace of the KlusterletAddonConfig is empty")

builder.errorMsg = "klusterletAddonConfig 'nsname' cannot be empty"

return builder
}

return builder
}

// Pull pulls an existing KlusterletAddonConfig into a Builder struct.
func Pull(apiClient *clients.Settings, name, nsname string) (*Builder, error) {
glog.V(100).Infof("Pulling existing KlusterletAddonConfig %s under namespace %s from cluster", name, nsname)

if apiClient == nil {
glog.V(100).Info("The apiClient is empty")

return nil, fmt.Errorf("klusterletAddonConfig 'apiClient' cannot be nil")
}

builder := &Builder{
apiClient: apiClient,
Definition: &kacv1.KlusterletAddonConfig{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: nsname,
},
},
}

if name == "" {
glog.V(100).Info("The name of the KlusterletAddonConfig is empty")

return nil, fmt.Errorf("klusterletAddonConfig 'name' cannot be empty")
}

if nsname == "" {
glog.V(100).Info("The namespace of the KlusterletAddonConfig is empty")

return nil, fmt.Errorf("klusterletAddonConfig 'nsname' cannot be empty")
}

if !builder.Exists() {
glog.V(100).Info("The KlusterletAddonConfig %s does not exist in namespace %s", name, nsname)

return nil, fmt.Errorf("klusterletAddonConfig object %s does not exist in namespace %s", name, nsname)
}

builder.Definition = builder.Object

return builder, nil
}

// Exists checks whether the given KlusterletAddonConfig exists on the cluster.
func (builder *Builder) Exists() bool {
if valid, _ := builder.validate(); !valid {
return false
}

glog.V(100).Infof(
"Checking if KlusterletAddonConfig %s exists in namespace %s", builder.Definition.Name, builder.Definition.Namespace)

klusterletAddonConfig := &kacv1.KlusterletAddonConfig{}
err := builder.apiClient.Get(context.TODO(), runtimeclient.ObjectKey{
Name: builder.Definition.Name,
Namespace: builder.Definition.Namespace,
}, klusterletAddonConfig)

if err == nil {
builder.Object = klusterletAddonConfig
}

return err == nil || !k8serrors.IsNotFound(err)
}

// Create makes a KlusterletAddonConfig on the cluster if it does not already exist.
func (builder *Builder) Create() (*Builder, error) {
if valid, err := builder.validate(); !valid {
return nil, err
}

glog.V(100).Infof(
"Creating KlusterletAddonConfig %s in namespace %s", builder.Definition.Name, builder.Definition.Namespace)

if builder.Exists() {
return builder, nil
}

err := builder.apiClient.Create(context.TODO(), builder.Definition)
if err != nil {
return nil, err
}

builder.Object = builder.Definition

return builder, err
}

// Update changes the existing KlusterletAddonConfig resource on the cluster, falling back to deleting and recreating if
// the update fails when force is set.
func (builder *Builder) Update(force bool) (*Builder, error) {
if valid, err := builder.validate(); !valid {
return nil, err
}

glog.V(100).Infof(
"Updating KlusterletAddonConfig %s in namespace %s", builder.Definition.Name, builder.Definition.Namespace)

if !builder.Exists() {
glog.V(100).Infof(
"KlusterletAddonConfig %s does not exist in namespace %s", builder.Definition.Name, builder.Definition.Namespace)

return nil, fmt.Errorf("cannot update non-existent klusterletAddonConfig")
}

err := builder.apiClient.Update(context.TODO(), builder.Definition)
if err != nil {
if force {
glog.V(100).Infof(msg.FailToUpdateNotification("klusterletAddonConfig", builder.Definition.Name))

err := builder.Delete()
if err != nil {
glog.V(100).Infof(msg.FailToUpdateError("klusterletAddonConfig", builder.Definition.Name))

return nil, err
}

return builder.Create()
}

return nil, err
}

builder.Object = builder.Definition

return builder, nil
}

// Delete removes a KlusterletAddonConfig from the cluster if it exists.
func (builder *Builder) Delete() error {
if valid, err := builder.validate(); !valid {
return err
}

glog.V(100).Infof(
"Deleting KlusterletAddonConfig %s in namespace %s", builder.Definition.Name, builder.Definition.Namespace)

if !builder.Exists() {
glog.V(100).Infof(
"KlusterletAddonConfig %s in namespace %s already does not exist",
builder.Definition.Name, builder.Definition.Namespace)

builder.Object = nil

return nil
}

err := builder.apiClient.Delete(context.TODO(), builder.Object)
if err != nil {
return err
}

builder.Object = nil

return nil
}

// validate checks that the builder, definition, and apiClient are properly initialized and there is no errorMsg.
func (builder *Builder) validate() (bool, error) {
resourceCRD := "klusterletAddonConfig"

if builder == nil {
glog.V(100).Infof("The %s builder is uninitialized", resourceCRD)

return false, fmt.Errorf("error: received nil %s builder", resourceCRD)
}

if builder.Definition == nil {
glog.V(100).Infof("The %s is uninitialized", resourceCRD)

return false, fmt.Errorf(msg.UndefinedCrdObjectErrString(resourceCRD))
}

if builder.apiClient == nil {
glog.V(100).Infof("The %s builder apiClient is nil", resourceCRD)

return false, fmt.Errorf("%s builder cannot have nil apiClient", resourceCRD)
}

if builder.errorMsg != "" {
glog.V(100).Infof("The %s builder has error message %s", resourceCRD, builder.errorMsg)

return false, fmt.Errorf(builder.errorMsg)
}

return true, nil
}
Loading

0 comments on commit fb36156

Please sign in to comment.