From 72d7574536bc306dd9d28340a50274e77f4db38e Mon Sep 17 00:00:00 2001 From: Serge Bazanski Date: Sat, 11 Sep 2021 12:20:07 +0000 Subject: [PATCH] kartongips: implement proper diffing of aggregated ClusterRoles For a while now we've had spurious diffs against Ceph on k0 because of a ClusterRole with an aggregationRule. The way these behave is that the config object has an empty rule list, and instead populates an aggregationRule which combines other existing ClusterRoles into that ClusterRole. The control plane then populates the rule field when the object is read/acted on, which caused us to always see a diff between the configuration of that ClusterRole. This hacks together a hardcoded fix for this particular behaviour. Porting kubecfg over to SSA would probably also fix this - but that's too much work for now. Change-Id: I357c1417d4023691e5809f1af23f58f364353388 --- cluster/tools/kartongips/pkg/kubecfg/diff.go | 33 ++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/cluster/tools/kartongips/pkg/kubecfg/diff.go b/cluster/tools/kartongips/pkg/kubecfg/diff.go index f1136be3..5d32b9e7 100644 --- a/cluster/tools/kartongips/pkg/kubecfg/diff.go +++ b/cluster/tools/kartongips/pkg/kubecfg/diff.go @@ -91,6 +91,7 @@ func (c DiffCmd) Run(ctx context.Context, apiObjects []*unstructured.Unstructure liveObjObject := liveObj.Object if c.DiffStrategy == "subset" { liveObjObject = removeMapFields(obj.Object, liveObjObject) + liveObjObject = removeClusterRoleAggregatedRules(liveObjObject) } liveObjText, _ := json.MarshalIndent(liveObjObject, "", " ") @@ -211,6 +212,38 @@ func removeMapFields(config, live map[string]interface{}) map[string]interface{} return result } +// removeClusterRoleAggregatedRules clears the rules field from live +// ClusterRole objects which have an aggregationRule. This allows us to diff a +// config object (which doesn't have these rules materialized) against a live +// obejct (which does have these rules materialized) without spurious diffs. +// +// See the Aggregated ClusterRole section of the Kubernetes RBAC docuementation +// for more information: +// +// https://kubernetes.io/docs/reference/access-authn-authz/rbac/#aggregated-clusterroles +func removeClusterRoleAggregatedRules(live map[string]interface{}) map[string]interface{} { + if version, ok := live["apiVersion"].(string); !ok || version != "rbac.authorization.k8s.io/v1" { + return live + } + + if kind, ok := live["kind"].(string); !ok || kind != "ClusterRole" { + return live + } + + if _, ok := live["aggregationRule"].(map[string]interface{}); !ok { + return live + } + + // Make copy of map. + res := make(map[string]interface{}) + for k, v := range live { + res[k] = v + } + // Clear rules field. + res["rules"] = []interface{}{} + return res +} + func removeListFields(config, live []interface{}) []interface{} { // If live is longer than config, then the extra elements at the end of the // list will be returned as is so they appear in the diff.