Skip to content

Commit

Permalink
Removed duplicate and updated error message
Browse files Browse the repository at this point in the history
  • Loading branch information
KeerthanaAP committed Feb 7, 2024
1 parent 4745292 commit 6910468
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 115 deletions.
2 changes: 0 additions & 2 deletions api/v1beta1/ibmvpcmachinetemplate_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ type IBMVPCMachineTemplateStatus struct {
// Important: Run "make" to regenerate code after modifying this file
}

// +kubebuilder:object:root=true

// +kubebuilder:object:root=true
// +kubebuilder:resource:path=ibmvpcmachinetemplates,scope=Namespaced,categories=cluster-api

Expand Down
8 changes: 4 additions & 4 deletions api/v1beta1/zz_generated.conversion.go

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

2 changes: 1 addition & 1 deletion api/v1beta2/zz_generated.deepcopy.go

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

62 changes: 38 additions & 24 deletions controllers/ibmvpcmachinetemplate_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import (
"context"
"fmt"
"reflect"
"regexp"
"strings"

"github.com/IBM/vpc-go-sdk/vpcv1"

corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
Expand All @@ -34,12 +34,15 @@ import (
"sigs.k8s.io/cluster-api/util/patch"

infrav1beta2 "sigs.k8s.io/cluster-api-provider-ibmcloud/api/v1beta2"
"sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/vpc"
"sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/endpoints"
)

// IBMVPCMachineTemplateReconciler reconciles a IBMVPCMachineTemplate object.
type IBMVPCMachineTemplateReconciler struct {
client.Client
Scheme *runtime.Scheme
Scheme *runtime.Scheme
ServiceEndpoint []endpoints.ServiceEndpoint
}

func (r *IBMVPCMachineTemplateReconciler) SetupWithManager(mgr ctrl.Manager) error {
Expand All @@ -53,25 +56,53 @@ func (r *IBMVPCMachineTemplateReconciler) SetupWithManager(mgr ctrl.Manager) err

func (r *IBMVPCMachineTemplateReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := ctrl.LoggerFrom(ctx)
log.V(1).Info("Reconciling IBMVPCMachineTemplate")
log.Info("Reconciling IBMVPCMachineTemplate")

var machineTemplate infrav1beta2.IBMVPCMachineTemplate
if err := r.Get(ctx, req.NamespacedName, &machineTemplate); err != nil {
log.Error(err, "Unable to fetch ibmvpcmachinetemplate")
return ctrl.Result{}, client.IgnoreNotFound(err)
}

region := endpoints.CostructRegionFromZone(machineTemplate.Spec.Template.Spec.Zone)

// Fetch the service endpoint.
svcEndpoint := endpoints.FetchVPCEndpoint(region, r.ServiceEndpoint)

vpcClient, err := vpc.NewService(svcEndpoint)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to create IBM VPC session: %w", err)
}

return r.reconcileNormal(ctx, vpcClient, machineTemplate)
}

func (r *IBMVPCMachineTemplateReconciler) reconcileNormal(ctx context.Context, vpcClient vpc.Vpc, machineTemplate infrav1beta2.IBMVPCMachineTemplate) (ctrl.Result, error) {
log := ctrl.LoggerFrom(ctx)
helper, err := patch.NewHelper(&machineTemplate, r.Client)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to init patch helper: %w", err)
}

capacity, err := getIBMVPCMachineCapacity(machineTemplate)
options := &vpcv1.GetInstanceProfileOptions{}
options.SetName(machineTemplate.Spec.Template.Spec.Profile)
profileDetails, _, err := vpcClient.GetInstanceProfile(options)
if err != nil {
log.Error(err, "Failed to get capacity from the ibmvpcmachine template")
return ctrl.Result{}, fmt.Errorf("failed to get capcity for machine template: %w", err)
return ctrl.Result{}, fmt.Errorf("failed to fetch profile Details: %w", err)
}

if profileDetails == nil {
return ctrl.Result{}, fmt.Errorf("failed to find profileDetails")
}

log.V(3).Info("Profile Details:", "profileDetails", profileDetails)

capacity := make(corev1.ResourceList)
memory := fmt.Sprintf("%vG", *profileDetails.Memory.(*vpcv1.InstanceProfileMemory).Value)
cpu := fmt.Sprintf("%v", *profileDetails.VcpuCount.(*vpcv1.InstanceProfileVcpu).Value)
capacity[corev1.ResourceCPU] = resource.MustParse(cpu)
capacity[corev1.ResourceMemory] = resource.MustParse(memory)

log.V(3).Info("Calculated capacity for machine template", "capacity", capacity)
if !reflect.DeepEqual(machineTemplate.Status.Capacity, capacity) {
machineTemplate.Status.Capacity = capacity
Expand All @@ -85,20 +116,3 @@ func (r *IBMVPCMachineTemplateReconciler) Reconcile(ctx context.Context, req ctr
log.V(3).Info("Machine template status", "status", machineTemplate.Status)
return ctrl.Result{}, nil
}

func getIBMVPCMachineCapacity(machineTemplate infrav1beta2.IBMVPCMachineTemplate) (corev1.ResourceList, error) {
capacity := make(corev1.ResourceList)
regex := "\\S+[-]\\d+[x]\\d+\\S*$"
re, err := regexp.Compile(regex)
if err != nil {
return nil, fmt.Errorf("failed to compile regular expression %s", regex)
}
if match := re.Match([]byte(machineTemplate.Spec.Template.Spec.Profile)); match {
Profile := strings.Split(strings.Split(machineTemplate.Spec.Template.Spec.Profile, "-")[1], "x")
capacity[corev1.ResourceCPU] = resource.MustParse(Profile[0])
capacity[corev1.ResourceMemory] = resource.MustParse(fmt.Sprintf("%sG", Profile[1]))
fmt.Printf("capacity : %+v", capacity)
return capacity, nil
}
return nil, fmt.Errorf("invalid Profile")
}
174 changes: 92 additions & 82 deletions controllers/ibmvpcmachinetemplate_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"testing"
"time"

"go.uber.org/mock/gomock"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -31,7 +32,10 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"

infrav1beta2 "sigs.k8s.io/cluster-api-provider-ibmcloud/api/v1beta2"
"sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/vpc/mock"

"github.com/IBM/go-sdk-core/v5/core"
"github.com/IBM/vpc-go-sdk/vpcv1"
. "github.com/onsi/gomega"
)

Expand All @@ -40,37 +44,25 @@ func TestIBMVPCMachineTemplateReconciler_Reconcile(t *testing.T) {
name string
expectError bool
VPCMachineTemplate *infrav1beta2.IBMVPCMachineTemplate
expectedCapacity corev1.ResourceList
}{
{
name: "Should Reconcile successfully if no IBMVPCMachineTemplate found",
expectError: false,
},
{
name: "Should Reconcile with valid profile value",
VPCMachineTemplate: stubVPCMachineTemplate("bx2-2x8"),
expectedCapacity: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: resource.MustParse("2"),
corev1.ResourceMemory: resource.MustParse("8G"),
},
expectError: false,
},
{
name: "Should Reconcile with high memory profile value",
VPCMachineTemplate: stubVPCMachineTemplate("vx2d-8x112"),
expectedCapacity: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: resource.MustParse("8"),
corev1.ResourceMemory: resource.MustParse("112G"),
},
expectError: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
g := NewWithT(t)
setup := func(t *testing.T) (*gomock.Controller, *mock.MockVpc, *IBMVPCMachineTemplateReconciler) {
t.Helper()
mockvpc := mock.NewMockVpc(gomock.NewController(t))
reconciler := &IBMVPCMachineTemplateReconciler{
Client: testEnv.Client,
}
return gomock.NewController(t), mockvpc, reconciler
}
t.Run(tc.name, func(t *testing.T) {
g := NewWithT(t)
mockController, _, reconciler := setup(t)
t.Cleanup(mockController.Finish)
ns, err := testEnv.CreateNamespace(ctx, fmt.Sprintf("namespace-%s", util.RandomString(5)))
g.Expect(err).To(BeNil())
defer func() {
Expand All @@ -90,7 +82,6 @@ func TestIBMVPCMachineTemplateReconciler_Reconcile(t *testing.T) {
err = testEnv.Get(ctx, key, machineTemplate)
return err == nil
}, 10*time.Second).Should(Equal(true))

_, err := reconciler.Reconcile(ctx, ctrl.Request{
NamespacedName: client.ObjectKey{
Namespace: ns.Name,
Expand All @@ -101,16 +92,6 @@ func TestIBMVPCMachineTemplateReconciler_Reconcile(t *testing.T) {
g.Expect(err).ToNot(BeNil())
} else {
g.Expect(err).To(BeNil())
g.Eventually(func() bool {
machineTemplate := &infrav1beta2.IBMVPCMachineTemplate{}
key := client.ObjectKey{
Name: tc.VPCMachineTemplate.Name,
Namespace: ns.Name,
}
err = testEnv.Get(ctx, key, machineTemplate)
g.Expect(err).To(BeNil())
return reflect.DeepEqual(machineTemplate.Status.Capacity, tc.expectedCapacity)
}, 10*time.Second).Should(Equal(true))
}
} else {
_, err = reconciler.Reconcile(ctx, ctrl.Request{
Expand All @@ -125,60 +106,89 @@ func TestIBMVPCMachineTemplateReconciler_Reconcile(t *testing.T) {
}
}

func TestGetIBMVPCMachineCapacity(t *testing.T) {
testCases := []struct {
name string
VPCMachineTemplate infrav1beta2.IBMVPCMachineTemplate
expectedCapacity corev1.ResourceList
expectErr bool
}{
{
name: "with instance storage profile ",
VPCMachineTemplate: *stubVPCMachineTemplate("bx2d-128x512"),
expectedCapacity: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: resource.MustParse("128"),
corev1.ResourceMemory: resource.MustParse("512G"),
},
},
{
name: "with compute profile",
VPCMachineTemplate: *stubVPCMachineTemplate("cx2d-16x32"),
expectedCapacity: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: resource.MustParse("16"),
corev1.ResourceMemory: resource.MustParse("32G"),
func TestIBMVPCMachineTemplateReconciler_reconcileNormal(t *testing.T) {
setup := func(t *testing.T) (*gomock.Controller, *mock.MockVpc, *IBMVPCMachineTemplateReconciler) {
t.Helper()
mockvpc := mock.NewMockVpc(gomock.NewController(t))
reconciler := &IBMVPCMachineTemplateReconciler{
Client: testEnv.Client,
}
return gomock.NewController(t), mockvpc, reconciler
}

t.Run("with valid profile ", func(tt *testing.T) {
g := NewWithT(tt)
var expectedCapacity corev1.ResourceList
profileDetails := vpcv1.InstanceProfile{
Name: pointer.String("bx2-4x16"),
VcpuCount: &vpcv1.InstanceProfileVcpu{
Type: pointer.String("fixed"),
Value: pointer.Int64(4),
},
},
{
name: "with GPU profile",
VPCMachineTemplate: *stubVPCMachineTemplate("gx2-32x256x2v100"),
expectedCapacity: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: resource.MustParse("32"),
corev1.ResourceMemory: resource.MustParse("256G"),
Memory: &vpcv1.InstanceProfileMemory{
Type: pointer.String("fixed"),
Value: pointer.Int64(16),
},
},
{
name: "with invalid profile",
VPCMachineTemplate: *stubVPCMachineTemplate("gx2-"),
expectErr: true,
},
}
}
ns, err := testEnv.CreateNamespace(ctx, fmt.Sprintf("namespace-%s", util.RandomString(5)))
VPCMachineTemplate := *stubVPCMachineTemplate("bx2-4x16")

for _, tc := range testCases {
t.Run(tc.name, func(tt *testing.T) {
g := NewWithT(tt)
capacity, err := getIBMVPCMachineCapacity(tc.VPCMachineTemplate)
if tc.expectErr {
if err == nil {
t.Fatal("getIBMPowerVSMachineCapacity expected to return an error")
}
} else {
if err != nil {
t.Fatalf("getIBMVPCMachineCapacity is not expected to return an error, error: %v", err)
}
g.Expect(capacity).To(Equal(tc.expectedCapacity))
expectedCapacity = map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: resource.MustParse("4"),
corev1.ResourceMemory: resource.MustParse("16G"),
}
createObject(g, &VPCMachineTemplate, ns.Name)
defer cleanupObject(g, &VPCMachineTemplate)

mockController, mockvpc, reconciler := setup(t)
t.Cleanup(mockController.Finish)
g.Expect(err).To(BeNil())
defer func() {
g.Expect(testEnv.Cleanup(ctx, ns)).To(Succeed())
}()
mockvpc.EXPECT().GetInstanceProfile(gomock.AssignableToTypeOf(&vpcv1.GetInstanceProfileOptions{})).Return(&profileDetails, &core.DetailedResponse{}, nil)
_, err = reconciler.reconcileNormal(ctx, mockvpc, VPCMachineTemplate)
if err != nil {
t.Fatalf("ReconcileNormal is not expected to return an error, error: %v", err)
}
g.Expect(err).To(BeNil())
g.Eventually(func() bool {
machineTemplate := &infrav1beta2.IBMVPCMachineTemplate{}
key := client.ObjectKey{
Name: VPCMachineTemplate.Name,
Namespace: ns.Name,
}
})
}
err = testEnv.Get(ctx, key, machineTemplate)
g.Expect(err).To(BeNil())
return reflect.DeepEqual(machineTemplate.Status.Capacity, expectedCapacity)
}, 10*time.Second).Should(Equal(true))

},
)

t.Run("with invalid profile ", func(tt *testing.T) {
g := NewWithT(tt)
ns, err := testEnv.CreateNamespace(ctx, fmt.Sprintf("namespace-%s", util.RandomString(5)))

VPCMachineTemplate := *stubVPCMachineTemplate("")
createObject(g, &VPCMachineTemplate, ns.Name)
defer cleanupObject(g, &VPCMachineTemplate)

mockController, mockvpc, reconciler := setup(t)
t.Cleanup(mockController.Finish)
g.Expect(err).To(BeNil())
defer func() {
g.Expect(testEnv.Cleanup(ctx, ns)).To(Succeed())
}()
mockvpc.EXPECT().GetInstanceProfile(gomock.AssignableToTypeOf(&vpcv1.GetInstanceProfileOptions{})).Return(nil, &core.DetailedResponse{}, nil)
_, err = reconciler.reconcileNormal(ctx, mockvpc, VPCMachineTemplate)
if err == nil {
t.Fatalf("ReconcileNormal is expected to return an error")
} else {
g.Expect(err).NotTo(BeNil())
}
},
)
}

func stubVPCMachineTemplate(profile string) *infrav1beta2.IBMVPCMachineTemplate {
Expand Down
5 changes: 3 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,9 @@ func setupReconcilers(mgr ctrl.Manager, serviceEndpoint []endpoints.ServiceEndpo
}

if err := (&controllers.IBMVPCMachineTemplateReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
ServiceEndpoint: serviceEndpoint,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ibmvpcmachinetemplate")
os.Exit(1)
Expand Down
Loading

0 comments on commit 6910468

Please sign in to comment.