2019-11-18 19:41:37 +00:00
|
|
|
/*
|
|
|
|
Cleans up stale webhookconfigurations created by kyverno that were not cleanedup
|
|
|
|
*/
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
2020-03-17 18:05:20 +00:00
|
|
|
"fmt"
|
2019-11-18 19:41:37 +00:00
|
|
|
"os"
|
|
|
|
"sync"
|
2019-12-16 20:55:44 +00:00
|
|
|
"time"
|
2019-11-18 19:41:37 +00:00
|
|
|
|
|
|
|
"github.com/nirmata/kyverno/pkg/config"
|
|
|
|
client "github.com/nirmata/kyverno/pkg/dclient"
|
|
|
|
"github.com/nirmata/kyverno/pkg/signal"
|
2020-05-19 00:00:52 +00:00
|
|
|
"github.com/nirmata/kyverno/pkg/utils"
|
2019-11-18 19:41:37 +00:00
|
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
|
|
rest "k8s.io/client-go/rest"
|
|
|
|
clientcmd "k8s.io/client-go/tools/clientcmd"
|
2020-03-17 23:25:34 +00:00
|
|
|
"k8s.io/klog"
|
|
|
|
"k8s.io/klog/klogr"
|
|
|
|
"sigs.k8s.io/controller-runtime/pkg/log"
|
2019-11-18 19:41:37 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
kubeconfig string
|
2020-03-17 18:05:20 +00:00
|
|
|
setupLog = log.Log.WithName("setup")
|
2019-11-18 19:41:37 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
mutatingWebhookConfigKind string = "MutatingWebhookConfiguration"
|
|
|
|
validatingWebhookConfigKind string = "ValidatingWebhookConfiguration"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
2020-03-17 23:25:34 +00:00
|
|
|
klog.InitFlags(nil)
|
|
|
|
log.SetLogger(klogr.New())
|
|
|
|
// arguments
|
|
|
|
flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
|
|
|
|
if err := flag.Set("v", "2"); err != nil {
|
|
|
|
klog.Fatalf("failed to set log level: %v", err)
|
|
|
|
}
|
|
|
|
flag.Parse()
|
|
|
|
|
2019-11-18 19:41:37 +00:00
|
|
|
// os signal handler
|
|
|
|
stopCh := signal.SetupSignalHandler()
|
|
|
|
// create client config
|
|
|
|
clientConfig, err := createClientConfig(kubeconfig)
|
|
|
|
if err != nil {
|
2020-03-17 18:05:20 +00:00
|
|
|
setupLog.Error(err, "Failed to build kubeconfig")
|
|
|
|
os.Exit(1)
|
2019-11-18 19:41:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// DYNAMIC CLIENT
|
|
|
|
// - client for all registered resources
|
2020-05-28 02:51:34 +00:00
|
|
|
client, err := client.NewClient(clientConfig, 15*time.Minute, stopCh, log.Log)
|
2019-11-18 19:41:37 +00:00
|
|
|
if err != nil {
|
2020-03-17 18:05:20 +00:00
|
|
|
setupLog.Error(err, "Failed to create client")
|
|
|
|
os.Exit(1)
|
2019-11-18 19:41:37 +00:00
|
|
|
}
|
|
|
|
|
2020-02-15 02:12:28 +00:00
|
|
|
// Exit for unsupported version of kubernetes cluster
|
|
|
|
// https://github.com/nirmata/kyverno/issues/700
|
|
|
|
// - supported from v1.12.7+
|
2020-05-19 03:10:30 +00:00
|
|
|
if !utils.HigherThanKubernetesVersion(client, log.Log, 1, 12, 7) {
|
2020-05-19 00:00:52 +00:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
2020-02-15 02:12:28 +00:00
|
|
|
|
2019-11-18 19:41:37 +00:00
|
|
|
requests := []request{
|
|
|
|
// Resource
|
2020-02-06 02:44:35 +00:00
|
|
|
{validatingWebhookConfigKind, config.ValidatingWebhookConfigurationName},
|
|
|
|
{validatingWebhookConfigKind, config.ValidatingWebhookConfigurationDebugName},
|
2020-02-03 21:38:24 +00:00
|
|
|
{mutatingWebhookConfigKind, config.MutatingWebhookConfigurationName},
|
|
|
|
{mutatingWebhookConfigKind, config.MutatingWebhookConfigurationDebugName},
|
2019-11-18 19:41:37 +00:00
|
|
|
// Policy
|
2020-02-03 21:38:24 +00:00
|
|
|
{validatingWebhookConfigKind, config.PolicyValidatingWebhookConfigurationName},
|
|
|
|
{validatingWebhookConfigKind, config.PolicyValidatingWebhookConfigurationDebugName},
|
|
|
|
{mutatingWebhookConfigKind, config.PolicyMutatingWebhookConfigurationName},
|
|
|
|
{mutatingWebhookConfigKind, config.PolicyMutatingWebhookConfigurationDebugName},
|
2019-11-18 19:41:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
done := make(chan struct{})
|
|
|
|
defer close(done)
|
|
|
|
failure := false
|
|
|
|
// use pipline to pass request to cleanup resources
|
|
|
|
// generate requests
|
|
|
|
in := gen(done, stopCh, requests...)
|
|
|
|
// process requests
|
|
|
|
// processing routine count : 2
|
|
|
|
p1 := process(client, done, stopCh, in)
|
|
|
|
p2 := process(client, done, stopCh, in)
|
|
|
|
// merge results from processing routines
|
|
|
|
for err := range merge(done, stopCh, p1, p2) {
|
|
|
|
if err != nil {
|
|
|
|
failure = true
|
2020-03-17 18:05:20 +00:00
|
|
|
log.Log.Error(err, "failed to cleanup resource")
|
2019-11-18 19:41:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// if there is any failure then we fail process
|
|
|
|
if failure {
|
2020-03-17 18:05:20 +00:00
|
|
|
log.Log.Info("failed to cleanup webhook configurations")
|
2019-11-18 19:41:37 +00:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func removeWebhookIfExists(client *client.Client, kind string, name string) error {
|
2020-03-17 18:05:20 +00:00
|
|
|
logger := log.Log.WithName("removeExistingWebhook").WithValues("kind", kind, "name", name)
|
2019-11-18 19:41:37 +00:00
|
|
|
var err error
|
|
|
|
// Get resource
|
2020-08-07 04:17:33 +00:00
|
|
|
_, err = client.GetResource("", kind, "", name)
|
2019-11-18 19:41:37 +00:00
|
|
|
if errors.IsNotFound(err) {
|
2020-03-17 18:05:20 +00:00
|
|
|
logger.V(4).Info("resource not found")
|
2019-11-18 19:41:37 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if err != nil {
|
2020-03-17 18:05:20 +00:00
|
|
|
logger.Error(err, "failed to get resource")
|
2019-11-18 19:41:37 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Delete resource
|
2020-08-07 04:17:33 +00:00
|
|
|
err = client.DeleteResource("", kind, "", name, false)
|
2019-11-18 19:41:37 +00:00
|
|
|
if err != nil {
|
2020-03-17 18:05:20 +00:00
|
|
|
logger.Error(err, "failed to delete resource")
|
2019-11-18 19:41:37 +00:00
|
|
|
return err
|
|
|
|
}
|
2020-03-17 18:05:20 +00:00
|
|
|
logger.Info("removed the resource")
|
2019-11-18 19:41:37 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func createClientConfig(kubeconfig string) (*rest.Config, error) {
|
2020-03-17 18:05:20 +00:00
|
|
|
logger := log.Log
|
2019-11-18 19:41:37 +00:00
|
|
|
if kubeconfig == "" {
|
2020-03-17 18:05:20 +00:00
|
|
|
logger.Info("Using in-cluster configuration")
|
2019-11-18 19:41:37 +00:00
|
|
|
return rest.InClusterConfig()
|
|
|
|
}
|
2020-03-17 18:05:20 +00:00
|
|
|
logger.Info(fmt.Sprintf("Using configuration from '%s'", kubeconfig))
|
2019-11-18 19:41:37 +00:00
|
|
|
return clientcmd.BuildConfigFromFlags("", kubeconfig)
|
|
|
|
}
|
|
|
|
|
|
|
|
type request struct {
|
|
|
|
kind string
|
|
|
|
name string
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Processing Pipeline
|
|
|
|
-> Process Requests
|
|
|
|
Generate Requests -> Process Requests -> Merge Results
|
|
|
|
-> Process Requests
|
|
|
|
- number of processes can be controlled
|
|
|
|
- stop processing on SIGTERM OR SIGNKILL signal
|
|
|
|
- stop processing if any process fails(supported)
|
|
|
|
*/
|
|
|
|
// Generates requests to be processed
|
|
|
|
func gen(done <-chan struct{}, stopCh <-chan struct{}, requests ...request) <-chan request {
|
|
|
|
out := make(chan request)
|
|
|
|
go func() {
|
|
|
|
defer close(out)
|
|
|
|
for _, req := range requests {
|
|
|
|
select {
|
|
|
|
case out <- req:
|
|
|
|
case <-done:
|
|
|
|
println("done generate")
|
|
|
|
return
|
|
|
|
case <-stopCh:
|
|
|
|
println("shutting down generate")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
// processes the requests
|
|
|
|
func process(client *client.Client, done <-chan struct{}, stopCh <-chan struct{}, requests <-chan request) <-chan error {
|
2020-03-17 18:05:20 +00:00
|
|
|
logger := log.Log.WithName("process")
|
2019-11-18 19:41:37 +00:00
|
|
|
out := make(chan error)
|
|
|
|
go func() {
|
|
|
|
defer close(out)
|
|
|
|
for req := range requests {
|
|
|
|
select {
|
|
|
|
case out <- removeWebhookIfExists(client, req.kind, req.name):
|
|
|
|
case <-done:
|
2020-03-17 18:05:20 +00:00
|
|
|
logger.Info("done")
|
2019-11-18 19:41:37 +00:00
|
|
|
return
|
|
|
|
case <-stopCh:
|
2020-03-17 18:05:20 +00:00
|
|
|
logger.Info("shutting down")
|
2019-11-18 19:41:37 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
// waits for all processes to be complete and merges result
|
|
|
|
func merge(done <-chan struct{}, stopCh <-chan struct{}, processes ...<-chan error) <-chan error {
|
2020-03-17 18:05:20 +00:00
|
|
|
logger := log.Log.WithName("merge")
|
2019-11-18 19:41:37 +00:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
out := make(chan error)
|
|
|
|
// gets the output from each process
|
|
|
|
output := func(ch <-chan error) {
|
|
|
|
defer wg.Done()
|
|
|
|
for err := range ch {
|
|
|
|
select {
|
|
|
|
case out <- err:
|
|
|
|
case <-done:
|
2020-03-17 18:05:20 +00:00
|
|
|
logger.Info("done")
|
2019-11-18 19:41:37 +00:00
|
|
|
return
|
|
|
|
case <-stopCh:
|
2020-03-17 18:05:20 +00:00
|
|
|
logger.Info("shutting down")
|
2019-11-18 19:41:37 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wg.Add(len(processes))
|
|
|
|
for _, process := range processes {
|
|
|
|
go output(process)
|
|
|
|
}
|
|
|
|
|
|
|
|
// close when all the process goroutines are done
|
|
|
|
go func() {
|
|
|
|
wg.Wait()
|
|
|
|
close(out)
|
|
|
|
}()
|
|
|
|
return out
|
|
|
|
}
|