-
Notifications
You must be signed in to change notification settings - Fork 3
/
webhook.go
156 lines (130 loc) · 4.68 KB
/
webhook.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package postgresqlcontroller
import (
"context"
"fmt"
exoscalesdk "github.com/exoscale/egoscale/v2"
exoscalev1 "github.com/vshn/provider-exoscale/apis/exoscale/v1"
"github.com/vshn/provider-exoscale/operator/pipelineutil"
"github.com/vshn/provider-exoscale/operator/webhook"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/runtime"
)
const serviceType = "pg"
// Validator validates admission requests.
type Validator struct {
log logr.Logger
kube client.Client
}
// ValidateCreate implements admission.CustomValidator.
func (v *Validator) ValidateCreate(ctx context.Context, obj runtime.Object) error {
instance, ok := obj.(*exoscalev1.PostgreSQL)
if !ok {
return fmt.Errorf("invalid managed resource type %T for postgres webhook", obj)
}
v.log.V(1).Info("Validate create")
availableVersions, err := v.getAvailableVersions(ctx, obj)
if err != nil {
return err
}
err = v.validateVersion(ctx, obj, *availableVersions)
if err != nil {
return err
}
return v.validateSpec(instance)
}
func (v *Validator) getAvailableVersions(ctx context.Context, obj runtime.Object) (*[]string, error) {
instance := obj.(*exoscalev1.PostgreSQL)
v.log.V(1).Info("get postgres available versions")
exo, err := pipelineutil.OpenExoscaleClient(ctx, v.kube, instance.GetProviderConfigName(), exoscalesdk.ClientOptWithAPIEndpoint(fmt.Sprintf("https://api-%s.exoscale.com", instance.Spec.ForProvider.Zone)))
if err != nil {
return nil, fmt.Errorf("open exoscale client failed: %w", err)
}
resp, err := exo.Exoscale.GetDbaasServiceTypeWithResponse(ctx, serviceType)
if err != nil {
return nil, fmt.Errorf("get DBaaS service type failed: %w", err)
}
v.log.V(1).Info("DBaaS service type", "body", string(resp.Body))
serviceType := *resp.JSON200
if serviceType.AvailableVersions == nil {
return nil, fmt.Errorf("postgres available versions not found")
}
return serviceType.AvailableVersions, nil
}
func (v *Validator) validateVersion(ctx context.Context, obj runtime.Object, availableVersions []string) error {
instance := obj.(*exoscalev1.PostgreSQL)
v.log.V(1).Info("validate version")
return webhook.ValidateVersions(instance.Spec.ForProvider.Version, availableVersions)
}
// ValidateUpdate implements admission.CustomValidator.
func (v *Validator) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) error {
newInstance, ok := newObj.(*exoscalev1.PostgreSQL)
if !ok {
return fmt.Errorf("invalid managed resource type %T for postgres webhook", newObj)
}
oldInstance, ok := oldObj.(*exoscalev1.PostgreSQL)
if !ok {
return fmt.Errorf("invalid managed resource type %T for postgres webhook", oldObj)
}
v.log.V(1).Info("Validate update")
err := v.validateSpec(newInstance)
if err != nil {
return err
}
return validateImmutable(*oldInstance, *newInstance)
}
// ValidateDelete implements admission.CustomValidator.
func (v *Validator) ValidateDelete(_ context.Context, obj runtime.Object) error {
v.log.V(1).Info("Validate delete (noop)")
return nil
}
func (v *Validator) validateSpec(obj *exoscalev1.PostgreSQL) error {
for _, validatorFn := range []func(exoscalev1.PostgreSQLParameters) error{
validateIpFilter,
validateMaintenanceSchedule,
validatePGSettings,
} {
if err := validatorFn(obj.Spec.ForProvider); err != nil {
return err
}
}
return nil
}
func validateIpFilter(obj exoscalev1.PostgreSQLParameters) error {
if len(obj.IPFilter) == 0 {
return fmt.Errorf("IP filter cannot be empty")
}
return nil
}
func validateMaintenanceSchedule(obj exoscalev1.PostgreSQLParameters) error {
if _, _, _, err := obj.Maintenance.TimeOfDay.Parse(); err != nil {
return err
}
return nil
}
func validatePGSettings(obj exoscalev1.PostgreSQLParameters) error {
return webhook.ValidateRawExtension(obj.PGSettings)
}
func validateImmutable(oldInst, newInst exoscalev1.PostgreSQL) error {
err := compareZone(oldInst.Spec.ForProvider, newInst.Spec.ForProvider)
if err != nil {
return err
}
return compareVersion(oldInst, newInst)
}
func compareZone(oldParams, newParams exoscalev1.PostgreSQLParameters) error {
if oldParams.Zone != newParams.Zone {
return fmt.Errorf("field is immutable: %s (old), %s (changed)", oldParams.Zone, newParams.Zone)
}
return nil
}
func compareVersion(oldInst, newInst exoscalev1.PostgreSQL) error {
if oldInst.Spec.ForProvider.Version == newInst.Spec.ForProvider.Version {
return nil
}
if oldInst.Spec.ForProvider.Version == "" {
// Fall back to reported version if no version was set before
oldInst.Spec.ForProvider.Version = oldInst.Status.AtProvider.Version
}
return webhook.ValidateUpdateVersion(oldInst.Status.AtProvider.Version, oldInst.Spec.ForProvider.Version, newInst.Spec.ForProvider.Version)
}