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(
|
load(
|
||||||
"@io_bazel_rules_go//go:def.bzl",
|
"@io_bazel_rules_go//go:def.bzl",
|
||||||
"go_library",
|
"go_library",
|
||||||
|
"go_test",
|
||||||
)
|
)
|
||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
|
@ -22,3 +23,20 @@ go_library(
|
||||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_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