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 }