Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 154 additions & 44 deletions controllers/operandrequest/reconcile_operand.go
Original file line number Diff line number Diff line change
Expand Up @@ -1010,10 +1010,22 @@ func (r *Reconciler) createK8sResource(ctx context.Context, k8sResTemplate unstr
if k8sResConfigUnmarshalErr != nil {
klog.Errorf("failed to unmarshal k8s Resource Config: %v", k8sResConfigUnmarshalErr)
}

for k, v := range k8sResConfigDecoded {
k8sResTemplate.Object[k] = v
}

if kind == "Route" {
if host, found := k8sResConfigDecoded["spec"].(map[string]interface{})["host"].(string); found {
hostHash := util.CalculateHash(host)

if newAnnotations == nil {
newAnnotations = make(map[string]string)
}
newAnnotations["operator.ibm.com/odlm.route.hashedData"] = hostHash
} else {
klog.Warningf("spec.host not found in Route %s/%s", namespace, name)
}
}
}

r.EnsureLabel(k8sResTemplate, map[string]string{constant.OpreqLabel: "true"})
Expand All @@ -1039,59 +1051,36 @@ func (r *Reconciler) updateK8sResource(ctx context.Context, existingK8sRes unstr
apiversion := existingK8sRes.GetAPIVersion()
name := existingK8sRes.GetName()
namespace := existingK8sRes.GetNamespace()
if kind == "Job" {
existingK8sRes := unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": apiversion,
"kind": kind,
},
}

err := r.Client.Get(ctx, types.NamespacedName{
Name: name,
Namespace: namespace,
}, &existingK8sRes)

if err != nil {
return errors.Wrapf(err, "failed to get k8s resource -- Kind: %s, NamespacedName: %s/%s", kind, namespace, name)
}
if !r.CheckLabel(existingK8sRes, map[string]string{constant.OpreqLabel: "true"}) && (newLabels == nil || newLabels[constant.OpreqLabel] != "true") {
return nil
if kind == "Job" {
if err := r.updateK8sJob(ctx, existingK8sRes, k8sResConfig, newLabels, newAnnotations, ownerReferences); err != nil {
return errors.Wrap(err, "failed to update Job")
}
return nil
}

var existingHashedData string
var newHashedData string
if existingK8sRes.GetAnnotations() != nil {
existingHashedData = existingK8sRes.GetAnnotations()[constant.HashedData]
if kind == "Route" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering if we are able to wrap the update action for Job and Route into their own functions?
It would help to reduce the number of lines for the function updateK8sResource

if err := r.updateK8sRoute(ctx, existingK8sRes, k8sResConfig, newLabels, newAnnotations, ownerReferences); err != nil {
return errors.Wrap(err, "failed to update Route")
}

if k8sResConfig != nil {
hashedData := sha256.Sum256(k8sResConfig.Raw)
newHashedData = hex.EncodeToString(hashedData[:7])
}

if existingHashedData != newHashedData {
// create a new template of k8s resource
var templatek8sRes unstructured.Unstructured
templatek8sRes.SetAPIVersion(apiversion)
templatek8sRes.SetKind(kind)
templatek8sRes.SetName(name)
templatek8sRes.SetNamespace(namespace)

if newAnnotations == nil {
newAnnotations = make(map[string]string)
k8sResConfigDecoded := make(map[string]interface{})
if k8sResConfigUnmarshalErr := json.Unmarshal(k8sResConfig.Raw, &k8sResConfigDecoded); k8sResConfigUnmarshalErr != nil {
return errors.Wrap(k8sResConfigUnmarshalErr, "failed to unmarshal k8s Resource Config")
}
newAnnotations[constant.HashedData] = newHashedData

if err := r.deleteK8sResource(ctx, existingK8sRes, namespace); err != nil {
return errors.Wrap(err, "failed to update k8s resource")
}
if err := r.createK8sResource(ctx, templatek8sRes, k8sResConfig, newLabels, newAnnotations, ownerReferences); err != nil {
return errors.Wrap(err, "failed to update k8s resource")
if host, found := k8sResConfigDecoded["spec"].(map[string]interface{})["host"].(string); found {
hostHash := util.CalculateHash(host)

if newAnnotations == nil {
newAnnotations = make(map[string]string)
}
newAnnotations["operator.ibm.com/odlm.route.hashedData"] = hostHash
} else {
klog.Warningf("spec.host not found in Route %s/%s", namespace, name)
}
}

return nil
}

// Update the k8s res
Expand Down Expand Up @@ -1178,6 +1167,127 @@ func (r *Reconciler) updateK8sResource(ctx context.Context, existingK8sRes unstr
return nil
}

func (r *Reconciler) updateK8sJob(ctx context.Context, existingK8sRes unstructured.Unstructured, k8sResConfig *runtime.RawExtension, newLabels, newAnnotations map[string]string, ownerReferences *[]operatorv1alpha1.OwnerReference) error {

kind := existingK8sRes.GetKind()
apiversion := existingK8sRes.GetAPIVersion()
name := existingK8sRes.GetName()
namespace := existingK8sRes.GetNamespace()

existingRes := unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": apiversion,
"kind": kind,
},
}

err := r.Client.Get(ctx, types.NamespacedName{
Name: name,
Namespace: namespace,
}, &existingRes)

if err != nil {
return errors.Wrapf(err, "failed to get k8s resource -- Kind: %s, NamespacedName: %s/%s", kind, namespace, name)
}
if !r.CheckLabel(existingRes, map[string]string{constant.OpreqLabel: "true"}) && (newLabels == nil || newLabels[constant.OpreqLabel] != "true") {
return nil
}

var existingHashedData string
var newHashedData string
if existingRes.GetAnnotations() != nil {
existingHashedData = existingRes.GetAnnotations()[constant.HashedData]
}

if k8sResConfig != nil {
hashedData := sha256.Sum256(k8sResConfig.Raw)
newHashedData = hex.EncodeToString(hashedData[:7])
}

if existingHashedData != newHashedData {
// create a new template of k8s resource
var templatek8sRes unstructured.Unstructured
templatek8sRes.SetAPIVersion(apiversion)
templatek8sRes.SetKind(kind)
templatek8sRes.SetName(name)
templatek8sRes.SetNamespace(namespace)

if newAnnotations == nil {
newAnnotations = make(map[string]string)
}
newAnnotations[constant.HashedData] = newHashedData

if err := r.deleteK8sResource(ctx, existingRes, namespace); err != nil {
return errors.Wrap(err, "failed to update k8s resource")
}
if err := r.createK8sResource(ctx, templatek8sRes, k8sResConfig, newLabels, newAnnotations, ownerReferences); err != nil {
return errors.Wrap(err, "failed to update k8s resource")
}
}
return nil
}

// update route resource
func (r *Reconciler) updateK8sRoute(ctx context.Context, existingK8sRes unstructured.Unstructured, k8sResConfig *runtime.RawExtension, newLabels, newAnnotations map[string]string, ownerReferences *[]operatorv1alpha1.OwnerReference) error {
kind := existingK8sRes.GetKind()
apiversion := existingK8sRes.GetAPIVersion()
name := existingK8sRes.GetName()
namespace := existingK8sRes.GetNamespace()

existingRes := unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": apiversion,
"kind": kind,
},
}
err := r.Client.Get(ctx, types.NamespacedName{
Name: name,
Namespace: namespace,
}, &existingRes)

