Skip to content

Commit

Permalink
Merge pull request #220 from MbolotSuse/0.3.3-bp
Browse files Browse the repository at this point in the history
Backport for v0.3.3
  • Loading branch information
MbolotSuse committed Apr 25, 2023
2 parents 6581bd8 + 035bb26 commit fe86ae5
Showing 1 changed file with 113 additions and 60 deletions.
173 changes: 113 additions & 60 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ import (
"github.com/rancher/webhook/pkg/capi"
"github.com/rancher/webhook/pkg/clients"
"github.com/rancher/webhook/pkg/health"
"github.com/rancher/wrangler/pkg/apply"
admissionregistration "github.com/rancher/wrangler/pkg/generated/controllers/admissionregistration.k8s.io/v1"
"github.com/sirupsen/logrus"
v1 "k8s.io/api/admissionregistration/v1"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/rest"
)
Expand Down Expand Up @@ -116,8 +117,8 @@ func setCertificateExpirationDays() error {

func listenAndServe(ctx context.Context, clients *clients.Clients, validators []admission.ValidatingAdmissionHandler, mutators []admission.MutatingAdmissionHandler) (rErr error) {
router := mux.NewRouter()
applyErrChecker := health.NewErrorChecker("Config Applied")
health.RegisterHealthCheckers(router, applyErrChecker)
errChecker := health.NewErrorChecker("Config Applied")
health.RegisterHealthCheckers(router, errChecker)

logrus.Debug("Creating Webhook routes")
for _, webhook := range validators {
Expand All @@ -131,9 +132,14 @@ func listenAndServe(ctx context.Context, clients *clients.Clients, validators []
logrus.Debugf("creating route: %s", path)
}

apply := clients.Apply.WithDynamicLookup()

clients.Core.Secret().OnChange(ctx, "secrets", updateWebhookConfigs(apply, applyErrChecker, validators, mutators))
handler := &secretHandler{
validators: validators,
mutators: mutators,
errChecker: errChecker,
validatingController: clients.Admission.ValidatingWebhookConfiguration(),
mutatingController: clients.Admission.MutatingWebhookConfiguration(),
}
clients.Core.Secret().OnChange(ctx, "secrets", handler.sync)

defer func() {
if rErr != nil {
Expand All @@ -160,67 +166,114 @@ func listenAndServe(ctx context.Context, clients *clients.Clients, validators []
})
}

func updateWebhookConfigs(applier apply.Apply, applyErrChecker *health.ErrorChecker, validators []admission.ValidatingAdmissionHandler, mutators []admission.MutatingAdmissionHandler) func(key string, secret *corev1.Secret) (*corev1.Secret, error) {
return func(key string, secret *corev1.Secret) (*corev1.Secret, error) {
if secret == nil || secret.Name != caName || secret.Namespace != namespace || len(secret.Data[corev1.TLSCertKey]) == 0 {
return nil, nil
}
type secretHandler struct {
validators []admission.ValidatingAdmissionHandler
mutators []admission.MutatingAdmissionHandler
errChecker *health.ErrorChecker
validatingController admissionregistration.ValidatingWebhookConfigurationClient
mutatingController admissionregistration.MutatingWebhookConfigurationClient
}

logrus.Info("Sleeping for 15 seconds then applying webhook config")
// Sleep here to make sure server is listening and all caches are primed
time.Sleep(15 * time.Second)
// sync updates the validating admission configuration whenever the TLS cert changes.
func (s *secretHandler) sync(key string, secret *corev1.Secret) (*corev1.Secret, error) {
if secret == nil || secret.Name != caName || secret.Namespace != namespace || len(secret.Data[corev1.TLSCertKey]) == 0 {
return nil, nil
}

validationClientConfig := v1.WebhookClientConfig{
Service: &v1.ServiceReference{
Namespace: namespace,
Name: serviceName,
Path: &validationPath,
Port: &clientPort,
},
CABundle: secret.Data[corev1.TLSCertKey],
logrus.Info("Sleeping for 15 seconds then applying webhook config")
// Sleep here to make sure server is listening and all caches are primed
time.Sleep(15 * time.Second)

validationClientConfig := v1.WebhookClientConfig{
Service: &v1.ServiceReference{
Namespace: namespace,
Name: serviceName,
Path: &validationPath,
Port: &clientPort,
},
CABundle: secret.Data[corev1.TLSCertKey],
}

mutationClientConfig := v1.WebhookClientConfig{
Service: &v1.ServiceReference{
Namespace: namespace,
Name: serviceName,
Path: &mutationPath,
Port: &clientPort,
},
CABundle: secret.Data[corev1.TLSCertKey],
}
if devURL, ok := os.LookupEnv("CATTLE_WEBHOOK_URL"); ok {
validationURL := devURL + validationPath
mutationURL := devURL + mutationPath
validationClientConfig = v1.WebhookClientConfig{
URL: &validationURL,
}
mutationClientConfig = v1.WebhookClientConfig{
URL: &mutationURL,
}
}
validatingWebhooks := make([]v1.ValidatingWebhook, 0, len(s.validators))
for _, webhook := range s.validators {
validatingWebhooks = append(validatingWebhooks, webhook.ValidatingWebhook(validationClientConfig)...)
}
mutatingWebhooks := make([]v1.MutatingWebhook, 0, len(s.mutators))
for _, webhook := range s.mutators {
mutatingWebhooks = append(mutatingWebhooks, webhook.MutatingWebhook(mutationClientConfig)...)
}
validatingConfig := &v1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: "rancher.cattle.io",
},
Webhooks: validatingWebhooks,
}
mutatingConfig := &v1.MutatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: "rancher.cattle.io",
},
Webhooks: mutatingWebhooks,
}
err := s.ensureWebhookConfiguration(validatingConfig, mutatingConfig)

mutationClientConfig := v1.WebhookClientConfig{
Service: &v1.ServiceReference{
Namespace: namespace,
Name: serviceName,
Path: &mutationPath,
Port: &clientPort,
},
CABundle: secret.Data[corev1.TLSCertKey],
s.errChecker.Store(err)
return secret, err

}

// ensureWebhookConfiguration creates or updates the current validating and mutating webhook configuration to have the desired webhook.
func (s *secretHandler) ensureWebhookConfiguration(validatingConfig *v1.ValidatingWebhookConfiguration, mutatingConfig *v1.MutatingWebhookConfiguration) error {

currValidating, err := s.validatingController.Get(validatingConfig.Name, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
_, err = s.validatingController.Create(validatingConfig)
if err != nil {
return fmt.Errorf("failed to create validating configuration: %w", err)
}
if devURL, ok := os.LookupEnv("CATTLE_WEBHOOK_URL"); ok {
validationURL := devURL + validationPath
mutationURL := devURL + mutationPath
validationClientConfig = v1.WebhookClientConfig{
URL: &validationURL,
}
mutationClientConfig = v1.WebhookClientConfig{
URL: &mutationURL,
}
} else if err != nil {
return fmt.Errorf("failed to get validating configuration: %w", err)
} else {
currValidating.Webhooks = validatingConfig.Webhooks
_, err = s.validatingController.Update(currValidating)
if err != nil {
return fmt.Errorf("failed to update validating configuration: %w", err)
}
validatingWebhooks := make([]v1.ValidatingWebhook, 0, len(validators))
for _, webhook := range validators {
validatingWebhooks = append(validatingWebhooks, webhook.ValidatingWebhook(validationClientConfig)...)
}

currMutation, err := s.mutatingController.Get(validatingConfig.Name, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
_, err = s.mutatingController.Create(currMutation)
if err != nil {
return fmt.Errorf("failed to create mutating configuration: %w", err)
}
mutatingWebhooks := make([]v1.MutatingWebhook, 0, len(mutators))
for _, webhook := range mutators {
mutatingWebhooks = append(mutatingWebhooks, webhook.MutatingWebhook(mutationClientConfig)...)
} else if err != nil {
return fmt.Errorf("failed to get mutating configuration: %w", err)
} else {
currMutation.Webhooks = mutatingConfig.Webhooks
_, err = s.mutatingController.Update(currMutation)
if err != nil {
return fmt.Errorf("failed to update mutating configuration: %w", err)
}
applyErr := applier.WithOwner(secret).ApplyObjects(
&v1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: "rancher.cattle.io",
},
Webhooks: validatingWebhooks,
}, &v1.MutatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: "rancher.cattle.io",
},
Webhooks: mutatingWebhooks,
})

applyErrChecker.Store(applyErr)
return secret, applyErr
}

return nil
}

0 comments on commit fe86ae5

Please sign in to comment.