Skip to content

Commit 96c5ff5

Browse files
jrhoustondak1n1
andauthored
Add kubernetes_mutating_webhook_configuration (#829)
Co-authored-by: Stef Forrester <[email protected]>
1 parent 262f9ed commit 96c5ff5

10 files changed

+1077
-211
lines changed

kubernetes/provider.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/hashicorp/terraform-plugin-sdk/terraform"
1212
"github.com/mitchellh/go-homedir"
1313
apimachineryschema "k8s.io/apimachinery/pkg/runtime/schema"
14+
discovery "k8s.io/client-go/discovery"
1415
kubernetes "k8s.io/client-go/kubernetes"
1516
_ "k8s.io/client-go/plugin/pkg/client/auth"
1617
restclient "k8s.io/client-go/rest"
@@ -173,6 +174,7 @@ func Provider() terraform.ResourceProvider {
173174
"kubernetes_stateful_set": resourceKubernetesStatefulSet(),
174175
"kubernetes_storage_class": resourceKubernetesStorageClass(),
175176
"kubernetes_validating_webhook_configuration": resourceKubernetesValidatingWebhookConfiguration(),
177+
"kubernetes_mutating_webhook_configuration": resourceKubernetesMutatingWebhookConfiguration(),
176178
},
177179
}
178180

