github.com/SUSE/skuba@v1.4.17/pkg/skuba/actions/node/remove/remove_test.go (about)

     1  /*
     2   * Copyright (c) 2019 SUSE LLC.
     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  
    18  package remove
    19  
    20  import (
    21  	"crypto/sha1"
    22  	"fmt"
    23  	"testing"
    24  
    25  	batchv1 "k8s.io/api/batch/v1"
    26  	corev1 "k8s.io/api/core/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/client-go/kubernetes/fake"
    29  )
    30  
    31  func Test_RemoveNode(t *testing.T) {
    32  	cm := &corev1.ConfigMap{
    33  		ObjectMeta: metav1.ObjectMeta{
    34  			Name:      "kubeadm-config",
    35  			Namespace: metav1.NamespaceSystem,
    36  		},
    37  		Data: map[string]string{"ClusterConfiguration": `
    38  apiVersion: kubeadm.k8s.io/v1beta1
    39  kind: ClusterConfiguration
    40  kubernetesVersion: "v1.17.13"
    41  apiServer:
    42    extraArgs:
    43      advertiseAddress: 1.2.3.4
    44  `},
    45  	}
    46  
    47  	jobSpec := batchv1.JobSpec{
    48  		Template: corev1.PodTemplateSpec{
    49  			Spec: corev1.PodSpec{
    50  				Containers: []corev1.Container{
    51  					{
    52  						Name:    "fake",
    53  						Image:   "fake",
    54  						Command: []string{"/bin/bash"},
    55  					},
    56  				},
    57  				RestartPolicy: corev1.RestartPolicyNever,
    58  				Volumes:       []corev1.Volume{},
    59  				NodeSelector: map[string]string{
    60  					"kubernetes.io/hostname": "fake",
    61  				},
    62  				Tolerations: []corev1.Toleration{
    63  					{
    64  						Operator: corev1.TolerationOpExists,
    65  					},
    66  				},
    67  			},
    68  		},
    69  	}
    70  
    71  	master1 := corev1.Node{
    72  		ObjectMeta: metav1.ObjectMeta{
    73  			Name:   "master-1",
    74  			Labels: map[string]string{"node-role.kubernetes.io/master": ""},
    75  		},
    76  	}
    77  
    78  	master2 := corev1.Node{
    79  		ObjectMeta: metav1.ObjectMeta{
    80  			Name:   "master-2",
    81  			Labels: map[string]string{"node-role.kubernetes.io/master": ""},
    82  		},
    83  	}
    84  
    85  	worker1 := corev1.Node{
    86  		ObjectMeta: metav1.ObjectMeta{
    87  			Name: "worker-1",
    88  		},
    89  	}
    90  
    91  	worker2 := corev1.Node{
    92  		ObjectMeta: metav1.ObjectMeta{
    93  			Name: "worker-2",
    94  		},
    95  	}
    96  
    97  	test := []struct {
    98  		name          string
    99  		target        string
   100  		executer      []string
   101  		clientset     *fake.Clientset
   102  		errorExpected bool
   103  		errorMessage  string
   104  	}{
   105  		{
   106  			name:          "should remove master from cluster",
   107  			target:        master2.Name,
   108  			executer:      []string{master1.Name, master2.Name},
   109  			clientset:     fake.NewSimpleClientset(&corev1.NodeList{Items: []corev1.Node{master1, master2}}),
   110  			errorExpected: false,
   111  		},
   112  		{
   113  			name:          "should fail when remove last master from cluster",
   114  			target:        master1.Name,
   115  			clientset:     fake.NewSimpleClientset(&corev1.NodeList{Items: []corev1.Node{master1}}),
   116  			errorExpected: true,
   117  			errorMessage:  "could not remove last master of the cluster",
   118  		},
   119  		{
   120  			name:          "should fail when remove node does not exist",
   121  			target:        "not-exist",
   122  			executer:      []string{master1.Name},
   123  			clientset:     fake.NewSimpleClientset(&corev1.NodeList{Items: []corev1.Node{master1}}),
   124  			errorExpected: true,
   125  			errorMessage:  "[remove-node] could not get node not-exist: nodes \"not-exist\" not found",
   126  		},
   127  		{
   128  			name:          "should remove worker from cluster",
   129  			target:        worker2.Name,
   130  			executer:      []string{master1.Name},
   131  			clientset:     fake.NewSimpleClientset(&corev1.NodeList{Items: []corev1.Node{master1, worker1, worker2}}),
   132  			errorExpected: false,
   133  		},
   134  	}
   135  
   136  	for _, tt := range test {
   137  		tt := tt // Parallel testing
   138  		t.Run(tt.name, func(t *testing.T) {
   139  			//nolint:errcheck
   140  			tt.clientset.CoreV1().ConfigMaps(metav1.NamespaceSystem).Create(cm)
   141  
   142  			shaTarget := fmt.Sprintf("%x", sha1.Sum([]byte(tt.target)))
   143  			shaExecuter := ""
   144  			for _, executer := range tt.executer {
   145  				shaExecuter = fmt.Sprintf("%x", sha1.Sum([]byte(executer)))
   146  				//nolint:errcheck
   147  				tt.clientset.BatchV1().Jobs(metav1.NamespaceSystem).Create(&batchv1.Job{
   148  					ObjectMeta: metav1.ObjectMeta{
   149  						Name:      fmt.Sprintf("caasp-remove-etcd-member-%.10s-from-%.10s", shaTarget, shaExecuter),
   150  						Namespace: metav1.NamespaceSystem,
   151  					},
   152  					Spec:   jobSpec,
   153  					Status: batchv1.JobStatus{Active: 1},
   154  				})
   155  			}
   156  			//nolint:errcheck
   157  			tt.clientset.BatchV1().Jobs(metav1.NamespaceSystem).Create(&batchv1.Job{
   158  				ObjectMeta: metav1.ObjectMeta{
   159  					Name:      fmt.Sprintf("caasp-kubelet-disarm-%s", shaTarget),
   160  					Namespace: metav1.NamespaceSystem,
   161  				},
   162  				Spec: jobSpec,
   163  			})
   164  
   165  			err := Remove(tt.clientset, tt.target, 0)
   166  			if tt.errorExpected && err == nil {
   167  				t.Errorf("error expected on %s, but no error reported", tt.name)
   168  				return
   169  			} else if !tt.errorExpected && err != nil {
   170  				t.Errorf("error not expected on %s, but an error was reported (%s)", tt.name, err.Error())
   171  				return
   172  			} else if tt.errorExpected && err.Error() != tt.errorMessage {
   173  				t.Errorf("returned error (%v) does not match the expected one (%v)", err.Error(), tt.errorMessage)
   174  				return
   175  			}
   176  		})
   177  	}
   178  }