notebooks/components/common/reconcilehelper/util.go

220 lines
6.2 KiB
Go

package reconcile
import (
"context"
"reflect"
"github.com/go-logr/logr"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
apierrs "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// Deployment reconciles a k8s deployment object.
func Deployment(ctx context.Context, r client.Client, deployment *appsv1.Deployment, log logr.Logger) error {
foundDeployment := &appsv1.Deployment{}
justCreated := false
if err := r.Get(ctx, types.NamespacedName{Name: deployment.Name, Namespace: deployment.Namespace}, foundDeployment); err != nil {
if apierrs.IsNotFound(err) {
log.Info("Creating Deployment", "namespace", deployment.Namespace, "name", deployment.Name)
if err := r.Create(ctx, deployment); err != nil {
log.Error(err, "unable to create deployment")
return err
}
justCreated = true
} else {
log.Error(err, "error getting deployment")
return err
}
}
if !justCreated && CopyDeploymentSetFields(deployment, foundDeployment) {
log.Info("Updating Deployment", "namespace", deployment.Namespace, "name", deployment.Name)
if err := r.Update(ctx, foundDeployment); err != nil {
log.Error(err, "unable to update deployment")
return err
}
}
return nil
}
// Service reconciles a k8s service object.
func Service(ctx context.Context, r client.Client, service *corev1.Service, log logr.Logger) error {
foundService := &corev1.Service{}
justCreated := false
if err := r.Get(ctx, types.NamespacedName{Name: service.Name, Namespace: service.Namespace}, foundService); err != nil {
if apierrs.IsNotFound(err) {
log.Info("Creating Service", "namespace", service.Namespace, "name", service.Name)
if err = r.Create(ctx, service); err != nil {
log.Error(err, "unable to create service")
return err
}
justCreated = true
} else {
log.Error(err, "error getting service")
return err
}
}
if !justCreated && CopyServiceFields(service, foundService) {
log.Info("Updating Service\n", "namespace", service.Namespace, "name", service.Name)
if err := r.Update(ctx, foundService); err != nil {
log.Error(err, "unable to update Service")
return err
}
}
return nil
}
// VirtualService reconciles an Istio virtual service object.
func VirtualService(ctx context.Context, r client.Client, virtualServiceName, namespace string, virtualservice *unstructured.Unstructured, log logr.Logger) error {
foundVirtualService := &unstructured.Unstructured{}
foundVirtualService.SetAPIVersion("networking.istio.io/v1alpha3")
foundVirtualService.SetKind("VirtualService")
justCreated := false
if err := r.Get(ctx, types.NamespacedName{Name: virtualServiceName, Namespace: namespace}, foundVirtualService); err != nil {
if apierrs.IsNotFound(err) {
log.Info("Creating virtual service", "namespace", namespace, "name", virtualServiceName)
if err := r.Create(ctx, virtualservice); err != nil {
log.Error(err, "unable to create virtual service")
return err
}
justCreated = true
} else {
log.Error(err, "error getting virtual service")
return err
}
}
if !justCreated && CopyVirtualService(virtualservice, foundVirtualService) {
log.Info("Updating virtual service", "namespace", namespace, "name", virtualServiceName)
if err := r.Update(ctx, foundVirtualService); err != nil {
log.Error(err, "unable to update virtual service")
return err
}
}
return nil
}
// Reference: https://github.com/pwittrock/kubebuilder-workshop/blob/master/pkg/util/util.go
// CopyStatefulSetFields copies the owned fields from one StatefulSet to another
// Returns true if the fields copied from don't match to.
func CopyStatefulSetFields(from, to *appsv1.StatefulSet) bool {
requireUpdate := false
for k, v := range to.Labels {
if from.Labels[k] != v {
requireUpdate = true
}
}
to.Labels = from.Labels
for k, v := range to.Annotations {
if from.Annotations[k] != v {
requireUpdate = true
}
}
to.Annotations = from.Annotations
if *from.Spec.Replicas != *to.Spec.Replicas {
*to.Spec.Replicas = *from.Spec.Replicas
requireUpdate = true
}
if !reflect.DeepEqual(to.Spec.Template.Spec, from.Spec.Template.Spec) {
requireUpdate = true
}
to.Spec.Template.Spec = from.Spec.Template.Spec
return requireUpdate
}
func CopyDeploymentSetFields(from, to *appsv1.Deployment) bool {
requireUpdate := false
for k, v := range to.Labels {
if from.Labels[k] != v {
requireUpdate = true
}
}
to.Labels = from.Labels
for k, v := range to.Annotations {
if from.Annotations[k] != v {
requireUpdate = true
}
}
to.Annotations = from.Annotations
if from.Spec.Replicas != to.Spec.Replicas {
to.Spec.Replicas = from.Spec.Replicas
requireUpdate = true
}
if !reflect.DeepEqual(to.Spec.Template.Spec, from.Spec.Template.Spec) {
requireUpdate = true
}
to.Spec.Template.Spec = from.Spec.Template.Spec
return requireUpdate
}
// CopyServiceFields copies the owned fields from one Service to another
func CopyServiceFields(from, to *corev1.Service) bool {
requireUpdate := false
for k, v := range to.Labels {
if from.Labels[k] != v {
requireUpdate = true
}
}
to.Labels = from.Labels
for k, v := range to.Annotations {
if from.Annotations[k] != v {
requireUpdate = true
}
}
to.Annotations = from.Annotations
// Don't copy the entire Spec, because we can't overwrite the clusterIp field
if !reflect.DeepEqual(to.Spec.Selector, from.Spec.Selector) {
requireUpdate = true
}
to.Spec.Selector = from.Spec.Selector
if !reflect.DeepEqual(to.Spec.Ports, from.Spec.Ports) {
requireUpdate = true
}
to.Spec.Ports = from.Spec.Ports
return requireUpdate
}
// Copy configuration related fields to another instance and returns true if there
// is a diff and thus needs to update.
func CopyVirtualService(from, to *unstructured.Unstructured) bool {
fromSpec, found, err := unstructured.NestedMap(from.Object, "spec")
if !found {
return false
}
if err != nil {
return false
}
toSpec, found, err := unstructured.NestedMap(to.Object, "spec")
if !found || err != nil {
unstructured.SetNestedMap(to.Object, fromSpec, "spec")
return true
}
requiresUpdate := !reflect.DeepEqual(fromSpec, toSpec)
if requiresUpdate {
unstructured.SetNestedMap(to.Object, fromSpec, "spec")
}
return requiresUpdate
}