Skip to content

Commit bf32dfc

Browse files
authored
feat: extend webhook init logic (#173)
* extend webhook init logic * task generate
1 parent b816249 commit bf32dfc

File tree

7 files changed

+182
-12
lines changed

7 files changed

+182
-12
lines changed

pkg/init/webhooks/apply.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@ import (
77
"strings"
88

99
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
10+
corev1 "k8s.io/api/core/v1"
1011
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/apimachinery/pkg/types"
1113
"k8s.io/utils/ptr"
1214
"sigs.k8s.io/controller-runtime/pkg/client"
1315
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
1416
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
17+
18+
"github.com/openmcp-project/controller-utils/pkg/collections/maps"
1519
)
1620

1721
func applyValidatingWebhook(ctx context.Context, opts *installOptions, obj client.Object) error {
@@ -30,6 +34,7 @@ func applyValidatingWebhook(ctx context.Context, opts *installOptions, obj clien
3034
resource := strings.ToLower(gvk.Kind + "s")
3135

3236
result, err := controllerutil.CreateOrUpdate(ctx, opts.remoteClient, cfg, func() error {
37+
cfg.Labels = maps.Merge(cfg.Labels, opts.managedLabels)
3338
webhook := admissionregistrationv1.ValidatingWebhook{
3439
Name: strings.ToLower(fmt.Sprintf("v%s.%s", gvk.Kind, gvk.Group)),
3540
FailurePolicy: ptr.To(admissionregistrationv1.Fail),
@@ -88,6 +93,7 @@ func applyMutatingWebhook(ctx context.Context, opts *installOptions, obj client.
8893
resource := strings.ToLower(gvk.Kind + "s")
8994

9095
result, err := controllerutil.CreateOrUpdate(ctx, opts.remoteClient, cfg, func() error {
96+
cfg.Labels = maps.Merge(cfg.Labels, opts.managedLabels)
9197
webhook := admissionregistrationv1.MutatingWebhook{
9298
Name: strings.ToLower(fmt.Sprintf("m%s.%s", gvk.Kind, gvk.Group)),
9399
FailurePolicy: ptr.To(admissionregistrationv1.Fail),
@@ -128,3 +134,29 @@ func applyMutatingWebhook(ctx context.Context, opts *installOptions, obj client.
128134
log.Println("Mutating webhook config", cfg.Name, result)
129135
return err
130136
}
137+
138+
func applyWebhookService(ctx context.Context, opts *installOptions) error {
139+
svc := &corev1.Service{
140+
ObjectMeta: metav1.ObjectMeta{
141+
Name: opts.webhookService.Name,
142+
Namespace: opts.webhookService.Namespace,
143+
},
144+
}
145+
146+
result, err := controllerutil.CreateOrUpdate(ctx, opts.localClient, svc, func() error {
147+
svc.Labels = maps.Merge(svc.Labels, opts.managedLabels)
148+
svc.Spec.Selector = opts.managedService.SelectorLabels
149+
svc.Spec.Type = corev1.ServiceTypeClusterIP
150+
svc.Spec.Ports = []corev1.ServicePort{
151+
{
152+
Name: "https",
153+
Protocol: corev1.ProtocolTCP,
154+
Port: opts.webhookServicePort,
155+
TargetPort: opts.managedService.TargetPort,
156+
},
157+
}
158+
return nil
159+
})
160+
log.Println("Webhook service", types.NamespacedName{Namespace: svc.Namespace, Name: svc.Name}.String(), result)
161+
return err
162+
}

pkg/init/webhooks/flags.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ const (
1212

1313
type Flags struct {
1414
Install bool
15-
InstallOptions []installOption
16-
CertOptions []certOption
15+
InstallOptions []InstallOption
16+
CertOptions []CertOption
1717
BindHost string
1818
BindPort int
1919
}

pkg/init/webhooks/flags_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ func Test_BindFlags(t *testing.T) {
2727
assert.NotNil(t, flags)
2828
assert.Equal(t, &Flags{
2929
Install: true,
30-
InstallOptions: []installOption{
30+
InstallOptions: []InstallOption{
3131
WithoutCA,
3232
WithCustomBaseURL("https://webhooks.example.com"),
3333
WithWebhookServicePort(1234),
3434
},
35-
CertOptions: []certOption{
35+
CertOptions: []CertOption{
3636
WithAdditionalDNSNames{"webhooks.example.com", "webhooks.example.org"},
3737
},
3838
BindHost: "someaddr",

pkg/init/webhooks/init.go

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var (
1818
)
1919

2020
// GenerateCertificate
21-
func GenerateCertificate(ctx context.Context, c client.Client, options ...certOption) error {
21+
func GenerateCertificate(ctx context.Context, c client.Client, options ...CertOption) error {
2222
opts := &certOptions{
2323
webhookService: getWebhookServiceFromEnv(),
2424
webhookSecret: getWebhookSecretFromEnv(),
@@ -71,7 +71,7 @@ func Install(
7171
c client.Client,
7272
scheme *runtime.Scheme,
7373
apiTypes []client.Object,
74-
options ...installOption,
74+
options ...InstallOption,
7575
) error {
7676
opts := &installOptions{
7777
localClient: c,
@@ -98,6 +98,12 @@ func Install(
9898
opts.caData = secret.Data[corev1.TLSCertKey]
9999
}
100100

101+
if opts.managedService != nil {
102+
if err := applyWebhookService(ctx, opts); err != nil {
103+
return err
104+
}
105+
}
106+
101107
for _, o := range apiTypes {
102108
_, isCustomValidator := o.(webhook.CustomValidator)
103109
if isCustomValidator {
@@ -116,3 +122,47 @@ func Install(
116122
log.Println("Webhooks initialized")
117123
return nil
118124
}
125+
126+
func Uninstall(
127+
ctx context.Context,
128+
c client.Client,
129+
scheme *runtime.Scheme,
130+
apiTypes []client.Object,
131+
options ...InstallOption,
132+
) error {
133+
opts := &installOptions{
134+
localClient: c,
135+
remoteClient: c,
136+
scheme: scheme,
137+
webhookService: getWebhookServiceFromEnv(),
138+
webhookSecret: getWebhookSecretFromEnv(),
139+
webhookServicePort: 443,
140+
}
141+
for _, io := range options {
142+
io.ApplyToInstallOptions(opts)
143+
}
144+
145+
if opts.managedService != nil {
146+
if err := removeWebhookService(ctx, opts); err != nil {
147+
return err
148+
}
149+
}
150+
151+
for _, o := range apiTypes {
152+
_, isCustomValidator := o.(webhook.CustomValidator)
153+
if isCustomValidator {
154+
if err := removeValidatingWebhook(ctx, opts, o); err != nil {
155+
return err
156+
}
157+
}
158+
_, isCustomDefaulter := o.(webhook.CustomDefaulter)
159+
if isCustomDefaulter {
160+
if err := removeMutatingWebhook(ctx, opts, o); err != nil {
161+
return err
162+
}
163+
}
164+
}
165+
166+
log.Println("Webhooks removed")
167+
return nil
168+
}

pkg/init/webhooks/init_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func Test_GenerateCertificate(t *testing.T) {
3030
desc string
3131
setup func(ctx context.Context, c client.Client) error
3232
validate func(ctx context.Context, c client.Client, t *testing.T, testErr error) error
33-
options []certOption
33+
options []CertOption
3434
}{
3535
{
3636
desc: "should generate certificate",
@@ -64,7 +64,7 @@ func Test_GenerateCertificate(t *testing.T) {
6464
},
6565
{
6666
desc: "should generate certificate with custom object names",
67-
options: []certOption{
67+
options: []CertOption{
6868
WithWebhookSecret{Name: "myothersecret", Namespace: "myothernamespace"},
6969
WithWebhookService{Name: "myotherservice", Namespace: "myothernamespace"},
7070
WithAdditionalDNSNames{"some.other.name.example.com"},
@@ -165,7 +165,7 @@ func Test_Install(t *testing.T) {
165165
desc string
166166
setup func(ctx context.Context, c client.Client) error
167167
validate func(ctx context.Context, c client.Client, t *testing.T, testErr error) error
168-
options []installOption
168+
options []InstallOption
169169
}{
170170
{
171171
desc: "should create webhook configurations for TestObj",
@@ -224,7 +224,7 @@ func Test_Install(t *testing.T) {
224224
},
225225
{
226226
desc: "should create webhook configurations for TestObj with custom values",
227-
options: []installOption{
227+
options: []InstallOption{
228228
WithoutCA,
229229
WithCustomBaseURL("https://webhooks.example.com"),
230230
},

pkg/init/webhooks/options.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package webhooks
33
import (
44
"k8s.io/apimachinery/pkg/runtime"
55
"k8s.io/apimachinery/pkg/types"
6+
"k8s.io/apimachinery/pkg/util/intstr"
67
"k8s.io/utils/ptr"
78
"sigs.k8s.io/controller-runtime/pkg/client"
89
)
@@ -21,9 +22,11 @@ type installOptions struct {
2122
webhookService types.NamespacedName
2223
webhookSecret types.NamespacedName
2324
webhookServicePort int32
25+
managedLabels map[string]string
26+
managedService *WithManagedWebhookService
2427
}
2528

26-
type installOption interface {
29+
type InstallOption interface {
2730
ApplyToInstallOptions(o *installOptions)
2831
}
2932

@@ -37,7 +40,7 @@ type certOptions struct {
3740
additionalDNSNames []string
3841
}
3942

40-
type certOption interface {
43+
type CertOption interface {
4144
ApplyToCertOptions(o *certOptions)
4245
}
4346

@@ -134,3 +137,28 @@ type WithAdditionalDNSNames []string
134137
func (opt WithAdditionalDNSNames) ApplyToCertOptions(o *certOptions) {
135138
o.additionalDNSNames = opt
136139
}
140+
141+
//
142+
// Managed Webhook Service
143+
//
144+
145+
// WithManagedWebhookService allows to have the webhook service created and managed by this library.
146+
type WithManagedWebhookService struct {
147+
TargetPort intstr.IntOrString
148+
SelectorLabels map[string]string
149+
}
150+
151+
func (opt WithManagedWebhookService) ApplyToInstallOptions(o *installOptions) {
152+
o.managedService = &opt
153+
}
154+
155+
//
156+
// Managed Labels
157+
//
158+
159+
// WithManagedLabels specifies labels which should be added to resources created by this library.
160+
type WithManagedLabels map[string]string
161+
162+
func (opt WithManagedLabels) ApplyToInstallOptions(o *installOptions) {
163+
o.managedLabels = map[string]string(opt)
164+
}

pkg/init/webhooks/remove.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package webhooks
2+
3+
import (
4+
"context"
5+
"log"
6+
7+
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
8+
corev1 "k8s.io/api/core/v1"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"k8s.io/apimachinery/pkg/types"
11+
"sigs.k8s.io/controller-runtime/pkg/client"
12+
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
13+
)
14+
15+
func removeValidatingWebhook(ctx context.Context, opts *installOptions, obj client.Object) error {
16+
gvk, err := apiutil.GVKForObject(obj, opts.scheme)
17+
if err != nil {
18+
return err
19+
}
20+
21+
cfg := &admissionregistrationv1.ValidatingWebhookConfiguration{
22+
ObjectMeta: metav1.ObjectMeta{
23+
Name: generateValidateName(gvk),
24+
},
25+
}
26+
27+
err = opts.remoteClient.Delete(ctx, cfg)
28+
log.Println("Removing validating webhook config", cfg.Name)
29+
return client.IgnoreNotFound(err)
30+
}
31+
32+
func removeMutatingWebhook(ctx context.Context, opts *installOptions, obj client.Object) error {
33+
gvk, err := apiutil.GVKForObject(obj, opts.scheme)
34+
if err != nil {
35+
return err
36+
}
37+
38+
cfg := &admissionregistrationv1.MutatingWebhookConfiguration{
39+
ObjectMeta: metav1.ObjectMeta{
40+
Name: generateMutateName(gvk),
41+
},
42+
}
43+
44+
err = opts.remoteClient.Delete(ctx, cfg)
45+
log.Println("Removing mutating webhook config", cfg.Name)
46+
return client.IgnoreNotFound(err)
47+
}
48+
49+
func removeWebhookService(ctx context.Context, opts *installOptions) error {
50+
svc := &corev1.Service{
51+
ObjectMeta: metav1.ObjectMeta{
52+
Name: opts.webhookService.Name,
53+
Namespace: opts.webhookService.Namespace,
54+
},
55+
}
56+
57+
err := opts.localClient.Delete(ctx, svc)
58+
log.Println("Removing webhook service", types.NamespacedName{Namespace: svc.Namespace, Name: svc.Name}.String())
59+
return err
60+
}

0 commit comments

Comments
 (0)