Skip to content
This repository was archived by the owner on Apr 17, 2025. It is now read-only.

Commit f8af5aa

Browse files
authored
Merge pull request #351 from hrk091/use-gvk-to-check-enforced-type
Use GVK to validate resource type
2 parents c0da0c5 + 74f1e20 commit f8af5aa

File tree

4 files changed

+103
-17
lines changed

4 files changed

+103
-17
lines changed

internal/hncconfig/reconciler.go

+44-12
Original file line numberDiff line numberDiff line change
@@ -146,15 +146,26 @@ func (r *Reconciler) ensureEnforcedTypes(inst *api.HNCConfiguration) error {
146146
// to retry but only set conditions since the configuration may be incorrect.
147147
func (r *Reconciler) reconcileConfigTypes(inst *api.HNCConfiguration) {
148148
// Get valid settings in the spec.resources of the `config` singleton.
149+
gvkChecker := newGVKChecker(r.resourceMapper)
149150
for _, rsc := range inst.Spec.Resources {
150151
gr := schema.GroupResource{Group: rsc.Group, Resource: rsc.Resource}
151-
// If there are multiple configurations of the same type, we will follow the
152+
// Look if the resource exists in the API server.
153+
gvk, err := r.resourceMapper.NamespacedKindFor(gr)
154+
if err != nil {
155+
// Log error and write conditions, but don't early exit since the other types can still be reconciled.
156+
r.Log.Error(err, "while trying to reconcile the configuration", "type", gr, "mode", rsc.Mode)
157+
r.writeCondition(inst, api.ConditionBadTypeConfiguration, reasonForGVKError(err), err.Error())
158+
continue
159+
}
160+
161+
// If there are multiple configurations of the same GVK, we will follow the
152162
// first configuration and ignore the rest.
153-
if gvkMode, exist := r.activeGVKMode[gr]; exist {
154-
log := r.Log.WithValues("resource", gr, "appliedMode", gvkMode.mode)
163+
if firstGR, exist := r.activeGR[gvk]; exist {
164+
gvkMode := r.activeGVKMode[firstGR]
165+
log := r.Log.WithValues("resource", gr, "appliedMode", rsc.Mode)
155166
msg := ""
156167
// Set a different message if the type is enforced by HNC.
157-
if api.IsEnforcedType(rsc) {
168+
if gvkChecker.isEnforced(gvk) {
158169
msg = fmt.Sprintf("The sync mode for %q is enforced by HNC as %q and cannot be overridden", gr, api.Propagate)
159170
log.Info("The sync mode for this resource is enforced by HNC and cannot be overridden")
160171
} else {
@@ -165,14 +176,6 @@ func (r *Reconciler) reconcileConfigTypes(inst *api.HNCConfiguration) {
165176
continue
166177
}
167178

168-
// Look if the resource exists in the API server.
169-
gvk, err := r.resourceMapper.NamespacedKindFor(gr)
170-
if err != nil {
171-
// Log error and write conditions, but don't early exit since the other types can still be reconciled.
172-
r.Log.Error(err, "while trying to reconcile the configuration", "type", gr, "mode", rsc.Mode)
173-
r.writeCondition(inst, api.ConditionBadTypeConfiguration, reasonForGVKError(err), err.Error())
174-
continue
175-
}
176179
r.activeGVKMode[gr] = gvkMode{gvk, rsc.Mode}
177180
r.activeGR[gvk] = gr
178181
}
@@ -539,3 +542,32 @@ func reasonForGVKError(err error) string {
539542
}
540543
return reason
541544
}
545+
546+
// gvkChecker checks if a GVK is an enforced type.
547+
// It is needed to check duplicated types in ResourceSpec using GVK instead of GR,
548+
// since the user-given GRs might include some ambiguity.
549+
// (e.g. singular/plural, empty group is handled as corev1).
550+
type gvkChecker struct {
551+
enforcedGVKs []schema.GroupVersionKind
552+
}
553+
554+
func newGVKChecker(mapper resourceMapper) *gvkChecker {
555+
var enforcedGVKs []schema.GroupVersionKind
556+
for _, enforcedType := range api.EnforcedTypes {
557+
enforcedGVK, _ := mapper.NamespacedKindFor(schema.GroupResource{
558+
Group: enforcedType.Group,
559+
Resource: enforcedType.Resource,
560+
})
561+
enforcedGVKs = append(enforcedGVKs, enforcedGVK)
562+
}
563+
return &gvkChecker{enforcedGVKs}
564+
}
565+
566+
func (c *gvkChecker) isEnforced(gvk schema.GroupVersionKind) bool {
567+
for _, enforcedGVK := range c.enforcedGVKs {
568+
if gvk == enforcedGVK {
569+
return true
570+
}
571+
}
572+
return false
573+
}

internal/hncconfig/reconciler_test.go

+32
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,22 @@ var _ = Describe("HNCConfiguration", func() {
8989
Eventually(GetHNCConfigCondition(ctx, api.ConditionBadTypeConfiguration, api.ReasonMultipleConfigsForType)).Should(ContainSubstring(api.RoleResource))
9090
})
9191

92+
It("should ignore the `roles` configuration in the spec and set `MultipleConfigurationsForType` condition even when given in a singular form", func() {
93+
AddToHNCConfig(ctx, api.RBACGroup, "role", api.Ignore)
94+
95+
Eventually(typeSpecMode(ctx, api.RBACGroup, "role")).Should(Equal(api.Ignore))
96+
Eventually(typeStatusMode(ctx, api.RBACGroup, api.RoleResource)).Should(Equal(api.Propagate))
97+
Eventually(GetHNCConfigCondition(ctx, api.ConditionBadTypeConfiguration, api.ReasonMultipleConfigsForType)).Should(ContainSubstring("role"))
98+
})
99+
100+
It("should ignore the `roles` configuration in the spec and set `MultipleConfigurationsForType` condition even when specified without group", func() {
101+
AddToHNCConfig(ctx, "", api.RoleResource, api.Ignore)
102+
103+
Eventually(typeSpecMode(ctx, "", api.RoleResource)).Should(Equal(api.Ignore))
104+
Eventually(typeStatusMode(ctx, api.RBACGroup, api.RoleResource)).Should(Equal(api.Propagate))
105+
Eventually(GetHNCConfigCondition(ctx, api.ConditionBadTypeConfiguration, api.ReasonMultipleConfigsForType)).Should(ContainSubstring(api.RoleResource))
106+
})
107+
92108
It("should propagate RoleBindings by default", func() {
93109
SetParent(ctx, barName, fooName)
94110
// Give foo a role binding.
@@ -106,6 +122,22 @@ var _ = Describe("HNCConfiguration", func() {
106122
Eventually(GetHNCConfigCondition(ctx, api.ConditionBadTypeConfiguration, api.ReasonMultipleConfigsForType)).Should(ContainSubstring(api.RoleBindingResource))
107123
})
108124

125+
It("should ignore the `rolebindings` configuration in the spec and set `MultipleConfigurationsForType` condition even when given in a singular form", func() {
126+
AddToHNCConfig(ctx, api.RBACGroup, "rolebinding", api.Ignore)
127+
128+
Eventually(typeSpecMode(ctx, api.RBACGroup, "rolebinding")).Should(Equal(api.Ignore))
129+
Eventually(typeStatusMode(ctx, api.RBACGroup, api.RoleBindingResource)).Should(Equal(api.Propagate))
130+
Eventually(GetHNCConfigCondition(ctx, api.ConditionBadTypeConfiguration, api.ReasonMultipleConfigsForType)).Should(ContainSubstring("rolebinding"))
131+
})
132+
133+
It("should ignore the `rolebindings` configuration in the spec and set `MultipleConfigurationsForType` condition even when specified without group", func() {
134+
AddToHNCConfig(ctx, "", api.RoleBindingResource, api.Ignore)
135+
136+
Eventually(typeSpecMode(ctx, "", api.RoleBindingResource)).Should(Equal(api.Ignore))
137+
Eventually(typeStatusMode(ctx, api.RBACGroup, api.RoleBindingResource)).Should(Equal(api.Propagate))
138+
Eventually(GetHNCConfigCondition(ctx, api.ConditionBadTypeConfiguration, api.ReasonMultipleConfigsForType)).Should(ContainSubstring(api.RoleBindingResource))
139+
})
140+
109141
It("should unset ResourceNotFound condition if a bad type spec is removed", func() {
110142
// Group of ConfigMap should be ""
111143
AddToHNCConfig(ctx, "wrong", "configmaps", api.Propagate)

internal/hncconfig/validator.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,10 @@ func (v *Validator) handle(inst *api.HNCConfiguration) admission.Response {
9191

9292
func (v *Validator) validateTypes(inst *api.HNCConfiguration, ts gvkSet) admission.Response {
9393
allErrs := field.ErrorList{}
94+
gvkChecker := newGVKChecker(v.mapper)
9495
for i, r := range inst.Spec.Resources {
9596
gr := schema.GroupResource{Group: r.Group, Resource: r.Resource}
9697
fldPath := field.NewPath("spec", "resources").Index(i)
97-
// Validate the type configured is not an HNC enforced type.
98-
if api.IsEnforcedType(r) {
99-
fldErr := field.Invalid(fldPath, gr, "always uses the 'Propagate' mode and cannot be configured")
100-
allErrs = append(allErrs, fldErr)
101-
}
10298

10399
// Validate the resource exists and is namespaced. And convert GR to GVK.
104100
// We use GVK because we will need to checkForest() later to avoid source
@@ -109,6 +105,12 @@ func (v *Validator) validateTypes(inst *api.HNCConfiguration, ts gvkSet) admissi
109105
allErrs = append(allErrs, fldErr)
110106
}
111107

108+
// Validate the type configured is not an HNC enforced type.
109+
if gvkChecker.isEnforced(gvk) {
110+
fldErr := field.Invalid(fldPath, gr, "always uses the 'Propagate' mode and cannot be configured")
111+
allErrs = append(allErrs, fldErr)
112+
}
113+
112114
// Validate if the configuration of a type already exists. Each type should
113115
// only have one configuration.
114116
if _, exists := ts[gvk]; exists {

internal/hncconfig/validator_test.go

+20
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ var (
2424
gr2gvk = map[schema.GroupResource]schema.GroupVersionKind{
2525
{Group: api.RBACGroup, Resource: api.RoleResource}: {Group: api.RBACGroup, Version: "v1", Kind: api.RoleKind},
2626
{Group: api.RBACGroup, Resource: api.RoleBindingResource}: {Group: api.RBACGroup, Version: "v1", Kind: api.RoleBindingKind},
27+
{Group: api.RBACGroup, Resource: "role"}: {Group: api.RBACGroup, Version: "v1", Kind: api.RoleKind},
28+
{Group: api.RBACGroup, Resource: "rolebinding"}: {Group: api.RBACGroup, Version: "v1", Kind: api.RoleBindingKind},
29+
{Resource: api.RoleResource}: {Group: api.RBACGroup, Version: "v1", Kind: api.RoleKind},
30+
{Resource: api.RoleBindingResource}: {Group: api.RBACGroup, Version: "v1", Kind: api.RoleBindingKind},
2731
{Group: "", Resource: "secrets"}: {Group: "", Version: "v1", Kind: "Secret"},
2832
{Group: "", Resource: "resourcequotas"}: {Group: "", Version: "v1", Kind: "ResourceQuota"},
2933
}
@@ -87,6 +91,22 @@ func TestRBACTypes(t *testing.T) {
8791
},
8892
allow: false,
8993
},
94+
{
95+
name: "Configure redundant enforced resources in a singular manner",
96+
configs: []api.ResourceSpec{
97+
{Group: api.RBACGroup, Resource: "role", Mode: api.AllowPropagate},
98+
{Group: api.RBACGroup, Resource: "rolebinding", Mode: api.AllowPropagate},
99+
},
100+
allow: false,
101+
},
102+
{
103+
name: "Configure redundant enforced resources without specifying group",
104+
configs: []api.ResourceSpec{
105+
{Resource: api.RoleResource, Mode: api.AllowPropagate},
106+
{Resource: api.RoleBindingResource, Mode: api.AllowPropagate},
107+
},
108+
allow: false,
109+
},
90110
}
91111

92112
for _, tc := range tests {

0 commit comments

Comments
 (0)