Skip to content
This repository was archived by the owner on Oct 10, 2023. It is now read-only.

Commit 86bd813

Browse files
committed
Add scaling support for TKGS v1alpha2 version
Signed-off-by: PremKumar Kalle <pkalle@vmware.com>
1 parent 4ed8343 commit 86bd813

File tree

4 files changed

+179
-32
lines changed

4 files changed

+179
-32
lines changed

pkg/v1/tkg/client/scale.go

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func (c *TkgClient) DoScaleCluster(clusterClient clusterclient.Client, options *
6565
if err != nil {
6666
return err
6767
}
68-
return c.ScalePacificCluster(*options, clusterClient)
68+
return c.ScalePacificCluster(options, clusterClient)
6969
}
7070

7171
errList := []error{}
@@ -99,7 +99,7 @@ func (c *TkgClient) DoScaleCluster(clusterClient clusterclient.Client, options *
9999
}
100100

101101
// ScalePacificCluster scale TKGS cluster
102-
func (c *TkgClient) ScalePacificCluster(options ScaleClusterOptions, clusterClient clusterclient.Client) error {
102+
func (c *TkgClient) ScalePacificCluster(options *ScaleClusterOptions, clusterClient clusterclient.Client) error {
103103
var err error
104104
errList := []error{}
105105
// If the option specifying the targetNamespace is empty, try to detect it.
@@ -117,7 +117,10 @@ func (c *TkgClient) ScalePacificCluster(options ScaleClusterOptions, clusterClie
117117
}
118118
}
119119
if options.WorkerCount > 0 {
120-
err := clusterClient.ScalePacificClusterWorkerNodes(options.ClusterName, options.Namespace, options.WorkerCount)
120+
if options.NodePoolName == "" {
121+
return errors.Errorf("unable to scale workers nodes for cluster %q in namespace %q , please specify the node pool name", options.ClusterName, options.Namespace)
122+
}
123+
err := c.scalePacificClusterNodePool(clusterClient, options)
121124
if err != nil {
122125
errList = append(errList, errors.Wrapf(err, "unable to scale workers nodes for workload cluster %s", options.ClusterName))
123126
} else {
@@ -171,14 +174,7 @@ func (c *TkgClient) scaleWorkersNodePool(clusterClient clusterclient.Client, opt
171174
return errors.Errorf("Could not find node pool with name %s", options.NodePoolName)
172175
}
173176

174-
mdOptions := SetMachineDeploymentOptions{
175-
Namespace: options.Namespace,
176-
ClusterName: options.ClusterName,
177-
NodePool: NodePool{
178-
Name: options.NodePoolName,
179-
Replicas: &options.WorkerCount,
180-
},
181-
}
177+
mdOptions := prepareSetMachineDeploymentOptions(options)
182178
if err := DoSetMachineDeployment(clusterClient, &mdOptions); err != nil {
183179
return errors.Wrapf(err, "Unable to scale node pool %s", options.NodePoolName)
184180
}
@@ -214,3 +210,46 @@ func (c *TkgClient) mdExists(clusterClient clusterclient.Client, options *ScaleC
214210

215211
return false, nil
216212
}
213+
214+
func (c *TkgClient) scalePacificClusterNodePool(clusterClient clusterclient.Client, options *ScaleClusterOptions) error {
215+
nodePoolExists, err := c.tkcNodePoolExists(clusterClient, options)
216+
if err != nil {
217+
return err
218+
}
219+
if !nodePoolExists {
220+
return errors.Errorf("could not find node pool with name %s", options.NodePoolName)
221+
}
222+
223+
mdOptions := prepareSetMachineDeploymentOptions(options)
224+
if err = c.SetNodePoolsForPacificCluster(clusterClient, &mdOptions); err != nil {
225+
return errors.Wrapf(err, "unable to scale node pool %s", options.NodePoolName)
226+
}
227+
228+
return nil
229+
}
230+
231+
func (c *TkgClient) tkcNodePoolExists(clusterClient clusterclient.Client, options *ScaleClusterOptions) (bool, error) {
232+
tkc, err := clusterClient.GetPacificClusterObject(options.ClusterName, options.Namespace)
233+
if err != nil {
234+
return false, errors.Wrapf(err, "unable to get TKC object %q in namespace %q", options.ClusterName, options.Namespace)
235+
}
236+
237+
nodePools := tkc.Spec.Topology.NodePools
238+
for idx := range nodePools {
239+
if nodePools[idx].Name == options.NodePoolName {
240+
return true, nil
241+
}
242+
}
243+
return false, nil
244+
}
245+
246+
func prepareSetMachineDeploymentOptions(options *ScaleClusterOptions) SetMachineDeploymentOptions {
247+
return SetMachineDeploymentOptions{
248+
Namespace: options.Namespace,
249+
ClusterName: options.ClusterName,
250+
NodePool: NodePool{
251+
Name: options.NodePoolName,
252+
Replicas: &options.WorkerCount,
253+
},
254+
}
255+
}

pkg/v1/tkg/client/scale_test.go

Lines changed: 71 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import (
1212
"github.com/pkg/errors"
1313
v1 "k8s.io/api/core/v1"
1414
"sigs.k8s.io/cluster-api/api/v1beta1"
15+
"sigs.k8s.io/controller-runtime/pkg/client"
1516

17+
tkgsv1alpha2 "github.com/vmware-tanzu/tanzu-framework/apis/run/v1alpha2"
1618
. "github.com/vmware-tanzu/tanzu-framework/pkg/v1/tkg/client"
1719
"github.com/vmware-tanzu/tanzu-framework/pkg/v1/tkg/fakes"
1820
fakehelper "github.com/vmware-tanzu/tanzu-framework/pkg/v1/tkg/fakes/helper"
@@ -35,74 +37,124 @@ var _ = Describe("Unit tests for scalePacificCluster", func() {
3537
})
3638

3739
JustBeforeEach(func() {
38-
err = tkgClient.ScalePacificCluster(scaleClusterOptions, regionalClusterClient)
40+
err = tkgClient.ScalePacificCluster(&scaleClusterOptions, regionalClusterClient)
3941
})
4042

41-
Context("When scaleClusterOptions is all set", func() {
43+
Context("When namespace is empty", func() {
4244
BeforeEach(func() {
45+
regionalClusterClient.GetCurrentNamespaceReturns("", errors.New("fake-error"))
4346
scaleClusterOptions = ScaleClusterOptions{
4447
ClusterName: "my-cluster",
45-
Namespace: "namespace-1",
48+
Namespace: "",
4649
WorkerCount: 5,
4750
ControlPlaneCount: 10,
4851
Kubeconfig: kubeconfig,
4952
}
50-
regionalClusterClient.ScalePacificClusterControlPlaneReturns(nil)
51-
regionalClusterClient.ScalePacificClusterWorkerNodesReturns(nil)
5253
})
53-
It("should not return error", func() {
54-
Expect(err).ToNot(HaveOccurred())
54+
It("returns an error", func() {
55+
Expect(err).To(HaveOccurred())
56+
Expect(err.Error()).To(ContainSubstring("failed to get current namespace"))
5557
})
5658
})
5759

58-
Context("When namespace is empty", func() {
60+
Context("When control plane for workload cluster cannot be scaled", func() {
5961
BeforeEach(func() {
60-
regionalClusterClient.GetCurrentNamespaceReturns("", errors.New("fake-error"))
6162
scaleClusterOptions = ScaleClusterOptions{
6263
ClusterName: "my-cluster",
63-
Namespace: "",
64-
WorkerCount: 5,
64+
Namespace: "namespace-1",
65+
WorkerCount: 0,
6566
ControlPlaneCount: 10,
6667
Kubeconfig: kubeconfig,
6768
}
69+
regionalClusterClient.ScalePacificClusterControlPlaneReturns(errors.New("fake-error"))
6870
})
6971
It("returns an error", func() {
7072
Expect(err).To(HaveOccurred())
71-
Expect(err.Error()).To(ContainSubstring("failed to get current namespace"))
73+
Expect(err.Error()).To(ContainSubstring("unable to scale control plane for workload cluster"))
7274
})
7375
})
7476

75-
Context("When control plane for workload cluster cannot be scaled", func() {
77+
Context("When workers count for workload cluster is provided but nodepool name is not provided", func() {
7678
BeforeEach(func() {
7779
scaleClusterOptions = ScaleClusterOptions{
7880
ClusterName: "my-cluster",
7981
Namespace: "namespace-1",
8082
WorkerCount: 5,
8183
ControlPlaneCount: 10,
8284
Kubeconfig: kubeconfig,
85+
NodePoolName: "",
8386
}
84-
regionalClusterClient.ScalePacificClusterControlPlaneReturns(errors.New("fake-error"))
8587
})
8688
It("returns an error", func() {
8789
Expect(err).To(HaveOccurred())
88-
Expect(err.Error()).To(ContainSubstring("unable to scale control plane for workload cluster"))
90+
errString := fmt.Sprintf(`unable to scale workers nodes for cluster "%s" in namespace "%s" , please specify the node pool name`,
91+
scaleClusterOptions.ClusterName, scaleClusterOptions.Namespace)
92+
Expect(err.Error()).To(ContainSubstring(errString))
8993
})
9094
})
91-
92-
Context("When workers nodes for workload cluster cannot be scaled", func() {
95+
Context("When nodepool to be scaled doesn't exists in TKC", func() {
96+
BeforeEach(func() {
97+
scaleClusterOptions = ScaleClusterOptions{
98+
ClusterName: "my-cluster",
99+
Namespace: "namespace-1",
100+
WorkerCount: 5,
101+
ControlPlaneCount: 10,
102+
Kubeconfig: kubeconfig,
103+
NodePoolName: "non-existing-nodepool",
104+
}
105+
tkc := GetDummyPacificCluster()
106+
regionalClusterClient.GetPacificClusterObjectReturns(&tkc, nil)
107+
})
108+
It("returns an error", func() {
109+
Expect(err).To(HaveOccurred())
110+
Expect(err.Error()).To(ContainSubstring(fmt.Sprintf("could not find node pool with name %s", scaleClusterOptions.NodePoolName)))
111+
})
112+
})
113+
Context("When nodepool update operation failed", func() {
93114
BeforeEach(func() {
94115
scaleClusterOptions = ScaleClusterOptions{
95116
ClusterName: "my-cluster",
96117
Namespace: "namespace-1",
97118
WorkerCount: 5,
98119
ControlPlaneCount: 10,
99120
Kubeconfig: kubeconfig,
121+
NodePoolName: "nodepool-1",
100122
}
101-
regionalClusterClient.ScalePacificClusterWorkerNodesReturns(errors.New("fake-error"))
123+
tkc := GetDummyPacificCluster()
124+
regionalClusterClient.GetPacificClusterObjectReturns(&tkc, nil)
125+
regionalClusterClient.UpdateResourceCalls(func(obj interface{}, objName string, namespace string, opts ...client.UpdateOption) error {
126+
return errors.New("fake-tkc-update-error")
127+
})
102128
})
103129
It("returns an error", func() {
104130
Expect(err).To(HaveOccurred())
105-
Expect(err.Error()).To(ContainSubstring("unable to scale workers nodes for workload cluster"))
131+
Expect(err.Error()).To(ContainSubstring(fmt.Sprintf("unable to scale node pool %s", scaleClusterOptions.NodePoolName)))
132+
Expect(err.Error()).To(ContainSubstring("fake-tkc-update-error"))
133+
})
134+
})
135+
Context("When scaleClusterOptions is all set and scaling controlplane and nodepools is success", func() {
136+
var gotTkc *tkgsv1alpha2.TanzuKubernetesCluster
137+
BeforeEach(func() {
138+
scaleClusterOptions = ScaleClusterOptions{
139+
ClusterName: "my-cluster",
140+
Namespace: "namespace-1",
141+
WorkerCount: 5,
142+
ControlPlaneCount: 10,
143+
Kubeconfig: kubeconfig,
144+
NodePoolName: "nodepool-1",
145+
}
146+
tkc := GetDummyPacificCluster()
147+
regionalClusterClient.GetPacificClusterObjectReturns(&tkc, nil)
148+
regionalClusterClient.ScalePacificClusterControlPlaneReturns(nil)
149+
regionalClusterClient.UpdateResourceCalls(func(obj interface{}, objName string, namespace string, opts ...client.UpdateOption) error {
150+
gotTkc = obj.(*tkgsv1alpha2.TanzuKubernetesCluster)
151+
return nil
152+
})
153+
})
154+
It("should not return error", func() {
155+
Expect(err).ToNot(HaveOccurred())
156+
Expect(gotTkc.Spec.Topology.NodePools[0].Name).To(Equal("nodepool-1"))
157+
Expect(*gotTkc.Spec.Topology.NodePools[0].Replicas).To(Equal(int32(5)))
106158
})
107159
})
108160
})

pkg/v1/tkg/clusterclient/clusterclient.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,8 +1603,11 @@ func (c *client) ScalePacificClusterControlPlane(clusterName, namespace string,
16031603
if err != nil {
16041604
return err
16051605
}
1606-
patchPacificControlPlaneCount := fmt.Sprintf("{\"spec\":{ \"topology\":{\"controlPlane\":{\"count\": %v}}}}", controlPlaneCount)
1607-
err = c.PatchResource(tkcObj, clusterName, namespace, patchPacificControlPlaneCount, types.MergePatchType, nil)
1606+
1607+
payloadFormatStr := `[{"op":"replace","path":"/spec/topology/controlPlane/replicas","value":%d}]`
1608+
payloadBytes := fmt.Sprintf(payloadFormatStr, controlPlaneCount)
1609+
log.V(3).Infof("Applying TanzuKubernetesCluster controlplane replicas update patch: %s", payloadBytes)
1610+
err = c.PatchResource(tkcObj, clusterName, namespace, payloadBytes, types.JSONPatchType, nil)
16081611
if err != nil {
16091612
return errors.Wrap(err, "unable to patch the cluster controlPlane count")
16101613
}

pkg/v1/tkg/clusterclient/clusterclient_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package clusterclient_test
55

66
import (
77
"context"
8+
"fmt"
89
"net/http"
910
"os"
1011
rt "runtime"
@@ -890,7 +891,59 @@ var _ = Describe("Cluster Client", func() {
890891
})
891892
})
892893
})
894+
Describe("ScalePacificClusterControlPlane", func() {
895+
var controlPlaneCount int32 = 1
896+
var clusterName string = "fake-cluster-name"
897+
var namespace string = "fake-namespace"
898+
BeforeEach(func() {
899+
reInitialize()
900+
kubeConfigPath := getConfigFilePath("config1.yaml")
901+
clstClient, err = NewClient(kubeConfigPath, "", clusterClientOptions)
902+
Expect(err).NotTo(HaveOccurred())
903+
})
904+
JustBeforeEach(func() {
905+
err = clstClient.ScalePacificClusterControlPlane(clusterName, namespace, controlPlaneCount)
906+
})
907+
Context("When getting Pacific cluster object fails", func() {
908+
BeforeEach(func() {
909+
clientset.GetReturns(errors.New("fake-error-while-get"))
910+
})
893911

912+
It("should return an error", func() {
913+
Expect(err).To(HaveOccurred())
914+
Expect(err.Error()).To(ContainSubstring(fmt.Sprintf(`failed to get TKC object in namespace: '%s'`, namespace)))
915+
})
916+
})
917+
Context("When clientset Patch return error", func() {
918+
BeforeEach(func() {
919+
clientset.GetReturns(nil)
920+
clientset.PatchReturns(errors.New("fake-error-while-patch"))
921+
})
922+
It("should return an error", func() {
923+
Expect(err).To(HaveOccurred())
924+
Expect(err.Error()).To(ContainSubstring("unable to patch the cluster controlPlane count"))
925+
})
926+
})
927+
Context("When clientset Get is successful but Patch return error", func() {
928+
var gotPatch string
929+
BeforeEach(func() {
930+
controlPlaneCount = 5
931+
clientset.GetReturns(nil)
932+
clientset.PatchCalls(func(ctx context.Context, cluster runtime.Object, patch crtclient.Patch, patchoptions ...crtclient.PatchOption) error {
933+
patchBytes, err := patch.Data(cluster)
934+
Expect(err).NotTo(HaveOccurred())
935+
gotPatch = string(patchBytes)
936+
return nil
937+
})
938+
})
939+
It("should return not an error", func() {
940+
Expect(err).ToNot(HaveOccurred())
941+
payloadFormatStr := `[{"op":"replace","path":"/spec/topology/controlPlane/replicas","value":%d}]`
942+
payloadBytes := fmt.Sprintf(payloadFormatStr, controlPlaneCount)
943+
Expect(gotPatch).To(Equal(payloadBytes))
944+
})
945+
})
946+
})
894947
Describe("DeactivateTanzuKubernetesReleases", func() {
895948
BeforeEach(func() {
896949
reInitialize()

0 commit comments

Comments
 (0)