@@ -364,3 +366,41 @@ func initializeConfiguration(d *schema.ResourceData) (*restclient.Config, error)
364366
log.Printf("[INFO] Successfully initialized config")
365367
return cfg, nil
366368
}
369+
370+
var useadmissionregistrationv1beta1 *bool
371+
372+
func useAdmissionregistrationV1beta1(conn *kubernetes.Clientset) (bool, error) {
373+
if useadmissionregistrationv1beta1 != nil {
374+
return *useadmissionregistrationv1beta1, nil
375+
}
376+
377+
d := conn.Discovery()
378+
379+
group := "admissionregistration.k8s.io"
380+
381+
v1, err := apimachineryschema.ParseGroupVersion(fmt.Sprintf("%s/v1", group))
382+
if err != nil {
383+
return false, err
384+
}
385+
386+
err = discovery.ServerSupportsVersion(d, v1)
387+
if err == nil {
388+
log.Printf("[INFO] Using %s/v1", group)
389+
useadmissionregistrationv1beta1 = ptrToBool(false)
390+
return false, nil
391+
}
392+
393+
v1beta1, err := apimachineryschema.ParseGroupVersion(fmt.Sprintf("%s/v1beta1", group))
394+
if err != nil {
395+
return false, err
396+
}
397+
398+
err = discovery.ServerSupportsVersion(d, v1beta1)
399+
if err != nil {
400+
return false, err
401+
}
402+
403+
log.Printf("[INFO] Using %s/v1beta1", group)
404+
useadmissionregistrationv1beta1 = ptrToBool(true)
405+
return true, nil
406+
}
Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
package kubernetes
2+
3+
import (
4+
"fmt"
5+
"log"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
8+
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
9+
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
10+
"k8s.io/apimachinery/pkg/api/errors"
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
types "k8s.io/apimachinery/pkg/types"
13+
14+
copier "github.com/jinzhu/copier"
15+
)
16+
17+
func resourceKubernetesMutatingWebhookConfiguration() *schema.Resource {
18+
apiDoc := admissionregistrationv1.MutatingWebhookConfiguration{}.SwaggerDoc()
19+
webhookDoc := admissionregistrationv1.MutatingWebhook{}.SwaggerDoc()
20+
return &schema.Resource{
21+
Create: resourceKubernetesMutatingWebhookConfigurationCreate,
22+
Read: resourceKubernetesMutatingWebhookConfigurationRead,
23+
Exists: resourceKubernetesMutatingWebhookConfigurationExists,
24+
Update: resourceKubernetesMutatingWebhookConfigurationUpdate,
25+
Delete: resourceKubernetesMutatingWebhookConfigurationDelete,
26+
Importer: &schema.ResourceImporter{
27+
State: schema.ImportStatePassthrough,
28+
},
29+
30+
Schema: map[string]*schema.Schema{
31+
"metadata": metadataSchema("mutating webhook configuration", true),
32+
"webhook": {
33+
Type: schema.TypeList,
34+
Description: apiDoc["webhooks"],
35+
Required: true,
36+
MinItems: 1,
37+
Elem: &schema.Resource{
38+
Schema: map[string]*schema.Schema{
39+
"admission_review_versions": {
40+
Type: schema.TypeList,
41+
Description: webhookDoc["admissionReviewVersions"],
42+
Optional: true,
43+
Elem: &schema.Schema{Type: schema.TypeString},
44+
},
45+
"client_config": {
46+
Type: schema.TypeList,
47+
Description: webhookDoc["clientConfig"],
48+
Required: true,
49+
MaxItems: 1,
50+
Elem: &schema.Resource{
51+
Schema: webhookClientConfigFields(),
52+
},
53+
},
54+
"failure_policy": {
55+
Type: schema.TypeString,
56+
Description: webhookDoc["failurePolicy"],
57+
Optional: true,
58+
Default: "Fail",
59+
},
60+
"match_policy": {
61+
Type: schema.TypeString,
62+
Description: webhookDoc["matchPolicy"],
63+
Optional: true,
64+
Default: "Equivalent",
65+
},
66+
"name": {
67+
Type: schema.TypeString,
68+
Description: webhookDoc["name"],
69+
Required: true,
70+
},
71+
"namespace_selector": {
72+
Type: schema.TypeList,
73+
Description: webhookDoc["namespaceSelector"],
74+
Optional: true,
75+
MaxItems: 1,
76+
Elem: &schema.Resource{
77+
Schema: labelSelectorFields(true),
78+
},
79+
},
80+
"object_selector": {
81+
Type: schema.TypeList,
82+
Description: webhookDoc["objectSelector"],
83+
Optional: true,
84+
MaxItems: 1,
85+
Elem: &schema.Resource{
86+
Schema: labelSelectorFields(true),
87+
},
88+
},
89+
"reinvocation_policy": {
90+
Type: schema.TypeString,
91+
Description: webhookDoc["reinvocationPolicy"],
92+
Optional: true,
93+
Default: "Never",
94+
},
95+
"rule": {
96+
Type: schema.TypeList,
97+
Description: webhookDoc["rules"],
98+
Required: true,
99+
MinItems: 1,
100+
Elem: &schema.Resource{
101+
Schema: ruleWithOperationsFields(),
102+
},
103+
},
104+
"side_effects": {
105+
Type: schema.TypeString,
106+
Description: webhookDoc["sideEffects"],
107+
Optional: true,
108+
},
109+
"timeout_seconds": {
110+
Type: schema.TypeInt,
111+
Description: webhookDoc["timeoutSeconds"],
112+
Default: 10,
113+
Optional: true,
114+
},
115+
},
116+
},
117+
},
118+
},
119+
}
120+
}
121+
122+
func resourceKubernetesMutatingWebhookConfigurationCreate(d *schema.ResourceData, meta interface{}) error {
123+
conn, err := meta.(KubeClientsets).MainClientset()
124+
if err != nil {
125+
return err
126+
}
127+
128+
cfg := admissionregistrationv1.MutatingWebhookConfiguration{
129+
ObjectMeta: expandMetadata(d.Get("metadata").([]interface{})),
130+
Webhooks: expandMutatingWebhooks(d.Get("webhook").([]interface{})),
131+
}
132+
133+
log.Printf("[INFO] Creating new MutatingWebhookConfiguration: %#v", cfg)
134+
135+
res := &admissionregistrationv1.MutatingWebhookConfiguration{}
136+
137+
useadmissionregistrationv1beta1, err := useAdmissionregistrationV1beta1(conn)
138+
if err != nil {
139+
return err
140+
}
141+
if useadmissionregistrationv1beta1 {
142+
requestv1beta1 := &admissionregistrationv1beta1.MutatingWebhookConfiguration{}
143+
responsev1beta1 := &admissionregistrationv1beta1.MutatingWebhookConfiguration{}
144+
copier.Copy(requestv1beta1, cfg)
145+
responsev1beta1, err = conn.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(requestv1beta1)
146+
copier.Copy(res, responsev1beta1)
147+
} else {
148+
res, err = conn.AdmissionregistrationV1().MutatingWebhookConfigurations().Create(&cfg)
149+
}
150+
151+
if err != nil {
152+
return err
153+
}
154+
155+
log.Printf("[INFO] Submitted new MutatingWebhookConfiguration: %#v", res)
156+
157+
d.SetId(res.Name)
158+
159+
return resourceKubernetesMutatingWebhookConfigurationRead(d, meta)
160+
}
161+
162+
func resourceKubernetesMutatingWebhookConfigurationRead(d *schema.ResourceData, meta interface{}) error {
163+
conn, err := meta.(KubeClientsets).MainClientset()
164+
if err != nil {
165+
return err
166+
}
167+
168+
name := d.Id()
169+
170+
cfg := &admissionregistrationv1.MutatingWebhookConfiguration{}
171+
172+
log.Printf("[INFO] Reading MutatingWebhookConfiguration %s", name)
173+
useadmissionregistrationv1beta1, err := useAdmissionregistrationV1beta1(conn)
174+
if err != nil {
175+
return err
176+
}
177+
if useadmissionregistrationv1beta1 {
178+
cfgv1beta1 := &admissionregistrationv1beta1.MutatingWebhookConfiguration{}
179+
cfgv1beta1, err = conn.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Get(name, metav1.GetOptions{})
180+
copier.Copy(cfg, cfgv1beta1)
181+
} else {
182+
cfg, err = conn.AdmissionregistrationV1().MutatingWebhookConfigurations().Get(name, metav1.GetOptions{})
183+
}
184+
if err != nil {
185+
return err
186+
}
187+
188+
err = d.Set("metadata", flattenMetadata(cfg.ObjectMeta, d))
189+
if err != nil {
190+
return nil
191+
}
192+
193+
log.Printf("[DEBUG] Setting webhook to: %#v", cfg.Webhooks)
194+
195+
err = d.Set("webhook", flattenMutatingWebhooks(cfg.Webhooks))
196+
if err != nil {
197+
return err
198+
}
199+
200+
return nil
201+
}
202+
203+
func resourceKubernetesMutatingWebhookConfigurationUpdate(d *schema.ResourceData, meta interface{}) error {
204+
conn, err := meta.(KubeClientsets).MainClientset()
205+
if err != nil {
206+
return err
207+
}
208+
209+
ops := patchMetadata("metadata.0.", "/metadata/", d)
210+
211+
if d.HasChange("webhook") {
212+
op := &ReplaceOperation{
213+
Path: "/webhooks",
214+
}
215+
216+
patch := expandMutatingWebhooks(d.Get("webhook").([]interface{}))
217+
218+
useadmissionregistrationv1beta1, err := useAdmissionregistrationV1beta1(conn)
219+
if err != nil {
220+
return err
221+
}
222+
if useadmissionregistrationv1beta1 {
223+
patchv1beta1 := []admissionregistrationv1beta1.MutatingWebhook{}
224+
copier.Copy(&patchv1beta1, &patch)
225+
op.Value = patchv1beta1
226+
} else {
227+
op.Value = patch
228+
}
229+
230+
ops = append(ops, op)
231+
}
232+
233+
data, err := ops.MarshalJSON()
234+
if err != nil {
235+
return fmt.Errorf("Failed to marshal update operations: %s", err)
236+
}
237+
238+
name := d.Id()
239+
log.Printf("[INFO] Updating MutatingWebhookConfiguration %q: %v", name, string(data))
240+
241+
res := &admissionregistrationv1.MutatingWebhookConfiguration{}
242+
243+
useadmissionregistrationv1beta1, err := useAdmissionregistrationV1beta1(conn)
244+
if err != nil {
245+
return err
246+
}
247+
if useadmissionregistrationv1beta1 {
248+
responsev1beta1 := &admissionregistrationv1beta1.MutatingWebhookConfiguration{}
249+
responsev1beta1, err = conn.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Patch(name, types.JSONPatchType, data)
250+
copier.Copy(res, responsev1beta1)
251+
} else {
252+
res, err = conn.AdmissionregistrationV1().MutatingWebhookConfigurations().Patch(name, types.JSONPatchType, data)
253+
}
254+
if err != nil {
255+
return fmt.Errorf("Failed to update MutatingWebhookConfiguration: %s", err)
256+
}
257+
258+
log.Printf("[INFO] Submitted updated MutatingWebhookConfiguration: %#v", res)
259+
260+
return resourceKubernetesMutatingWebhookConfigurationRead(d, meta)
261+
}
262+
263+
func resourceKubernetesMutatingWebhookConfigurationDelete(d *schema.ResourceData, meta interface{}) error {
264+
conn, err := meta.(KubeClientsets).MainClientset()
265+
if err != nil {
266+
return err
267+
}
268+
269+
name := d.Id()
270+
271+
log.Printf("[INFO] Deleting MutatingWebhookConfiguration: %#v", name)
272+
useadmissionregistrationv1beta1, err := useAdmissionregistrationV1beta1(conn)
273+
if err != nil {
274+
return err
275+
}
276+
if useadmissionregistrationv1beta1 {
277+
err = conn.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Delete(name, &metav1.DeleteOptions{})
278+
} else {
279+
err = conn.AdmissionregistrationV1().MutatingWebhookConfigurations().Delete(name, &metav1.DeleteOptions{})
280+
}
281+
if err != nil {
282+
return err
283+
}
284+
285+
log.Printf("[INFO] MutatingWebhookConfiguration %#v is deleted", name)
286+
287+
d.SetId("")
288+
return nil
289+
}
290+
291+
func resourceKubernetesMutatingWebhookConfigurationExists(d *schema.ResourceData, meta interface{}) (bool, error) {
292+
conn, err := meta.(KubeClientsets).MainClientset()
293+
if err != nil {
294+
return false, err
295+
}
296+
297+
name := d.Id()
298+
299+
log.Printf("[INFO] Checking MutatingWebhookConfiguration %s", name)
300+
301+
useadmissionregistrationv1beta1, err := useAdmissionregistrationV1beta1(conn)
302+
if err != nil {
303+
return false, err
304+
}
305+
if useadmissionregistrationv1beta1 {
306+
_, err = conn.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Get(name, metav1.GetOptions{})
307+
} else {
308+
_, err = conn.AdmissionregistrationV1().MutatingWebhookConfigurations().Get(name, metav1.GetOptions{})
309+
}
310+
311+
if err != nil {
312+
if statusErr, ok := err.(*errors.StatusError); ok && statusErr.ErrStatus.Code == 404 {
313+
return false, nil
314+
}
315+
return false, err
316+
}
317+
318+
return true, nil
319+
}

0 commit comments

Comments
 (0)