apiserver: add pkg/util/webhook tests
This commit adds tests for pkg/util/webhooks. The purpose of this was not only for better code coverage but also to alleviate the need for consumers to write their own tests for core functionality. Kubernetes-commit: d15dba7e8bff943d91ba6f58fcb0dfefa357a7f1
This commit is contained in:
parent
486f24b81b
commit
02115850aa
|
@ -5,6 +5,7 @@ licenses(["notice"])
|
|||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
|
@ -22,3 +23,20 @@ go_library(
|
|||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"certs_test.go",
|
||||
"webhook_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/client-go/pkg/api:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd/api/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// This file was generated using openssl by the gencerts.sh script
|
||||
// and holds raw certificates for the webhook tests.
|
||||
|
||||
package webhook
|
||||
|
||||
var caKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAwmdGuDsPPyirvNqlqoDmwf/bmF3zTJBFYQRsJK0vKBAdrfMY
|
||||
MVwoPEpM5ZZ+VCHEB6vSuGYbC0PyO97H/kIV22FsMwN7zifhJP2T2Hb+B7Nc6s8W
|
||||
tAQn1J4xUY31xOXOEcXe2nuGezrlKfX3DpA1FVXp6/Cf8vhyUQ0fJXwuZE/Pbhvp
|
||||
bBUciQLqfPSH6EnShZvzjJBP5Bs13UqzaRobhUf9A3pyk2Mb3PXkTetET7hLc4J2
|
||||
uIp5BxOoNZSvgQCvCjRyV5s1his7BNRALKG3qz/48i6JRyO7FvNoxDkWC09zIqD8
|
||||
1bw9I1d/+EwWdqAPZLa8iLpSsd0gD03gDSFbOwIDAQABAoIBAQC0JPO5oLDePBf4
|
||||
pzxBJbWwLCIXrWfZmQ9RecGksv8xxs1Z9hyDEP0P8WIUlkJ2P9vhp+1ahvOkmtAL
|
||||
fsQg7qhGZJ7ZHu9I+Fd/6aNpQcrg4+rEhCZrpjYqpnTZOA146eLtQUjjePgDlW3q
|
||||
Vk0cJ7GpFbXwt0fg5S05wkkMeWib9mKvme3vooNbog52164U1wo/p4WBTpbAMoYA
|
||||
XlJSqXeoxBpsLWBZHlRG+AYfYpk7BXk8MkIslcKh97RmLsZt52Fh3SbsFJeIEmD5
|
||||
2hQDvn/PJojAnM6SMkUqfvv87SdkryvqQYJ80b2D6qd+y8o7gUFr8WkEqVRCqVLh
|
||||
GaD2C06hAoGBAO9JOe+typoQUPi24aj5BoqWcpyrHQkCdjLlxS805oLsPfmb+EqF
|
||||
1HwnA8UHNMlrdiczJ8f2M7Y4cSUIEXv6LSE5r4teSiYWASidDLREi0q8scw21CGH
|
||||
BnCc7PUhUnBngXJ3B1MtCj+r3TFfpOEEi1J1HtMK1AxAaq7zEFzdOrtjAoGBAM/7
|
||||
fC89Awvd7yJsgTVumKVx/bA+Q54YJOFMkdba3JbcLsQyn4TBaFv0pwqqXqmkTLZz
|
||||
WHjkNscomRf9VY34D4q07nO4YGXCBNqm3MaV3mE0xhIyBsATRZnf03O2a/pnRPu/
|
||||
yTE1EyuIqK/l4+5iv2O5mWzxorC4qdV34Wf5WCRJAoGBANfmfjvf1zoDFswSVrGb
|
||||
X2eUL31kdyI18mgiITRiysm+VnztWa4D6qDKowAXbG2AZG8iHPazEh2L96quCPiP
|
||||
1kBwSA+717Ndj1YRvfC5F+UrNFFJ90T5C7p4HOVgV33MJmQdOaK2tNSWQVHXNnFB
|
||||
JGQWAOXykzkqthd8gHsJsYB5AoGAd7BfKAQxg5vFqYbN2MT7vYJbHxjF6u40Ex/w
|
||||
cbfj6EFv/GKxoEF5YCnsE1w2O+QcbYb1rCSRTY2UhNS6bogJ0aYL77Z0azr7diU+
|
||||
ul226zPmpMP7VIACtumzE00w2JqjfUlCbDoB/TSY9xkSUbasM6S0oZhxKsgqnHlv
|
||||
01kQG1kCgYBPZfZiqKwnnsOSRy8UBN4Oo5zg9QrbMki472/s4FhHaunXF0pFmIUG
|
||||
QU/9kYteJ8DlCppvvtU5C3qmEkW6c2u8KAfJXRmA51uS6v36kEx/8313ZJ5afwLU
|
||||
i2ZMmS8OabHjIhdnCSA2N7geqaAZa7BCLqt8735Doys1p0KB0y+ZNw==
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
var caCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDNzCCAh+gAwIBAgIJAM1Z9H27fWCyMA0GCSqGSIb3DQEBBQUAMBsxGTAXBgNV
|
||||
BAMUEHdlYmhvb2tfdGVzdHNfY2EwIBcNMTcwMjExMDAyMjUzWhgPMjI5MDExMjcw
|
||||
MDIyNTNaMBsxGTAXBgNVBAMUEHdlYmhvb2tfdGVzdHNfY2EwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDCZ0a4Ow8/KKu82qWqgObB/9uYXfNMkEVhBGwk
|
||||
rS8oEB2t8xgxXCg8Skzlln5UIcQHq9K4ZhsLQ/I73sf+QhXbYWwzA3vOJ+Ek/ZPY
|
||||
dv4Hs1zqzxa0BCfUnjFRjfXE5c4Rxd7ae4Z7OuUp9fcOkDUVVenr8J/y+HJRDR8l
|
||||
fC5kT89uG+lsFRyJAup89IfoSdKFm/OMkE/kGzXdSrNpGhuFR/0DenKTYxvc9eRN
|
||||
60RPuEtzgna4inkHE6g1lK+BAK8KNHJXmzWGKzsE1EAsoberP/jyLolHI7sW82jE
|
||||
ORYLT3MioPzVvD0jV3/4TBZ2oA9ktryIulKx3SAPTeANIVs7AgMBAAGjfDB6MB0G
|
||||
A1UdDgQWBBS0/gwwXmdxIe8o+a7WKbdiTYPy+TBLBgNVHSMERDBCgBS0/gwwXmdx
|
||||
Ie8o+a7WKbdiTYPy+aEfpB0wGzEZMBcGA1UEAxQQd2ViaG9va190ZXN0c19jYYIJ
|
||||
AM1Z9H27fWCyMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHGUdTCN
|
||||
rm0Mx6V5SmSIGbOB/+3QMqE1ZBIWwPsVFKHhROLaouELZFO+QysfLufB8SS54aM6
|
||||
ewufjfSz4KL26DnoSwOFirPxpG0+Sdry55lCjmZ50KtENZDu6g288Qx9GBzqgVHz
|
||||
kGi/eciV4fZ4HYIhZY+oR29n3YYQOID4UqbQ86lSoN781dmsEQLL+TEK4mJJFcNg
|
||||
SKHM526WdwJ15zqpKNlcqXtTyx3UfBFlNwvrxHNFbth1vOfdTW8zAs9Xzcr5vSm2
|
||||
G8nJ3FF/UF4dYpjDzggO3ALZZqUJHnl/XusETo5kYY3Ozp0xQYg2beR8irElqP8f
|
||||
oNcE4Ycfe10Hmec=
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
var badCAKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAxrCZsZHIeKuYSUYw4qKB8P+HeU81VP7F0tVbLOffj587sfDu
|
||||
N2QsOsnmiWKS9HiNVUs5ap/Zg3rekbnZV5eBeS5qOPC95c8Oyac7A2HNYSRp08kQ
|
||||
dUUo3ueDqwmZ5dNiDp0SriQCWfPYxiWHdWSacqDxxzXPFWCs6z3vwFYV35VxDFhr
|
||||
M8gPWgSqjWdqj+p5cHrL88me7vWWsj6ceL/i2KWo8lmuHuhUzggn0ekU/aKsavHi
|
||||
X/MNVd5tPzGIR+B49aywQZ9KyrJ7V8SdFqKYEGfH6RaiDhaNNUOv9E9668PdupnG
|
||||
qei0260zB8SDY4j6VKPGOS90YBA66qOP6J11rwIDAQABAoIBAQCsgs0PNep/i01v
|
||||
4Xe0bzCvVM4Fb9Z4c7TDN+gv9ytOggzMlMngYiNc78wwYNwDU2AzPFsfzqaG1/nD
|
||||
QUAKI0uRMdGcmrnmfH70azR73UD7JSiVb6/QgjnYP988c9uhhoVO9uYvOKip/WSr
|
||||
tg4EyVKoUEFcm8WvY/7/SQmPT68yLf5VpbtuCysAkSLPUjcBer4A6eWwlZ9PWrtj
|
||||
rLjUCGXXDKRgMmQRAwL+tpBgMwb1+euriv4+M6ddZCkcyaohW076qA3aaq3+XtAB
|
||||
RTGQubWshuri3N1WRQcn1ZvGURLCAhI8q+9i/wXADKrAlL6imDuYzTW+LMQdZLuH
|
||||
bwHvq/yRAoGBAP3beuc2R/jjkDttFsSce2dMx6F8AKPpmdbzVw7/DhflzNRDM/Yo
|
||||
dfVOabRLqcAyfhNm2L6CdUaIJuHRKyRJT3X5wgxUapAjXFUE0kH+qnaq3BxZCCjU
|
||||
fwDUZ4SUVDAuyaMo5OfVbqkI/L3rvSSgklNOnSkXMPtftDkz8pVljLo9AoGBAMhd
|
||||
6uiddCt3Dpt75C1BDRX0xGKc4KwtPK0CnQeQmQNXUx192m6IhfPW7YUoKvIZibWB
|
||||
f9NNJ/KCxDGG+QP7X+0sWQZMfdp5f1l6EsM6HFPLAOgjQ4PyBVWqxknJyxy6GCnt
|
||||
vI3s6cwMxN7B7QJ/87ffO23elEu7bCdg0lrOAmpbAoGBALN6fI+B6irGwU+ylflV
|
||||
5U2olC/Q2ycIXtMBYpjgrRcqSsH77X3pJ1TTJprpL9AKIucWvMEcvUursUnQt97E
|
||||
0iBH//D1sg3MYlhdu0Ybhmu16z9Dlyg+7LgqdDHhKRCT082+ePCMDtwF1aN1S1nd
|
||||
CPdLSoQluGTRSjtzRdxoWrHFAoGAJqNlz2G9qzwURwuHHuryeQ9wZ4vVD57RmpNs
|
||||
cK8Dss8+KevBGZueKT2DJDBwx6sBEU1dtwOj9nIdH2fl0UzCXNw2dq59fon7cufF
|
||||
gnxMRiRZkmpqdKFRQgninwwY7Ps9+afsunm7RCwaMtK2v8qo1wZnUXKgqlIEMzvK
|
||||
lNQxRw0CgYEA0uT5TkrZ3S+GAbyQtwb6FtXiLoocL+8QyBw6d1+5Fy7D0pYYQDLw
|
||||
TMeR2NOEqQGLgsqCnbuKzKBIuIY8wm0VIzOqRGmk4fWiOGxrtEBrfPl+A21bexyC
|
||||
qv5UBMLcEinZEM3x1e/rloDwKi0IGfyKiRfVpxdVKebs7dJfFYkhmw0=
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
var badCACert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDNzCCAh+gAwIBAgIJAPbb5w6p8Cw8MA0GCSqGSIb3DQEBBQUAMBsxGTAXBgNV
|
||||
BAMUEHdlYmhvb2tfdGVzdHNfY2EwIBcNMTcwMjExMDAyMjUzWhgPMjI5MDExMjcw
|
||||
MDIyNTNaMBsxGTAXBgNVBAMUEHdlYmhvb2tfdGVzdHNfY2EwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDGsJmxkch4q5hJRjDiooHw/4d5TzVU/sXS1Vss
|
||||
59+Pnzux8O43ZCw6yeaJYpL0eI1VSzlqn9mDet6RudlXl4F5Lmo48L3lzw7JpzsD
|
||||
Yc1hJGnTyRB1RSje54OrCZnl02IOnRKuJAJZ89jGJYd1ZJpyoPHHNc8VYKzrPe/A
|
||||
VhXflXEMWGszyA9aBKqNZ2qP6nlwesvzyZ7u9ZayPpx4v+LYpajyWa4e6FTOCCfR
|
||||
6RT9oqxq8eJf8w1V3m0/MYhH4Hj1rLBBn0rKsntXxJ0WopgQZ8fpFqIOFo01Q6/0
|
||||
T3rrw926mcap6LTbrTMHxINjiPpUo8Y5L3RgEDrqo4/onXWvAgMBAAGjfDB6MB0G
|
||||
A1UdDgQWBBTTHlbuK0loVSNNa+TCM0Bt7dLEcTBLBgNVHSMERDBCgBTTHlbuK0lo
|
||||
VSNNa+TCM0Bt7dLEcaEfpB0wGzEZMBcGA1UEAxQQd2ViaG9va190ZXN0c19jYYIJ
|
||||
APbb5w6p8Cw8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBACrsdqfQ
|
||||
7be0HI4+/cz/VwzvbQwjr9O6Ybxqs/eKOprh8RWRtjYeuagvTc6f5jH39W9kZdjs
|
||||
E3ktCG3RJJ/SyooeJlzNhgaAaATnMqEf7GyiQv3ch0B/Mc4TOPJeQ2E/pzFj3snq
|
||||
Edm8Xu9+WLwTTF4j/WlSY1sgVSnFk3Yzl5cn0ip00DVCOsL2sP3JlX9HRG4IrdiX
|
||||
jYXb+nGUPYFultSGSUw+5SiL2yM1ZyHfOBaO1RH8QIRA1/aFTfE+1QRuja5YtCwl
|
||||
ahpWVRhii7GVR3zKEgKFTxjELHm8x3vBC/HAhj5J3433nlRrgvwZXsZYplqp8422
|
||||
IpexMtsutA+y9aE=
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
var serverKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA2lF1EXRYzsUIwM1vrMM/OIinPvOzqSQSQ0euiUYgcNTw2Y87
|
||||
2fNWnQXOP3JDHgzZw231kJbdRxeWgRdeTnvCFUaBzA63GXMMCljs1sgAVLXnAmWR
|
||||
sE/TzG/OwsZUnyYzQMdG49PAEpw8GlX9t0eojmXPy7C/M0GnyAMx/UyMrq40lht6
|
||||
gdUI2gYRUofRRtHFs+a0EX8/q+49ciwgQx5inj2BX3Jc2cvc35Y3bSY86CYsAZic
|
||||
PZP84wP5iWkYmvFhJUoS/JY2FMC6CvRFPcTi8Dnp28kHEaqrocmwajSfyiJe/1qJ
|
||||
dMlHAInvTxp9E53cOzfCP6nmHbSKQPxm5u8hSQIDAQABAoIBAQCDhfNbEpa16qn9
|
||||
TUZr9CxQKLNpD3Q6/8oo0jRp6t98WizHRK0v/pM9gdPhETsyDVfbjpEUDG8+dw1q
|
||||
s+NSsOgZ3SIxBuRz5oVobm4wbskUP4nuPbZpW44jaXBMkyNDxcW2ztb8RgM+svTa
|
||||
gNea5Qa80sU+1zo47OLhcltZWBag3KCU/JQT+3LThVZDHt3GRx4QCASTJx3v/vBB
|
||||
o9M5wCYZp6sP7wmFUZfwEpkTfJ5M7sG1h7ibD/8kjIvpnQj+OFpcoylDxTINvqsN
|
||||
ADAe1NPK00Rx6vE9GNQ8ZA/lg0pih+EpK4PpE5cDDkYs3VchUlYHBSrsc7+K6kUk
|
||||
mMTdmVvpAoGBAP7sHhKMEpmPIUqxb5M95l+PX5uOS0x08HM40Vr8mxgx4z849CpW
|
||||
1vcQlZwcXwkxUfJyXZCPx9CK0Sw877Afpac1OL4RiEKZ3qmwLeI9RnRTeKC5qXJ9
|
||||
u31l+dgoSbRZDUdcM1ZwFs9+V8+zId58SifDaBjm3466VCMnD7KQUz4jAoGBANs9
|
||||
udy4Os+SvCYVUaiVvtoYMdcyym+VQzf3ycVAk91dO8Qha/5uFYD/f7ykgEgg7QCd
|
||||
jQp+ZVYPD7Hbh8XNwAt/6T+bF1qe8TSM3K8uk2Wt/tlk1ZqRnNNYsIZ8BO8c4T+f
|
||||
pbu/mCDdmTKWQWVEwCj2kKNBHptmlLO5Ie2nebujAoGBAIqoZccS138dAi+9iYHe
|
||||
VnM96fQTltN0e+FAU2eZJMcpQ4D8+poY9/4U0DvEltDKOdeU612ZR0cgapwUXQ9A
|
||||
d3sWkNGZebM4PIux35NCXxMg3+kUc51p1FRl5lrztvtYwMdC2E241D9yalL4DYEV
|
||||
u8QbHoEE+y6IHQGt2nT22cBfAoGAWmZuT+uLHHIFsLJTtG7ifi1Bx9lCjZX/XIGI
|
||||
qhQBpGJANZQOYp/jsAgqFI/D8XnaH8nXET+i60RUlWLO7inziQpaFAcQLyagkKmQ
|
||||
iY9r6Z5AGkWwqgZmouLMDvfuVOYUntZmUS8kPFEDTU+VcXtSvNFGPHqqcytuH1kz
|
||||
+zl2QX8CgYB9nFMpqARHl0kJp1UXtq9prUei+Lyu2gvrl3a74w96gqg3yx9+fU/n
|
||||
FzGF2VXnC5n1KvCvQi3xjLNzCOXKM3igu7u50CiaA/HEYmyWyOJw2Nt2+ICvxcCH
|
||||
rnsA8P8I/R5Esl0rvv2BbA1Q1O6SLC+Dfnhf7KulWmNgqVXKllj+Ng==
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
var serverCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDCTCCAfGgAwIBAgIJAPJbY53f15/vMA0GCSqGSIb3DQEBBQUAMBsxGTAXBgNV
|
||||
BAMUEHdlYmhvb2tfdGVzdHNfY2EwIBcNMTcwMjExMDAyMjU0WhgPMjI5MDExMjcw
|
||||
MDIyNTRaMB8xHTAbBgNVBAMMFHdlYmhvb2tfdGVzdHNfc2VydmVyMIIBIjANBgkq
|
||||
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2lF1EXRYzsUIwM1vrMM/OIinPvOzqSQS
|
||||
Q0euiUYgcNTw2Y872fNWnQXOP3JDHgzZw231kJbdRxeWgRdeTnvCFUaBzA63GXMM
|
||||
Cljs1sgAVLXnAmWRsE/TzG/OwsZUnyYzQMdG49PAEpw8GlX9t0eojmXPy7C/M0Gn
|
||||
yAMx/UyMrq40lht6gdUI2gYRUofRRtHFs+a0EX8/q+49ciwgQx5inj2BX3Jc2cvc
|
||||
35Y3bSY86CYsAZicPZP84wP5iWkYmvFhJUoS/JY2FMC6CvRFPcTi8Dnp28kHEaqr
|
||||
ocmwajSfyiJe/1qJdMlHAInvTxp9E53cOzfCP6nmHbSKQPxm5u8hSQIDAQABo0ow
|
||||
SDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAgYI
|
||||
KwYBBQUHAwEwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0BAQUFAAOCAQEAX/CG
|
||||
3C1nwCWabpBw6h0k7UlDI55nnTH6xHdSX9EFmHz49NmAym9gUwXK5xDPVDNYURjb
|
||||
TD3R2e76Cov7wXRzw99BMzKOhNrMgjiOrc0WT4Ck5MOaKgjzZEIXRSSBllsrF9ut
|
||||
hnnuSaaKwUVn4D/9vPMp/TuZoK7yZaW3Pyv0ScQfpkECDLKYIkXOlyhC/I5Tfbof
|
||||
+zReStbTsc0EWMVLLIAbP7uPf1VcH5HnElh1ignxRAPBsXwF8jQzjUBTWcZ5dEi9
|
||||
ofIrWo+AVKvcoRlyZZyLjOKPzhA5+pwG4yBkWJB5Cshq2trOYVf3+uUN8lz6i57M
|
||||
wqxS1Q1MmtLhyhy79Q==
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
var clientKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAt6auF7X7jl94ilPpJid6k15/Tnt/hFWMRv2eGeuSBT1YMjDP
|
||||
X5pIaseeIVdXl0ePKzPeyYJma4t7EpGhUO7TX12Cr1e5S8IMExNDpYGti3ncLf1O
|
||||
HP+faJNAlbKMTgf2xr0HPg8Q2lsDKfam/OEn+7jqv3FpZ5guwQQy7beQhWV38YuR
|
||||
vf2ChNJVcalN0h+MRNkT+hsGKGM9XKgKFGknDuCP8N0H7HrP7LLf/tLOMNq/PeMz
|
||||
I6MXMlXmB4VRPMlf1zJGfvE6i0sSbNM0p2ZjArpjhjdveLuvBwan/Guk9vW970ON
|
||||
sNn9gdLiwCSLqzhRy0cTlIJsSnlkhbuOQZsqKwIDAQABAoIBAE2gCVQCWsrJ9dRa
|
||||
NWEoLLpfpeXRc4vG8R0MlCgWl0jZrg7A7NZXCyb/KwqitWY/G/fB2/hGwu3QLfwi
|
||||
TBI+cF+N0fA1Xx/zbFEfwmcRkf4zSuqxd7PwJDv6icD8kCtnWFqWiZokmhYBhCvX
|
||||
kquuq8zNU4QJ9uiPvateD/zEqzSGgMeL+j7RGJsRmh2TnSBgKXLwadRhYLeHiFu/
|
||||
AwoWljlhLNXrCzCLx2kJPIA9CNYYtShhQncfZfkfC0I02vPWX9hu8lMpKQp2MmD9
|
||||
b3DvVW3H6cjAtm/nsjGghYNCngep8uPX2twcrLOZfzJgsZJf+yn/KLWb/yhGBXjd
|
||||
TERHRCECgYEA2i5OfkIrBIPQcjhQCtkjYBgKUKVS54KTTPQQ0zoiGRPkXb6kpqrt
|
||||
kaCKGYXT4oqvQQapNZQykrLUQ/xzbdAAzdIwZ8hTWS5K5cxHOnkmOcPiKu2+jM4I
|
||||
zT7sdAYn0aSbrh1pNRQDV0tQZcI1Urp/OcEuniaEblWhq5/VRCmpCBECgYEA13wg
|
||||
jKRobq4QBoQM8pu1Ha7waeJZ26NcZwCxno0TwH2JZ6X9e4iXfhywUOgVW7hXzcs5
|
||||
2nBciVX5h31u1EDPJz6aPyzzQHi0YspDy/zuO0GWEJxLKm5QMyjh5vakhF5bVP6f
|
||||
Dh3rXts/ZYKk3+p4ezXs2b+uTelowuq8Kk55qnsCgYAy/tvN2v1fAsg3yj27K2F/
|
||||
Vl8i1mF4RybSt8Eu/cl2fxXDa4nkgtMgVJuyt3r82ll4I2xtX4Qqka3Xbiw0oIdv
|
||||
lA9IUqRYld9fss17N1Hd8pDsY8FD++xGvMxbmgy4jXbtzWYHx/O39ZyHDEuWWIzg
|
||||
HO0effY6K72r9aHNWsdtYQKBgQDNXUw8Hbg1u4gkXZdlZEYxevc/Qmz3KXK36+5b
|
||||
uAJaEopwkL7LC/utQjQ7d2RbnI154TRK3Ykjjh+ZJE8K1JVYxo4EpZdTG3Z3LGN+
|
||||
tphpOvGE9R+h2a5vg4gAMZHLYY3TrDL0JkmahoOd/+uYR4L5kgQf5lF9iXTBRyt7
|
||||
enzznwKBgBr9rHUsdd8whEo/UntKaGQ6sqevd/ESxDErUqkTkiTtDAT2vuLQwgA0
|
||||
JnzYUv1/wmoLCz6Nbe6Fg9shAgRY/tz7GI75NBi4HBjp286df6Uu6qEJ4LAELc0+
|
||||
Yh/uRF/H4WkpmBckEXobnRxoX/0HWFY5SETLoG/nwaxJG9YL/Acr
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
var clientCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDCTCCAfGgAwIBAgIJAPJbY53f15/wMA0GCSqGSIb3DQEBBQUAMBsxGTAXBgNV
|
||||
BAMUEHdlYmhvb2tfdGVzdHNfY2EwIBcNMTcwMjExMDAyMjU0WhgPMjI5MDExMjcw
|
||||
MDIyNTRaMB8xHTAbBgNVBAMMFHdlYmhvb2tfdGVzdHNfY2xpZW50MIIBIjANBgkq
|
||||
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt6auF7X7jl94ilPpJid6k15/Tnt/hFWM
|
||||
Rv2eGeuSBT1YMjDPX5pIaseeIVdXl0ePKzPeyYJma4t7EpGhUO7TX12Cr1e5S8IM
|
||||
ExNDpYGti3ncLf1OHP+faJNAlbKMTgf2xr0HPg8Q2lsDKfam/OEn+7jqv3FpZ5gu
|
||||
wQQy7beQhWV38YuRvf2ChNJVcalN0h+MRNkT+hsGKGM9XKgKFGknDuCP8N0H7HrP
|
||||
7LLf/tLOMNq/PeMzI6MXMlXmB4VRPMlf1zJGfvE6i0sSbNM0p2ZjArpjhjdveLuv
|
||||
Bwan/Guk9vW970ONsNn9gdLiwCSLqzhRy0cTlIJsSnlkhbuOQZsqKwIDAQABo0ow
|
||||
SDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAgYI
|
||||
KwYBBQUHAwEwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0BAQUFAAOCAQEAD8aZ
|
||||
E0nof9Rh7s+uwtF70KPgcz71ft0c1+vSmeLm4IkN0f+amcvgaT8xZLwNv1b77NZo
|
||||
uMWXvit24eIuiqzq7umKiHP/UrFv+Rl+9ue+lA3N0e3WikRoJsh3aoIn8BQUBbnX
|
||||
Nr9R69SeRYYRpMrs19N5Wn4gN7Nfie+1FKWsL3myJYDFsg+8GMEcOJ0YdOMALMy0
|
||||
tIJdYji28mTQ++lpGbekjhf7p9wazQ/6CVd8WNpIbGO84QbGCcpCaVM2XxOSiV/F
|
||||
hIGO1Z30SBq8rQw51XbhdRX+uvRM1ya4RuBMCSX/hpsMu9lVRqCzbkU4PvuUTqLA
|
||||
CebKCgjYbM0CWrP9kw==
|
||||
-----END CERTIFICATE-----`)
|
|
@ -0,0 +1,107 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Copyright 2017 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -e
|
||||
|
||||
# gencerts.sh generates the certificates for the webhook tests.
|
||||
#
|
||||
# It is not expected to be run often (there is no go generate rule), and mainly
|
||||
# exists for documentation purposes.
|
||||
|
||||
CN_BASE="webhook_tests"
|
||||
|
||||
cat > server.conf << EOF
|
||||
[req]
|
||||
req_extensions = v3_req
|
||||
distinguished_name = req_distinguished_name
|
||||
[req_distinguished_name]
|
||||
[ v3_req ]
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = clientAuth, serverAuth
|
||||
subjectAltName = @alt_names
|
||||
[alt_names]
|
||||
IP.1 = 127.0.0.1
|
||||
EOF
|
||||
|
||||
cat > client.conf << EOF
|
||||
[req]
|
||||
req_extensions = v3_req
|
||||
distinguished_name = req_distinguished_name
|
||||
[req_distinguished_name]
|
||||
[ v3_req ]
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = clientAuth, serverAuth
|
||||
subjectAltName = @alt_names
|
||||
[alt_names]
|
||||
IP.1 = 127.0.0.1
|
||||
EOF
|
||||
|
||||
# Create a certificate authority
|
||||
openssl genrsa -out caKey.pem 2048
|
||||
openssl req -x509 -new -nodes -key caKey.pem -days 100000 -out caCert.pem -subj "/CN=${CN_BASE}_ca"
|
||||
|
||||
# Create a second certificate authority
|
||||
openssl genrsa -out badCAKey.pem 2048
|
||||
openssl req -x509 -new -nodes -key badCAKey.pem -days 100000 -out badCACert.pem -subj "/CN=${CN_BASE}_ca"
|
||||
|
||||
# Create a server certiticate
|
||||
openssl genrsa -out serverKey.pem 2048
|
||||
openssl req -new -key serverKey.pem -out server.csr -subj "/CN=${CN_BASE}_server" -config server.conf
|
||||
openssl x509 -req -in server.csr -CA caCert.pem -CAkey caKey.pem -CAcreateserial -out serverCert.pem -days 100000 -extensions v3_req -extfile server.conf
|
||||
|
||||
# Create a client certiticate
|
||||
openssl genrsa -out clientKey.pem 2048
|
||||
openssl req -new -key clientKey.pem -out client.csr -subj "/CN=${CN_BASE}_client" -config client.conf
|
||||
openssl x509 -req -in client.csr -CA caCert.pem -CAkey caKey.pem -CAcreateserial -out clientCert.pem -days 100000 -extensions v3_req -extfile client.conf
|
||||
|
||||
outfile=certs_test.go
|
||||
|
||||
cat > $outfile << EOF
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
EOF
|
||||
|
||||
echo "// This file was generated using openssl by the gencerts.sh script" >> $outfile
|
||||
echo "// and holds raw certificates for the webhook tests." >> $outfile
|
||||
echo "" >> $outfile
|
||||
echo "package webhook" >> $outfile
|
||||
for file in caKey caCert badCAKey badCACert serverKey serverCert clientKey clientCert; do
|
||||
data=$(cat ${file}.pem)
|
||||
echo "" >> $outfile
|
||||
echo "var $file = []byte(\`$data\`)" >> $outfile
|
||||
done
|
||||
|
||||
# Clean up after we're done.
|
||||
rm *.pem
|
||||
rm *.csr
|
||||
rm *.srl
|
||||
rm *.conf
|
|
@ -0,0 +1,607 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/pkg/api"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd/api/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
errBadCertificate = "Get .*: remote error: tls: bad certificate"
|
||||
errNoConfiguration = "invalid configuration: no configuration has been provided"
|
||||
errMissingCertPath = "invalid configuration: unable to read %s %s for %s due to open %s: .*"
|
||||
errSignedByUnknownCA = "Get .*: x509: certificate signed by unknown authority"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultCluster = v1.NamedCluster{
|
||||
Cluster: v1.Cluster{
|
||||
Server: "https://webhook.example.com",
|
||||
CertificateAuthorityData: caCert,
|
||||
},
|
||||
}
|
||||
defaultUser = v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: clientCert,
|
||||
ClientKeyData: clientKey,
|
||||
},
|
||||
}
|
||||
namedCluster = v1.NamedCluster{
|
||||
Cluster: v1.Cluster{
|
||||
Server: "https://webhook.example.com",
|
||||
CertificateAuthorityData: caCert,
|
||||
},
|
||||
Name: "test-cluster",
|
||||
}
|
||||
groupVersions = []schema.GroupVersion{}
|
||||
retryBackoff = time.Duration(500) * time.Millisecond
|
||||
)
|
||||
|
||||
// TestDisabledGroupVersion ensures that requiring a group version works as expected
|
||||
func TestDisabledGroupVersion(t *testing.T) {
|
||||
gv := schema.GroupVersion{Group: "webhook.util.k8s.io", Version: "v1"}
|
||||
gvs := []schema.GroupVersion{gv}
|
||||
_, err := NewGenericWebhook(api.Registry, api.Codecs, "/some/path", gvs, retryBackoff)
|
||||
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
} else {
|
||||
aErrMsg := err.Error()
|
||||
eErrMsg := fmt.Sprintf("webhook plugin requires enabling extension resource: %s", gv)
|
||||
|
||||
if aErrMsg != eErrMsg {
|
||||
t.Errorf("unexpected error message mismatch:\n Expected: %s\n Actual: %s", eErrMsg, aErrMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestKubeConfigFile ensures that a kube config file, regardless of validity, is handled properly
|
||||
func TestKubeConfigFile(t *testing.T) {
|
||||
badCAPath := "/tmp/missing/ca.pem"
|
||||
badClientCertPath := "/tmp/missing/client.pem"
|
||||
badClientKeyPath := "/tmp/missing/client-key.pem"
|
||||
dir := bootstrapTestDir(t)
|
||||
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// These tests check for all of the ways in which a Kubernetes config file could be malformed within the context of
|
||||
// configuring a webhook. Configuration issues that arise while using the webhook are tested elsewhere.
|
||||
tests := []struct {
|
||||
test string
|
||||
cluster *v1.NamedCluster
|
||||
context *v1.NamedContext
|
||||
currentContext string
|
||||
user *v1.NamedAuthInfo
|
||||
errRegex string
|
||||
}{
|
||||
{
|
||||
test: "missing context (no default, none specified)",
|
||||
cluster: &namedCluster,
|
||||
errRegex: errNoConfiguration,
|
||||
},
|
||||
{
|
||||
test: "missing context (specified context is missing)",
|
||||
cluster: &namedCluster,
|
||||
currentContext: "missing-context",
|
||||
errRegex: errNoConfiguration,
|
||||
},
|
||||
{
|
||||
test: "context without cluster",
|
||||
context: &v1.NamedContext{
|
||||
Context: v1.Context{},
|
||||
},
|
||||
currentContext: "testing-context",
|
||||
errRegex: errNoConfiguration,
|
||||
},
|
||||
{
|
||||
test: "context without user",
|
||||
cluster: &namedCluster,
|
||||
context: &v1.NamedContext{
|
||||
Context: v1.Context{
|
||||
Cluster: namedCluster.Name,
|
||||
},
|
||||
},
|
||||
currentContext: "testing-context",
|
||||
errRegex: "", // Not an error at parse time, only when using the webhook
|
||||
},
|
||||
{
|
||||
test: "context with missing cluster",
|
||||
cluster: &namedCluster,
|
||||
context: &v1.NamedContext{
|
||||
Context: v1.Context{
|
||||
Cluster: "missing-cluster",
|
||||
},
|
||||
},
|
||||
errRegex: errNoConfiguration,
|
||||
},
|
||||
{
|
||||
test: "context with missing user",
|
||||
cluster: &namedCluster,
|
||||
context: &v1.NamedContext{
|
||||
Context: v1.Context{
|
||||
Cluster: namedCluster.Name,
|
||||
AuthInfo: "missing-user",
|
||||
},
|
||||
},
|
||||
currentContext: "testing-context",
|
||||
errRegex: "", // Not an error at parse time, only when using the webhook
|
||||
},
|
||||
{
|
||||
test: "cluster with invalid CA certificate path",
|
||||
cluster: &v1.NamedCluster{
|
||||
Cluster: v1.Cluster{
|
||||
Server: namedCluster.Cluster.Server,
|
||||
CertificateAuthority: badCAPath,
|
||||
},
|
||||
},
|
||||
user: &defaultUser,
|
||||
errRegex: fmt.Sprintf(errMissingCertPath, "certificate-authority", badCAPath, "", badCAPath),
|
||||
},
|
||||
{
|
||||
test: "cluster with invalid CA certificate ",
|
||||
cluster: &v1.NamedCluster{
|
||||
Cluster: v1.Cluster{
|
||||
Server: namedCluster.Cluster.Server,
|
||||
CertificateAuthorityData: caKey,
|
||||
},
|
||||
},
|
||||
user: &defaultUser,
|
||||
errRegex: "", // Not an error at parse time, only when using the webhook
|
||||
},
|
||||
{
|
||||
test: "user with invalid client certificate path",
|
||||
cluster: &defaultCluster,
|
||||
user: &v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificate: badClientCertPath,
|
||||
ClientKeyData: defaultUser.AuthInfo.ClientKeyData,
|
||||
},
|
||||
},
|
||||
errRegex: fmt.Sprintf(errMissingCertPath, "client-cert", badClientCertPath, "", badClientCertPath),
|
||||
},
|
||||
{
|
||||
test: "user with invalid client certificate",
|
||||
cluster: &defaultCluster,
|
||||
user: &v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: clientKey,
|
||||
ClientKeyData: defaultUser.AuthInfo.ClientKeyData,
|
||||
},
|
||||
},
|
||||
errRegex: "tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched",
|
||||
},
|
||||
{
|
||||
test: "user with invalid client certificate path",
|
||||
cluster: &defaultCluster,
|
||||
user: &v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: defaultUser.AuthInfo.ClientCertificateData,
|
||||
ClientKey: badClientKeyPath,
|
||||
},
|
||||
},
|
||||
errRegex: fmt.Sprintf(errMissingCertPath, "client-key", badClientKeyPath, "", badClientKeyPath),
|
||||
},
|
||||
{
|
||||
test: "user with invalid client certificate",
|
||||
cluster: &defaultCluster,
|
||||
user: &v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: defaultUser.AuthInfo.ClientCertificateData,
|
||||
ClientKeyData: clientCert,
|
||||
},
|
||||
},
|
||||
errRegex: "tls: found a certificate rather than a key in the PEM for the private key",
|
||||
},
|
||||
{
|
||||
test: "valid configuration (certificate data embeded in config)",
|
||||
cluster: &defaultCluster,
|
||||
user: &defaultUser,
|
||||
errRegex: "",
|
||||
},
|
||||
{
|
||||
test: "valid configuration (certificate files referenced in config)",
|
||||
cluster: &v1.NamedCluster{
|
||||
Cluster: v1.Cluster{
|
||||
Server: "https://webhook.example.com",
|
||||
CertificateAuthority: filepath.Join(dir, "ca.pem"),
|
||||
},
|
||||
},
|
||||
user: &v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificate: filepath.Join(dir, "client.pem"),
|
||||
ClientKey: filepath.Join(dir, "client-key.pem"),
|
||||
},
|
||||
},
|
||||
errRegex: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
// Use a closure so defer statements trigger between loop iterations.
|
||||
err := func() error {
|
||||
kubeConfig := v1.Config{}
|
||||
|
||||
if tt.cluster != nil {
|
||||
kubeConfig.Clusters = []v1.NamedCluster{*tt.cluster}
|
||||
}
|
||||
|
||||
if tt.context != nil {
|
||||
kubeConfig.Contexts = []v1.NamedContext{*tt.context}
|
||||
}
|
||||
|
||||
if tt.user != nil {
|
||||
kubeConfig.AuthInfos = []v1.NamedAuthInfo{*tt.user}
|
||||
}
|
||||
|
||||
kubeConfigFile, err := newKubeConfigFile(kubeConfig)
|
||||
|
||||
if err == nil {
|
||||
defer os.Remove(kubeConfigFile)
|
||||
|
||||
_, err = NewGenericWebhook(api.Registry, api.Codecs, kubeConfigFile, groupVersions, retryBackoff)
|
||||
}
|
||||
|
||||
return err
|
||||
}()
|
||||
|
||||
if err == nil {
|
||||
if tt.errRegex != "" {
|
||||
t.Errorf("%s: expected an error", tt.test)
|
||||
}
|
||||
} else {
|
||||
if tt.errRegex == "" {
|
||||
t.Errorf("%s: unexpected error: %v", tt.test, err)
|
||||
} else if !regexp.MustCompile(tt.errRegex).MatchString(err.Error()) {
|
||||
t.Errorf("%s: unexpected error message to match:\n Expected: %s\n Actual: %s", tt.test, tt.errRegex, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestMissingKubeConfigFile ensures that a kube config path to a missing file is handled properly
|
||||
func TestMissingKubeConfigFile(t *testing.T) {
|
||||
kubeConfigPath := "/some/missing/path"
|
||||
_, err := NewGenericWebhook(api.Registry, api.Codecs, kubeConfigPath, groupVersions, retryBackoff)
|
||||
|
||||
if err == nil {
|
||||
t.Errorf("creating the webhook should had failed")
|
||||
} else if strings.Index(err.Error(), fmt.Sprintf("stat %s", kubeConfigPath)) != 0 {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTLSConfig ensures that the TLS-based communication between client and server works as expected
|
||||
func TestTLSConfig(t *testing.T) {
|
||||
invalidCert := []byte("invalid")
|
||||
tests := []struct {
|
||||
test string
|
||||
clientCert, clientKey, clientCA []byte
|
||||
serverCert, serverKey, serverCA []byte
|
||||
errRegex string
|
||||
}{
|
||||
{
|
||||
test: "invalid server CA",
|
||||
clientCert: clientCert, clientKey: clientKey, clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey, serverCA: invalidCert,
|
||||
errRegex: errBadCertificate,
|
||||
},
|
||||
{
|
||||
test: "invalid client certificate",
|
||||
clientCert: invalidCert, clientKey: clientKey, clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey, serverCA: caCert,
|
||||
errRegex: "tls: failed to find any PEM data in certificate input",
|
||||
},
|
||||
{
|
||||
test: "invalid client key",
|
||||
clientCert: clientCert, clientKey: invalidCert, clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey, serverCA: caCert,
|
||||
errRegex: "tls: failed to find any PEM data in key input",
|
||||
},
|
||||
{
|
||||
test: "client does not trust server",
|
||||
clientCert: clientCert, clientKey: clientKey,
|
||||
serverCert: serverCert, serverKey: serverKey,
|
||||
errRegex: errSignedByUnknownCA,
|
||||
},
|
||||
{
|
||||
test: "server does not trust client",
|
||||
clientCert: clientCert, clientKey: clientKey, clientCA: badCACert,
|
||||
serverCert: serverCert, serverKey: serverKey, serverCA: caCert,
|
||||
errRegex: errSignedByUnknownCA + " .*",
|
||||
},
|
||||
{
|
||||
test: "server requires auth, client provides it",
|
||||
clientCert: clientCert, clientKey: clientKey, clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey, serverCA: caCert,
|
||||
errRegex: "",
|
||||
},
|
||||
{
|
||||
test: "server does not require client auth",
|
||||
clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey,
|
||||
errRegex: "",
|
||||
},
|
||||
{
|
||||
test: "server does not require client auth, client provides it",
|
||||
clientCert: clientCert, clientKey: clientKey, clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey,
|
||||
errRegex: "",
|
||||
},
|
||||
{
|
||||
test: "webhook does not support insecure servers",
|
||||
errRegex: errSignedByUnknownCA,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
// Use a closure so defer statements trigger between loop iterations.
|
||||
func() {
|
||||
// Create and start a simple HTTPS server
|
||||
server, err := newTestServer(tt.serverCert, tt.serverKey, tt.serverCA, nil)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("%s: failed to create server: %v", tt.test, err)
|
||||
return
|
||||
}
|
||||
|
||||
defer server.Close()
|
||||
|
||||
// Create a Kubernetes client configuration file
|
||||
configFile, err := newKubeConfigFile(v1.Config{
|
||||
Clusters: []v1.NamedCluster{
|
||||
{
|
||||
Cluster: v1.Cluster{
|
||||
Server: server.URL,
|
||||
CertificateAuthorityData: tt.clientCA,
|
||||
},
|
||||
},
|
||||
},
|
||||
AuthInfos: []v1.NamedAuthInfo{
|
||||
{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: tt.clientCert,
|
||||
ClientKeyData: tt.clientKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", tt.test, err)
|
||||
return
|
||||
}
|
||||
|
||||
defer os.Remove(configFile)
|
||||
|
||||
wh, err := NewGenericWebhook(api.Registry, api.Codecs, configFile, groupVersions, retryBackoff)
|
||||
|
||||
if err == nil {
|
||||
err = wh.RestClient.Get().Do().Error()
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
if tt.errRegex != "" {
|
||||
t.Errorf("%s: expected an error", tt.test)
|
||||
}
|
||||
} else {
|
||||
if tt.errRegex == "" {
|
||||
t.Errorf("%s: unexpected error: %v", tt.test, err)
|
||||
} else if !regexp.MustCompile(tt.errRegex).MatchString(err.Error()) {
|
||||
t.Errorf("%s: unexpected error message mismatch:\n Expected: %s\n Actual: %s", tt.test, tt.errRegex, err.Error())
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// TestWithExponentialBackoff ensures that the webhook's exponential backoff support works as expected
|
||||
func TestWithExponentialBackoff(t *testing.T) {
|
||||
count := 0 // To keep track of the requests
|
||||
gr := schema.GroupResource{
|
||||
Group: "webhook.util.k8s.io",
|
||||
Resource: "test",
|
||||
}
|
||||
|
||||
// Handler that will handle all backoff CONDITIONS
|
||||
ebHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
switch count++; count {
|
||||
case 1:
|
||||
// Timeout error with retry supplied
|
||||
w.WriteHeader(http.StatusGatewayTimeout)
|
||||
json.NewEncoder(w).Encode(apierrors.NewServerTimeout(gr, "get", 2))
|
||||
case 2:
|
||||
// Internal server error
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
json.NewEncoder(w).Encode(apierrors.NewInternalError(fmt.Errorf("nope")))
|
||||
case 3:
|
||||
// HTTP error that is not retryable
|
||||
w.WriteHeader(http.StatusNotAcceptable)
|
||||
json.NewEncoder(w).Encode(apierrors.NewGenericServerResponse(http.StatusNotAcceptable, "get", gr, "testing", "nope", 0, false))
|
||||
case 4:
|
||||
// Successful request
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]string{
|
||||
"status": "OK",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Create and start a simple HTTPS server
|
||||
server, err := newTestServer(clientCert, clientKey, caCert, ebHandler)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("failed to create server: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
defer server.Close()
|
||||
|
||||
// Create a Kubernetes client configuration file
|
||||
configFile, err := newKubeConfigFile(v1.Config{
|
||||
Clusters: []v1.NamedCluster{
|
||||
{
|
||||
Cluster: v1.Cluster{
|
||||
Server: server.URL,
|
||||
CertificateAuthorityData: caCert,
|
||||
},
|
||||
},
|
||||
},
|
||||
AuthInfos: []v1.NamedAuthInfo{
|
||||
{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: clientCert,
|
||||
ClientKeyData: clientKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("failed to create the client config file: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
defer os.Remove(configFile)
|
||||
|
||||
wh, err := NewGenericWebhook(api.Registry, api.Codecs, configFile, groupVersions, retryBackoff)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create the webhook: %v", err)
|
||||
}
|
||||
|
||||
result := wh.WithExponentialBackoff(func() rest.Result {
|
||||
return wh.RestClient.Get().Do()
|
||||
})
|
||||
|
||||
var statusCode int
|
||||
|
||||
result.StatusCode(&statusCode)
|
||||
|
||||
if statusCode != http.StatusNotAcceptable {
|
||||
t.Errorf("unexpected status code: %d", statusCode)
|
||||
}
|
||||
|
||||
result = wh.WithExponentialBackoff(func() rest.Result {
|
||||
return wh.RestClient.Get().Do()
|
||||
})
|
||||
|
||||
result.StatusCode(&statusCode)
|
||||
|
||||
if statusCode != http.StatusOK {
|
||||
t.Errorf("unexpected status code: %d", statusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func bootstrapTestDir(t *testing.T) string {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// The certificates needed on disk for the tests
|
||||
files := map[string][]byte{
|
||||
"ca.pem": caCert,
|
||||
"client.pem": clientCert,
|
||||
"client-key.pem": clientKey,
|
||||
}
|
||||
|
||||
// Write the certificate files to disk or fail
|
||||
for fileName, fileData := range files {
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, fileName), fileData, 0400); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
return dir
|
||||
}
|
||||
|
||||
func newKubeConfigFile(config v1.Config) (string, error) {
|
||||
configFile, err := ioutil.TempFile("", "")
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to create the Kubernetes client config file: %v", err)
|
||||
}
|
||||
|
||||
if err = json.NewEncoder(configFile).Encode(config); err != nil {
|
||||
return "", fmt.Errorf("unable to write the Kubernetes client configuration to disk: %v", err)
|
||||
}
|
||||
|
||||
return configFile.Name(), nil
|
||||
}
|
||||
|
||||
func newTestServer(clientCert, clientKey, caCert []byte, handler func(http.ResponseWriter, *http.Request)) (*httptest.Server, error) {
|
||||
var tlsConfig *tls.Config
|
||||
|
||||
if clientCert != nil {
|
||||
cert, err := tls.X509KeyPair(clientCert, clientKey)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsConfig = &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
}
|
||||
}
|
||||
|
||||
if caCert != nil {
|
||||
rootCAs := x509.NewCertPool()
|
||||
|
||||
rootCAs.AppendCertsFromPEM(caCert)
|
||||
|
||||
if tlsConfig == nil {
|
||||
tlsConfig = &tls.Config{}
|
||||
}
|
||||
|
||||
tlsConfig.ClientCAs = rootCAs
|
||||
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
}
|
||||
|
||||
if handler == nil {
|
||||
handler = func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("OK"))
|
||||
}
|
||||
}
|
||||
|
||||
server := httptest.NewUnstartedServer(http.HandlerFunc(handler))
|
||||
|
||||
server.TLS = tlsConfig
|
||||
server.StartTLS()
|
||||
|
||||
return server, nil
|
||||
}
|
Loading…
Reference in New Issue