Like Prometheus, but for logs.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
loki/operator/internal/manifests/mutate.go

242 lines
7.3 KiB

package manifests
import (
"reflect"
"github.com/ViaQ/logerr/v2/kverrors"
"github.com/imdario/mergo"
routev1 "github.com/openshift/api/route/v1"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
rbacv1 "k8s.io/api/rbac/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// MutateFuncFor returns a mutate function based on the
// existing resource's concrete type. It supports currently
// only the following types or else panics:
// - ConfigMap
// - Service
// - Deployment
// - StatefulSet
// - ServiceMonitor
func MutateFuncFor(existing, desired client.Object, depAnnotations map[string]string) controllerutil.MutateFn {
return func() error {
existingAnnotations := existing.GetAnnotations()
if err := mergeWithOverride(&existingAnnotations, desired.GetAnnotations()); err != nil {
return err
}
existing.SetAnnotations(existingAnnotations)
existingLabels := existing.GetLabels()
if err := mergeWithOverride(&existingLabels, desired.GetLabels()); err != nil {
return err
}
existing.SetLabels(existingLabels)
if ownerRefs := desired.GetOwnerReferences(); len(ownerRefs) > 0 {
existing.SetOwnerReferences(ownerRefs)
}
switch existing.(type) {
case *corev1.ConfigMap:
cm := existing.(*corev1.ConfigMap)
wantCm := desired.(*corev1.ConfigMap)
mutateConfigMap(cm, wantCm)
case *corev1.Secret:
s := existing.(*corev1.Secret)
wantS := desired.(*corev1.Secret)
mutateSecret(s, wantS)
existingAnnotations := s.GetAnnotations()
if err := mergeWithOverride(&existingAnnotations, depAnnotations); err != nil {
return err
}
s.SetAnnotations(existingAnnotations)
case *corev1.Service:
svc := existing.(*corev1.Service)
wantSvc := desired.(*corev1.Service)
return mutateService(svc, wantSvc)
case *corev1.ServiceAccount:
sa := existing.(*corev1.ServiceAccount)
wantSa := desired.(*corev1.ServiceAccount)
mutateServiceAccount(sa, wantSa)
case *rbacv1.ClusterRole:
cr := existing.(*rbacv1.ClusterRole)
wantCr := desired.(*rbacv1.ClusterRole)
mutateClusterRole(cr, wantCr)
case *rbacv1.ClusterRoleBinding:
crb := existing.(*rbacv1.ClusterRoleBinding)
wantCrb := desired.(*rbacv1.ClusterRoleBinding)
mutateClusterRoleBinding(crb, wantCrb)
case *rbacv1.Role:
r := existing.(*rbacv1.Role)
wantR := desired.(*rbacv1.Role)
mutateRole(r, wantR)
case *rbacv1.RoleBinding:
rb := existing.(*rbacv1.RoleBinding)
wantRb := desired.(*rbacv1.RoleBinding)
mutateRoleBinding(rb, wantRb)
case *appsv1.Deployment:
dpl := existing.(*appsv1.Deployment)
wantDpl := desired.(*appsv1.Deployment)
return mutateDeployment(dpl, wantDpl)
case *appsv1.StatefulSet:
sts := existing.(*appsv1.StatefulSet)
wantSts := desired.(*appsv1.StatefulSet)
return mutateStatefulSet(sts, wantSts)
case *monitoringv1.ServiceMonitor:
svcMonitor := existing.(*monitoringv1.ServiceMonitor)
wantSvcMonitor := desired.(*monitoringv1.ServiceMonitor)
mutateServiceMonitor(svcMonitor, wantSvcMonitor)
case *networkingv1.Ingress:
ing := existing.(*networkingv1.Ingress)
wantIng := desired.(*networkingv1.Ingress)
mutateIngress(ing, wantIng)
case *routev1.Route:
rt := existing.(*routev1.Route)
wantRt := desired.(*routev1.Route)
mutateRoute(rt, wantRt)
case *monitoringv1.PrometheusRule:
pr := existing.(*monitoringv1.PrometheusRule)
wantPr := desired.(*monitoringv1.PrometheusRule)
mutatePrometheusRule(pr, wantPr)
default:
t := reflect.TypeOf(existing).String()
return kverrors.New("missing mutate implementation for resource type", "type", t)
}
return nil
}
}
func mergeWithOverride(dst, src interface{}) error {
err := mergo.Merge(dst, src, mergo.WithOverride)
if err != nil {
return kverrors.Wrap(err, "unable to mergeWithOverride", "dst", dst, "src", src)
}
return nil
}
func mutateConfigMap(existing, desired *corev1.ConfigMap) {
existing.Annotations = desired.Annotations
existing.Labels = desired.Labels
existing.BinaryData = desired.BinaryData
existing.Data = desired.Data
}
func mutateSecret(existing, desired *corev1.Secret) {
existing.Annotations = desired.Annotations
existing.Labels = desired.Labels
existing.Data = desired.Data
}
func mutateServiceAccount(existing, desired *corev1.ServiceAccount) {
existing.Annotations = desired.Annotations
existing.Labels = desired.Labels
}
func mutateClusterRole(existing, desired *rbacv1.ClusterRole) {
existing.Annotations = desired.Annotations
existing.Labels = desired.Labels
existing.Rules = desired.Rules
}
func mutateClusterRoleBinding(existing, desired *rbacv1.ClusterRoleBinding) {
existing.Annotations = desired.Annotations
existing.Labels = desired.Labels
existing.Subjects = desired.Subjects
}
func mutateRole(existing, desired *rbacv1.Role) {
existing.Annotations = desired.Annotations
existing.Labels = desired.Labels
existing.Rules = desired.Rules
}
func mutateRoleBinding(existing, desired *rbacv1.RoleBinding) {
existing.Annotations = desired.Annotations
existing.Labels = desired.Labels
existing.Subjects = desired.Subjects
}
func mutateServiceMonitor(existing, desired *monitoringv1.ServiceMonitor) {
// ServiceMonitor selector is immutable so we set this value only if
// a new object is going to be created
existing.Annotations = desired.Annotations
existing.Labels = desired.Labels
existing.Spec.Endpoints = desired.Spec.Endpoints
existing.Spec.JobLabel = desired.Spec.JobLabel
}
func mutateIngress(existing, desired *networkingv1.Ingress) {
existing.Labels = desired.Labels
existing.Annotations = desired.Annotations
existing.Spec.DefaultBackend = desired.Spec.DefaultBackend
existing.Spec.Rules = desired.Spec.Rules
existing.Spec.TLS = desired.Spec.TLS
}
func mutateRoute(existing, desired *routev1.Route) {
existing.Annotations = desired.Annotations
existing.Labels = desired.Labels
existing.Spec = desired.Spec
}
func mutatePrometheusRule(existing, desired *monitoringv1.PrometheusRule) {
existing.Annotations = desired.Annotations
existing.Labels = desired.Labels
existing.Spec = desired.Spec
}
func mutateService(existing, desired *corev1.Service) error {
existing.Spec.Ports = desired.Spec.Ports
if err := mergeWithOverride(&existing.Spec.Selector, desired.Spec.Selector); err != nil {
return err
}
return nil
}
func mutateDeployment(existing, desired *appsv1.Deployment) error {
// Deployment selector is immutable so we set this value only if
// a new object is going to be created
if existing.CreationTimestamp.IsZero() {
existing.Spec.Selector = desired.Spec.Selector
}
existing.Spec.Replicas = desired.Spec.Replicas
if err := mergeWithOverride(&existing.Spec.Template, desired.Spec.Template); err != nil {
return err
}
if err := mergeWithOverride(&existing.Spec.Strategy, desired.Spec.Strategy); err != nil {
return err
}
return nil
}
func mutateStatefulSet(existing, desired *appsv1.StatefulSet) error {
// StatefulSet selector is immutable so we set this value only if
// a new object is going to be created
if existing.CreationTimestamp.IsZero() {
existing.Spec.Selector = desired.Spec.Selector
}
existing.Spec.Replicas = desired.Spec.Replicas
if err := mergeWithOverride(&existing.Spec.Template, desired.Spec.Template); err != nil {
return err
}
return nil
}