Skip to content

Commit

Permalink
webhook based API-Coverage tool: webhook setup (knative#355)
Browse files Browse the repository at this point in the history
* webhook based API-Coverage tool: webhook setup

This changeset adds the webhook package that provides helper method to setup
the webhook required by the tool. Webhook setup is generic so that it can be
called by any repo providing its own implementation of the web-server.

* Update tools/webhook-apicoverage/webhook/webhook.go

Co-Authored-By: dushyanthsc <[email protected]>

* Update tools/webhook-apicoverage/webhook/README.md

Co-Authored-By: dushyanthsc <[email protected]>

* Update tools/webhook-apicoverage/webhook/README.md

Co-Authored-By: dushyanthsc <[email protected]>

* Update tools/webhook-apicoverage/webhook/README.md

Co-Authored-By: dushyanthsc <[email protected]>

* Update tools/webhook-apicoverage/webhook/README.md

Co-Authored-By: dushyanthsc <[email protected]>

* Update tools/webhook-apicoverage/webhook/README.md

Co-Authored-By: dushyanthsc <[email protected]>

* Update tools/webhook-apicoverage/webhook/README.md

Co-Authored-By: dushyanthsc <[email protected]>

* Update tools/webhook-apicoverage/webhook/README.md

Co-Authored-By: dushyanthsc <[email protected]>

* Update tools/webhook-apicoverage/webhook/README.md

Co-Authored-By: dushyanthsc <[email protected]>

* Update tools/webhook-apicoverage/webhook/README.md

Co-Authored-By: dushyanthsc <[email protected]>

* Update tools/webhook-apicoverage/webhook/README.md

Co-Authored-By: dushyanthsc <[email protected]>

* Update tools/webhook-apicoverage/webhook/README.md

Co-Authored-By: dushyanthsc <[email protected]>
  • Loading branch information
dushyanthsc authored and knative-prow-robot committed Jan 15, 2019
1 parent cd1bb37 commit 253146d
Show file tree
Hide file tree
Showing 423 changed files with 152,086 additions and 25 deletions.
206 changes: 181 additions & 25 deletions Gopkg.lock

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions tools/webhook-apicoverage/webhook/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Webhook

Webhook based API-Coverage tool uses [ValidatingAdmissionWebhook](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#validatingadmissionwebhook)
which is a web-server that the K8 API-Server calls into for every API-Object
update to verify if the object is valid before storing it into its datastore.
Each validation request has the json representation of the object being
created/modified, that the tool uses to capture coverage data. `webhook`
package inside this folder provides a mechanism for individual repos to
setup ValidatingAdmissionWebhook.

[APICoverageWebhook](webhook.go) type inside the package encapsulates
necessary configuration details and helper methods required to setup
the webhook. Each repo is expected to call into `SetupWebhook()`
providing following three parameters:

1. `http.Handler`: This is the http handler (that implements `ServeHTTP(
w http.ResponseWriter, r *http.Request)`) that the web server
created by APICoverageWebhook uses.
1. `rules`: This is an array of `RuleWithOperations` objects
from the `k8s.io/api/admissionregistration/v1beta1` package
that the webhook uses for validation on each API Object update.
e.g: knative-serving while calling this method would
provide rules that will handle API Objects like `Service`,
`Configuration`, `Route` and `Revision`.

1. `stop` channel: Channel to terminate webhook's web server.

`SetupWebhook()` method in its implementation creates a TLS based web server
and registers the webhook by creating a ValidatingWebhookConfiguration
object inside the K8 cluster.
182 changes: 182 additions & 0 deletions tools/webhook-apicoverage/webhook/webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/*
Copyright 2018 The Knative 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 (
"context"
"crypto/tls"
"errors"
"fmt"
"net/http"
"time"

"github.com/knative/pkg/webhook"
"github.com/knative/serving/pkg/system"
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
"go.uber.org/zap"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)

var (
// GroupVersionKind for deployment to be used to set the webhook's owner reference.
deploymentKind = extensionsv1beta1.SchemeGroupVersion.WithKind("Deployment")
)

// APICoverageWebhook encapsulates necessary configuration details for the api-coverage webhook.
type APICoverageWebhook struct {
// WebhookName is the name of the validation webhook we create to intercept API calls.
WebhookName string

// ServiceName is the name of K8 service under which the webhook runs.
ServiceName string

// DeploymentName is the deployment name for the webhook.
DeploymentName string

// Namespace is the namespace in which everything above lives.
Namespace string

// Port where the webhook is served.
Port int

// RegistrationDelay controls how long validation requests
// occurs after the webhook is started. This is used to avoid
// potential races where registration completes and k8s apiserver
// invokes the webhook before the HTTP server is started.
RegistrationDelay time.Duration

// ClientAuthType declares the policy the webhook server will follow for TLS Client Authentication.
ClientAuth tls.ClientAuthType

// CaCert is the CA Cert for the webhook server.
CaCert []byte

// FailurePolicy policy governs the webhook validation decisions.
FailurePolicy admissionregistrationv1beta1.FailurePolicyType

// Logger is the configured logger for the webhook.
Logger *zap.SugaredLogger

// KubeClient is the K8 client to the target cluster.
KubeClient kubernetes.Interface
}

func (acw *APICoverageWebhook) generateServerConfig() (*tls.Config , error) {
serverKey, serverCert, caCert, err := webhook.CreateCerts(context.Background(), acw.WebhookName, acw.Namespace)
if err != nil {
return nil, fmt.Errorf("Error creating webhook certificates: %v", err)
}

cert, err := tls.X509KeyPair(serverCert, serverKey)
if err != nil {
return nil, fmt.Errorf("Error creating X509 Key pair for webhook server: %v", err)
}

acw.CaCert = caCert
return &tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: acw.ClientAuth,
}, nil
}

func (acw *APICoverageWebhook) getWebhookServer(handler http.Handler) (*http.Server, error) {
tlsConfig, err := acw.generateServerConfig()
if err != nil {
// generateServerConfig() is expected to provided explanatory error message.
return nil, err
}

return &http.Server{
Handler: handler,
Addr: fmt.Sprintf(":%d", acw.Port),
TLSConfig: tlsConfig,
}, nil
}

func (acw *APICoverageWebhook) registerWebhook(rules []admissionregistrationv1beta1.RuleWithOperations) error {
webhook := &admissionregistrationv1beta1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: acw.WebhookName,
Namespace: system.Namespace,
},
Webhooks: []admissionregistrationv1beta1.Webhook{{
Name: acw.WebhookName,
Rules: rules,
ClientConfig: admissionregistrationv1beta1.WebhookClientConfig{
Service: &admissionregistrationv1beta1.ServiceReference{
Namespace: system.Namespace,
Name: acw.ServiceName,
},
CABundle: acw.CaCert,
},
FailurePolicy: &acw.FailurePolicy,
},
},
}

deployment, err := acw.KubeClient.ExtensionsV1beta1().Deployments(system.Namespace).Get(acw.DeploymentName, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("Error retrieving Deployment Extension object: %v", err)
}
deploymentRef := metav1.NewControllerRef(deployment, deploymentKind)
webhook.OwnerReferences = append(webhook.OwnerReferences, *deploymentRef)

_, err = acw.KubeClient.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Create(webhook)
if err != nil {
return fmt.Errorf("Error creating ValidatingWebhookConfigurations object: %v", err)
}

return nil
}

// SetupWebhook sets up the webhook with the provided http.handler, webhook-rules and stop channel.
func (acw *APICoverageWebhook) SetupWebhook(handler http.Handler, rules []admissionregistrationv1beta1.RuleWithOperations, stop <-chan struct{}) error {
server, err := acw.getWebhookServer(handler)
if err != nil {
return fmt.Errorf("Webhook server object creation failed: %v", err)
}

select {
case <-time.After(acw.RegistrationDelay):
err = acw.registerWebhook(rules)
if err != nil {
return fmt.Errorf("Webhook registration failed: %v", err)
}
acw.Logger.Info("Successfully registered webhook")
case <-stop:
return nil
}

serverBootstrapErrCh := make(chan struct{})
go func() {
if err := server.ListenAndServeTLS("", ""); err != nil {
acw.Logger.Error("ListenAndServeTLS for admission webhook returned error", zap.Error(err))
close(serverBootstrapErrCh)
return
}
acw.Logger.Info("Successfully started webhook server")
}()

select {
case <-stop:
return server.Close()
case <-serverBootstrapErrCh:
return errors.New("webhook server bootstrap failed")
}
}
8 changes: 8 additions & 0 deletions vendor/github.com/gobuffalo/envy/LICENSE.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 253146d

Please sign in to comment.