Skip to content

Commit b806c27

Browse files
authored
Add TargetAllocator CR webhook (open-telemetry#3101)
1 parent 8fc9219 commit b806c27

5 files changed

+597
-45
lines changed
+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package v1alpha1
16+
17+
import (
18+
"context"
19+
"fmt"
20+
21+
"github.com/go-logr/logr"
22+
"k8s.io/apimachinery/pkg/runtime"
23+
"k8s.io/apimachinery/pkg/util/intstr"
24+
ctrl "sigs.k8s.io/controller-runtime"
25+
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
26+
27+
"github.com/open-telemetry/opentelemetry-operator/apis/v1beta1"
28+
"github.com/open-telemetry/opentelemetry-operator/internal/config"
29+
"github.com/open-telemetry/opentelemetry-operator/internal/rbac"
30+
)
31+
32+
var (
33+
_ admission.CustomValidator = &TargetAllocatorWebhook{}
34+
_ admission.CustomDefaulter = &TargetAllocatorWebhook{}
35+
)
36+
37+
// TODO: Uncomment this webhook after enabling the TargetAllocator controller
38+
// //+kubebuilder:webhook:path=/mutate-opentelemetry-io-v1beta1-targetallocator,mutating=true,failurePolicy=fail,groups=opentelemetry.io,resources=targetallocators,verbs=create;update,versions=v1beta1,name=mtargetallocatorbeta.kb.io,sideEffects=none,admissionReviewVersions=v1
39+
// //+kubebuilder:webhook:verbs=create;update,path=/validate-opentelemetry-io-v1beta1-targetallocator,mutating=false,failurePolicy=fail,groups=opentelemetry.io,resources=targetallocators,versions=v1beta1,name=vtargetallocatorcreateupdatebeta.kb.io,sideEffects=none,admissionReviewVersions=v1
40+
// //+kubebuilder:webhook:verbs=delete,path=/validate-opentelemetry-io-v1beta1-targetallocator,mutating=false,failurePolicy=ignore,groups=opentelemetry.io,resources=targetallocators,versions=v1beta1,name=vtargetallocatordeletebeta.kb.io,sideEffects=none,admissionReviewVersions=v1
41+
// +kubebuilder:object:generate=false
42+
43+
type TargetAllocatorWebhook struct {
44+
logger logr.Logger
45+
cfg config.Config
46+
scheme *runtime.Scheme
47+
reviewer *rbac.Reviewer
48+
}
49+
50+
func (w TargetAllocatorWebhook) Default(_ context.Context, obj runtime.Object) error {
51+
targetallocator, ok := obj.(*TargetAllocator)
52+
if !ok {
53+
return fmt.Errorf("expected an TargetAllocator, received %T", obj)
54+
}
55+
return w.defaulter(targetallocator)
56+
}
57+
58+
func (w TargetAllocatorWebhook) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
59+
otelcol, ok := obj.(*TargetAllocator)
60+
if !ok {
61+
return nil, fmt.Errorf("expected an TargetAllocator, received %T", obj)
62+
}
63+
return w.validate(ctx, otelcol)
64+
}
65+
66+
func (w TargetAllocatorWebhook) ValidateUpdate(ctx context.Context, _, newObj runtime.Object) (admission.Warnings, error) {
67+
otelcol, ok := newObj.(*TargetAllocator)
68+
if !ok {
69+
return nil, fmt.Errorf("expected an TargetAllocator, received %T", newObj)
70+
}
71+
return w.validate(ctx, otelcol)
72+
}
73+
74+
func (w TargetAllocatorWebhook) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
75+
otelcol, ok := obj.(*TargetAllocator)
76+
if !ok || otelcol == nil {
77+
return nil, fmt.Errorf("expected an TargetAllocator, received %T", obj)
78+
}
79+
return w.validate(ctx, otelcol)
80+
}
81+
82+
func (w TargetAllocatorWebhook) defaulter(ta *TargetAllocator) error {
83+
if ta.Labels == nil {
84+
ta.Labels = map[string]string{}
85+
}
86+
87+
one := int32(1)
88+
89+
if ta.Spec.Replicas == nil {
90+
ta.Spec.Replicas = &one
91+
}
92+
// if pdb isn't provided for target allocator and it's enabled
93+
// using a valid strategy (consistent-hashing),
94+
// we set MaxUnavailable 1, which will work even if there is
95+
// just one replica, not blocking node drains but preventing
96+
// out-of-the-box from disruption generated by them with replicas > 1
97+
if ta.Spec.AllocationStrategy == v1beta1.TargetAllocatorAllocationStrategyConsistentHashing &&
98+
ta.Spec.PodDisruptionBudget == nil {
99+
ta.Spec.PodDisruptionBudget = &v1beta1.PodDisruptionBudgetSpec{
100+
MaxUnavailable: &intstr.IntOrString{
101+
Type: intstr.Int,
102+
IntVal: 1,
103+
},
104+
}
105+
}
106+
107+
return nil
108+
}
109+
110+
func (w TargetAllocatorWebhook) validate(ctx context.Context, ta *TargetAllocator) (admission.Warnings, error) {
111+
// TODO: Further validate scrape configs
112+
113+
warnings := admission.Warnings{}
114+
115+
// validate port config
116+
if err := v1beta1.ValidatePorts(ta.Spec.Ports); err != nil {
117+
return warnings, err
118+
}
119+
120+
// if the prometheusCR is enabled, it needs a suite of permissions to function
121+
if ta.Spec.PrometheusCR.Enabled {
122+
warnings, err := v1beta1.CheckTargetAllocatorPrometheusCRPolicyRules(ctx, w.reviewer, ta.Spec.ServiceAccount, ta.GetNamespace())
123+
if err != nil || len(warnings) > 0 {
124+
return warnings, err
125+
}
126+
}
127+
128+
return warnings, nil
129+
}
130+
131+
func SetupTargetAllocatorWebhook(mgr ctrl.Manager, cfg config.Config, reviewer *rbac.Reviewer) error {
132+
cvw := &TargetAllocatorWebhook{
133+
reviewer: reviewer,
134+
logger: mgr.GetLogger().WithValues("handler", "TargetAllocatorWebhook", "version", "v1beta1"),
135+
scheme: mgr.GetScheme(),
136+
cfg: cfg,
137+
}
138+
return ctrl.NewWebhookManagedBy(mgr).
139+
For(&TargetAllocator{}).
140+
WithValidator(cvw).
141+
WithDefaulter(cvw).
142+
Complete()
143+
}

0 commit comments

Comments
 (0)