diff --git a/api/v1/constructors.go b/api/v1/constructors.go index 7d28c5f1..0f0fb2cd 100644 --- a/api/v1/constructors.go +++ b/api/v1/constructors.go @@ -6,6 +6,7 @@ package v1 import ( "fmt" "regexp" + "slices" "strconv" "strings" @@ -202,6 +203,10 @@ func parseProcessorInfo(profile *HostProfileSpec, host v1info.HostInfo) error { node.Functions = append(node.Functions, data) } + slices.SortFunc(node.Functions, func(a, b ProcessorFunctionInfo) int { + return strings.Compare(a.Function, b.Function) + }) + result = append(result, node) } diff --git a/api/v1/datanetwork_types.go b/api/v1/datanetwork_types.go index 6bdd9993..77b6c0d0 100644 --- a/api/v1/datanetwork_types.go +++ b/api/v1/datanetwork_types.go @@ -128,6 +128,18 @@ func (d *DataNetwork) SetAnnotations(annotations map[string]string) { d.ObjectMeta.Annotations = annotations } +func (d *DataNetwork) GetStatusDelta() string { + return d.Status.Delta +} + +func (d *DataNetwork) SetStatusDelta(delta string) { + d.Status.Delta = delta +} + +func (d *DataNetwork) GetInsync() bool { + return d.Status.InSync +} + // +kubebuilder:object:root=true // DataNetworks defines the attributes that represent the data network level // attributes of a StarlingX system. This is a composition of the following diff --git a/api/v1/host_types.go b/api/v1/host_types.go index a9e81f30..8f0819f6 100644 --- a/api/v1/host_types.go +++ b/api/v1/host_types.go @@ -155,6 +155,18 @@ type HostStatus struct { Delta string `json:"delta"` } +func (h *Host) SetStatusDelta(delta string) { + h.Status.Delta = delta +} + +func (h *Host) GetStatusDelta() string { + return h.Status.Delta +} + +func (h *Host) GetInsync() bool { + return h.Status.InSync +} + func (h *Host) GetStrategyRequired() string { return h.Status.StrategyRequired } diff --git a/api/v1/system_types.go b/api/v1/system_types.go index 64a90f6e..b35891b5 100644 --- a/api/v1/system_types.go +++ b/api/v1/system_types.go @@ -538,6 +538,18 @@ type System struct { Status SystemStatus `json:"status,omitempty"` } +func (s *System) SetStatusDelta(delta string) { + s.Status.Delta = delta +} + +func (s *System) GetStatusDelta() string { + return s.Status.Delta +} + +func (s *System) GetInsync() bool { + return s.Status.InSync +} + // +kubebuilder:object:root=true // SystemList contains a list of System // +deepequal-gen=false diff --git a/controllers/common/common.go b/controllers/common/common.go index 8ae549d3..b464522a 100644 --- a/controllers/common/common.go +++ b/controllers/common/common.go @@ -14,7 +14,6 @@ import ( "time" "github.com/go-logr/logr" - "github.com/google/go-cmp/cmp" "github.com/gophercloud/gophercloud" perrors "github.com/pkg/errors" starlingxv1 "github.com/wind-river/cloud-platform-deployment-manager/api/v1" @@ -92,6 +91,14 @@ var ( // mechanism and no retry is necessary. RetryNever = reconcile.Result{Requeue: false} + // Properties contained on DataNetwork resouce + DataNetworkPropers = map[string]interface{}{ + "type": nil, + "description": nil, + "mtu": nil, + "vxlan": []string{"multicastgroup", "udpPortNumber", "ttl", "endpointMode"}, + } + // Properties contained on System resource SystemProperties = map[string]interface{}{ "certificates": nil, @@ -397,37 +404,6 @@ func (in *EventLogger) WarningEvent(object runtime.Object, reason string, messag in.event(object, v1.EventTypeWarning, 1, reason, messageFmt, args...) } -func GetDeltaString(spec interface{}, current interface{}, parameters map[string]interface{}) (string, error) { - - specBytes, err := json.Marshal(spec) - if err != nil { - return "", err - } - - currentBytes, err := json.Marshal(current) - if err != nil { - return "", err - } - - var specData map[string]interface{} - var currentData map[string]interface{} - - err = json.Unmarshal([]byte(specBytes), &specData) - if err != nil { - return "", err - } - - err = json.Unmarshal([]byte(currentBytes), ¤tData) - if err != nil { - return "", err - } - - diff := cmp.Diff(currentData, specData) - deltaString := collectDiffValues(diff, parameters) - deltaString = strings.TrimSuffix(deltaString, "\n") - return deltaString, nil -} - /* CollectDiffValues collects and returns the diff values from the given diff string. The function returns lines starting with '+' or '-' that represent the differences, diff --git a/controllers/common/delta.go b/controllers/common/delta.go new file mode 100644 index 00000000..ca2aadc9 --- /dev/null +++ b/controllers/common/delta.go @@ -0,0 +1,76 @@ +package common + +import ( + "context" + "encoding/json" + "fmt" + "strings" + + "github.com/go-logr/logr" + "github.com/google/go-cmp/cmp" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type instance interface { + client.Object + + SetStatusDelta(string) + GetStatusDelta() string + GetInsync() bool +} + +func GetDeltaString(spec interface{}, current interface{}, parameters map[string]interface{}) (string, error) { + + specBytes, err := json.Marshal(spec) + if err != nil { + return "", err + } + + currentBytes, err := json.Marshal(current) + if err != nil { + return "", err + } + + var specData map[string]interface{} + var currentData map[string]interface{} + + err = json.Unmarshal([]byte(specBytes), &specData) + if err != nil { + return "", err + } + + err = json.Unmarshal([]byte(currentBytes), ¤tData) + if err != nil { + return "", err + } + + diff := cmp.Diff(currentData, specData) + deltaString := collectDiffValues(diff, parameters) + deltaString = strings.TrimSuffix(deltaString, "\n") + return deltaString, nil +} + +func SetInstanceDelta( + inst instance, spec, current interface{}, + parameters map[string]interface{}, + status client.StatusWriter, log logr.Logger, +) { + kind := inst.GetObjectKind().GroupVersionKind().Kind + log.Info(fmt.Sprintf("Updating delta for kind %s", kind)) + + oldDelta := inst.GetStatusDelta() + if inst.GetInsync() { + inst.SetStatusDelta("") + } else if delta, err := GetDeltaString(spec, current, parameters); err == nil { + inst.SetStatusDelta(delta) + } else { + log.Info(fmt.Sprintf("Failed to get Delta string for kind %s: %s\n", kind, err)) + } + + if oldDelta != inst.GetStatusDelta() { + err := status.Update(context.TODO(), inst) + if err != nil { + log.Info(fmt.Sprintf("Failed to update the status for kind %s: %s", kind, err)) + } + } +} diff --git a/controllers/datanetwork_controller.go b/controllers/datanetwork_controller.go index 6f134172..2fec6908 100644 --- a/controllers/datanetwork_controller.go +++ b/controllers/datanetwork_controller.go @@ -7,7 +7,6 @@ import ( "context" "encoding/json" "fmt" - "strings" "github.com/go-logr/logr" "github.com/gophercloud/gophercloud" @@ -49,29 +48,24 @@ type DataNetworkReconciler struct { // update is needed to a data network system resource in order to reconcile // with the latest stored configuration. func dataNetworkUpdateRequired(instance *starlingxv1.DataNetwork, n *datanetworks.DataNetwork, r *DataNetworkReconciler) (opts datanetworks.DataNetworkOpts, result bool) { - var delta strings.Builder if instance.Name != n.Name { opts.Name = &instance.Name - delta.WriteString(fmt.Sprintf("\t+Name: %s\n", *opts.Name)) result = true } spec := instance.Spec if spec.Type != n.Type { opts.Type = &spec.Type - delta.WriteString(fmt.Sprintf("\t+Type: %s\n", *opts.Type)) result = true } if spec.MTU != nil && *spec.MTU != n.MTU { opts.MTU = spec.MTU - delta.WriteString(fmt.Sprintf("\t+MTU: %d\n", *opts.MTU)) result = true } if spec.Description != nil && *spec.Description != n.Description { opts.Description = spec.Description - delta.WriteString(fmt.Sprintf("\t+Description: %s\n", *opts.Description)) result = true } @@ -80,7 +74,6 @@ func dataNetworkUpdateRequired(instance *starlingxv1.DataNetwork, n *datanetwork if vxlan.EndpointMode != nil && n.Mode != nil { if *vxlan.EndpointMode != *n.Mode { opts.Mode = vxlan.EndpointMode - delta.WriteString(fmt.Sprintf("\t+Mode: %s\n", *opts.Mode)) result = true } } @@ -88,7 +81,6 @@ func dataNetworkUpdateRequired(instance *starlingxv1.DataNetwork, n *datanetwork if vxlan.UDPPortNumber != nil && n.UDPPortNumber != nil { if *vxlan.UDPPortNumber != *n.UDPPortNumber { opts.PortNumber = vxlan.UDPPortNumber - delta.WriteString(fmt.Sprintf("\t+PortNumber: %d\n", *opts.PortNumber)) result = true } } @@ -96,7 +88,6 @@ func dataNetworkUpdateRequired(instance *starlingxv1.DataNetwork, n *datanetwork if vxlan.TTL != nil && n.TTL != nil { if *vxlan.TTL != *n.TTL { opts.TTL = vxlan.TTL - delta.WriteString(fmt.Sprintf("\t+TTL: %d\n", *opts.TTL)) result = true } } @@ -104,22 +95,21 @@ func dataNetworkUpdateRequired(instance *starlingxv1.DataNetwork, n *datanetwork if vxlan.MulticastGroup != nil && n.MulticastGroup != nil { if *vxlan.MulticastGroup != *n.MulticastGroup { opts.MulticastGroup = vxlan.MulticastGroup - delta.WriteString(fmt.Sprintf("\t+MulticastGroup: %s\n", *opts.MulticastGroup)) result = true } } } - deltaString := delta.String() - if deltaString != "" { - deltaString = "\n" + strings.TrimSuffix(deltaString, "\n") - logDataNetwork.Info(fmt.Sprintf("delta configuration:%s\n", deltaString)) - } - instance.Status.Delta = deltaString - err := r.Client.Status().Update(context.TODO(), instance) + + currentSpec, err := starlingxv1.NewDataNetworkSpec(*n) if err != nil { - logDataNetwork.Info(fmt.Sprintf("failed to update status: %s\n", err)) + return opts, false } + common.SetInstanceDelta( + instance, currentSpec, spec, common.DataNetworkPropers, + r.Client.Status(), logDataNetwork, + ) + return opts, result } diff --git a/controllers/host/host_controller.go b/controllers/host/host_controller.go index 5f6306e9..3d92bd2e 100644 --- a/controllers/host/host_controller.go +++ b/controllers/host/host_controller.go @@ -1774,37 +1774,19 @@ func (r *HostReconciler) ReconcileExistingHost(client *gophercloud.ServiceClient } logHost.Info("comparing profile attributes", "host", host.ID) - inSync := r.CompareAttributes(profile, current, instance, host.Personality, system_info) + instance.Status.InSync = r.CompareAttributes(profile, current, instance, host.Personality, system_info) + common.SetInstanceDelta(instance, profile, current, common.HostProperties, r.Client.Status(), logHost) - // Continue the following steps even inSync: // strategy not finished - if inSync && (instance.Status.StrategyRequired == cloudManager.StrategyNotRequired) { + if instance.Status.InSync && (instance.Status.StrategyRequired == cloudManager.StrategyNotRequired) { logHost.V(2).Info("no changes between composite profile and current config", "host", host.ID) - instance.Status.Delta = "" return nil } logHost.Info("defaults are:", "values", defaults) - logHost.Info("final profile is:", "values", profile) - logHost.Info("current config is:", "values", current) - deltaString, err := common.GetDeltaString(profile, current, common.HostProperties) - if err != nil { - logHost.Info(fmt.Sprintf("failed to get Delta status: %s\n", err)) - } - - if deltaString != "" { - logHost.V(2).Info(fmt.Sprintf("delta configuration:%s\n", deltaString)) - instance.Status.Delta = deltaString - - err = r.Client.Status().Update(context.TODO(), instance) - if err != nil { - logHost.Info(fmt.Sprintf("failed to update status: %s", err)) - } - } - if instance.Status.Reconciled && r.StopAfterInSync() && instance.Status.StrategyRequired != cloudManager.StrategyLockRequired { diff --git a/controllers/system/system_controller.go b/controllers/system/system_controller.go index 8d5ba804..c77a0773 100644 --- a/controllers/system/system_controller.go +++ b/controllers/system/system_controller.go @@ -1125,29 +1125,8 @@ func (r *SystemReconciler) ReconcileRequired(instance *starlingxv1.System, spec current.Certificates = &res } - if spec.DeepEqual(current) { - logSystem.V(2).Info("no changes between spec and current configuration") - instance.Status.Delta = "" - return nil, false - } - - logSystem.Info("spec is:", "values", spec) - - logSystem.Info("current is:", "values", current) - - deltaString, err := common.GetDeltaString(current, spec, common.SystemProperties) - if err != nil { - logSystem.Info(fmt.Sprintf("failed to get Delta status: %s\n", err)) - } - - if deltaString != "" { - logSystem.Info(fmt.Sprintf("delta configuration:%s\n", deltaString)) - instance.Status.Delta = deltaString - err = r.Client.Status().Update(context.TODO(), instance) - if err != nil { - logSystem.Info(fmt.Sprintf("failed to update status: %s\n", err)) - } - } + instance.Status.InSync = spec.DeepEqual(current) + common.SetInstanceDelta(instance, current, spec, common.SystemProperties, r.Client.Status(), logSystem) if instance.Status.Reconciled && r.StopAfterInSync() { // Do not process any further changes once we have reached a @@ -1497,7 +1476,7 @@ func (r *SystemReconciler) ReconcileResource(client *gophercloud.ServiceClient, } if r.statusUpdateRequired(instance, systemInfo, inSync) { - logSystem.Info("updating status for system", "status", instance.Status) + logSystem.V(2).Info("updating status for system", "status", instance.Status) err3 := r.Client.Status().Update(context.TODO(), instance) if err3 != nil {