istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/pkg/tag/tag_test.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package tag
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"strings"
    21  	"testing"
    22  
    23  	admitv1 "k8s.io/api/admissionregistration/v1"
    24  	corev1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/client-go/kubernetes/fake"
    27  
    28  	"istio.io/api/label"
    29  	"istio.io/istio/istioctl/pkg/util"
    30  	"istio.io/istio/pkg/kube"
    31  )
    32  
    33  func TestTagList(t *testing.T) {
    34  	tcs := []struct {
    35  		name          string
    36  		webhooks      admitv1.MutatingWebhookConfigurationList
    37  		namespaces    corev1.NamespaceList
    38  		outputMatches []string
    39  		error         string
    40  	}{
    41  		{
    42  			name: "TestBasicTag",
    43  			webhooks: admitv1.MutatingWebhookConfigurationList{
    44  				Items: []admitv1.MutatingWebhookConfiguration{
    45  					{
    46  						ObjectMeta: metav1.ObjectMeta{
    47  							Name: "istio-revision-tag-sample",
    48  							Labels: map[string]string{
    49  								IstioTagLabel:                 "sample",
    50  								label.IoIstioRev.Name:         "sample-revision",
    51  								"operator.istio.io/component": "Pilot",
    52  							},
    53  						},
    54  					},
    55  				},
    56  			},
    57  			namespaces:    corev1.NamespaceList{},
    58  			outputMatches: []string{"sample", "sample-revision"},
    59  			error:         "",
    60  		},
    61  		{
    62  			name: "TestNonTagWebhooksIncluded",
    63  			webhooks: admitv1.MutatingWebhookConfigurationList{
    64  				Items: []admitv1.MutatingWebhookConfiguration{
    65  					{
    66  						ObjectMeta: metav1.ObjectMeta{
    67  							Name:   "istio-revision-test",
    68  							Labels: map[string]string{label.IoIstioRev.Name: "test"},
    69  						},
    70  					},
    71  				},
    72  			},
    73  			namespaces:    corev1.NamespaceList{},
    74  			outputMatches: []string{"test"},
    75  			error:         "",
    76  		},
    77  		{
    78  			name: "TestNamespacesIncluded",
    79  			webhooks: admitv1.MutatingWebhookConfigurationList{
    80  				Items: []admitv1.MutatingWebhookConfiguration{
    81  					{
    82  						ObjectMeta: metav1.ObjectMeta{
    83  							Name: "istio-revision-test",
    84  							Labels: map[string]string{
    85  								label.IoIstioRev.Name: "revision",
    86  								IstioTagLabel:         "test",
    87  							},
    88  						},
    89  					},
    90  				},
    91  			},
    92  			namespaces: corev1.NamespaceList{
    93  				Items: []corev1.Namespace{
    94  					{
    95  						ObjectMeta: metav1.ObjectMeta{
    96  							Name:   "dependent",
    97  							Labels: map[string]string{label.IoIstioRev.Name: "test"},
    98  						},
    99  					},
   100  				},
   101  			},
   102  			outputMatches: []string{"test", "revision", "dependent"},
   103  			error:         "",
   104  		},
   105  	}
   106  
   107  	for _, tc := range tcs {
   108  		t.Run(tc.name, func(t *testing.T) {
   109  			var out bytes.Buffer
   110  			client := fake.NewSimpleClientset(tc.webhooks.DeepCopyObject(), tc.namespaces.DeepCopyObject())
   111  			outputFormat = util.JSONFormat
   112  			err := listTags(context.Background(), client, &out)
   113  			if tc.error == "" && err != nil {
   114  				t.Fatalf("expected no error, got %v", err)
   115  			}
   116  			if tc.error != "" {
   117  				if err == nil {
   118  					t.Fatalf("expected error to include \"%s\" but got none", tc.error)
   119  				}
   120  				if !strings.Contains(err.Error(), tc.error) {
   121  					t.Fatalf("expected \"%s\" in error, got %v", tc.error, err)
   122  				}
   123  			}
   124  
   125  			commandOutput := out.String()
   126  			for _, s := range tc.outputMatches {
   127  				if !strings.Contains(commandOutput, s) {
   128  					t.Fatalf("expected \"%s\" in command output, got %s", s, commandOutput)
   129  				}
   130  			}
   131  		})
   132  	}
   133  }
   134  
   135  func TestRemoveTag(t *testing.T) {
   136  	tcs := []struct {
   137  		name             string
   138  		tag              string
   139  		webhooksBefore   admitv1.MutatingWebhookConfigurationList
   140  		webhooksAfter    admitv1.MutatingWebhookConfigurationList
   141  		namespaces       corev1.NamespaceList
   142  		outputMatches    []string
   143  		skipConfirmation bool
   144  		error            string
   145  	}{
   146  		{
   147  			name: "TestSimpleRemove",
   148  			tag:  "sample",
   149  			webhooksBefore: admitv1.MutatingWebhookConfigurationList{
   150  				Items: []admitv1.MutatingWebhookConfiguration{
   151  					{
   152  						ObjectMeta: metav1.ObjectMeta{
   153  							Name:   "istio-revision-tag-sample",
   154  							Labels: map[string]string{IstioTagLabel: "sample"},
   155  						},
   156  					},
   157  				},
   158  			},
   159  			webhooksAfter:    admitv1.MutatingWebhookConfigurationList{},
   160  			namespaces:       corev1.NamespaceList{},
   161  			outputMatches:    []string{},
   162  			skipConfirmation: true,
   163  			error:            "",
   164  		},
   165  		{
   166  			name: "TestWrongTagLabelNotRemoved",
   167  			tag:  "sample",
   168  			webhooksBefore: admitv1.MutatingWebhookConfigurationList{
   169  				Items: []admitv1.MutatingWebhookConfiguration{
   170  					{
   171  						ObjectMeta: metav1.ObjectMeta{
   172  							Name:   "istio-revision-tag-wrong",
   173  							Labels: map[string]string{IstioTagLabel: "wrong"},
   174  						},
   175  					},
   176  				},
   177  			},
   178  			webhooksAfter: admitv1.MutatingWebhookConfigurationList{
   179  				Items: []admitv1.MutatingWebhookConfiguration{
   180  					{
   181  						ObjectMeta: metav1.ObjectMeta{
   182  							Name:   "istio-revision-tag-wrong",
   183  							Labels: map[string]string{IstioTagLabel: "wrong"},
   184  						},
   185  					},
   186  				},
   187  			},
   188  			namespaces:       corev1.NamespaceList{},
   189  			outputMatches:    []string{},
   190  			skipConfirmation: true,
   191  			error:            "cannot remove tag \"sample\"",
   192  		},
   193  		{
   194  			name: "TestDeleteTagWithDependentNamespace",
   195  			tag:  "match",
   196  			webhooksBefore: admitv1.MutatingWebhookConfigurationList{
   197  				Items: []admitv1.MutatingWebhookConfiguration{
   198  					{
   199  						ObjectMeta: metav1.ObjectMeta{
   200  							Name:   "istio-revision-tag-match",
   201  							Labels: map[string]string{IstioTagLabel: "match"},
   202  						},
   203  					},
   204  				},
   205  			},
   206  			webhooksAfter: admitv1.MutatingWebhookConfigurationList{
   207  				Items: []admitv1.MutatingWebhookConfiguration{
   208  					{
   209  						ObjectMeta: metav1.ObjectMeta{
   210  							Name:   "istio-revision-tag-match",
   211  							Labels: map[string]string{IstioTagLabel: "match"},
   212  						},
   213  					},
   214  				},
   215  			},
   216  			namespaces: corev1.NamespaceList{
   217  				Items: []corev1.Namespace{
   218  					{
   219  						ObjectMeta: metav1.ObjectMeta{
   220  							Name:   "dependent",
   221  							Labels: map[string]string{label.IoIstioRev.Name: "match"},
   222  						},
   223  					},
   224  				},
   225  			},
   226  			outputMatches:    []string{"Caution, found 1 namespace(s) still injected by tag \"match\": dependent"},
   227  			skipConfirmation: false,
   228  			error:            "",
   229  		},
   230  	}
   231  
   232  	for _, tc := range tcs {
   233  		t.Run(tc.name, func(t *testing.T) {
   234  			var out bytes.Buffer
   235  			client := fake.NewSimpleClientset(tc.webhooksBefore.DeepCopyObject(), tc.namespaces.DeepCopyObject())
   236  			err := removeTag(context.Background(), client, tc.tag, tc.skipConfirmation, &out)
   237  			if tc.error == "" && err != nil {
   238  				t.Fatalf("expected no error, got %v", err)
   239  			}
   240  			if tc.error != "" {
   241  				if err == nil {
   242  					t.Fatalf("expected error to include \"%s\" but got none", tc.error)
   243  				}
   244  				if !strings.Contains(err.Error(), tc.error) {
   245  					t.Fatalf("expected \"%s\" in error, got %v", tc.error, err)
   246  				}
   247  			}
   248  
   249  			commandOutput := out.String()
   250  			for _, s := range tc.outputMatches {
   251  				if !strings.Contains(commandOutput, s) {
   252  					t.Fatalf("expected \"%s\" in command output, got %s", s, commandOutput)
   253  				}
   254  			}
   255  
   256  			// check mutating webhooks after run
   257  			webhooksAfter, _ := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(context.Background(), metav1.ListOptions{})
   258  			if len(webhooksAfter.Items) != len(tc.webhooksAfter.Items) {
   259  				t.Fatalf("expected %d after running, got %d", len(tc.webhooksAfter.Items), len(webhooksAfter.Items))
   260  			}
   261  		})
   262  	}
   263  }
   264  
   265  func TestSetTagErrors(t *testing.T) {
   266  	tcs := []struct {
   267  		name           string
   268  		tag            string
   269  		revision       string
   270  		webhooksBefore admitv1.MutatingWebhookConfigurationList
   271  		namespaces     corev1.NamespaceList
   272  		outputMatches  []string
   273  		error          string
   274  	}{
   275  		{
   276  			name:     "TestErrorWhenRevisionWithNameCollision",
   277  			tag:      "revision",
   278  			revision: "revision",
   279  			webhooksBefore: admitv1.MutatingWebhookConfigurationList{
   280  				Items: []admitv1.MutatingWebhookConfiguration{revisionCanonicalWebhook},
   281  			},
   282  			namespaces:    corev1.NamespaceList{},
   283  			outputMatches: []string{},
   284  			error:         "cannot create revision tag \"revision\"",
   285  		},
   286  	}
   287  
   288  	for _, tc := range tcs {
   289  		t.Run(tc.name, func(t *testing.T) {
   290  			var out bytes.Buffer
   291  
   292  			client := kube.NewFakeClient(tc.webhooksBefore.DeepCopyObject(), tc.namespaces.DeepCopyObject())
   293  			skipConfirmation = true
   294  			err := setTag(context.Background(), client, tc.tag, tc.revision, "istio-system", false, &out, nil)
   295  			if tc.error == "" && err != nil {
   296  				t.Fatalf("expected no error, got %v", err)
   297  			}
   298  			if tc.error != "" {
   299  				if err == nil {
   300  					t.Fatalf("expected error to include \"%s\" but got none", tc.error)
   301  				}
   302  				if !strings.Contains(err.Error(), tc.error) {
   303  					t.Fatalf("expected \"%s\" in error, got %v", tc.error, err)
   304  				}
   305  			}
   306  		})
   307  	}
   308  }