forked from knative/client
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
webhook based API-Coverage tool: webhook setup (knative#355)
* 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
1 parent
cd1bb37
commit 253146d
Showing
423 changed files
with
152,086 additions
and
25 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.