1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-15 17:51:20 +00:00
kyverno/cmd/initContainer/main.go

226 lines
5.8 KiB
Go
Raw Normal View History

/*
Cleans up stale webhookconfigurations created by kyverno that were not cleanedup
*/
package main
import (
"flag"
2020-03-17 18:05:20 +00:00
"fmt"
"os"
"sync"
"time"
"github.com/nirmata/kyverno/pkg/config"
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/signal"
"github.com/nirmata/kyverno/pkg/utils"
"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"
)
var (
kubeconfig string
2020-03-17 18:05:20 +00:00
setupLog = log.Log.WithName("setup")
)
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()
// 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)
}
// DYNAMIC CLIENT
// - client for all registered resources
client, err := client.NewClient(clientConfig, 15*time.Minute, stopCh, log.Log)
if err != nil {
2020-03-17 18:05:20 +00:00
setupLog.Error(err, "Failed to create client")
os.Exit(1)
}
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) {
os.Exit(1)
}
2020-02-15 02:12:28 +00:00
requests := []request{
// Resource
2020-02-06 02:44:35 +00:00
{validatingWebhookConfigKind, config.ValidatingWebhookConfigurationName},
{validatingWebhookConfigKind, config.ValidatingWebhookConfigurationDebugName},
{mutatingWebhookConfigKind, config.MutatingWebhookConfigurationName},
{mutatingWebhookConfigKind, config.MutatingWebhookConfigurationDebugName},
// Policy
{validatingWebhookConfigKind, config.PolicyValidatingWebhookConfigurationName},
{validatingWebhookConfigKind, config.PolicyValidatingWebhookConfigurationDebugName},
{mutatingWebhookConfigKind, config.PolicyMutatingWebhookConfigurationName},
{mutatingWebhookConfigKind, config.PolicyMutatingWebhookConfigurationDebugName},
}
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")
}
}
// 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")
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)
var err error
// Get resource
_, err = client.GetResource("", kind, "", name)
if errors.IsNotFound(err) {
2020-03-17 18:05:20 +00:00
logger.V(4).Info("resource not found")
return nil
}
if err != nil {
2020-03-17 18:05:20 +00:00
logger.Error(err, "failed to get resource")
return err
}
// Delete resource
err = client.DeleteResource("", kind, "", name, false)
if err != nil {
2020-03-17 18:05:20 +00:00
logger.Error(err, "failed to delete resource")
return err
}
2020-03-17 18:05:20 +00:00
logger.Info("removed the resource")
return nil
}
func createClientConfig(kubeconfig string) (*rest.Config, error) {
2020-03-17 18:05:20 +00:00
logger := log.Log
if kubeconfig == "" {
2020-03-17 18:05:20 +00:00
logger.Info("Using in-cluster configuration")
return rest.InClusterConfig()
}
2020-03-17 18:05:20 +00:00
logger.Info(fmt.Sprintf("Using configuration from '%s'", kubeconfig))
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")
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")
return
case <-stopCh:
2020-03-17 18:05:20 +00:00
logger.Info("shutting down")
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")
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")
return
case <-stopCh:
2020-03-17 18:05:20 +00:00
logger.Info("shutting down")
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
}