...

Source file src/k8s.io/kubernetes/pkg/controller/controller_ref_manager_test.go

Documentation: k8s.io/kubernetes/pkg/controller

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package controller
    18  
    19  import (
    20  	"context"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/google/go-cmp/cmp"
    26  	apps "k8s.io/api/apps/v1"
    27  	"k8s.io/api/core/v1"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/labels"
    30  	"k8s.io/apimachinery/pkg/runtime/schema"
    31  	"k8s.io/apimachinery/pkg/types"
    32  )
    33  
    34  var (
    35  	productionLabel         = map[string]string{"type": "production"}
    36  	testLabel               = map[string]string{"type": "testing"}
    37  	productionLabelSelector = labels.Set{"type": "production"}.AsSelector()
    38  	controllerUID           = "123"
    39  )
    40  
    41  func newPod(podName string, label map[string]string, owner metav1.Object) *v1.Pod {
    42  	pod := &v1.Pod{
    43  		ObjectMeta: metav1.ObjectMeta{
    44  			Name:      podName,
    45  			Labels:    label,
    46  			Namespace: metav1.NamespaceDefault,
    47  		},
    48  		Spec: v1.PodSpec{
    49  			Containers: []v1.Container{
    50  				{
    51  					Image: "foo/bar",
    52  				},
    53  			},
    54  		},
    55  	}
    56  	if owner != nil {
    57  		pod.OwnerReferences = []metav1.OwnerReference{*metav1.NewControllerRef(owner, apps.SchemeGroupVersion.WithKind("Fake"))}
    58  	}
    59  	return pod
    60  }
    61  
    62  func TestClaimPods(t *testing.T) {
    63  	controllerKind := schema.GroupVersionKind{}
    64  	type test struct {
    65  		name    string
    66  		manager *PodControllerRefManager
    67  		pods    []*v1.Pod
    68  		claimed []*v1.Pod
    69  		patches int
    70  	}
    71  	var tests = []test{
    72  		func() test {
    73  			controller := v1.ReplicationController{}
    74  			controller.Namespace = metav1.NamespaceDefault
    75  			return test{
    76  				name: "Claim pods with correct label",
    77  				manager: NewPodControllerRefManager(&FakePodControl{},
    78  					&controller,
    79  					productionLabelSelector,
    80  					controllerKind,
    81  					func(ctx context.Context) error { return nil }),
    82  				pods:    []*v1.Pod{newPod("pod1", productionLabel, nil), newPod("pod2", testLabel, nil)},
    83  				claimed: []*v1.Pod{newPod("pod1", productionLabel, nil)},
    84  				patches: 1,
    85  			}
    86  		}(),
    87  		func() test {
    88  			controller := v1.ReplicationController{}
    89  			controller.Namespace = metav1.NamespaceDefault
    90  			controller.UID = types.UID(controllerUID)
    91  			now := metav1.Now()
    92  			controller.DeletionTimestamp = &now
    93  			return test{
    94  				name: "Controller marked for deletion can not claim pods",
    95  				manager: NewPodControllerRefManager(&FakePodControl{},
    96  					&controller,
    97  					productionLabelSelector,
    98  					controllerKind,
    99  					func(ctx context.Context) error { return nil }),
   100  				pods:    []*v1.Pod{newPod("pod1", productionLabel, nil), newPod("pod2", productionLabel, nil)},
   101  				claimed: nil,
   102  			}
   103  		}(),
   104  		func() test {
   105  			controller := v1.ReplicationController{}
   106  			controller.Namespace = metav1.NamespaceDefault
   107  			controller.UID = types.UID(controllerUID)
   108  			now := metav1.Now()
   109  			controller.DeletionTimestamp = &now
   110  			return test{
   111  				name: "Controller marked for deletion can not claim new pods",
   112  				manager: NewPodControllerRefManager(&FakePodControl{},
   113  					&controller,
   114  					productionLabelSelector,
   115  					controllerKind,
   116  					func(ctx context.Context) error { return nil }),
   117  				pods:    []*v1.Pod{newPod("pod1", productionLabel, &controller), newPod("pod2", productionLabel, nil)},
   118  				claimed: []*v1.Pod{newPod("pod1", productionLabel, &controller)},
   119  			}
   120  		}(),
   121  		func() test {
   122  			controller := v1.ReplicationController{}
   123  			controller2 := v1.ReplicationController{}
   124  			controller.UID = types.UID(controllerUID)
   125  			controller.Namespace = metav1.NamespaceDefault
   126  			controller2.UID = types.UID("AAAAA")
   127  			controller2.Namespace = metav1.NamespaceDefault
   128  			return test{
   129  				name: "Controller can not claim pods owned by another controller",
   130  				manager: NewPodControllerRefManager(&FakePodControl{},
   131  					&controller,
   132  					productionLabelSelector,
   133  					controllerKind,
   134  					func(ctx context.Context) error { return nil }),
   135  				pods:    []*v1.Pod{newPod("pod1", productionLabel, &controller), newPod("pod2", productionLabel, &controller2)},
   136  				claimed: []*v1.Pod{newPod("pod1", productionLabel, &controller)},
   137  			}
   138  		}(),
   139  		func() test {
   140  			controller := v1.ReplicationController{}
   141  			controller.Namespace = metav1.NamespaceDefault
   142  			controller.UID = types.UID(controllerUID)
   143  			return test{
   144  				name: "Controller releases claimed pods when selector doesn't match",
   145  				manager: NewPodControllerRefManager(&FakePodControl{},
   146  					&controller,
   147  					productionLabelSelector,
   148  					controllerKind,
   149  					func(ctx context.Context) error { return nil }),
   150  				pods:    []*v1.Pod{newPod("pod1", productionLabel, &controller), newPod("pod2", testLabel, &controller)},
   151  				claimed: []*v1.Pod{newPod("pod1", productionLabel, &controller)},
   152  				patches: 1,
   153  			}
   154  		}(),
   155  		func() test {
   156  			controller := v1.ReplicationController{}
   157  			controller.Namespace = metav1.NamespaceDefault
   158  			controller.UID = types.UID(controllerUID)
   159  			podToDelete1 := newPod("pod1", productionLabel, &controller)
   160  			podToDelete2 := newPod("pod2", productionLabel, nil)
   161  			now := metav1.Now()
   162  			podToDelete1.DeletionTimestamp = &now
   163  			podToDelete2.DeletionTimestamp = &now
   164  
   165  			return test{
   166  				name: "Controller does not claim orphaned pods marked for deletion",
   167  				manager: NewPodControllerRefManager(&FakePodControl{},
   168  					&controller,
   169  					productionLabelSelector,
   170  					controllerKind,
   171  					func(ctx context.Context) error { return nil }),
   172  				pods:    []*v1.Pod{podToDelete1, podToDelete2},
   173  				claimed: []*v1.Pod{podToDelete1},
   174  			}
   175  		}(),
   176  		func() test {
   177  			controller := v1.ReplicationController{}
   178  			controller.Namespace = metav1.NamespaceDefault
   179  			controller.UID = types.UID(controllerUID)
   180  			return test{
   181  				name: "Controller claims or release pods according to selector with finalizers",
   182  				manager: NewPodControllerRefManager(&FakePodControl{},
   183  					&controller,
   184  					productionLabelSelector,
   185  					controllerKind,
   186  					func(ctx context.Context) error { return nil },
   187  					"foo-finalizer", "bar-finalizer"),
   188  				pods:    []*v1.Pod{newPod("pod1", productionLabel, &controller), newPod("pod2", testLabel, &controller), newPod("pod3", productionLabel, nil)},
   189  				claimed: []*v1.Pod{newPod("pod1", productionLabel, &controller), newPod("pod3", productionLabel, nil)},
   190  				patches: 2,
   191  			}
   192  		}(),
   193  		func() test {
   194  			controller := v1.ReplicationController{}
   195  			controller.Namespace = metav1.NamespaceDefault
   196  			controller.UID = types.UID(controllerUID)
   197  			pod1 := newPod("pod1", productionLabel, nil)
   198  			pod2 := newPod("pod2", productionLabel, nil)
   199  			pod2.Namespace = "fakens"
   200  			return test{
   201  				name: "Controller does not claim pods of different namespace",
   202  				manager: NewPodControllerRefManager(&FakePodControl{},
   203  					&controller,
   204  					productionLabelSelector,
   205  					controllerKind,
   206  					func(ctx context.Context) error { return nil }),
   207  				pods:    []*v1.Pod{pod1, pod2},
   208  				claimed: []*v1.Pod{pod1},
   209  				patches: 1,
   210  			}
   211  		}(),
   212  		func() test {
   213  			// act as a cluster-scoped controller
   214  			controller := v1.ReplicationController{}
   215  			controller.Namespace = ""
   216  			controller.UID = types.UID(controllerUID)
   217  			pod1 := newPod("pod1", productionLabel, nil)
   218  			pod2 := newPod("pod2", productionLabel, nil)
   219  			pod2.Namespace = "fakens"
   220  			return test{
   221  				name: "Cluster scoped controller claims pods of specified namespace",
   222  				manager: NewPodControllerRefManager(&FakePodControl{},
   223  					&controller,
   224  					productionLabelSelector,
   225  					controllerKind,
   226  					func(ctx context.Context) error { return nil }),
   227  				pods:    []*v1.Pod{pod1, pod2},
   228  				claimed: []*v1.Pod{pod1, pod2},
   229  				patches: 2,
   230  			}
   231  		}(),
   232  	}
   233  	for _, test := range tests {
   234  		t.Run(test.name, func(t *testing.T) {
   235  			claimed, err := test.manager.ClaimPods(context.TODO(), test.pods)
   236  			if err != nil {
   237  				t.Fatalf("Unexpected error: %v", err)
   238  			}
   239  			if diff := cmp.Diff(test.claimed, claimed); diff != "" {
   240  				t.Errorf("Claimed wrong pods (-want,+got):\n%s", diff)
   241  			}
   242  			fakePodControl, ok := test.manager.podControl.(*FakePodControl)
   243  			if !ok {
   244  				return
   245  			}
   246  			if p := len(fakePodControl.Patches); p != test.patches {
   247  				t.Errorf("ClaimPods issues %d patches, want %d", p, test.patches)
   248  			}
   249  			for _, p := range fakePodControl.Patches {
   250  				patch := string(p)
   251  				if uid := string(test.manager.Controller.GetUID()); !strings.Contains(patch, uid) {
   252  					t.Errorf("Patch doesn't contain controller UID %s", uid)
   253  				}
   254  				for _, f := range test.manager.finalizers {
   255  					if !strings.Contains(patch, f) {
   256  						t.Errorf("Patch doesn't contain finalizer %s, %q", patch, f)
   257  					}
   258  				}
   259  			}
   260  		})
   261  	}
   262  }
   263  
   264  func TestGeneratePatchBytesForDelete(t *testing.T) {
   265  	tests := []struct {
   266  		name         string
   267  		ownerUID     []types.UID
   268  		dependentUID types.UID
   269  		finalizers   []string
   270  		want         []byte
   271  	}{
   272  		{
   273  			name:         "check the structure of patch bytes",
   274  			ownerUID:     []types.UID{"ss1"},
   275  			dependentUID: "ss2",
   276  			finalizers:   []string{},
   277  			want:         []byte(`{"metadata":{"uid":"ss2","ownerReferences":[{"$patch":"delete","uid":"ss1"}]}}`),
   278  		},
   279  		{
   280  			name:         "check if parent uid is escaped",
   281  			ownerUID:     []types.UID{`ss1"hello`},
   282  			dependentUID: "ss2",
   283  			finalizers:   []string{},
   284  			want:         []byte(`{"metadata":{"uid":"ss2","ownerReferences":[{"$patch":"delete","uid":"ss1\"hello"}]}}`),
   285  		},
   286  		{
   287  			name:         "check if revision uid uid is escaped",
   288  			ownerUID:     []types.UID{`ss1`},
   289  			dependentUID: `ss2"hello`,
   290  			finalizers:   []string{},
   291  			want:         []byte(`{"metadata":{"uid":"ss2\"hello","ownerReferences":[{"$patch":"delete","uid":"ss1"}]}}`),
   292  		},
   293  		{
   294  			name:         "check the structure of patch bytes with multiple owners",
   295  			ownerUID:     []types.UID{"ss1", "ss2"},
   296  			dependentUID: "ss2",
   297  			finalizers:   []string{},
   298  			want:         []byte(`{"metadata":{"uid":"ss2","ownerReferences":[{"$patch":"delete","uid":"ss1"},{"$patch":"delete","uid":"ss2"}]}}`),
   299  		},
   300  		{
   301  			name:         "check the structure of patch bytes with a finalizer and multiple owners",
   302  			ownerUID:     []types.UID{"ss1", "ss2"},
   303  			dependentUID: "ss2",
   304  			finalizers:   []string{"f1"},
   305  			want:         []byte(`{"metadata":{"uid":"ss2","ownerReferences":[{"$patch":"delete","uid":"ss1"},{"$patch":"delete","uid":"ss2"}],"$deleteFromPrimitiveList/finalizers":["f1"]}}`),
   306  		},
   307  	}
   308  	for _, tt := range tests {
   309  		t.Run(tt.name, func(t *testing.T) {
   310  			got, _ := GenerateDeleteOwnerRefStrategicMergeBytes(tt.dependentUID, tt.ownerUID, tt.finalizers...)
   311  			if !reflect.DeepEqual(got, tt.want) {
   312  				t.Errorf("generatePatchBytesForDelete() got = %s, want %s", got, tt.want)
   313  			}
   314  		})
   315  	}
   316  }
   317  

View as plain text