1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-31 03:45:17 +00:00

create resource mutating webhook after verifying webhook is active

This commit is contained in:
Shuting Zhao 2019-11-25 18:07:11 -08:00
parent 8b0fb4b801
commit f506789498
7 changed files with 96 additions and 47 deletions

View file

@ -74,10 +74,18 @@ func main() {
glog.Fatalf("Error creating kubernetes client: %v\n", err) glog.Fatalf("Error creating kubernetes client: %v\n", err)
} }
// KUBERNETES RESOURCES INFORMER
// watches namespace resource
// - cache resync time: 10 seconds
kubeInformer := kubeinformers.NewSharedInformerFactoryWithOptions(
kubeClient,
10*time.Second)
// WERBHOOK REGISTRATION CLIENT // WERBHOOK REGISTRATION CLIENT
webhookRegistrationClient := webhookconfig.NewWebhookRegistrationClient( webhookRegistrationClient := webhookconfig.NewWebhookRegistrationClient(
clientConfig, clientConfig,
client, client,
kubeInformer.Admissionregistration().V1beta1().MutatingWebhookConfigurations(),
serverIP, serverIP,
int32(webhookTimeout)) int32(webhookTimeout))
@ -90,13 +98,6 @@ func main() {
pclient, pclient,
10*time.Second) 10*time.Second)
// KUBERNETES RESOURCES INFORMER
// watches namespace resource
// - cache resync time: 10 seconds
kubeInformer := kubeinformers.NewSharedInformerFactoryWithOptions(
kubeClient,
10*time.Second)
// Configuration Data // Configuration Data
// dyamically load the configuration from configMap // dyamically load the configuration from configMap
// - resource filters // - resource filters
@ -183,9 +184,9 @@ func main() {
} }
// WEBHOOK REGISTRATION // WEBHOOK REGISTRATION
// - validationwebhookconfiguration (Policy) // - mutating,validatingwebhookconfiguration (Policy)
// - mutatingwebhookconfiguration (All resources) // - verifymutatingwebhookconfiguration (Kyverno Deployment)
// webhook confgiuration is also generated dynamically in the policy controller // resource webhook confgiuration is generated dynamically in the webhook server and policy controller
// based on the policy resources created // based on the policy resources created
if err = webhookRegistrationClient.Register(); err != nil { if err = webhookRegistrationClient.Register(); err != nil {
glog.Fatalf("Failed registering Admission Webhooks: %v\n", err) glog.Fatalf("Failed registering Admission Webhooks: %v\n", err)

View file

@ -12,7 +12,11 @@ import (
) )
//MaxRetryCount defines the max deadline count //MaxRetryCount defines the max deadline count
const MaxRetryCount int = 3 const (
MaxRetryCount int = 3
DefaultDeadline time.Duration = 60 * time.Second
DefaultResync time.Duration = 60 * time.Second
)
// LastReqTime // LastReqTime
type LastReqTime struct { type LastReqTime struct {
@ -54,13 +58,13 @@ func checkIfPolicyWithMutateAndGenerateExists(pLister kyvernolister.ClusterPolic
} }
//Run runs the checker and verify the resource update //Run runs the checker and verify the resource update
func (t *LastReqTime) Run(pLister kyvernolister.ClusterPolicyLister,eventGen event.Interface, client *dclient.Client, defaultResync time.Duration, deadline time.Duration, stopCh <-chan struct{}) { func (t *LastReqTime) Run(pLister kyvernolister.ClusterPolicyLister, eventGen event.Interface, client *dclient.Client, defaultResync time.Duration, deadline time.Duration, stopCh <-chan struct{}) {
glog.V(2).Infof("starting default resync for webhook checker with resync time %d", defaultResync) glog.V(2).Infof("starting default resync for webhook checker with resync time %d", defaultResync)
maxDeadline := deadline * time.Duration(MaxRetryCount) maxDeadline := deadline * time.Duration(MaxRetryCount)
ticker := time.NewTicker(defaultResync) ticker := time.NewTicker(defaultResync)
var statuscontrol StatusInterface var statuscontrol StatusInterface
/// interface to update and increment kyverno webhook status via annotations /// interface to update and increment kyverno webhook status via annotations
statuscontrol = NewVerifyControl(client,eventGen) statuscontrol = NewVerifyControl(client, eventGen)
// send the initial update status // send the initial update status
if checkIfPolicyWithMutateAndGenerateExists(pLister) { if checkIfPolicyWithMutateAndGenerateExists(pLister) {
if err := statuscontrol.SuccessStatus(); err != nil { if err := statuscontrol.SuccessStatus(); err != nil {

View file

@ -475,7 +475,7 @@ func (pc *PolicyController) syncPolicy(key string) error {
return err return err
} }
if err := pc.createResourceMutatingWebhookConfigurationIfRequired(*policy); err != nil { if err := pc.webhookRegistrationClient.CreateResourceMutatingWebhookConfigurationIfRequired(*policy); err != nil {
glog.V(4).Infof("failed to create resource mutating webhook configurations, policies wont be applied on resources: %v", err) glog.V(4).Infof("failed to create resource mutating webhook configurations, policies wont be applied on resources: %v", err)
glog.Errorln(err) glog.Errorln(err)
} }

View file

@ -50,27 +50,6 @@ func (pc *PolicyController) removeResourceWebhookConfiguration() error {
return nil return nil
} }
func (pc *PolicyController) createResourceMutatingWebhookConfigurationIfRequired(policy kyverno.ClusterPolicy) error {
// if the policy contains mutating & validation rules and it config does not exist we create one
if policy.HasMutateOrValidate() {
// check cache
configName := pc.webhookRegistrationClient.GetResourceMutatingWebhookConfigName()
config, err := pc.mWebhookConfigLister.Get(configName)
if err != nil {
glog.V(4).Infof("failed to list mutating webhook configuration: %v", err)
return err
}
if config != nil {
// mutating webhoook configuration already exists
return nil
}
if err := pc.webhookRegistrationClient.CreateResourceMutatingWebhookConfiguration(); err != nil {
return err
}
}
return nil
}
func hasMutateOrValidatePolicies(policies []*kyverno.ClusterPolicy) bool { func hasMutateOrValidatePolicies(policies []*kyverno.ClusterPolicy) bool {
for _, policy := range policies { for _, policy := range policies {
if (*policy).HasMutateOrValidate() { if (*policy).HasMutateOrValidate() {

View file

@ -2,14 +2,20 @@ package webhookconfig
import ( import (
"errors" "errors"
"fmt"
"sync" "sync"
"time" "time"
"github.com/golang/glog" "github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/checker"
"github.com/nirmata/kyverno/pkg/config" "github.com/nirmata/kyverno/pkg/config"
client "github.com/nirmata/kyverno/pkg/dclient" client "github.com/nirmata/kyverno/pkg/dclient"
admregapi "k8s.io/api/admissionregistration/v1beta1" admregapi "k8s.io/api/admissionregistration/v1beta1"
errorsapi "k8s.io/apimachinery/pkg/api/errors" errorsapi "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/util/wait"
mconfiginformer "k8s.io/client-go/informers/admissionregistration/v1beta1"
mconfiglister "k8s.io/client-go/listers/admissionregistration/v1beta1"
rest "k8s.io/client-go/rest" rest "k8s.io/client-go/rest"
) )
@ -22,22 +28,29 @@ const (
type WebhookRegistrationClient struct { type WebhookRegistrationClient struct {
client *client.Client client *client.Client
clientConfig *rest.Config clientConfig *rest.Config
// list/get mutatingwebhookconfigurations
mWebhookConfigLister mconfiglister.MutatingWebhookConfigurationLister
// serverIP should be used if running Kyverno out of clutser // serverIP should be used if running Kyverno out of clutser
serverIP string serverIP string
timeoutSeconds int32 timeoutSeconds int32
LastReqTime *checker.LastReqTime
} }
// NewWebhookRegistrationClient creates new WebhookRegistrationClient instance // NewWebhookRegistrationClient creates new WebhookRegistrationClient instance
func NewWebhookRegistrationClient( func NewWebhookRegistrationClient(
clientConfig *rest.Config, clientConfig *rest.Config,
client *client.Client, client *client.Client,
mconfigwebhookinformer mconfiginformer.MutatingWebhookConfigurationInformer,
serverIP string, serverIP string,
webhookTimeout int32) *WebhookRegistrationClient { webhookTimeout int32) *WebhookRegistrationClient {
return &WebhookRegistrationClient{ return &WebhookRegistrationClient{
clientConfig: clientConfig, clientConfig: clientConfig,
client: client, client: client,
serverIP: serverIP, mWebhookConfigLister: mconfigwebhookinformer.Lister(),
timeoutSeconds: webhookTimeout, serverIP: serverIP,
timeoutSeconds: webhookTimeout,
LastReqTime: checker.NewLastReqTime(),
} }
} }
@ -82,10 +95,39 @@ func (wrc *WebhookRegistrationClient) RemoveWebhookConfigurations(cleanUp chan<-
close(cleanUp) close(cleanUp)
} }
// CreateResourceMutatingWebhookConfigurationIfRequired creates a Mutatingwebhookconfiguration for all resource types
// if there's no mutatingwebhookcfg for existing policy
func (wrc *WebhookRegistrationClient) CreateResourceMutatingWebhookConfigurationIfRequired(policy kyverno.ClusterPolicy) error {
// if the policy contains mutating & validation rules and it config does not exist we create one
if policy.HasMutateOrValidate() {
// check cache
configName := wrc.GetResourceMutatingWebhookConfigName()
config, err := wrc.mWebhookConfigLister.Get(configName)
if !errorsapi.IsNotFound(err) {
glog.V(4).Infof("failed to list mutating webhook configuration: %v", err)
return err
}
if config != nil {
// mutating webhoook configuration already exists
return nil
}
if err := wrc.createResourceMutatingWebhookConfiguration(); err != nil {
return err
}
}
return nil
}
//CreateResourceMutatingWebhookConfiguration create a Mutatingwebhookconfiguration resource for all resource type //CreateResourceMutatingWebhookConfiguration create a Mutatingwebhookconfiguration resource for all resource type
// used to forward request to kyverno webhooks to apply policeis // used to forward request to kyverno webhooks to apply policeis
// Mutationg webhook is be used for Mutating & Validating purpose // Mutationg webhook is be used for Mutating & Validating purpose
func (wrc *WebhookRegistrationClient) CreateResourceMutatingWebhookConfiguration() error { func (wrc *WebhookRegistrationClient) createResourceMutatingWebhookConfiguration() error {
if err := wrc.waitForWebhookSuccess(); err != nil {
return fmt.Errorf("inactive webhook: %v", err)
}
var caData []byte var caData []byte
var config *admregapi.MutatingWebhookConfiguration var config *admregapi.MutatingWebhookConfiguration
@ -283,3 +325,28 @@ func (wrc *WebhookRegistrationClient) removePolicyValidatingWebhookConfiguration
glog.V(4).Infof("succesfully deleted policy webhook configuration %s", validatingConfig) glog.V(4).Infof("succesfully deleted policy webhook configuration %s", validatingConfig)
} }
} }
// waitForWebhookSuccess checks webhook status by checking the
// LastReqTime set in webhook server, returns immediately if active
// if not, then waits for webhook to be active, times out in 60s
func (wrc *WebhookRegistrationClient) waitForWebhookSuccess() error {
// as default timeout for webhook checker is set to 60s
// resource webhook creation times out in a cycle of(60s) webhook checker
backoff := wait.Backoff{
Duration: time.Second,
Factor: 2,
Steps: 7,
}
count := 0
return wait.ExponentialBackoff(backoff, func() (bool, error) {
timeDiff := time.Since(wrc.LastReqTime.Time())
if timeDiff < checker.DefaultDeadline {
glog.Infof("Verified webhook status, creating resource mutating webhook configuration")
return true, nil
}
glog.V(4).Infof("webhook is inactive, retrying #%d", count)
count++
return false, nil
})
}

View file

@ -37,11 +37,9 @@ func (ws *WebhookServer) handlePolicyValidation(request *v1beta1.AdmissionReques
} }
if admissionResp.Allowed { if admissionResp.Allowed {
if policy.HasMutateOrValidate() { // create mutating resource mutatingwebhookconfiguration if not present
// create mutating resource mutatingwebhookconfiguration if not present if err := ws.webhookRegistrationClient.CreateResourceMutatingWebhookConfigurationIfRequired(*policy); err != nil {
if err := ws.webhookRegistrationClient.CreateResourceMutatingWebhookConfiguration(); err != nil { glog.Error("failed to created resource mutating webhook configuration, policies wont be applied on the resource")
glog.Error("failed to created resource mutating webhook configuration, policies wont be applied on the resource")
}
} }
} }
return admissionResp return admissionResp

View file

@ -110,7 +110,7 @@ func NewWebhookServer(
policyStatus: policyStatus, policyStatus: policyStatus,
configHandler: configHandler, configHandler: configHandler,
cleanUp: cleanUp, cleanUp: cleanUp,
lastReqTime: checker.NewLastReqTime(), lastReqTime: webhookRegistrationClient.LastReqTime,
pvGenerator: pvGenerator, pvGenerator: pvGenerator,
pMetaStore: pMetaStore, pMetaStore: pMetaStore,
} }
@ -263,7 +263,7 @@ func (ws *WebhookServer) RunAsync(stopCh <-chan struct{}) {
// resync: 60 seconds // resync: 60 seconds
// deadline: 60 seconds (send request) // deadline: 60 seconds (send request)
// max deadline: deadline*3 (set the deployment annotation as false) // max deadline: deadline*3 (set the deployment annotation as false)
go ws.lastReqTime.Run(ws.pLister, ws.eventGen, ws.client, 60*time.Second, 60*time.Second, stopCh) go ws.lastReqTime.Run(ws.pLister, ws.eventGen, ws.client, checker.DefaultResync, checker.DefaultDeadline, stopCh)
} }
// Stop TLS server and returns control after the server is shut down // Stop TLS server and returns control after the server is shut down