204 lines
5.4 KiB
Go
204 lines
5.4 KiB
Go
package render
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"reflect"
|
|
|
|
appsv1 "k8s.io/api/apps/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
rbacv1 "k8s.io/api/rbac/v1"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/client-go/kubernetes"
|
|
)
|
|
|
|
func ApplyAgentDaemonSets(ctx context.Context, kubeClient kubernetes.Interface, conf AgentConf) error {
|
|
objs, err := buildAgentDaemonSetObjects(conf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := applyAgentNamespace(ctx, kubeClient, buildAgentNamespace(conf)); err != nil {
|
|
return fmt.Errorf("apply namespace: %w", err)
|
|
}
|
|
|
|
for _, obj := range objs {
|
|
if err := applyAgentObject(ctx, kubeClient, obj); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func applyAgentObject(ctx context.Context, kubeClient kubernetes.Interface, obj runtime.Object) error {
|
|
switch want := obj.(type) {
|
|
case *corev1.ServiceAccount:
|
|
return applyAgentServiceAccount(ctx, kubeClient, want)
|
|
case *rbacv1.ClusterRole:
|
|
return applyAgentClusterRole(ctx, kubeClient, want)
|
|
case *rbacv1.ClusterRoleBinding:
|
|
return applyAgentClusterRoleBinding(ctx, kubeClient, want)
|
|
case *appsv1.DaemonSet:
|
|
return applyAgentDaemonSet(ctx, kubeClient, want)
|
|
default:
|
|
return fmt.Errorf("unsupported agent object type %T", obj)
|
|
}
|
|
}
|
|
|
|
func applyAgentNamespace(ctx context.Context, kubeClient kubernetes.Interface, want *corev1.Namespace) error {
|
|
existing, err := kubeClient.CoreV1().Namespaces().Get(ctx, want.Name, metav1.GetOptions{})
|
|
if apierrors.IsNotFound(err) {
|
|
_, err = kubeClient.CoreV1().Namespaces().Create(ctx, want, metav1.CreateOptions{})
|
|
return err
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
labels, changed := mergeStringMapsInto(existing.Labels, want.Labels)
|
|
if !changed {
|
|
return nil
|
|
}
|
|
|
|
existing.Labels = labels
|
|
_, err = kubeClient.CoreV1().Namespaces().Update(ctx, existing, metav1.UpdateOptions{})
|
|
return err
|
|
}
|
|
|
|
func applyAgentServiceAccount(ctx context.Context, kubeClient kubernetes.Interface, want *corev1.ServiceAccount) error {
|
|
existing, err := kubeClient.CoreV1().ServiceAccounts(want.Namespace).Get(ctx, want.Name, metav1.GetOptions{})
|
|
if apierrors.IsNotFound(err) {
|
|
_, err = kubeClient.CoreV1().ServiceAccounts(want.Namespace).Create(ctx, want, metav1.CreateOptions{})
|
|
return err
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
changed := false
|
|
if !reflect.DeepEqual(existing.Labels, want.Labels) {
|
|
existing.Labels = want.Labels
|
|
changed = true
|
|
}
|
|
|
|
if !changed {
|
|
return nil
|
|
}
|
|
|
|
_, err = kubeClient.CoreV1().ServiceAccounts(want.Namespace).Update(ctx, existing, metav1.UpdateOptions{})
|
|
return err
|
|
}
|
|
|
|
func applyAgentClusterRole(ctx context.Context, kubeClient kubernetes.Interface, want *rbacv1.ClusterRole) error {
|
|
existing, err := kubeClient.RbacV1().ClusterRoles().Get(ctx, want.Name, metav1.GetOptions{})
|
|
if apierrors.IsNotFound(err) {
|
|
_, err = kubeClient.RbacV1().ClusterRoles().Create(ctx, want, metav1.CreateOptions{})
|
|
return err
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
changed := false
|
|
if !reflect.DeepEqual(existing.Labels, want.Labels) {
|
|
existing.Labels = want.Labels
|
|
changed = true
|
|
}
|
|
if !reflect.DeepEqual(existing.Rules, want.Rules) {
|
|
existing.Rules = want.Rules
|
|
changed = true
|
|
}
|
|
|
|
if !changed {
|
|
return nil
|
|
}
|
|
|
|
_, err = kubeClient.RbacV1().ClusterRoles().Update(ctx, existing, metav1.UpdateOptions{})
|
|
return err
|
|
}
|
|
|
|
func applyAgentClusterRoleBinding(ctx context.Context, kubeClient kubernetes.Interface, want *rbacv1.ClusterRoleBinding) error {
|
|
existing, err := kubeClient.RbacV1().ClusterRoleBindings().Get(ctx, want.Name, metav1.GetOptions{})
|
|
if apierrors.IsNotFound(err) {
|
|
_, err = kubeClient.RbacV1().ClusterRoleBindings().Create(ctx, want, metav1.CreateOptions{})
|
|
return err
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// roleRef is immutable. If it differs, fail loudly instead of pretending we can patch it.
|
|
if !reflect.DeepEqual(existing.RoleRef, want.RoleRef) {
|
|
return fmt.Errorf("existing ClusterRoleBinding %q has different roleRef and must be recreated", want.Name)
|
|
}
|
|
|
|
changed := false
|
|
if !reflect.DeepEqual(existing.Labels, want.Labels) {
|
|
existing.Labels = want.Labels
|
|
changed = true
|
|
}
|
|
if !reflect.DeepEqual(existing.Subjects, want.Subjects) {
|
|
existing.Subjects = want.Subjects
|
|
changed = true
|
|
}
|
|
|
|
if !changed {
|
|
return nil
|
|
}
|
|
|
|
_, err = kubeClient.RbacV1().ClusterRoleBindings().Update(ctx, existing, metav1.UpdateOptions{})
|
|
return err
|
|
}
|
|
|
|
func applyAgentDaemonSet(ctx context.Context, kubeClient kubernetes.Interface, want *appsv1.DaemonSet) error {
|
|
existing, err := kubeClient.AppsV1().DaemonSets(want.Namespace).Get(ctx, want.Name, metav1.GetOptions{})
|
|
if apierrors.IsNotFound(err) {
|
|
_, err = kubeClient.AppsV1().DaemonSets(want.Namespace).Create(ctx, want, metav1.CreateOptions{})
|
|
return err
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
changed := false
|
|
if !reflect.DeepEqual(existing.Labels, want.Labels) {
|
|
existing.Labels = want.Labels
|
|
changed = true
|
|
}
|
|
if !reflect.DeepEqual(existing.Spec, want.Spec) {
|
|
existing.Spec = want.Spec
|
|
changed = true
|
|
}
|
|
|
|
if !changed {
|
|
return nil
|
|
}
|
|
|
|
_, err = kubeClient.AppsV1().DaemonSets(want.Namespace).Update(ctx, existing, metav1.UpdateOptions{})
|
|
return err
|
|
}
|
|
|
|
func mergeStringMapsInto(dst map[string]string, src map[string]string) (map[string]string, bool) {
|
|
if len(src) == 0 {
|
|
return dst, false
|
|
}
|
|
|
|
changed := false
|
|
if dst == nil {
|
|
dst = map[string]string{}
|
|
changed = true
|
|
}
|
|
|
|
for k, v := range src {
|
|
if dst[k] != v {
|
|
dst[k] = v
|
|
changed = true
|
|
}
|
|
}
|
|
|
|
return dst, changed
|
|
}
|