Skip to content

Commit 42a991e

Browse files
chore(logging): update logging for nodeclass status controllers (#955)
* update logging for nodeclass status controllers * update comments * update image logging to add existing when updating * remove cm being on nodeclass for mw * Update pkg/controllers/nodeclass/status/images.go Co-authored-by: Matthew Christopher <[email protected]> * Update pkg/controllers/nodeclass/status/kubernetesversion.go Co-authored-by: Matthew Christopher <[email protected]> * Update pkg/controllers/nodeclass/status/kubernetesversion.go Co-authored-by: Matthew Christopher <[email protected]> * Update pkg/controllers/nodeclass/status/images.go Co-authored-by: Matthew Christopher <[email protected]> * update mw checking func * update based on PR comments * add comment on reasoning for ordering being important * add copilot tests for HasChanged with minor modifications --------- Co-authored-by: Matthew Christopher <[email protected]>
1 parent 42ddc5b commit 42a991e

File tree

4 files changed

+67
-10
lines changed

4 files changed

+67
-10
lines changed

pkg/controllers/nodeclass/status/images.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@ import (
3232
"sigs.k8s.io/controller-runtime/pkg/log"
3333

3434
"github.com/awslabs/operatorpkg/reasonable"
35+
"github.com/mitchellh/hashstructure/v2"
3536
"github.com/samber/lo"
3637

3738
"github.com/Azure/karpenter-provider-azure/pkg/apis/v1beta1"
3839
"github.com/Azure/karpenter-provider-azure/pkg/providers/imagefamily"
40+
"github.com/Azure/karpenter-provider-azure/pkg/utils"
3941

4042
controllerruntime "sigs.k8s.io/controller-runtime"
4143
"sigs.k8s.io/controller-runtime/pkg/manager"
@@ -109,7 +111,6 @@ func (r *NodeImageReconciler) Register(_ context.Context, m manager.Manager) err
109111
func (r *NodeImageReconciler) Reconcile(ctx context.Context, nodeClass *v1beta1.AKSNodeClass) (reconcile.Result, error) {
110112
ctx = log.IntoContext(ctx, log.FromContext(ctx).WithName(nodeImageReconcilerName))
111113
logger := log.FromContext(ctx)
112-
logger.V(1).Info("starting reconcile")
113114

114115
nodeImages, err := r.nodeImageProvider.List(ctx, nodeClass)
115116
if err != nil {
@@ -154,14 +155,16 @@ func (r *NodeImageReconciler) Reconcile(ctx context.Context, nodeClass *v1beta1.
154155
if len(goalImages) == 0 {
155156
nodeClass.Status.Images = nil
156157
nodeClass.StatusConditions().SetFalse(v1beta1.ConditionTypeImagesReady, "ImagesNotFound", "ImageSelectors did not match any Images")
157-
logger.Info("no node images")
158+
logger.Info("no available node images")
158159
return reconcile.Result{RequeueAfter: 5 * time.Minute}, nil
159160
}
160161

162+
// We care about the ordering of the slices here, as it translates to priority during selection, so not treating them as sets
163+
if utils.HasChanged(nodeClass.Status.Images, goalImages, &hashstructure.HashOptions{SlicesAsSets: false}) {
164+
logger.WithValues("existingImages", nodeClass.Status.Images).WithValues("newImages", goalImages).Info("new available images updated for nodeclass")
165+
}
161166
nodeClass.Status.Images = goalImages
162167
nodeClass.StatusConditions().SetTrue(v1beta1.ConditionTypeImagesReady)
163-
164-
logger.V(1).Info("success")
165168
return reconcile.Result{RequeueAfter: 5 * time.Minute}, nil
166169
}
167170

@@ -178,6 +181,7 @@ func imageVersionsUnready(nodeClass *v1beta1.AKSNodeClass) bool {
178181
// range from the ConfigMap into its own helper function using channel as a parameter.
179182
// nolint: gocyclo
180183
func (r *NodeImageReconciler) isMaintenanceWindowOpen(ctx context.Context) (bool, error) {
184+
logger := log.FromContext(ctx)
181185
if r.systemNamespace == "" {
182186
// We fail open here, since the default case should be to upgrade
183187
return true, nil
@@ -191,6 +195,16 @@ func (r *NodeImageReconciler) isMaintenanceWindowOpen(ctx context.Context) (bool
191195
}
192196
return false, fmt.Errorf("error getting maintenance window configmap, %w", err)
193197
}
198+
// Monitoring the entire ConfigMap's data might catch more data changes than we care about. However, I think it makes sense to monitor
199+
// here as it does catch the entire spread of cases we care about, and will give us direct insight on the raw data.
200+
// Note: we don't need to add the nodeclass name into the monitoring here, as we actually want the entries to collide, since
201+
// maintenance windows are a cluster level concept, rather that a nodeclass level type, meaning we'd have repeat redundant info
202+
// if scoping to the nodeclass.
203+
// TODO: In the longer run, the maintenance window handling should be factored out into a sharable provider, rather than being contained
204+
// within the image controller itself.
205+
if r.cm.HasChanged("nodeclass-maintenancewindowdata", mwConfigMap.Data) {
206+
logger.WithValues("maintenanceWindowData", mwConfigMap.Data).Info("new maintenance window data discovered")
207+
}
194208
if len(mwConfigMap.Data) == 0 {
195209
// An empty configmap means there's no maintenance windows defined, and its up to us when to preform maintenance
196210
return true, nil

pkg/controllers/nodeclass/status/kubernetesversion.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,7 @@ func (r *KubernetesVersionReconciler) Register(_ context.Context, m manager.Mana
7171
// - Note: We will indirectly trigger an upgrade to latest image version as well, by resetting the Images readiness.
7272
func (r *KubernetesVersionReconciler) Reconcile(ctx context.Context, nodeClass *v1beta1.AKSNodeClass) (reconcile.Result, error) {
7373
ctx = log.IntoContext(ctx, log.FromContext(ctx).WithName(kubernetesVersionReconcilerName))
74-
logger := log.FromContext(ctx)
75-
logger.V(1).Info("starting reconcile")
74+
logger := log.FromContext(ctx).WithValues("existingKubernetesVersion", nodeClass.Status.KubernetesVersion)
7675

7776
goalK8sVersion, err := r.kubernetesVersionProvider.KubeServerVersion(ctx)
7877
if err != nil {
@@ -81,7 +80,7 @@ func (r *KubernetesVersionReconciler) Reconcile(ctx context.Context, nodeClass *
8180

8281
// Handles case 1: init, update kubernetes status to API server version found
8382
if !nodeClass.StatusConditions().Get(v1beta1.ConditionTypeKubernetesVersionReady).IsTrue() || nodeClass.Status.KubernetesVersion == "" {
84-
logger.Info(fmt.Sprintf("init kubernetes version: %s", goalK8sVersion))
83+
logger.V(1).Info(fmt.Sprintf("init kubernetes version: %s", goalK8sVersion))
8584
} else {
8685
// Check if there is an upgrade
8786
newK8sVersion, err := semver.Parse(goalK8sVersion)
@@ -94,16 +93,18 @@ func (r *KubernetesVersionReconciler) Reconcile(ctx context.Context, nodeClass *
9493
}
9594
// Handles case 2: Upgrade kubernetes version [Note: we set node image to not ready, since we upgrade node image when there is a kubernetes upgrade]
9695
if newK8sVersion.GT(currentK8sVersion) {
97-
logger.Info(fmt.Sprintf("kubernetes upgrade detected: from %s (current), to %s (discovered)", currentK8sVersion.String(), newK8sVersion.String()))
96+
logger.V(1).Info(fmt.Sprintf("kubernetes upgrade detected: from %s (current), to %s (discovered)", currentK8sVersion.String(), newK8sVersion.String()))
9897
nodeClass.StatusConditions().SetFalse(v1beta1.ConditionTypeImagesReady, "KubernetesUpgrade", "Performing kubernetes upgrade, need to get latest images")
9998
} else if newK8sVersion.LT(currentK8sVersion) {
100-
logger.Info(fmt.Sprintf("detected potential kubernetes downgrade: from %s (current), to %s (discovered)", currentK8sVersion.String(), newK8sVersion.String()))
99+
logger.Info(fmt.Sprintf("WARN: detected potential kubernetes downgrade: from %s (current), to %s (discovered)", currentK8sVersion.String(), newK8sVersion.String()))
101100
// We do not currently support downgrading, so keep the kubernetes version the same
102101
goalK8sVersion = nodeClass.Status.KubernetesVersion
103102
}
104103
}
105104
nodeClass.Status.KubernetesVersion = goalK8sVersion
106105
nodeClass.StatusConditions().SetTrue(v1beta1.ConditionTypeKubernetesVersionReady)
107-
logger.V(1).Info("successful reconcile")
106+
if r.cm.HasChanged(fmt.Sprintf("nodeclass-%s-kubernetesversion", nodeClass.Name), nodeClass.Status.KubernetesVersion) {
107+
logger.WithValues("newKubernetesVersion", nodeClass.Status.KubernetesVersion).Info("new kubernetes version updated for nodeclass")
108+
}
108109
return reconcile.Result{RequeueAfter: azurecache.KubernetesVersionTTL}, nil
109110
}

pkg/utils/utils.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"strings"
2626

2727
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
28+
"github.com/mitchellh/hashstructure/v2"
2829

2930
"github.com/Azure/karpenter-provider-azure/pkg/apis/v1beta1"
3031
"github.com/Azure/karpenter-provider-azure/pkg/consts"
@@ -178,3 +179,14 @@ func IsAKSManagedVNET(nodeResourceGroup string, subnetID string) (bool, error) {
178179
strings.EqualFold(nodeResourceGroup, id.ResourceGroupName) &&
179180
strings.EqualFold(id.SubnetName, managedSubnetName), nil
180181
}
182+
183+
// HasChanged returns if the given value has changed, given the existing and new instance
184+
//
185+
// This option is accessible in place of using a ChangeMonitor, when there's access to both
186+
// the existing and new data.
187+
func HasChanged(existing, new any, options *hashstructure.HashOptions) bool {
188+
// In the case of errors, the zero value from hashing will be compared, similar to ChangeMonitor
189+
existingHV, _ := hashstructure.Hash(existing, hashstructure.FormatV2, options)
190+
newHV, _ := hashstructure.Hash(new, hashstructure.FormatV2, options)
191+
return existingHV != newHV
192+
}

pkg/utils/utils_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"testing"
2121

2222
"github.com/Azure/karpenter-provider-azure/pkg/utils"
23+
"github.com/mitchellh/hashstructure/v2"
2324
. "github.com/onsi/gomega"
2425
)
2526

@@ -92,3 +93,32 @@ func TestIsAKSManagedVNET(t *testing.T) {
9293
})
9394
}
9495
}
96+
func TestHasChanged_SimpleCases(t *testing.T) {
97+
g := NewWithT(t)
98+
99+
// Case: Has not changed (same int)
100+
g.Expect(utils.HasChanged(42, 42, nil)).To(BeFalse())
101+
102+
// Case: Has changed (different int)
103+
g.Expect(utils.HasChanged(42, 43, nil)).To(BeTrue())
104+
105+
// Case: Has not changed (same string)
106+
g.Expect(utils.HasChanged("azure", "azure", nil)).To(BeFalse())
107+
108+
// Case: Has changed (different string)
109+
g.Expect(utils.HasChanged("azure", "cloud", nil)).To(BeTrue())
110+
}
111+
112+
func TestHasChanged_SliceOrderWithSlicesAsSets(t *testing.T) {
113+
g := NewWithT(t)
114+
115+
a := []int{1, 2, 3}
116+
b := []int{3, 2, 1}
117+
118+
// By default, order matters
119+
g.Expect(utils.HasChanged(a, b, nil)).To(BeTrue())
120+
121+
// With SlicesAsSets, order does not matter
122+
opts := &hashstructure.HashOptions{SlicesAsSets: true}
123+
g.Expect(utils.HasChanged(a, b, opts)).To(BeFalse())
124+
}

0 commit comments

Comments
 (0)