Skip to content
This repository has been archived by the owner on Oct 7, 2020. It is now read-only.

Initial implementation of validation webhook #441

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions cmd/manager/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,19 @@ import (
"fmt"
"os"

"sigs.k8s.io/controller-runtime/pkg/webhook"
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
_ "k8s.io/client-go/plugin/pkg/client/auth"

drm "github.com/openshift/cluster-network-operator/pkg/util/k8s"
"github.com/spf13/cobra"
_ "k8s.io/client-go/plugin/pkg/client/auth"
"sigs.k8s.io/controller-runtime/pkg/client/config"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/manager/signals"

"istio.io/operator/pkg/apis"
"istio.io/operator/pkg/controller"
"istio.io/operator/pkg/controller/istiocontrolplane"
iscpwebhook "istio.io/operator/pkg/webhook/istiocontrolplane"
"istio.io/pkg/ctrlz"
"istio.io/pkg/log"
)
Expand Down Expand Up @@ -132,6 +133,14 @@ func run() {
log.Fatalf("Could not add all controllers to operator manager: %v", err)
}

// setup webhooks
log.Info("setting up webhook server")
crv := mgr.GetWebhookServer()
crv.CertDir = "/tmp/k8s-webhook-server/serving-certs"
crv.Port = 8443
crv.Register("/validate-install-istio-io-v1alpha2-istiocontrolplane",
&webhook.Admission{Handler: &iscpwebhook.IscpValidator{}})

log.Info("Starting the Cmd.")

// Start the Cmd
Expand Down
26 changes: 26 additions & 0 deletions deploy/cert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
apiVersion: certmanager.k8s.io/v1alpha1
richardwxn marked this conversation as resolved.
Show resolved Hide resolved
kind: Certificate
metadata:
labels:
app: istio-operator
name: webhook-server-cert
namespace: istio-operator
spec:
commonName: istio-operator.istio-operator.svc
dnsNames:
- istio-operator.istio-operator.svc.cluster.local
issuerRef:
kind: Issuer
name: webhook-selfsigned-issuer
secretName: webhook-server-cert
---

apiVersion: certmanager.k8s.io/v1alpha1
kind: Issuer
metadata:
labels:
app: istio-operator
name: webhook-selfsigned-issuer
namespace: istio-operator
spec:
selfSigned: {}
13 changes: 13 additions & 0 deletions deploy/clusterrole.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,17 @@ rules:
- serviceaccounts
verbs:
- '*'
- apiGroups:
- batch
resources:
- jobs
verbs:
- '*'
- apiGroups:
- certmanager.k8s.io
resources:
- certificate
- issuer
verbs:
- '*'
...
2 changes: 2 additions & 0 deletions deploy/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ resources:
- service_account.yaml
- operator.yaml
- service.yaml
- cert.yaml
- webhook.yaml
...
17 changes: 15 additions & 2 deletions deploy/operator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ spec:
serviceAccountName: istio-operator
containers:
- name: istio-operator
image: gcr.io/istio-testing/operator:1.5-dev
image: richardwxn/operator:test
ports:
- containerPort: 443
name: webhook-server
protocol: TCP
command:
- istio-operator
- server
Expand All @@ -42,4 +46,13 @@ spec:
fieldPath: metadata.name
- name: OPERATOR_NAME
value: "istio-operator"
...
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true
volumes:
- name: cert
secret:
defaultMode: 420
secretName: webhook-server-cert
...
5 changes: 4 additions & 1 deletion deploy/service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ metadata:
namespace: istio-operator
labels:
name: istio-operator
name: istio-operator-metrics
name: istio-operator
spec:
ports:
- port: 443
name: webhook
targetPort: 8443
- name: http-metrics
port: 8383
targetPort: 8383
Expand Down
26 changes: 26 additions & 0 deletions deploy/webhook.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
apiVersion: admissionregistration.k8s.io/v1beta1
richardwxn marked this conversation as resolved.
Show resolved Hide resolved
kind: ValidatingWebhookConfiguration
metadata:
creationTimestamp: null
name: validating-webhook-configuration
annotations:
certmanager.k8s.io/inject-ca-from: istio-operator/webhook-server-cert
webhooks:
- clientConfig:
caBundle: Cg==
service:
name: istio-operator
namespace: istio-operator
path: /validate-install-istio-io-v1alpha2-istiocontrolplane
richardwxn marked this conversation as resolved.
Show resolved Hide resolved
failurePolicy: Fail
name: vistiocontrolplane.kb.io
rules:
- apiGroups:
- install.istio.io
apiVersions:
- v1alpha2
operations:
- CREATE
- UPDATE
resources:
- istiocontrolplanes
5 changes: 5 additions & 0 deletions pkg/validate/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ func CheckIstioControlPlaneSpec(is *v1alpha2.IstioControlPlaneSpec, checkRequire
return util.AppendErrs(errs, validate(defaultValidations, is, nil, checkRequired))
}

// CheckIstioControlPlaneSpecExcludeValues validates the IstioControlPlane spec schema only, excluding the values.yaml pass through part.
func CheckIstioControlPlaneSpecExcludeValues(is *v1alpha2.IstioControlPlaneSpec, checkRequired bool) (errs util.Errors) {
return util.AppendErrs(errs, validate(defaultValidations, is, nil, checkRequired))
}

func validate(validations map[string]ValidatorFunc, structPtr interface{}, path util.Path, checkRequired bool) (errs util.Errors) {
scope.Debugf("validate with path %s, %v (%T)", path, structPtr, structPtr)
if structPtr == nil {
Expand Down
63 changes: 63 additions & 0 deletions pkg/webhook/istiocontrolplane/iscpwebhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2019 Istio 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 istiocontrolplane

import (
"context"
"fmt"

"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"

"istio.io/operator/pkg/apis/istio/v1alpha2"
"istio.io/operator/pkg/validate"
)

// +kubebuilder:webhook:path=/validate-v1alpha2-istiocontrolplane,mutating=true,failurePolicy=fail,
// groups="install.istio.io",resources=IstioControlPlane,verbs=create;update,versions=v1alpha2,name=mistiocontrolplane.kb.io

// podAnnotator annotates Pods
type IscpValidator struct {
client client.Client
decoder *admission.Decoder
}

// iscpValidator validates created IstioControlPlane CR.
func (a *IscpValidator) Handle(ctx context.Context, req admission.Request) admission.Response {
icp := &v1alpha2.IstioControlPlane{}
err := a.decoder.Decode(req, icp)
if err != nil {
return admission.Denied(err.Error())
}
//TODO: update to full validation including values part after values schema formalized.
if errs := validate.CheckIstioControlPlaneSpecExcludeValues(icp.Spec, false); len(errs) != 0 {
fmt.Printf("proceed with validation err: %v", errs.Error())
// TODO: allow the request now until we fully done the validation logic.
return admission.Allowed("IstioControlPlane schema validated with err")
}
return admission.Allowed("IstioControlPlane schema validated")
}

// InjectClient injects the client.
func (a *IscpValidator) InjectClient(c client.Client) error {
a.client = c
return nil
}

// InjectDecoder injects the decoder.
func (a *IscpValidator) InjectDecoder(d *admission.Decoder) error {
a.decoder = d
return nil
}