knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/webhook/helper_test.go (about) 1 /* 2 Copyright 2018 The Knative 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 webhook 18 19 import ( 20 "context" 21 "crypto/tls" 22 "crypto/x509" 23 "net" 24 "net/http" 25 "testing" 26 "time" 27 28 "github.com/google/go-cmp/cmp" 29 corev1 "k8s.io/api/core/v1" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 "k8s.io/apimachinery/pkg/util/wait" 32 "k8s.io/client-go/kubernetes" 33 "knative.dev/pkg/system" 34 certresources "knative.dev/pkg/webhook/certificates/resources" 35 36 . "knative.dev/pkg/logging/testing" 37 ) 38 39 func TestEnsureLabelSelectorExpressions(t *testing.T) { 40 fooExpression := metav1.LabelSelectorRequirement{ 41 Key: "foo.bar/baz", 42 Operator: metav1.LabelSelectorOpDoesNotExist, 43 } 44 knativeExpression := metav1.LabelSelectorRequirement{ 45 Key: "knative.dev/foo", 46 Operator: metav1.LabelSelectorOpDoesNotExist, 47 } 48 49 tests := []struct { 50 name string 51 current *metav1.LabelSelector 52 want *metav1.LabelSelector 53 expect *metav1.LabelSelector 54 }{{ 55 name: "all nil", 56 }, { 57 name: "current nil", 58 current: nil, 59 want: &metav1.LabelSelector{ 60 MatchExpressions: []metav1.LabelSelectorRequirement{fooExpression}, 61 }, 62 expect: &metav1.LabelSelector{ 63 MatchExpressions: []metav1.LabelSelectorRequirement{fooExpression}, 64 }, 65 }, { 66 name: "current empty", 67 current: &metav1.LabelSelector{}, 68 want: &metav1.LabelSelector{ 69 MatchExpressions: []metav1.LabelSelectorRequirement{fooExpression}, 70 }, 71 expect: &metav1.LabelSelector{ 72 MatchExpressions: []metav1.LabelSelectorRequirement{fooExpression}, 73 }, 74 }, { 75 name: "want nil", 76 current: &metav1.LabelSelector{ 77 MatchExpressions: []metav1.LabelSelectorRequirement{fooExpression}, 78 }, 79 want: nil, 80 expect: &metav1.LabelSelector{ 81 MatchExpressions: []metav1.LabelSelectorRequirement{fooExpression}, 82 }, 83 }, { 84 name: "want empty", 85 current: &metav1.LabelSelector{ 86 MatchExpressions: []metav1.LabelSelectorRequirement{fooExpression}, 87 }, 88 want: &metav1.LabelSelector{}, 89 expect: &metav1.LabelSelector{ 90 MatchExpressions: []metav1.LabelSelectorRequirement{fooExpression}, 91 }, 92 }, { 93 name: "add new", 94 current: &metav1.LabelSelector{ 95 MatchExpressions: []metav1.LabelSelectorRequirement{fooExpression}, 96 }, 97 want: &metav1.LabelSelector{ 98 MatchExpressions: []metav1.LabelSelectorRequirement{knativeExpression}, 99 }, 100 expect: &metav1.LabelSelector{ 101 MatchExpressions: []metav1.LabelSelectorRequirement{ 102 knativeExpression, fooExpression, 103 }, 104 }, 105 }, { 106 name: "remove obsolete", 107 current: &metav1.LabelSelector{ 108 MatchExpressions: []metav1.LabelSelectorRequirement{fooExpression, { 109 Key: "knative.dev/bar", 110 Operator: metav1.LabelSelectorOpDoesNotExist, 111 }}, 112 }, 113 want: &metav1.LabelSelector{ 114 MatchExpressions: []metav1.LabelSelectorRequirement{knativeExpression}, 115 }, 116 expect: &metav1.LabelSelector{ 117 MatchExpressions: []metav1.LabelSelectorRequirement{ 118 knativeExpression, fooExpression, 119 }, 120 }, 121 }} 122 123 for _, tc := range tests { 124 t.Run(tc.name, func(t *testing.T) { 125 got := EnsureLabelSelectorExpressions(tc.current, tc.want) 126 if !cmp.Equal(got, tc.expect) { 127 t.Errorf("LabelSelector mismatch: diff(-want,+got):\n%s", cmp.Diff(tc.expect, got)) 128 } 129 }) 130 } 131 } 132 133 func waitForNonTLSServerAvailable(t *testing.T, serverURL string, timeout time.Duration) error { 134 t.Helper() 135 interval := 100 * time.Millisecond 136 137 conditionFunc := func(ctx context.Context) (done bool, err error) { 138 var conn net.Conn 139 conn, _ = net.DialTimeout("tcp", serverURL, timeout) 140 if conn != nil { 141 conn.Close() 142 return true, nil 143 } 144 return false, nil 145 } 146 147 return wait.PollUntilContextTimeout(context.Background(), interval, timeout, true, conditionFunc) 148 } 149 150 func waitForServerAvailable(t *testing.T, serverURL string, timeout time.Duration) error { 151 t.Helper() 152 153 var ( 154 // if this is too low you'll see TLS handshake EOF warnings 155 interval = 500 * time.Millisecond 156 tlsConf = &tls.Config{InsecureSkipVerify: true} 157 dialer = &net.Dialer{ 158 Timeout: interval, // Initial duration. 159 KeepAlive: 5 * time.Second, 160 DualStack: true, 161 } 162 ) 163 164 conditionFunc := func(ctx context.Context) (done bool, err error) { 165 conn, _ := tls.DialWithDialer(dialer, "tcp", serverURL, tlsConf) 166 if conn != nil { 167 conn.Close() 168 return true, nil 169 } 170 return false, nil 171 } 172 173 return wait.PollUntilContextTimeout(context.Background(), interval, timeout, true, conditionFunc) 174 } 175 176 func createNamespace(t *testing.T, kubeClient kubernetes.Interface, name string) error { 177 t.Helper() 178 testns := &corev1.Namespace{ 179 ObjectMeta: metav1.ObjectMeta{ 180 Name: name, 181 }, 182 } 183 _, err := kubeClient.CoreV1().Namespaces().Create(context.Background(), testns, metav1.CreateOptions{}) 184 if err != nil { 185 return err 186 } 187 return nil 188 } 189 190 func createTestConfigMap(t *testing.T, kubeClient kubernetes.Interface) error { 191 t.Helper() 192 configMaps := &corev1.ConfigMap{ 193 ObjectMeta: metav1.ObjectMeta{ 194 Name: "extension-apiserver-authentication", 195 }, 196 Data: map[string]string{"requestheader-client-ca-file": "test-client-file"}, 197 } 198 _, err := kubeClient.CoreV1().ConfigMaps(metav1.NamespaceSystem).Create(context.Background(), configMaps, metav1.CreateOptions{}) 199 if err != nil { 200 return err 201 } 202 return nil 203 } 204 205 func createSecureTLSClient(t *testing.T, kubeClient kubernetes.Interface, acOpts *Options) (*http.Client, error) { 206 t.Helper() 207 ctx := TestContextWithLogger(t) 208 209 secret, err := kubeClient.CoreV1().Secrets(system.Namespace()).Get(ctx, acOpts.SecretName, metav1.GetOptions{}) 210 if err != nil { 211 return nil, err 212 } 213 214 sKey, sCert := getSecretDataKeyNamesOrDefault(acOpts.ServerPrivateKeyName, acOpts.ServerCertificateName) 215 216 serverKey := secret.Data[sKey] 217 serverCert := secret.Data[sCert] 218 caCert := secret.Data[certresources.CACert] 219 220 // Build cert pool with CA Cert 221 pool := x509.NewCertPool() 222 pool.AppendCertsFromPEM(caCert) 223 224 // Build key pair 225 cert, err := tls.X509KeyPair(serverCert, serverKey) 226 if err != nil { 227 return nil, err 228 } 229 230 tlsClientConfig := &tls.Config{ 231 // Add knative namespace as CN 232 ServerName: "webhook." + system.Namespace(), 233 RootCAs: pool, 234 Certificates: []tls.Certificate{cert}, 235 } 236 return &http.Client{ 237 Transport: &http.Transport{ 238 TLSClientConfig: tlsClientConfig, 239 }, 240 }, nil 241 } 242 243 func createNonTLSClient() *http.Client { 244 return &http.Client{ 245 Transport: &http.Transport{}, 246 } 247 }