Skip to content

Commit e314c02

Browse files
committed
flesh out service quota validator; refactor & handle edge cases
1 parent f3184c1 commit e314c02

12 files changed

Lines changed: 480 additions & 202 deletions

File tree

config/samples/awsvalidator-quota.yaml

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,39 +10,33 @@ spec:
1010
serviceCode: ec2
1111
serviceQuotas:
1212
- name: "EC2-VPC Elastic IPs"
13+
buffer: 1
14+
- name: "Public AMIs"
15+
buffer: 1
16+
- region: us-east-2
17+
serviceCode: elasticfilesystem
18+
serviceQuotas:
19+
- name: "File systems per account"
20+
buffer: 5
21+
- region: us-east-2
22+
serviceCode: elasticloadbalancing
23+
serviceQuotas:
24+
- name: "Application Load Balancers per Region"
25+
buffer: 5
26+
- name: "Classic Load Balancers per Region"
27+
buffer: 5
28+
- name: "Network Load Balancers per Region"
1329
buffer: 5
14-
# - region: us-east-2
15-
# serviceCode: elb
16-
# serviceQuotas:
17-
# - name: "Application Load Balancers per Region"
18-
# buffer: 5
19-
# - region: us-east-2
20-
# serviceCode: elb
21-
# serviceQuotas:
22-
# - name: "Classic Load Balancers per Region"
23-
# buffer: 5
2430
- region: us-east-2
2531
serviceCode: vpc
2632
serviceQuotas:
2733
- name: "VPCs per Region"
2834
buffer: 2
29-
# - region: us-east-2
30-
# serviceCode: vpc
31-
# serviceQuotas:
32-
# - name: "Subnets per VPC"
33-
# buffer: 5
34-
# - region: us-east-2
35-
# serviceCode: vpc
36-
# serviceQuotas:
37-
# - name: "NAT gateways per Availability Zone"
38-
# buffer: 5
39-
# - region: us-east-2
40-
# serviceCode: vpc
41-
# serviceQuotas:
42-
# - name: "Network interfaces per Region"
43-
# buffer: 5
44-
# - region: us-east-2
45-
# serviceCode: vpc
46-
# serviceQuotas:
47-
# - name: "Internet gateways per Region"
48-
# buffer: 5
35+
- name: "Subnets per VPC"
36+
buffer: 5
37+
- name: "NAT gateways per Availability Zone"
38+
buffer: 2
39+
- name: "Network interfaces per Region"
40+
buffer: 5
41+
- name: "Internet gateways per Region"
42+
buffer: 1

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ require (
88
github.com/go-logr/logr v1.2.4
99
github.com/onsi/ginkgo/v2 v2.9.5
1010
github.com/onsi/gomega v1.27.7
11-
github.com/spectrocloud-labs/valid8or v0.0.0-20230824230819-6ac1c98bffb8
11+
github.com/spectrocloud-labs/valid8or v0.0.0-20230825153231-57f7e708100e
1212
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63
1313
k8s.io/api v0.27.2
1414
k8s.io/apimachinery v0.27.2

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR
143143
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
144144
github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
145145
github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
146-
github.com/spectrocloud-labs/valid8or v0.0.0-20230824230819-6ac1c98bffb8 h1:BawkBTwqPCRvQZ707WrM5ujt53UwxjviICVh2j0oe4A=
147-
github.com/spectrocloud-labs/valid8or v0.0.0-20230824230819-6ac1c98bffb8/go.mod h1:d2uYGVs/uB0FnyTBup6EL94SQ27bLE0gejLkr3akqvQ=
146+
github.com/spectrocloud-labs/valid8or v0.0.0-20230825153231-57f7e708100e h1:pnBEWlOAbBrmGnVybAUGbX3wBRv1jsQyLQwG0XgRhec=
147+
github.com/spectrocloud-labs/valid8or v0.0.0-20230825153231-57f7e708100e/go.mod h1:d2uYGVs/uB0FnyTBup6EL94SQ27bLE0gejLkr3akqvQ=
148148
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
149149
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
150150
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=

internal/constants/constants.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
package constants
22

3-
const ValidationRulePrefix string = "validation"
3+
const (
4+
ValidationRulePrefix string = "validation"
5+
6+
ValidationTypeIAMRolePolicy = "aws-iam-role-policy"
7+
ValidationTypeServiceQuota = "aws-service-quota"
8+
ValidationTypeTag = "aws-tag"
9+
)

internal/controller/awsvalidator_controller.go

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ import (
3737

3838
"github.com/spectrocloud-labs/valid8or-plugin-aws/api/v1alpha1"
3939
"github.com/spectrocloud-labs/valid8or-plugin-aws/internal/constants"
40-
"github.com/spectrocloud-labs/valid8or-plugin-aws/internal/iam"
41-
"github.com/spectrocloud-labs/valid8or-plugin-aws/internal/servicequota"
42-
"github.com/spectrocloud-labs/valid8or-plugin-aws/internal/tag"
4340
"github.com/spectrocloud-labs/valid8or-plugin-aws/internal/types"
41+
"github.com/spectrocloud-labs/valid8or-plugin-aws/internal/validators/iam"
42+
"github.com/spectrocloud-labs/valid8or-plugin-aws/internal/validators/servicequota"
43+
"github.com/spectrocloud-labs/valid8or-plugin-aws/internal/validators/tag"
4444
valid8orv1alpha1 "github.com/spectrocloud-labs/valid8or/api/v1alpha1"
4545
)
4646

@@ -51,12 +51,14 @@ type AwsValidatorReconciler struct {
5151
Scheme *runtime.Scheme
5252
}
5353

54+
// monotonicBool starts off false and remains true permanently if updated to true
5455
type monotonicBool struct {
5556
ok bool
5657
}
5758

59+
// Update updates the status of a monotonic bool. If the monotonic bool is already true, Update() is a noop.
5860
func (m *monotonicBool) Update(ok bool) {
59-
m.ok = !ok || m.ok
61+
m.ok = ok || m.ok
6062
}
6163

6264
//+kubebuilder:rbac:groups=validation.spectrocloud.labs,resources=awsvalidators,verbs=get;list;watch;create;update;patch;delete
@@ -127,7 +129,7 @@ func (r *AwsValidatorReconciler) Reconcile(ctx context.Context, req ctrl.Request
127129
if err != nil {
128130
r.Log.V(0).Error(err, "failed to reconcile IAM rule")
129131
}
130-
r.safeUpdateValidationResult(nn, *validationResult, failed)
132+
r.safeUpdateValidationResult(nn, validationResult, failed, err)
131133
}
132134

133135
// Service Quota rules
@@ -136,7 +138,7 @@ func (r *AwsValidatorReconciler) Reconcile(ctx context.Context, req ctrl.Request
136138
if err != nil {
137139
r.Log.V(0).Error(err, "failed to reconcile Service Quota rule")
138140
}
139-
r.safeUpdateValidationResult(nn, *validationResult, failed)
141+
r.safeUpdateValidationResult(nn, validationResult, failed, err)
140142
}
141143

142144
// Tag rules
@@ -145,7 +147,7 @@ func (r *AwsValidatorReconciler) Reconcile(ctx context.Context, req ctrl.Request
145147
if err != nil {
146148
r.Log.V(0).Error(err, "failed to reconcile Tag rule")
147149
}
148-
r.safeUpdateValidationResult(nn, *validationResult, failed)
150+
r.safeUpdateValidationResult(nn, validationResult, failed, err)
149151
}
150152

151153
r.Log.V(0).Info("Requeuing for re-validation in two minutes.", "name", req.Name, "namespace", req.Namespace)
@@ -159,18 +161,6 @@ func (r *AwsValidatorReconciler) SetupWithManager(mgr ctrl.Manager) error {
159161
Complete(r)
160162
}
161163

162-
// safeUpdateValidationResult updates the overall validation result, ensuring that the overall validation status remains failed if a single rule fails
163-
func (r *AwsValidatorReconciler) safeUpdateValidationResult(nn k8stypes.NamespacedName, validationResult types.ValidationResult, failed *monotonicBool) {
164-
didFail := validationResult.State == valid8orv1alpha1.ValidationFailed
165-
failed.Update(didFail)
166-
if failed.ok && !didFail {
167-
validationResult.State = valid8orv1alpha1.ValidationFailed
168-
}
169-
if err := r.updateValidationResult(nn, validationResult); err != nil {
170-
r.Log.V(0).Error(err, "failed to update ValidationResult")
171-
}
172-
}
173-
174164
// secretKeyAuth creates AWS credentials from a secret containing an access key id and secret access key
175165
func (r *AwsValidatorReconciler) secretKeyAuth(req ctrl.Request, validator *v1alpha1.AwsValidator) (*credentials.Credentials, *reconcile.Result) {
176166
authSecret := &corev1.Secret{}
@@ -259,6 +249,26 @@ func (r *AwsValidatorReconciler) handleNewValidationResult(nn k8stypes.Namespace
259249
return nil, nil
260250
}
261251

252+
// safeUpdateValidationResult updates the overall validation result, ensuring that the overall validation status remains failed if a single rule fails
253+
func (r *AwsValidatorReconciler) safeUpdateValidationResult(nn k8stypes.NamespacedName, validationResult *types.ValidationResult, failed *monotonicBool, err error) {
254+
if err != nil {
255+
validationResult.State = valid8orv1alpha1.ValidationFailed
256+
validationResult.Condition.Status = corev1.ConditionFalse
257+
validationResult.Condition.Message = "Validation failed with an unexpected error"
258+
validationResult.Condition.Failures = append(validationResult.Condition.Failures, err.Error())
259+
}
260+
261+
didFail := validationResult.State == valid8orv1alpha1.ValidationFailed
262+
failed.Update(didFail)
263+
if failed.ok && !didFail {
264+
validationResult.State = valid8orv1alpha1.ValidationFailed
265+
}
266+
267+
if err := r.updateValidationResult(nn, *validationResult); err != nil {
268+
r.Log.V(0).Error(err, "failed to update ValidationResult")
269+
}
270+
}
271+
262272
// updateValidationResult updates the ValidationResult for the active validation rule
263273
func (r *AwsValidatorReconciler) updateValidationResult(nn k8stypes.NamespacedName, res types.ValidationResult) error {
264274
vr := &valid8orv1alpha1.ValidationResult{}
@@ -269,9 +279,9 @@ func (r *AwsValidatorReconciler) updateValidationResult(nn k8stypes.NamespacedNa
269279

270280
idx := getConditionIndexByValidationRule(vr.Status.Conditions, res.Condition.ValidationRule)
271281
if idx == -1 {
272-
vr.Status.Conditions = append(vr.Status.Conditions, res.Condition)
282+
vr.Status.Conditions = append(vr.Status.Conditions, *res.Condition)
273283
} else {
274-
vr.Status.Conditions[idx] = res.Condition
284+
vr.Status.Conditions[idx] = *res.Condition
275285
}
276286

277287
if err := r.Status().Update(context.Background(), vr); err != nil {

internal/servicequota/servicequota_validator.go

Lines changed: 0 additions & 128 deletions
This file was deleted.

internal/types/types.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,30 @@ package types
22

33
import valid8orv1alpha1 "github.com/spectrocloud-labs/valid8or/api/v1alpha1"
44

5+
// UsageResult describes the maximum usage for an arbitrary category
6+
type UsageResult struct {
7+
Description string
8+
MaxUsage float64
9+
}
10+
11+
// UsageMap maps categories to their usage
12+
type UsageMap map[string]float64
13+
14+
// Max returns a UsageResult describing the category with the maximum usage within a UsageMap
15+
func (u UsageMap) Max() *UsageResult {
16+
var maxUsage float64
17+
var maxUsageKey string
18+
for k, v := range u {
19+
if v > maxUsage {
20+
maxUsage = v
21+
maxUsageKey = k
22+
}
23+
}
24+
return &UsageResult{Description: maxUsageKey, MaxUsage: maxUsage}
25+
}
26+
27+
// ValidationResult is the result of the execution of a validation rule by a validator
528
type ValidationResult struct {
6-
Condition valid8orv1alpha1.ValidationCondition
29+
Condition *valid8orv1alpha1.ValidationCondition
730
State valid8orv1alpha1.ValidationState
831
}

0 commit comments

Comments
 (0)