Files
gitlab-cleanup-handler/kube/fetcher.go
T
2022-09-09 14:50:14 +02:00

149 lines
3.4 KiB
Go

package kube
import (
"context"
"strings"
"github.com/apex/log"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
appsv1 "k8s.io/client-go/kubernetes/typed/apps/v1"
batchv1 "k8s.io/client-go/kubernetes/typed/batch/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
type Client struct {
provider ClientProvider
}
type Options func(*Client)
func WithInClusterProvider() func(c *Client) {
return func(c *Client) {
c.provider = &DefaultProvider{provider: &InClusterProvider{}}
}
}
func WithKubeConfigProvider(kubeconfig string) func(c *Client) {
return func(c *Client) {
c.provider = &DefaultProvider{provider: &PathConfigProvider{kubecfg: kubeconfig}}
}
}
func New(opts ...Options) *Client {
c := &Client{}
for _, opt := range opts {
opt(c)
}
return c
}
func (c *Client) GetImages(ctx context.Context, logger log.Interface, namespaces []string) (map[string][]string, error) {
client, err := c.provider.Provide()
if err != nil {
return nil, err
}
collector := NewImageCollector()
for _, ns := range namespaces {
deployments, err := client.AppsV1().Deployments(ns).List(ctx, metav1.ListOptions{})
if err != nil {
return nil, err
}
for _, deployment := range deployments.Items {
for _, container := range deployment.Spec.Template.Spec.Containers {
if strings.HasPrefix(container.Image, "registry.gitlab.com") {
logger.Infof("Found image '%s' in deployment %s.%s", container.Image[20:], ns, deployment.Name)
collector.Add(container.Image)
}
}
}
cronjobs, err := client.BatchV1().CronJobs(ns).List(ctx, metav1.ListOptions{})
if err != nil {
return nil, err
}
for _, cronjob := range cronjobs.Items {
for _, container := range cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers {
if strings.HasPrefix(container.Image, "registry.gitlab.com") {
logger.Infof("Found image '%s' in cronjob %s.%s", container.Image[20:], ns, cronjob.Name)
collector.Add(container.Image)
}
}
}
}
return collector.Images(), nil
}
type APIClient interface {
AppsV1() appsv1.AppsV1Interface
BatchV1() batchv1.BatchV1Interface
}
type ClientProvider interface {
Provide() (APIClient, error)
}
type DefaultProvider struct {
provider ConfigProvider
}
func (d DefaultProvider) Provide() (APIClient, error) {
config, err := d.provider.Provide()
if err != nil {
return nil, err
}
return kubernetes.NewForConfig(config)
}
var _ ClientProvider = &DefaultProvider{}
type ConfigProvider interface {
Provide() (*rest.Config, error)
}
type InClusterProvider struct{}
func (i InClusterProvider) Provide() (*rest.Config, error) {
return rest.InClusterConfig()
}
var _ ConfigProvider = &InClusterProvider{}
type PathConfigProvider struct {
kubecfg string
}
func (k PathConfigProvider) Provide() (*rest.Config, error) {
return clientcmd.BuildConfigFromFlags("", k.kubecfg)
}
var _ ConfigProvider = &PathConfigProvider{}
type ImageCollector map[string]map[string]struct{}
func NewImageCollector() ImageCollector {
return make(map[string]map[string]struct{})
}
func (c *ImageCollector) Add(image string) {
parts := strings.Split(image[20:], ":")
if x, exists := (*c)[parts[0]]; exists {
x[parts[1]] = struct{}{}
} else {
(*c)[parts[0]] = map[string]struct{}{
parts[1]: {},
}
}
}
func (c *ImageCollector) Images() map[string][]string {
images := make(map[string][]string)
for i, x := range *c {
for v := range x {
images[i] = append(images[i], v)
}
}
return images
}