mirror of https://github.com/openkruise/kruise.git
458 lines
13 KiB
Go
458 lines
13 KiB
Go
package scale
|
|
|
|
import (
|
|
"context"
|
|
"reflect"
|
|
"sort"
|
|
"testing"
|
|
|
|
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
|
|
clonesettest "github.com/openkruise/kruise/pkg/controller/cloneset/test"
|
|
"github.com/openkruise/kruise/pkg/util"
|
|
"github.com/openkruise/kruise/pkg/util/expectations"
|
|
apps "k8s.io/api/apps/v1"
|
|
v1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/api/resource"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
"k8s.io/client-go/tools/record"
|
|
corev1 "k8s.io/kubernetes/pkg/apis/core/v1"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
|
)
|
|
|
|
func newFakeControl() *realControl {
|
|
return &realControl{
|
|
Client: fake.NewFakeClient(),
|
|
recorder: record.NewFakeRecorder(10),
|
|
exp: expectations.NewScaleExpectations(),
|
|
}
|
|
}
|
|
|
|
func TestCreatePods(t *testing.T) {
|
|
currentCS := clonesettest.NewCloneSet(3)
|
|
updateCS := currentCS.DeepCopy()
|
|
updateCS.Spec.Template.Spec.Containers[0].Env = []v1.EnvVar{{Name: "e-key", Value: "e-value"}}
|
|
currentRevision := "revision-abc"
|
|
updateRevision := "revision-xyz"
|
|
|
|
ctrl := newFakeControl()
|
|
created, err := ctrl.createPods(
|
|
3,
|
|
1,
|
|
currentCS,
|
|
updateCS,
|
|
currentRevision,
|
|
updateRevision,
|
|
[]string{"id1", "id3", "id4"},
|
|
sets.NewString("datadir-foo-id3"),
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("got unexpected error: %v", err)
|
|
} else if !created {
|
|
t.Fatalf("got unexpected created: %v", created)
|
|
}
|
|
|
|
pods := v1.PodList{}
|
|
if err := ctrl.List(context.TODO(), &pods, client.InNamespace("default")); err != nil {
|
|
t.Fatalf("failed to list pods: %v", err)
|
|
}
|
|
expectedPods := []v1.Pod{
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "foo-id1",
|
|
GenerateName: "foo-",
|
|
Labels: map[string]string{
|
|
appsv1alpha1.CloneSetInstanceID: "id1",
|
|
apps.ControllerRevisionHashLabelKey: "revision-abc",
|
|
"foo": "bar",
|
|
},
|
|
OwnerReferences: []metav1.OwnerReference{
|
|
{
|
|
APIVersion: "apps.kruise.io/v1alpha1",
|
|
Kind: "CloneSet",
|
|
Name: "foo",
|
|
UID: "test",
|
|
Controller: func() *bool { a := true; return &a }(),
|
|
BlockOwnerDeletion: func() *bool { a := true; return &a }(),
|
|
},
|
|
},
|
|
ResourceVersion: "1",
|
|
},
|
|
Spec: v1.PodSpec{
|
|
ReadinessGates: []v1.PodReadinessGate{{ConditionType: appsv1alpha1.InPlaceUpdateReady}},
|
|
Containers: []v1.Container{
|
|
{
|
|
Name: "nginx",
|
|
Image: "nginx",
|
|
VolumeMounts: []v1.VolumeMount{
|
|
{Name: "datadir", MountPath: "/tmp/data"},
|
|
{Name: "home", MountPath: "/home"},
|
|
},
|
|
},
|
|
},
|
|
Volumes: []v1.Volume{
|
|
{
|
|
Name: "datadir",
|
|
VolumeSource: v1.VolumeSource{
|
|
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
|
|
ClaimName: "datadir-foo-id1",
|
|
ReadOnly: false,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "home",
|
|
VolumeSource: v1.VolumeSource{
|
|
HostPath: &v1.HostPathVolumeSource{
|
|
Path: "/tmp/home",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "foo-id3",
|
|
GenerateName: "foo-",
|
|
Labels: map[string]string{
|
|
appsv1alpha1.CloneSetInstanceID: "id3",
|
|
apps.ControllerRevisionHashLabelKey: "revision-xyz",
|
|
"foo": "bar",
|
|
},
|
|
OwnerReferences: []metav1.OwnerReference{
|
|
{
|
|
APIVersion: "apps.kruise.io/v1alpha1",
|
|
Kind: "CloneSet",
|
|
Name: "foo",
|
|
UID: "test",
|
|
Controller: func() *bool { a := true; return &a }(),
|
|
BlockOwnerDeletion: func() *bool { a := true; return &a }(),
|
|
},
|
|
},
|
|
ResourceVersion: "1",
|
|
},
|
|
Spec: v1.PodSpec{
|
|
ReadinessGates: []v1.PodReadinessGate{{ConditionType: appsv1alpha1.InPlaceUpdateReady}},
|
|
Containers: []v1.Container{
|
|
{
|
|
Name: "nginx",
|
|
Image: "nginx",
|
|
Env: []v1.EnvVar{{Name: "e-key", Value: "e-value"}},
|
|
VolumeMounts: []v1.VolumeMount{
|
|
{Name: "datadir", MountPath: "/tmp/data"},
|
|
{Name: "home", MountPath: "/home"},
|
|
},
|
|
},
|
|
},
|
|
Volumes: []v1.Volume{
|
|
{
|
|
Name: "datadir",
|
|
VolumeSource: v1.VolumeSource{
|
|
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
|
|
ClaimName: "datadir-foo-id3",
|
|
ReadOnly: false,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "home",
|
|
VolumeSource: v1.VolumeSource{
|
|
HostPath: &v1.HostPathVolumeSource{
|
|
Path: "/tmp/home",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "foo-id4",
|
|
GenerateName: "foo-",
|
|
Labels: map[string]string{
|
|
appsv1alpha1.CloneSetInstanceID: "id4",
|
|
apps.ControllerRevisionHashLabelKey: "revision-xyz",
|
|
"foo": "bar",
|
|
},
|
|
OwnerReferences: []metav1.OwnerReference{
|
|
{
|
|
APIVersion: "apps.kruise.io/v1alpha1",
|
|
Kind: "CloneSet",
|
|
Name: "foo",
|
|
UID: "test",
|
|
Controller: func() *bool { a := true; return &a }(),
|
|
BlockOwnerDeletion: func() *bool { a := true; return &a }(),
|
|
},
|
|
},
|
|
ResourceVersion: "1",
|
|
},
|
|
Spec: v1.PodSpec{
|
|
ReadinessGates: []v1.PodReadinessGate{{ConditionType: appsv1alpha1.InPlaceUpdateReady}},
|
|
Containers: []v1.Container{
|
|
{
|
|
Name: "nginx",
|
|
Image: "nginx",
|
|
Env: []v1.EnvVar{{Name: "e-key", Value: "e-value"}},
|
|
VolumeMounts: []v1.VolumeMount{
|
|
{Name: "datadir", MountPath: "/tmp/data"},
|
|
{Name: "home", MountPath: "/home"},
|
|
},
|
|
},
|
|
},
|
|
Volumes: []v1.Volume{
|
|
{
|
|
Name: "datadir",
|
|
VolumeSource: v1.VolumeSource{
|
|
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
|
|
ClaimName: "datadir-foo-id4",
|
|
ReadOnly: false,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "home",
|
|
VolumeSource: v1.VolumeSource{
|
|
HostPath: &v1.HostPathVolumeSource{
|
|
Path: "/tmp/home",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for i := range expectedPods {
|
|
appsv1alpha1.SetDefaultPod(&expectedPods[i])
|
|
}
|
|
|
|
sort.Slice(pods.Items, func(i, j int) bool { return pods.Items[i].Name < pods.Items[j].Name })
|
|
if !reflect.DeepEqual(expectedPods, pods.Items) {
|
|
t.Fatalf("expected pods \n%s\ngot pods\n%s", util.DumpJSON(expectedPods), util.DumpJSON(pods.Items))
|
|
}
|
|
|
|
pvcs := v1.PersistentVolumeClaimList{}
|
|
if err := ctrl.List(context.TODO(), &pvcs, client.InNamespace("default")); err != nil {
|
|
t.Fatalf("failed to list pvcs: %v", err)
|
|
}
|
|
expectedPVCs := []v1.PersistentVolumeClaim{
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "datadir-foo-id1",
|
|
Labels: map[string]string{
|
|
appsv1alpha1.CloneSetInstanceID: "id1",
|
|
"foo": "bar",
|
|
},
|
|
OwnerReferences: []metav1.OwnerReference{
|
|
{
|
|
APIVersion: "apps.kruise.io/v1alpha1",
|
|
Kind: "CloneSet",
|
|
Name: "foo",
|
|
UID: "test",
|
|
Controller: func() *bool { a := true; return &a }(),
|
|
BlockOwnerDeletion: func() *bool { a := true; return &a }(),
|
|
},
|
|
},
|
|
ResourceVersion: "1",
|
|
},
|
|
Spec: v1.PersistentVolumeClaimSpec{
|
|
Resources: v1.ResourceRequirements{
|
|
Requests: v1.ResourceList{
|
|
v1.ResourceStorage: resource.MustParse("10"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "datadir-foo-id4",
|
|
Labels: map[string]string{
|
|
appsv1alpha1.CloneSetInstanceID: "id4",
|
|
"foo": "bar",
|
|
},
|
|
OwnerReferences: []metav1.OwnerReference{
|
|
{
|
|
APIVersion: "apps.kruise.io/v1alpha1",
|
|
Kind: "CloneSet",
|
|
Name: "foo",
|
|
UID: "test",
|
|
Controller: func() *bool { a := true; return &a }(),
|
|
BlockOwnerDeletion: func() *bool { a := true; return &a }(),
|
|
},
|
|
},
|
|
ResourceVersion: "1",
|
|
},
|
|
Spec: v1.PersistentVolumeClaimSpec{
|
|
Resources: v1.ResourceRequirements{
|
|
Requests: v1.ResourceList{
|
|
v1.ResourceStorage: resource.MustParse("10"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for i := range expectedPVCs {
|
|
corev1.SetDefaults_PersistentVolumeClaim(&expectedPVCs[i])
|
|
}
|
|
sort.Slice(pvcs.Items, func(i, j int) bool { return pvcs.Items[i].Name < pvcs.Items[j].Name })
|
|
if !reflect.DeepEqual(expectedPVCs, pvcs.Items) {
|
|
t.Fatalf("expected pvcs \n%s\ngot pvcs\n%s", util.DumpJSON(expectedPVCs), util.DumpJSON(pvcs.Items))
|
|
}
|
|
|
|
exp := ctrl.exp.GetExpectations("default/foo")
|
|
expectedExp := map[expectations.ScaleAction]sets.String{
|
|
expectations.Create: sets.NewString("foo-id1", "foo-id3", "foo-id4", "datadir-foo-id1", "datadir-foo-id4"),
|
|
}
|
|
if !reflect.DeepEqual(expectedExp, exp) {
|
|
t.Fatalf("expected expectations: %v, got %v", expectedExp, exp)
|
|
}
|
|
}
|
|
|
|
func TestDeletePods(t *testing.T) {
|
|
cs := &appsv1alpha1.CloneSet{ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "foo"}}
|
|
podsToDelete := []*v1.Pod{
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "foo-id1",
|
|
GenerateName: "foo-",
|
|
Labels: map[string]string{
|
|
appsv1alpha1.CloneSetInstanceID: "id1",
|
|
"foo": "bar",
|
|
},
|
|
OwnerReferences: []metav1.OwnerReference{
|
|
{
|
|
APIVersion: "apps.kruise.io/v1alpha1",
|
|
Kind: "CloneSet",
|
|
Name: "foo",
|
|
UID: "test",
|
|
Controller: func() *bool { a := true; return &a }(),
|
|
BlockOwnerDeletion: func() *bool { a := true; return &a }(),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "foo-id3",
|
|
GenerateName: "foo-",
|
|
Labels: map[string]string{
|
|
appsv1alpha1.CloneSetInstanceID: "id3",
|
|
"foo": "bar",
|
|
},
|
|
OwnerReferences: []metav1.OwnerReference{
|
|
{
|
|
APIVersion: "apps.kruise.io/v1alpha1",
|
|
Kind: "CloneSet",
|
|
Name: "foo",
|
|
UID: "test",
|
|
Controller: func() *bool { a := true; return &a }(),
|
|
BlockOwnerDeletion: func() *bool { a := true; return &a }(),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
pvcs := []*v1.PersistentVolumeClaim{
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "datadir-foo-id1",
|
|
Labels: map[string]string{
|
|
appsv1alpha1.CloneSetInstanceID: "id1",
|
|
"foo": "bar",
|
|
},
|
|
OwnerReferences: []metav1.OwnerReference{
|
|
{
|
|
APIVersion: "apps.kruise.io/v1alpha1",
|
|
Kind: "CloneSet",
|
|
Name: "foo",
|
|
UID: "test",
|
|
Controller: func() *bool { a := true; return &a }(),
|
|
BlockOwnerDeletion: func() *bool { a := true; return &a }(),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "datadir-foo-id2",
|
|
Labels: map[string]string{
|
|
appsv1alpha1.CloneSetInstanceID: "id2",
|
|
"foo": "bar",
|
|
},
|
|
OwnerReferences: []metav1.OwnerReference{
|
|
{
|
|
APIVersion: "apps.kruise.io/v1alpha1",
|
|
Kind: "CloneSet",
|
|
Name: "foo",
|
|
UID: "test",
|
|
Controller: func() *bool { a := true; return &a }(),
|
|
BlockOwnerDeletion: func() *bool { a := true; return &a }(),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "datadir-foo-id3",
|
|
Labels: map[string]string{
|
|
appsv1alpha1.CloneSetInstanceID: "id3",
|
|
"foo": "bar",
|
|
},
|
|
OwnerReferences: []metav1.OwnerReference{
|
|
{
|
|
APIVersion: "apps.kruise.io/v1alpha1",
|
|
Kind: "CloneSet",
|
|
Name: "foo",
|
|
UID: "test",
|
|
Controller: func() *bool { a := true; return &a }(),
|
|
BlockOwnerDeletion: func() *bool { a := true; return &a }(),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctrl := newFakeControl()
|
|
for _, p := range podsToDelete {
|
|
_ = ctrl.Create(context.TODO(), p)
|
|
}
|
|
for _, p := range pvcs {
|
|
_ = ctrl.Create(context.TODO(), p)
|
|
}
|
|
|
|
deleted, err := ctrl.deletePods(cs, podsToDelete, pvcs)
|
|
if err != nil {
|
|
t.Fatalf("failed to delete got pods: %v", err)
|
|
} else if !deleted {
|
|
t.Fatalf("failed to delete got pods: not deleted")
|
|
}
|
|
|
|
gotPods := v1.PodList{}
|
|
if err := ctrl.List(context.TODO(), &gotPods, client.InNamespace("default")); err != nil {
|
|
t.Fatalf("failed to list pods: %v", err)
|
|
}
|
|
if len(gotPods.Items) > 0 {
|
|
t.Fatalf("expected no pods left, actually: %v", gotPods.Items)
|
|
}
|
|
|
|
gotPVCs := v1.PersistentVolumeClaimList{}
|
|
if err := ctrl.List(context.TODO(), &gotPVCs, client.InNamespace("default")); err != nil {
|
|
t.Fatalf("failed to list pvcs: %v", err)
|
|
}
|
|
if len(gotPVCs.Items) != 1 || reflect.DeepEqual(gotPVCs.Items[0], pvcs[1]) {
|
|
t.Fatalf("unexpected pvcs: %v", util.DumpJSON(gotPVCs.Items))
|
|
}
|
|
}
|