Skip to content

Commit 05f3c48

Browse files
committed
[controller] add support for adding multiple instances to a placement at once
1 parent 06ebd7b commit 05f3c48

File tree

9 files changed

+98
-76
lines changed

9 files changed

+98
-76
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,6 @@ linux-amd64/
3434

3535
# Helm packages
3636
helm/**/*.tgz
37+
38+
# Vim swap files
39+
*.swp

pkg/apis/m3dboperator/v1alpha1/cluster.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ const (
4141
// been created for the cluster.
4242
ClusterConditionPlacementInitialized ClusterConditionType = "PlacementInitialized"
4343

44-
// ClusterConditionPodBootstrapping indicates there is a pod bootstrapping.
45-
ClusterConditionPodBootstrapping ClusterConditionType = "PodBootstrapping"
44+
// ClusterConditionPodsBootstrapping indicates there are pods bootstrapping.
45+
ClusterConditionPodsBootstrapping ClusterConditionType = "PodsBootstrapping"
4646
)
4747

4848
// M3DBCluster defines the cluster
@@ -103,10 +103,10 @@ func (s *M3DBStatus) HasInitializedPlacement() bool {
103103
return s.hasConditionTrue(ClusterConditionPlacementInitialized)
104104
}
105105

106-
// HasPodBootstrapping returns true if conditions indicate a pod is currently
106+
// HasPodsBootstrapping returns true if conditions indicate a pod is currently
107107
// bootstrapping.
108-
func (s *M3DBStatus) HasPodBootstrapping() bool {
109-
return s.hasConditionTrue(ClusterConditionPodBootstrapping)
108+
func (s *M3DBStatus) HasPodsBootstrapping() bool {
109+
return s.hasConditionTrue(ClusterConditionPodsBootstrapping)
110110
}
111111

112112
// GetCondition returns the specified cluster condition if it exists with a bool

pkg/controller/m3admin_client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ func (c errorPlacementClient) Delete() error {
267267
return c.err
268268
}
269269

270-
func (c errorPlacementClient) Add(placementpb.Instance) error {
270+
func (c errorPlacementClient) Add([]*placementpb.Instance) error {
271271
return c.err
272272
}
273273

pkg/controller/m3admin_client_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ func TestErrorPlacementClient(t *testing.T) {
237237
err = cl.Delete()
238238
assert.Equal(t, clErr, err)
239239

240-
err = cl.Add(placementpb.Instance{})
240+
err = cl.Add([]*placementpb.Instance{&placementpb.Instance{}})
241241
assert.Equal(t, clErr, err)
242242

243243
err = cl.Remove("foo")

pkg/controller/update_cluster.go

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
package controller
2222

2323
import (
24+
"bytes"
2425
"encoding/json"
2526
"errors"
2627
"fmt"
@@ -38,12 +39,14 @@ import (
3839
"github.com/m3db/m3db-operator/pkg/m3admin/namespace"
3940
"github.com/m3db/m3db-operator/pkg/util/eventer"
4041

42+
"github.com/m3db/m3/src/cluster/generated/proto/placementpb"
4143
"github.com/m3db/m3/src/cluster/placement"
4244
dbns "github.com/m3db/m3/src/dbnode/generated/proto/namespace"
4345
"github.com/m3db/m3/src/query/generated/proto/admin"
4446

4547
appsv1 "k8s.io/api/apps/v1"
4648
corev1 "k8s.io/api/core/v1"
49+
v1 "k8s.io/api/core/v1"
4750
klabels "k8s.io/apimachinery/pkg/labels"
4851
"k8s.io/apimachinery/pkg/util/runtime"
4952

@@ -306,16 +309,14 @@ func (c *M3DBController) setStatusPlacementCreated(cluster *myspec.M3DBCluster)
306309
return cluster, nil
307310
}
308311

309-
func (c *M3DBController) setStatusPodBootstrapping(cluster *myspec.M3DBCluster,
312+
func (c *M3DBController) setStatusPodsBootstrapping(cluster *myspec.M3DBCluster,
310313
status corev1.ConditionStatus,
311314
reason, message string) (*myspec.M3DBCluster, error) {
312-
313-
return c.setStatus(cluster, myspec.ClusterConditionPodBootstrapping, status, reason, message)
315+
return c.setStatus(cluster, myspec.ClusterConditionPodsBootstrapping, status, reason, message)
314316
}
315317

316318
func (c *M3DBController) setStatus(cluster *myspec.M3DBCluster, condition myspec.ClusterConditionType,
317319
status corev1.ConditionStatus, reason, message string) (*myspec.M3DBCluster, error) {
318-
319320
cond, ok := cluster.Status.GetCondition(condition)
320321
if !ok {
321322
cond = myspec.ClusterCondition{
@@ -355,43 +356,52 @@ func (c *M3DBController) reconcileBootstrappingStatus(cluster *myspec.M3DBCluste
355356
}
356357
}
357358

358-
return c.setStatus(cluster, myspec.ClusterConditionPodBootstrapping, corev1.ConditionFalse,
359+
return c.setStatus(cluster, myspec.ClusterConditionPodsBootstrapping, corev1.ConditionFalse,
359360
"BootstrapComplete", "no bootstraps in progress")
360361
}
361362

362-
func (c *M3DBController) addPodToPlacement(cluster *myspec.M3DBCluster, pod *corev1.Pod) error {
363-
c.logger.Info("found pod not in placement", zap.String("pod", pod.Name))
364-
inst, err := m3db.PlacementInstanceFromPod(cluster, pod, c.podIDProvider)
365-
if err != nil {
366-
err := fmt.Errorf("error creating instance for pod %s", pod.Name)
367-
c.logger.Error(err.Error())
368-
return err
363+
func (c *M3DBController) addPodsToPlacement(cluster *myspec.M3DBCluster, pods []*corev1.Pod) error {
364+
var (
365+
instances = make([]*placementpb.Instance, 0, len(pods))
366+
reasonBuf = bytes.NewBufferString("adding pods to placement (")
367+
loggerFields = make([]zap.Field, 0, len(pods))
368+
)
369+
for _, pod := range pods {
370+
c.logger.Info("found pod not in placement", zap.String("pod", pod.Name))
371+
inst, err := m3db.PlacementInstanceFromPod(cluster, pod, c.podIDProvider)
372+
if err != nil {
373+
err := fmt.Errorf("error creating instance for pod %s", pod.Name)
374+
c.logger.Error(err.Error())
375+
return err
376+
}
377+
instances = append(instances, inst)
378+
reasonBuf.WriteString(fmt.Sprintf("%s,", pod.Name))
379+
loggerFields = append(loggerFields, zap.String("pod", pod.Name))
369380
}
370-
371-
reason := fmt.Sprintf("adding pod %s to placement", pod.Name)
372-
_, err = c.setStatusPodBootstrapping(cluster, corev1.ConditionTrue, "PodAdded", reason)
381+
reasonBuf.WriteString(")")
382+
reason := reasonBuf.String()
383+
_, err := c.setStatusPodsBootstrapping(cluster, corev1.ConditionTrue, "PodAdded", reason)
373384
if err != nil {
374-
err := fmt.Errorf("error setting pod bootstrapping status: %v", err)
385+
err := fmt.Errorf("error setting pods bootstrapping status: %v", err)
375386
c.logger.Error(err.Error())
376387
return err
377388
}
378389

379-
err = c.adminClient.placementClientForCluster(cluster).Add(*inst)
390+
err = c.adminClient.placementClientForCluster(cluster).Add(instances)
380391
if err != nil {
381-
err := fmt.Errorf("error adding pod to placement: %s", pod.Name)
392+
err := fmt.Errorf("error: %s", reason)
382393
c.logger.Error(err.Error())
383394
return err
384395
}
385396

386-
c.logger.Info("added pod to placement", zap.String("pod", pod.Name))
397+
c.logger.Info("added pods to placement", loggerFields...)
387398
return nil
388399
}
389400

390401
func (c *M3DBController) checkPodsForReplacement(
391402
cluster *myspec.M3DBCluster,
392403
pods []*corev1.Pod,
393404
pl placement.Placement) (string, *corev1.Pod, error) {
394-
395405
insts := pl.Instances()
396406
sort.Sort(placement.ByIDAscending(insts))
397407

@@ -429,7 +439,6 @@ func (c *M3DBController) replacePodInPlacement(
429439
pl placement.Placement,
430440
leavingInstanceID string,
431441
newPod *corev1.Pod) error {
432-
433442
c.logger.Info("replacing pod in placement", zap.String("pod", leavingInstanceID))
434443

435444
newInst, err := m3db.PlacementInstanceFromPod(cluster, newPod, c.podIDProvider)
@@ -440,7 +449,7 @@ func (c *M3DBController) replacePodInPlacement(
440449
}
441450

442451
reason := fmt.Sprintf("replacing %s pod in placement", newPod.Name)
443-
_, err = c.setStatusPodBootstrapping(cluster, corev1.ConditionTrue, "PodReplaced", reason)
452+
_, err = c.setStatusPodsBootstrapping(cluster, corev1.ConditionTrue, "PodReplaced", reason)
444453
if err != nil {
445454
err := fmt.Errorf("error setting replacement pod bootstrapping status: %v", err)
446455
c.logger.Error(err.Error())
@@ -461,7 +470,6 @@ func (c *M3DBController) replacePodInPlacement(
461470
// be added to the placement and chooses a pod to expand to the placement.
462471
func (c *M3DBController) expandPlacementForSet(cluster *myspec.M3DBCluster, set *appsv1.StatefulSet,
463472
group myspec.IsolationGroup, placement placement.Placement) error {
464-
465473
existInsts := instancesInIsoGroup(placement, group.Name)
466474
if len(existInsts) >= int(group.NumInstances) {
467475
c.logger.Warn("not expanding set, already at desired capacity",
@@ -483,6 +491,7 @@ func (c *M3DBController) expandPlacementForSet(cluster *myspec.M3DBCluster, set
483491
return err
484492
}
485493

494+
podsToAdd := make([]*v1.Pod, 0, len(pods))
486495
for _, pod := range pods {
487496
id, err := c.podIDProvider.Identity(pod, cluster)
488497
if err != nil {
@@ -494,11 +503,14 @@ func (c *M3DBController) expandPlacementForSet(cluster *myspec.M3DBCluster, set
494503
}
495504
_, ok := placement.Instance(idStr)
496505
if !ok {
497-
return c.addPodToPlacement(cluster, pod)
506+
podsToAdd = append(podsToAdd, pod)
498507
}
499508
}
509+
if len(podsToAdd) == 0 {
510+
return errors.New("could not find pod absent from placement")
511+
}
500512

501-
return errors.New("could not find pod absent from placement")
513+
return c.addPodsToPlacement(cluster, podsToAdd)
502514
}
503515

504516
// shrinkPlacementForSet takes a StatefulSet that needs to be shrunk and

pkg/controller/update_cluster_test.go

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -365,18 +365,18 @@ func TestNamespacesToReady(t *testing.T) {
365365

366366
func TestSetPodBootstrappingStatus(t *testing.T) {
367367
cluster := getFixture("cluster-simple.yaml", t)
368-
assert.False(t, cluster.Status.HasPodBootstrapping())
368+
assert.False(t, cluster.Status.HasPodsBootstrapping())
369369

370370
deps := newTestDeps(t, &testOpts{
371371
crdObjects: []runtime.Object{cluster},
372372
})
373373
controller := deps.newController(t)
374374
defer deps.cleanup()
375375

376-
cluster, err := controller.setStatusPodBootstrapping(cluster, corev1.ConditionTrue, "foo", "bar")
376+
cluster, err := controller.setStatusPodsBootstrapping(cluster, corev1.ConditionTrue, "foo", "bar")
377377
assert.NoError(t, err)
378378

379-
assert.True(t, cluster.Status.HasPodBootstrapping())
379+
assert.True(t, cluster.Status.HasPodsBootstrapping())
380380
}
381381

382382
func TestSetStatus(t *testing.T) {
@@ -426,7 +426,7 @@ func TestReconcileBootstrappingStatus(t *testing.T) {
426426
controller := deps.newController(t)
427427
defer deps.cleanup()
428428

429-
const cond = myspec.ClusterConditionPodBootstrapping
429+
const cond = myspec.ClusterConditionPodsBootstrapping
430430

431431
newPl := func(state shard.State) placement.Placement {
432432
return placement.NewPlacement().SetInstances([]placement.Instance{
@@ -452,7 +452,7 @@ func TestReconcileBootstrappingStatus(t *testing.T) {
452452
assert.Equal(t, string(corev1.ConditionFalse), string(c.Status))
453453
}
454454

455-
func TestAddPodToPlacement(t *testing.T) {
455+
func TestAddPodsToPlacement(t *testing.T) {
456456
cluster := getFixture("cluster-simple.yaml", t)
457457

458458
deps := newTestDeps(t, &testOpts{
@@ -461,36 +461,40 @@ func TestAddPodToPlacement(t *testing.T) {
461461
controller := deps.newController(t)
462462
defer deps.cleanup()
463463

464-
pod := &corev1.Pod{
465-
ObjectMeta: metav1.ObjectMeta{
466-
Name: "pod-a",
467-
Labels: map[string]string{
468-
"operator.m3db.io/isolation-group": "zone-a",
464+
var (
465+
pods []*corev1.Pod
466+
placements []*placementpb.Instance
467+
)
468+
for _, name := range []string{"pod-a1", "pod-a2"} {
469+
pod := &corev1.Pod{
470+
ObjectMeta: metav1.ObjectMeta{
471+
Name: name,
472+
Labels: map[string]string{
473+
"operator.m3db.io/isolation-group": "zone-a",
474+
},
469475
},
470-
},
471-
}
472-
473-
deps.idProvider.EXPECT().Identity(pod, cluster).Return(&myspec.PodIdentity{}, nil)
474-
475-
expInstance := placementpb.Instance{
476-
Id: "{}",
477-
IsolationGroup: "zone-a",
478-
Zone: "embedded",
479-
Endpoint: "pod-a.m3dbnode-cluster-simple:9000",
480-
Hostname: "pod-a.m3dbnode-cluster-simple",
481-
Port: 9000,
482-
Weight: 100,
476+
}
477+
deps.idProvider.EXPECT().Identity(pod, cluster).Return(&myspec.PodIdentity{}, nil)
478+
pods = append(pods, pod)
479+
placements = append(placements, &placementpb.Instance{
480+
Id: "{}",
481+
IsolationGroup: "zone-a",
482+
Zone: "embedded",
483+
Endpoint: fmt.Sprintf("%s.m3dbnode-cluster-simple:9000", name),
484+
Hostname: fmt.Sprintf("%s.m3dbnode-cluster-simple", name),
485+
Port: 9000,
486+
Weight: 100,
487+
})
483488
}
489+
deps.placementClient.EXPECT().Add(placements)
484490

485-
deps.placementClient.EXPECT().Add(expInstance)
486-
487-
err := controller.addPodToPlacement(cluster, pod)
491+
err := controller.addPodsToPlacement(cluster, pods)
488492
assert.NoError(t, err)
489493

490494
cluster, err = controller.crdClient.OperatorV1alpha1().M3DBClusters(cluster.Namespace).Get(cluster.Name, metav1.GetOptions{})
491495
assert.NoError(t, err)
492496

493-
assert.True(t, cluster.Status.HasPodBootstrapping())
497+
assert.True(t, cluster.Status.HasPodsBootstrapping())
494498
}
495499

496500
func podsForClusterSet(cluster *myspec.M3DBCluster, set *appsv1.StatefulSet, numPods int) []*corev1.Pod {
@@ -599,19 +603,23 @@ func TestExpandPlacementForSet(t *testing.T) {
599603

600604
identifyPods(idProvider, pods, nil)
601605

602-
pl := placementFromPods(t, cluster, pods[:2], idProvider)
606+
pl := placementFromPods(t, cluster, pods[:1], idProvider)
603607
group := cluster.Spec.IsolationGroups[0]
604608

605-
instPb, err := m3db.PlacementInstanceFromPod(cluster, pods[2], idProvider)
606-
require.NoError(t, err)
609+
var placements []*placementpb.Instance
610+
for _, pod := range pods[1:] {
611+
instPb, err := m3db.PlacementInstanceFromPod(cluster, pod, idProvider)
612+
require.NoError(t, err)
613+
placements = append(placements, instPb)
614+
}
607615

608-
placementMock.EXPECT().Add(*instPb)
616+
placementMock.EXPECT().Add(placements)
609617
err = controller.expandPlacementForSet(cluster, set, group, pl)
610618
assert.NoError(t, err)
611619

612620
cluster, err = deps.crdClient.OperatorV1alpha1().M3DBClusters(cluster.Namespace).Get(cluster.Name, metav1.GetOptions{})
613621
require.NoError(t, err)
614-
assert.True(t, cluster.Status.HasPodBootstrapping())
622+
assert.True(t, cluster.Status.HasPodsBootstrapping())
615623
}
616624

617625
func TestExpandPlacementForSet_Nop(t *testing.T) {
@@ -853,7 +861,6 @@ func TestSortPods(t *testing.T) {
853861
}
854862

855863
func TestCheckPodsForReplacement(t *testing.T) {
856-
857864
cluster := getFixture("cluster-3-zones.yaml", t)
858865
deps := newTestDeps(t, &testOpts{
859866
crdObjects: []runtime.Object{cluster},
@@ -888,7 +895,8 @@ func TestCheckPodsForReplacement(t *testing.T) {
888895
Labels: map[string]string{
889896
"different": "label",
890897
},
891-
}})
898+
},
899+
})
892900

893901
identifyPods(idProvider, replacePods, nil)
894902

@@ -950,7 +958,6 @@ func TestReplacePodInPlacement(t *testing.T) {
950958

951959
err = controller.replacePodInPlacement(cluster, pl, testLeavingInstanceID, testNewPod)
952960
require.NoError(t, err)
953-
954961
}
955962

956963
func TestReplacePodInPlacementWithError(t *testing.T) {
@@ -972,7 +979,7 @@ func TestReplacePodInPlacementWithError(t *testing.T) {
972979
pl := placementFromPods(t, cluster, podsForPlacement, idProvider)
973980

974981
// error creating instance
975-
var badPod = &corev1.Pod{
982+
badPod := &corev1.Pod{
976983
ObjectMeta: metav1.ObjectMeta{
977984
Name: podsForPlacement[0].Name,
978985
UID: types.UID("ABC"),

pkg/m3admin/placement/client.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,11 @@ func (p *placementClient) Get() (m3placement.Placement, error) {
114114
return m3placement.NewPlacementFromProto(resp.Placement)
115115
}
116116

117-
// Add will add an instance to the current placement
118-
func (p *placementClient) Add(instance placementpb.Instance) error {
117+
// Add multiple instances to the current placement.
118+
func (p *placementClient) Add(instances []*placementpb.Instance) error {
119119
url := p.url + placementBaseURL
120120
req := &admin.PlacementAddRequest{
121-
Instances: []*placementpb.Instance{&instance},
121+
Instances: instances,
122122
}
123123

124124
err := p.client.DoHTTPJSONPBRequest(http.MethodPost, url, req, nil)

0 commit comments

Comments
 (0)