Skip to content

Commit

Permalink
all configmap should be updated when not in manual mode (#258)
Browse files Browse the repository at this point in the history
* all configmap should be updated when not in manual mode

Signed-off-by: haorenfsa <[email protected]>

* remove unnecessary code

Signed-off-by: haorenfsa <[email protected]>

---------

Signed-off-by: haorenfsa <[email protected]>
  • Loading branch information
haorenfsa authored Feb 10, 2025
1 parent e7f0518 commit 24b390a
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 79 deletions.
1 change: 1 addition & 0 deletions apis/milvus.io/v1beta1/components_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ type MilvusComponents struct {
// note: the active configmap won't switch automatically
// because we may want to change configmap for existing pods
// so it's hard to determine when to switch. you need to switch it manually.
// if EnableManualMode is set to false, changes will be applied to all configmaps
ActiveConfigMap string `json:"activeConfigMap,omitempty"`

// RunAsNonRoot whether to run milvus as non-root user
Expand Down
2 changes: 1 addition & 1 deletion pkg/controllers/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ func MergeComponentSpec(src, dst ComponentSpec) ComponentSpec {
}

if src.RunWithSubProcess != nil {
dst.RunWithSubProcess = &(*src.RunWithSubProcess)
dst.RunWithSubProcess = src.RunWithSubProcess
}

if src.HostNetwork {
Expand Down
4 changes: 0 additions & 4 deletions pkg/controllers/conditions.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"sync"
"time"

"github.com/apache/pulsar-client-go/pulsar"
"github.com/go-logr/logr"
"github.com/milvus-io/milvus-operator/apis/milvus.io/v1beta1"
"github.com/milvus-io/milvus-operator/pkg/external"
Expand All @@ -23,9 +22,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)

// pulsarNewClient wraps pulsar.NewClient for test mock convenience
var pulsarNewClient = pulsar.NewClient

func GetCondition(getter func() v1beta1.MilvusCondition, eps []string) v1beta1.MilvusCondition {
// lock & get again
for !endpointCheckCache.TryStartProbeFor(eps) {
Expand Down
38 changes: 37 additions & 1 deletion pkg/controllers/configmaps.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import (
corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"

"github.com/milvus-io/milvus-operator/apis/milvus.io/v1beta1"
"github.com/milvus-io/milvus-operator/pkg/config"
"github.com/milvus-io/milvus-operator/pkg/util"
"github.com/pkg/errors"
)

func (r *MilvusReconciler) getMinioAccessInfo(ctx context.Context, mc v1beta1.Milvus) (string, string) {
Expand Down Expand Up @@ -116,7 +119,40 @@ func (r *MilvusReconciler) updateConfigMap(ctx context.Context, mc v1beta1.Milvu
}

func (r *MilvusReconciler) ReconcileConfigMaps(ctx context.Context, mc v1beta1.Milvus) error {
namespacedName := NamespacedName(mc.Namespace, mc.GetActiveConfigMap())
if mc.Spec.Com.EnableManualMode {
namespacedName := NamespacedName(mc.Namespace, mc.GetActiveConfigMap())
return reconcileOneConfigMap(r, ctx, mc, namespacedName)
}
// reconcile all configmaps
cmLabels := NewAppLabels(mc.Name)
cmList := &corev1.ConfigMapList{}
err := r.List(ctx, cmList, &client.ListOptions{
Namespace: mc.Namespace,
LabelSelector: labels.SelectorFromSet(cmLabels),
})
if err != nil {
return errors.Wrap(err, "list configmaps")
}
if len(cmList.Items) == 0 {
// when no configmap found, create one
cmList.Items = append(cmList.Items, corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: mc.GetActiveConfigMap(),
Namespace: mc.Namespace,
},
})
}

for _, cm := range cmList.Items {
err = reconcileOneConfigMap(r, ctx, mc, client.ObjectKeyFromObject(&cm))
if err != nil {
return errors.Wrapf(err, "reconcile configmap[%s]", &cm.ObjectMeta)
}
}
return nil
}

var reconcileOneConfigMap = func(r *MilvusReconciler, ctx context.Context, mc v1beta1.Milvus, namespacedName types.NamespacedName) error {
old := &corev1.ConfigMap{}
err := r.Get(ctx, namespacedName, old)
if kerrors.IsNotFound(err) {
Expand Down
135 changes: 98 additions & 37 deletions pkg/controllers/configmaps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,76 @@ import (
corev1 "k8s.io/api/core/v1"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
)

func TestReconcileConfigMaps_CreateIfNotfound(t *testing.T) {
func TestReconcileConfigMaps(t *testing.T) {
env := newTestEnv(t)
defer env.checkMocks()
r := env.Reconciler
mockClient := env.MockClient
r := env.Reconciler
ctx := env.ctx
mc := env.Inst

// mock reconcileOneConfigMap
bak := reconcileOneConfigMap
defer func() {
reconcileOneConfigMap = bak
}()
var reconcileCMCount int
reconcileOneConfigMap = func(r *MilvusReconciler, ctx context.Context, mc v1beta1.Milvus, namespacedName types.NamespacedName) error {
reconcileCMCount++
return nil
}
t.Run("reconcile only active cm when manual mode enabled", func(t *testing.T) {
reconcileCMCount = 0
mc.Spec.Com.EnableManualMode = true
err := r.ReconcileConfigMaps(ctx, mc)
assert.NoError(t, err)
assert.Equal(t, 1, reconcileCMCount)
})

t.Run("create if none", func(t *testing.T) {
reconcileCMCount = 0
mc.Spec.Com.EnableManualMode = false
mockClient.EXPECT().
List(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
err := r.ReconcileConfigMaps(ctx, mc)
assert.NoError(t, err)
assert.Equal(t, 1, reconcileCMCount)
})

t.Run("reconcile all cm when manual mode disabled", func(t *testing.T) {
reconcileCMCount = 0
mc.Spec.Com.EnableManualMode = false
mockClient.EXPECT().
List(gomock.Any(), gomock.Any(), gomock.Any()).
DoAndReturn(func(ctx, obj any, opts ...any) error {
list := obj.(*corev1.ConfigMapList)
list.Items = []corev1.ConfigMap{
{}, {}, {},
}
return nil
})
err := r.ReconcileConfigMaps(ctx, mc)
assert.NoError(t, err)
assert.Equal(t, 3, reconcileCMCount)
})
}

func TestReconcileOneConfigMap_CreateIfNotfound(t *testing.T) {
env := newTestEnv(t)
defer env.checkMocks()
r := env.Reconciler
mockClient := env.MockClient
ctx := env.ctx
mc := env.Inst
nn := types.NamespacedName{
Namespace: mc.Namespace,
Name: mc.GetActiveConfigMap(),
}
// all ok
gomock.InOrder(
mockClient.EXPECT().
Expand All @@ -36,36 +94,64 @@ func TestReconcileConfigMaps_CreateIfNotfound(t *testing.T) {
mockClient.EXPECT().
Create(gomock.Any(), gomock.Any()).Return(nil),
)
err := r.ReconcileConfigMaps(ctx, mc)
err := reconcileOneConfigMap(r, ctx, mc, nn)
assert.NoError(t, err)

// get failed
mockClient.EXPECT().
Get(gomock.Any(), gomock.Any(), gomock.AssignableToTypeOf(&corev1.ConfigMap{})).
Return(errors.New("some network issue"))
err = r.ReconcileConfigMaps(ctx, mc)
err = reconcileOneConfigMap(r, ctx, mc, nn)
assert.Error(t, err)

// get failed
mockClient.EXPECT().
Get(gomock.Any(), gomock.Any(), gomock.AssignableToTypeOf(&corev1.ConfigMap{})).
Return(errors.New("some network issue"))
err = r.ReconcileConfigMaps(ctx, mc)
err = reconcileOneConfigMap(r, ctx, mc, nn)
assert.Error(t, err)
}

func TestReconcileConfigMaps_Existed(t *testing.T) {
func TestReconcileOneConfigMap_Existed(t *testing.T) {
env := newTestEnv(t)
defer env.checkMocks()
r := env.Reconciler
mockClient := env.MockClient
ctx := env.ctx
mc := env.Inst

nn := types.NamespacedName{
Namespace: mc.Namespace,
Name: mc.GetActiveConfigMap(),
}
t.Run("call client.Update if changed configmap", func(t *testing.T) {
mc.Spec.HookConf.Data = map[string]interface{}{
"x": "y",
}
checkHook := func(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...any) error {
cm := obj.(*corev1.ConfigMap)
cm.Name = "cm1"
cm.Namespace = "ns"
err := mockClient.Get(ctx, client.ObjectKeyFromObject(cm), cm)
if err != nil {
return err
}
if len(cm.Data) == 0 {
return errors.New("expect data in configmap")
}
if _, ok := cm.Data["hook.yaml"]; !ok {
return errors.New("expect hook.yaml as key in data")
}
expected, err := yaml.Marshal(map[string]string{
"x": "y",
})
if err != nil {
return err
}
if cm.Data["hook.yaml"] != string(expected) {
return fmt.Errorf("content not match, expected: %s, got: %s", cm.Data["hook.yaml"], string(expected))
}
return nil
}
gomock.InOrder(
mockClient.EXPECT().
Get(gomock.Any(), gomock.Any(), gomock.AssignableToTypeOf(&corev1.ConfigMap{})).
Expand All @@ -82,36 +168,11 @@ func TestReconcileConfigMaps_Existed(t *testing.T) {
mockClient.EXPECT().
Update(gomock.Any(), gomock.Any()).Return(nil),
)

err := r.ReconcileConfigMaps(ctx, mc)
assert.NoError(t, err)
mockClient.EXPECT().
Get(gomock.Any(), gomock.Any(), gomock.AssignableToTypeOf(&corev1.ConfigMap{})).
DoAndReturn(func(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...any) error {
cm := obj.(*corev1.ConfigMap)
cm.Name = "cm1"
cm.Namespace = "ns"
err := mockClient.Get(ctx, client.ObjectKeyFromObject(cm), cm)
if err != nil {
return err
}
if len(cm.Data) == 0 {
return errors.New("expect data in configmap")
}
if _, ok := cm.Data["hook.yaml"]; !ok {
return errors.New("expect hook.yaml as key in data")
}
expected, err := yaml.Marshal(map[string]string{
"x": "y",
})
if err != nil {
return err
}
if cm.Data["hook.yaml"] != string(expected) {
return fmt.Errorf("content not match, expected: %s, got: %s", cm.Data["hook.yaml"], string(expected))
}
return nil
})
DoAndReturn(checkHook)
err := reconcileOneConfigMap(r, ctx, mc, nn)
assert.NoError(t, err)
})

t.Run("not call client.Update if configmap not changed", func(t *testing.T) {
Expand All @@ -130,7 +191,7 @@ func TestReconcileConfigMaps_Existed(t *testing.T) {
Get(gomock.Any(), gomock.Any(), gomock.AssignableToTypeOf(&corev1.Secret{})).
Return(k8sErrors.NewNotFound(schema.GroupResource{}, "mockErr")).Times(2),
)
err := r.ReconcileConfigMaps(ctx, mc)
err := reconcileOneConfigMap(r, ctx, mc, nn)
assert.NoError(t, err)
})

Expand All @@ -150,7 +211,7 @@ func TestReconcileConfigMaps_Existed(t *testing.T) {
Get(gomock.Any(), gomock.Any(), gomock.AssignableToTypeOf(&corev1.Secret{})).
Return(k8sErrors.NewNotFound(schema.GroupResource{}, "mockErr")).Times(2),
)
err := r.ReconcileConfigMaps(ctx, mc)
err := reconcileOneConfigMap(r, ctx, mc, nn)
assert.NoError(t, err)
})

Expand Down
1 change: 0 additions & 1 deletion pkg/controllers/deploy_ctrl_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ var _ DeployControllerBizFactory = &DeployControllerBizFactoryImpl{}
// DeployControllerBizFactoryImpl is the implementation of DeployControllerBizFactory
type DeployControllerBizFactoryImpl struct {
modeChangerFactory DeployModeChangerFactory
statusSyncer MilvusStatusSyncerInterface
utilFactory DeployControllerBizUtilFactory
cli client.Client
}
Expand Down
28 changes: 14 additions & 14 deletions pkg/controllers/deploy_ctrl_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -720,10 +720,10 @@ func TestDeployControllerBizUtilImpl_ScaleDeployements(t *testing.T) {
mc := *milvus.DeepCopy()
currentDeploy := deployTemplate.DeepCopy()
currentDeploy.Spec.Replicas = int32Ptr(0)
currentDeploy.Spec.Template.Spec.Containers = []corev1.Container{corev1.Container{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("3")}}}}
currentDeploy.Spec.Template.Spec.Containers = []corev1.Container{{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("3")}}}}
v1beta1.Labels().SetGroupIDStr(DataNodeName, currentDeploy.Labels, "1")
lastDeploy := deployTemplate.DeepCopy()
lastDeploy.Spec.Template.Spec.Containers = []corev1.Container{corev1.Container{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("3")}}}}
lastDeploy.Spec.Template.Spec.Containers = []corev1.Container{{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("3")}}}}
lastDeploy.Spec.Replicas = int32Ptr(2)
mockutil.EXPECT().MarkMilvusComponentGroupId(ctx, mc, DataNode, 1).Return(nil)
mockutil.EXPECT().ListDeployPods(ctx, lastDeploy, DataNode).Return(pods, nil)
Expand Down Expand Up @@ -891,10 +891,10 @@ func TestDeployControllerBizUtilImpl_ScaleDeployements(t *testing.T) {
mc := *milvus.DeepCopy()
currentDeploy := deployTemplate.DeepCopy()
currentDeploy.Spec.Replicas = int32Ptr(0)
currentDeploy.Spec.Template.Spec.Containers = []corev1.Container{corev1.Container{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("3")}}}}
currentDeploy.Spec.Template.Spec.Containers = []corev1.Container{{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("3")}}}}
v1beta1.Labels().SetGroupIDStr(DataNodeName, currentDeploy.Labels, "1")
lastDeploy := deployTemplate.DeepCopy()
lastDeploy.Spec.Template.Spec.Containers = []corev1.Container{corev1.Container{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("4")}}}}
lastDeploy.Spec.Template.Spec.Containers = []corev1.Container{{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("4")}}}}
lastDeploy.Spec.Replicas = int32Ptr(3)
mockutil.EXPECT().MarkMilvusComponentGroupId(ctx, mc, DataNode, 1).Return(nil)
mockutil.EXPECT().ListDeployPods(ctx, lastDeploy, DataNode).Return(pods, nil)
Expand All @@ -914,10 +914,10 @@ func TestDeployControllerBizUtilImpl_ScaleDeployements(t *testing.T) {
mc.Spec.Com.DataNode.Replicas = int32Ptr(3)
currentDeploy := deployTemplate.DeepCopy()
currentDeploy.Spec.Replicas = int32Ptr(1)
currentDeploy.Spec.Template.Spec.Containers = []corev1.Container{corev1.Container{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("3")}}}}
currentDeploy.Spec.Template.Spec.Containers = []corev1.Container{{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("3")}}}}
v1beta1.Labels().SetGroupIDStr(DataNodeName, currentDeploy.Labels, "1")
lastDeploy := deployTemplate.DeepCopy()
lastDeploy.Spec.Template.Spec.Containers = []corev1.Container{corev1.Container{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("4")}}}}
lastDeploy.Spec.Template.Spec.Containers = []corev1.Container{{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("4")}}}}
lastDeploy.Spec.Replicas = int32Ptr(2)
mockutil.EXPECT().MarkMilvusComponentGroupId(ctx, mc, DataNode, 1).Return(nil)
mockutil.EXPECT().ListDeployPods(ctx, lastDeploy, DataNode).Return(pods, nil)
Expand All @@ -938,10 +938,10 @@ func TestDeployControllerBizUtilImpl_ScaleDeployements(t *testing.T) {
mc.Spec.Com.DataNode.Replicas = int32Ptr(3)
currentDeploy := deployTemplate.DeepCopy()
currentDeploy.Spec.Replicas = int32Ptr(3)
currentDeploy.Spec.Template.Spec.Containers = []corev1.Container{corev1.Container{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("3")}}}}
currentDeploy.Spec.Template.Spec.Containers = []corev1.Container{{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("3")}}}}
v1beta1.Labels().SetGroupIDStr(DataNodeName, currentDeploy.Labels, "1")
lastDeploy := deployTemplate.DeepCopy()
lastDeploy.Spec.Template.Spec.Containers = []corev1.Container{corev1.Container{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("4")}}}}
lastDeploy.Spec.Template.Spec.Containers = []corev1.Container{{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("4")}}}}
lastDeploy.Spec.Replicas = int32Ptr(2)
mockutil.EXPECT().MarkMilvusComponentGroupId(ctx, mc, DataNode, 1).Return(nil)
mockutil.EXPECT().ListDeployPods(ctx, lastDeploy, DataNode).Return(pods, nil)
Expand All @@ -962,10 +962,10 @@ func TestDeployControllerBizUtilImpl_ScaleDeployements(t *testing.T) {
mc.Spec.Com.DataNode.Replicas = int32Ptr(3)
currentDeploy := deployTemplate.DeepCopy()
currentDeploy.Spec.Replicas = int32Ptr(6)
currentDeploy.Spec.Template.Spec.Containers = []corev1.Container{corev1.Container{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("3")}}}}
currentDeploy.Spec.Template.Spec.Containers = []corev1.Container{{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("3")}}}}
v1beta1.Labels().SetGroupIDStr(DataNodeName, currentDeploy.Labels, "1")
lastDeploy := deployTemplate.DeepCopy()
lastDeploy.Spec.Template.Spec.Containers = []corev1.Container{corev1.Container{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("4")}}}}
lastDeploy.Spec.Template.Spec.Containers = []corev1.Container{{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("4")}}}}
lastDeploy.Spec.Replicas = int32Ptr(0)
mockutil.EXPECT().MarkMilvusComponentGroupId(ctx, mc, DataNode, 1).Return(nil)
mockutil.EXPECT().ListDeployPods(ctx, lastDeploy, DataNode).Return(pods, nil)
Expand All @@ -986,10 +986,10 @@ func TestDeployControllerBizUtilImpl_ScaleDeployements(t *testing.T) {
mc.Spec.Com.DataNode.Replicas = int32Ptr(6)
currentDeploy := deployTemplate.DeepCopy()
currentDeploy.Spec.Replicas = int32Ptr(3)
currentDeploy.Spec.Template.Spec.Containers = []corev1.Container{corev1.Container{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("3")}}}}
currentDeploy.Spec.Template.Spec.Containers = []corev1.Container{{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("3")}}}}
v1beta1.Labels().SetGroupIDStr(DataNodeName, currentDeploy.Labels, "1")
lastDeploy := deployTemplate.DeepCopy()
lastDeploy.Spec.Template.Spec.Containers = []corev1.Container{corev1.Container{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("4")}}}}
lastDeploy.Spec.Template.Spec.Containers = []corev1.Container{{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("4")}}}}
lastDeploy.Spec.Replicas = int32Ptr(0)
mockutil.EXPECT().MarkMilvusComponentGroupId(ctx, mc, DataNode, 1).Return(nil)
mockutil.EXPECT().ListDeployPods(ctx, lastDeploy, DataNode).Return(pods, nil)
Expand All @@ -1010,10 +1010,10 @@ func TestDeployControllerBizUtilImpl_ScaleDeployements(t *testing.T) {
mc.Spec.Com.DataNode.Replicas = int32Ptr(3)
currentDeploy := deployTemplate.DeepCopy()
currentDeploy.Spec.Replicas = int32Ptr(3)
currentDeploy.Spec.Template.Spec.Containers = []corev1.Container{corev1.Container{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("3")}}}}
currentDeploy.Spec.Template.Spec.Containers = []corev1.Container{{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("3")}}}}
v1beta1.Labels().SetGroupIDStr(DataNodeName, currentDeploy.Labels, "1")
lastDeploy := deployTemplate.DeepCopy()
lastDeploy.Spec.Template.Spec.Containers = []corev1.Container{corev1.Container{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("4")}}}}
lastDeploy.Spec.Template.Spec.Containers = []corev1.Container{{Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{"cpu": resource.MustParse("4")}}}}
lastDeploy.Spec.Replicas = int32Ptr(0)
mockutil.EXPECT().MarkMilvusComponentGroupId(ctx, mc, DataNode, 1).Return(nil)
mockutil.EXPECT().ListDeployPods(ctx, lastDeploy, DataNode).Return(pods, nil)
Expand Down
Loading

0 comments on commit 24b390a

Please sign in to comment.