if err != nil {
return errors.Wrapf(err, "failed to get k8s resource -- Kind: %s, NamespacedName: %s/%s", kind, namespace, name)
}
if !r.CheckLabel(existingRes, map[string]string{constant.OpreqLabel: "true"}) && (newLabels == nil || newLabels[constant.OpreqLabel] != "true") {
return nil
}

existingAnnos := existingRes.GetAnnotations()
existingHostHash := existingAnnos["operator.ibm.com/odlm.route.hashedData"]

if k8sResConfig != nil {
k8sResConfigDecoded := make(map[string]interface{})
k8sResConfigUnmarshalErr := json.Unmarshal(k8sResConfig.Raw, &k8sResConfigDecoded)
if k8sResConfigUnmarshalErr != nil {
klog.Errorf("failed to unmarshal k8s Resource Config: %v", k8sResConfigUnmarshalErr)
}

// Read the host from the OperandConfig
if newHost, found := k8sResConfigDecoded["spec"].(map[string]interface{})["host"].(string); found {
newHostHash := util.CalculateHash(newHost)

// Only re-create the route if the custom host has been removed
if newHost == "" && existingHostHash != newHostHash {

// create a new template of k8s resource
var templatek8sRes unstructured.Unstructured
templatek8sRes.SetAPIVersion(apiversion)
templatek8sRes.SetKind(kind)
templatek8sRes.SetName(name)
templatek8sRes.SetNamespace(namespace)

if err := r.deleteK8sResource(ctx, existingRes, namespace); err != nil {
return errors.Wrap(err, "failed to delete Route for recreation")
}
if err := r.createK8sResource(ctx, templatek8sRes, k8sResConfig, newLabels, newAnnotations, ownerReferences); err != nil {
return errors.Wrap(err, "failed to update k8s resource")
}
}
}
}
return nil
}

func (r *Reconciler) deleteK8sResource(ctx context.Context, existingK8sRes unstructured.Unstructured, namespace string) error {

kind := existingK8sRes.GetKind()
Expand Down
10 changes: 10 additions & 0 deletions controllers/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package util

import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
"os"
"regexp"
Expand Down Expand Up @@ -152,6 +154,14 @@ func StringSliceContentEqual(a, b []string) bool {
return true
}

func CalculateHash(input string) string {
if input == "" {
return ""
}
hashedData := sha256.Sum256([]byte(input))
return hex.EncodeToString(hashedData[:7])
}

// WaitTimeout waits for the waitgroup for the specified max timeout.
// Returns true if waiting timed out.
func WaitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
Expand Down