Skip to content

Commit 1f2f004

Browse files
alvaroalemankubermatic-bot
authored andcommitted
Add a MutatingAdmissionWebook + Defaulting/Validation for MachineDeployment (#362)
* Initial plumbing * Full plumbing * Add business logic to machineDeployment defaulting and validation * Check for len(errs) not fore != nil * Only create a jsonpatch if there were changes * Use evanphx/json-patch, hopefully not buggy * Test * Revert back to mattbairds jsonpatch library * Add some debug logging * Update e2e testing to use MatatingAdmissionWebhook * Increase size of e2e controlller to check if it helps * Use ipvs on kubeadm-controller in e2e tests * Revert "Use ipvs on kubeadm-controller in e2e tests" This reverts commit 4b7aef7. * Move webhook into a dedicated app * Move webhook into a dedicated deployment * Fix name of service * Block webhook shutdown * Use one replica for the webhook * Small fixes * Clean up unneeded temporal changes * Fix docker-image target * Fix test if machine-controller is running * Use one docker image for everything
1 parent d2a3916 commit 1f2f004

File tree

14 files changed

+947
-8
lines changed

14 files changed

+947
-8
lines changed

.circleci/config.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,12 @@ jobs:
4848
key: repo-{{ .Environment.CIRCLE_SHA1 }}
4949
- run: DEP_RELEASE_TAG=v0.5.0 curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
5050
- run: make machine-controller
51+
- run: make webhook
5152
- save_cache:
5253
key: machine-controller-{{ .Revision }}
5354
paths:
5455
- /go/src/github.com/kubermatic/machine-controller
56+
- /go/src/github.com/kubermatic/webhook
5557
end-to-end:
5658
<<: *defaults
5759
steps:

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,8 @@ test/tools/verify/verify
88
terraform
99
terraform-provider-hcloud
1010
.kubeconfig
11+
examples/*.pem
12+
examples/*.csr
13+
examples/*.srl
14+
webhook
15+
!cmd/webhook

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ FROM alpine:3.7
33
RUN apk add --no-cache ca-certificates cdrkit
44

55
COPY machine-controller /usr/local/bin
6+
COPY webhook /usr/local/bin
67

78
USER nobody

Gopkg.lock

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Makefile

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,13 @@ machine-controller: $(shell find cmd pkg -name '*.go') vendor
3030
-o machine-controller \
3131
github.com/kubermatic/machine-controller/cmd/controller
3232

33-
docker-image: machine-controller docker-image-nodep
33+
webhook: $(shell find cmd pkg -name '*.go') vendor
34+
go build -v \
35+
-ldflags '-s -w' \
36+
-o webhook \
37+
github.com/kubermatic/machine-controller/cmd/webhook
38+
39+
docker-image: machine-controller admission-webhook docker-image-nodep
3440

3541
# This target exists because in our CI
3642
# we do not want to restore the vendor
@@ -68,3 +74,31 @@ e2e-cluster:
6874
e2e-destroy:
6975
./test/tools/integration/cleanup_machines.sh
7076
make -C test/tools/integration destroy
77+
78+
examples/ca-key.pem:
79+
openssl genrsa -out examples/ca-key.pem 4096
80+
81+
examples/ca-cert.pem: examples/ca-key.pem
82+
openssl req -x509 -new -nodes -key examples/ca-key.pem \
83+
-subj "/C=US/ST=CA/O=Acme/CN=k8s-machine-controller-ca" \
84+
-sha256 -days 10000 -out examples/ca-cert.pem
85+
86+
examples/admission-key.pem: examples/ca-cert.pem
87+
openssl genrsa -out examples/admission-key.pem 2048
88+
chmod 0600 examples/admission-key.pem
89+
90+
examples/admission-cert.pem: examples/admission-key.pem
91+
openssl req -new -sha256 \
92+
-key examples/admission-key.pem \
93+
-subj "/C=US/ST=CA/O=Acme/CN=machine-controller-webhook.kube-system.svc" \
94+
-out examples/admission.csr
95+
openssl x509 -req -in examples/admission.csr -CA examples/ca-cert.pem \
96+
-CAkey examples/ca-key.pem -CAcreateserial \
97+
-out examples/admission-cert.pem -days 10000 -sha256
98+
99+
deploy: examples/admission-cert.pem
100+
@cat examples/machine-controller.yaml \
101+
|sed "s/__admission_ca_cert__/$(shell cat examples/ca-cert.pem|base64 -w0)/g" \
102+
|sed "s/__admission_cert__/$(shell cat examples/admission-cert.pem|base64 -w0)/g" \
103+
|sed "s/__admission_key__/$(shell cat examples/admission-key.pem|base64 -w0)/g" \
104+
|kubectl apply -f -

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
## Deploy the machine-controller
2525

26-
`kubectl apply -f examples/machine-controller.yaml`
26+
`make deploy`
2727

2828
## Creating a machineDeployment
2929
```bash

cmd/webhook/main.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
6+
"github.com/golang/glog"
7+
8+
"github.com/kubermatic/machine-controller/pkg/admission"
9+
)
10+
11+
var (
12+
admissionListenAddress string
13+
admissionTLSCertPath string
14+
admissionTLSKeyPath string
15+
)
16+
17+
func main() {
18+
flag.StringVar(&admissionListenAddress, "listen-address", ":9876", "The address on which the MutatingWebhook will listen on")
19+
flag.StringVar(&admissionTLSCertPath, "tls-cert-path", "/tmp/cert/cert.pem", "The path of the TLS cert for the MutatingWebhook")
20+
flag.StringVar(&admissionTLSKeyPath, "tls-key-path", "/tmp/cert/key.pem", "The path of the TLS key for the MutatingWebhook")
21+
flag.Parse()
22+
23+
s := admission.New(admissionListenAddress)
24+
if err := s.ListenAndServeTLS(admissionTLSCertPath, admissionTLSKeyPath); err != nil {
25+
glog.Fatalf("Failed to start server: %v", err)
26+
}
27+
defer func() {
28+
if err := s.Close(); err != nil {
29+
glog.Fatalf("Failed to shutdown server: %v", err)
30+
}
31+
}()
32+
glog.Infof("Listening on %s", admissionListenAddress)
33+
select {}
34+
}

examples/machine-controller.yaml

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,75 @@ spec:
189189
port: 8085
190190
periodSeconds: 5
191191
---
192+
apiVersion: apps/v1beta2
193+
kind: Deployment
194+
metadata:
195+
name: machine-controller-webhook
196+
namespace: kube-system
197+
spec:
198+
replicas: 1
199+
selector:
200+
matchLabels:
201+
app: machine-controller-webhook
202+
template:
203+
metadata:
204+
labels:
205+
app: machine-controller-webhook
206+
spec:
207+
containers:
208+
- image: kubermatic/machine-controller:latest
209+
imagePullPolicy: IfNotPresent
210+
name: webhook
211+
command:
212+
- /usr/local/bin/webhook
213+
- -logtostderr
214+
- -v=6
215+
- -listen-address=0.0.0.0:9876
216+
volumeMounts:
217+
- name: machine-controller-admission-cert
218+
mountPath: /tmp/cert
219+
livenessProbe:
220+
httpGet:
221+
path: /healthz
222+
port: 9876
223+
scheme: HTTPS
224+
initialDelaySeconds: 5
225+
periodSeconds: 5
226+
readinessProbe:
227+
httpGet:
228+
path: /healthz
229+
port: 9876
230+
scheme: HTTPS
231+
periodSeconds: 5
232+
volumes:
233+
- name: machine-controller-admission-cert
234+
secret:
235+
secretName: machine-controller-admission-cert
236+
---
237+
apiVersion: v1
238+
kind: Secret
239+
metadata:
240+
name: machine-controller-admission-cert
241+
namespace: kube-system
242+
data:
243+
"cert.pem": __admission_cert__
244+
"key.pem": __admission_key__
245+
---
246+
apiVersion: v1
247+
kind: Service
248+
metadata:
249+
name: machine-controller-webhook
250+
namespace: kube-system
251+
spec:
252+
ports:
253+
- name: 443-9876
254+
port: 443
255+
protocol: TCP
256+
targetPort: 9876
257+
selector:
258+
app: machine-controller-webhook
259+
type: ClusterIP
260+
---
192261
apiVersion: v1
193262
kind: ServiceAccount
194263
metadata:
@@ -404,3 +473,27 @@ subjects:
404473
- kind: ServiceAccount
405474
name: machine-controller
406475
namespace: kube-system
476+
---
477+
apiVersion: admissionregistration.k8s.io/v1beta1
478+
kind: MutatingWebhookConfiguration
479+
metadata:
480+
name: machinedeployments.machine-controller.kubermatic.io
481+
webhooks:
482+
- name: machinedeployments.machine-controller.kubermatic.io
483+
failurePolicy: Fail
484+
rules:
485+
- apiGroups:
486+
- "cluster.k8s.io"
487+
apiVersions:
488+
- v1alpha1
489+
operations:
490+
- CREATE
491+
- UPDATE
492+
resources:
493+
- machinedeployments
494+
clientConfig:
495+
service:
496+
namespace: kube-system
497+
name: machine-controller-webhook
498+
path: /machinedeployments
499+
caBundle: __admission_ca_cert__

pkg/admission/admission.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package admission
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
"reflect"
8+
"time"
9+
10+
"github.com/golang/glog"
11+
"github.com/mattbaird/jsonpatch"
12+
13+
"k8s.io/apimachinery/pkg/runtime"
14+
)
15+
16+
func New(listenAddress string) *http.Server {
17+
m := http.NewServeMux()
18+
m.HandleFunc("/machinedeployments", handleFuncFactory(mutateMachineDeployments))
19+
m.HandleFunc("/healthz", healthZHandler)
20+
return &http.Server{
21+
Addr: listenAddress,
22+
Handler: m,
23+
ReadTimeout: 5 * time.Second,
24+
WriteTimeout: 10 * time.Second,
25+
}
26+
}
27+
28+
func healthZHandler(w http.ResponseWriter, r *http.Request) {
29+
w.WriteHeader(http.StatusOK)
30+
}
31+
32+
func newJSONPatch(original, current runtime.Object) ([]jsonpatch.JsonPatchOperation, error) {
33+
originalGVK := original.GetObjectKind().GroupVersionKind()
34+
currentGVK := current.GetObjectKind().GroupVersionKind()
35+
if !reflect.DeepEqual(originalGVK, currentGVK) {
36+
return nil, fmt.Errorf("GroupVersionKind %#v is expected to match %#v", originalGVK, currentGVK)
37+
}
38+
ori, err := json.Marshal(original)
39+
if err != nil {
40+
return nil, err
41+
}
42+
glog.V(4).Infof("jsonpatch: Marshaled original: %s", string(ori))
43+
cur, err := json.Marshal(current)
44+
if err != nil {
45+
return nil, err
46+
}
47+
glog.V(4).Infof("jsonpatch: Marshaled target: %s", string(cur))
48+
return jsonpatch.CreatePatch(ori, cur)
49+
}

0 commit comments

Comments
 (0)