-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Validation webhooks to cluster api manager
Performs validation for Machines, MachineSets and MachineDeployments. For Machines it validates that Bootstrap has either ConfigRef or Data set. For MachineSets and MachineDeployments it verifies selectors match template labels. Signed-off-by: Warren Fernandes <wfernandes@pivotal.io>
- Loading branch information
Warren Fernandes
committed
Dec 2, 2019
1 parent
6b9b7d7
commit 97da628
Showing
15 changed files
with
564 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* | ||
Copyright 2019 The Kubernetes Authors. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package v1alpha3 | ||
|
||
import ( | ||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
runtime "k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/util/validation/field" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/webhook" | ||
) | ||
|
||
func (m *Machine) SetupWebhookWithManager(mgr ctrl.Manager) error { | ||
return ctrl.NewWebhookManagedBy(mgr). | ||
For(m). | ||
Complete() | ||
} | ||
|
||
// +kubebuilder:webhook:verbs=create;update,path=/validate-cluster-x-k8s-io-v1alpha3-machine,mutating=false,failurePolicy=fail,groups=cluster.x-k8s.io,resources=machines,versions=v1alpha3,name=validation.machine.cluster.x-k8s.io | ||
|
||
var _ webhook.Validator = &Machine{} | ||
|
||
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type | ||
func (m *Machine) ValidateCreate() error { | ||
return m.validate() | ||
} | ||
|
||
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type | ||
func (m *Machine) ValidateUpdate(old runtime.Object) error { | ||
return m.validate() | ||
} | ||
|
||
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type | ||
func (m *Machine) ValidateDelete() error { | ||
return nil | ||
} | ||
|
||
func (m *Machine) validate() error { | ||
var allErrs field.ErrorList | ||
if m.Spec.Bootstrap.ConfigRef == nil && m.Spec.Bootstrap.Data == nil { | ||
allErrs = append( | ||
allErrs, | ||
field.Required(field.NewPath("spec", "bootstrap", "data"), "expected spec.bootstrap.data or spec.bootstrap.configRef to be populated"), | ||
) | ||
|
||
} | ||
if len(allErrs) == 0 { | ||
return nil | ||
} | ||
return apierrors.NewInvalid(GroupVersion.WithKind("Machine").GroupKind(), m.Name, allErrs) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/* | ||
Copyright 2019 The Kubernetes Authors. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package v1alpha3 | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/onsi/gomega" | ||
corev1 "k8s.io/api/core/v1" | ||
) | ||
|
||
func TestMachineBootstrapValidation(t *testing.T) { | ||
data := "some bootstrap data" | ||
tests := []struct { | ||
name string | ||
bootstrap Bootstrap | ||
expectErr bool | ||
}{ | ||
{ | ||
name: "should return error if configref and data are nil", | ||
bootstrap: Bootstrap{ConfigRef: nil, Data: nil}, | ||
expectErr: true, | ||
}, | ||
{ | ||
name: "should not return error if data is set", | ||
bootstrap: Bootstrap{ConfigRef: nil, Data: &data}, | ||
expectErr: false, | ||
}, | ||
{ | ||
name: "should not return error if config ref is set", | ||
bootstrap: Bootstrap{ConfigRef: &corev1.ObjectReference{}, Data: nil}, | ||
expectErr: false, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
g := gomega.NewWithT(t) | ||
m := &Machine{ | ||
Spec: MachineSpec{Bootstrap: tt.bootstrap}, | ||
} | ||
if tt.expectErr { | ||
err := m.ValidateCreate() | ||
g.Expect(err).To(gomega.HaveOccurred()) | ||
err = m.ValidateUpdate(nil) | ||
g.Expect(err).To(gomega.HaveOccurred()) | ||
} else { | ||
g.Expect(m.ValidateCreate()).To(gomega.Succeed()) | ||
g.Expect(m.ValidateUpdate(nil)).To(gomega.Succeed()) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/* | ||
Copyright 2019 The Kubernetes Authors. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package v1alpha3 | ||
|
||
import ( | ||
"fmt" | ||
|
||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/labels" | ||
runtime "k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/util/validation/field" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/webhook" | ||
) | ||
|
||
func (m *MachineDeployment) SetupWebhookWithManager(mgr ctrl.Manager) error { | ||
return ctrl.NewWebhookManagedBy(mgr). | ||
For(m). | ||
Complete() | ||
} | ||
|
||
// +kubebuilder:webhook:verbs=create;update,path=/validate-cluster-x-k8s-io-v1alpha3-machinedeployment,mutating=false,failurePolicy=fail,groups=cluster.x-k8s.io,resources=machinedeployments,versions=v1alpha3,name=validation.machinedeployment.cluster.x-k8s.io | ||
|
||
var _ webhook.Validator = &MachineDeployment{} | ||
|
||
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type | ||
func (m *MachineDeployment) ValidateCreate() error { | ||
return m.validate() | ||
} | ||
|
||
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type | ||
func (m *MachineDeployment) ValidateUpdate(old runtime.Object) error { | ||
return m.validate() | ||
} | ||
|
||
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type | ||
func (m *MachineDeployment) ValidateDelete() error { | ||
return nil | ||
} | ||
|
||
func (m *MachineDeployment) validate() error { | ||
var allErrs field.ErrorList | ||
selector, err := metav1.LabelSelectorAsSelector(&m.Spec.Selector) | ||
if err != nil { | ||
allErrs = append( | ||
allErrs, | ||
field.Invalid(field.NewPath("spec", "selector"), m.Spec.Selector, err.Error()), | ||
) | ||
} else if !selector.Matches(labels.Set(m.Spec.Template.Labels)) { | ||
allErrs = append( | ||
allErrs, | ||
field.Invalid( | ||
field.NewPath("spec", "template", "labels"), | ||
m.Spec.Template.Labels, | ||
fmt.Sprintf("must match spec.selector %q", selector.String()), | ||
), | ||
) | ||
} | ||
|
||
if len(allErrs) == 0 { | ||
return nil | ||
} | ||
|
||
return apierrors.NewInvalid(GroupVersion.WithKind("MachineDeployment").GroupKind(), m.Name, allErrs) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
/* | ||
Copyright 2019 The Kubernetes Authors. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package v1alpha3 | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/onsi/gomega" | ||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
func TestMachineDeploymentValidation(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
selectors map[string]string | ||
labels map[string]string | ||
expectErr bool | ||
}{ | ||
{ | ||
name: "should return error on mismatch", | ||
selectors: map[string]string{"foo": "bar"}, | ||
labels: map[string]string{"foo": "baz"}, | ||
expectErr: true, | ||
}, | ||
{ | ||
name: "should return error on missing labels", | ||
selectors: map[string]string{"foo": "bar"}, | ||
labels: map[string]string{"": ""}, | ||
expectErr: true, | ||
}, | ||
{ | ||
name: "should return error if all selectors don't match", | ||
selectors: map[string]string{"foo": "bar", "hello": "world"}, | ||
labels: map[string]string{"foo": "bar"}, | ||
expectErr: true, | ||
}, | ||
{ | ||
name: "should not return error on match", | ||
selectors: map[string]string{"foo": "bar"}, | ||
labels: map[string]string{"foo": "bar"}, | ||
expectErr: false, | ||
}, | ||
{ | ||
name: "should return error for invalid selector", | ||
selectors: map[string]string{"-123-foo": "bar"}, | ||
labels: map[string]string{"-123-foo": "bar"}, | ||
expectErr: true, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
g := gomega.NewWithT(t) | ||
md := &MachineDeployment{ | ||
Spec: MachineDeploymentSpec{ | ||
Selector: v1.LabelSelector{ | ||
MatchLabels: tt.selectors, | ||
}, | ||
Template: MachineTemplateSpec{ | ||
ObjectMeta: ObjectMeta{ | ||
Labels: tt.labels, | ||
}, | ||
}, | ||
}, | ||
} | ||
if tt.expectErr { | ||
err := md.ValidateCreate() | ||
g.Expect(err).To(gomega.HaveOccurred()) | ||
err = md.ValidateUpdate(nil) | ||
g.Expect(err).To(gomega.HaveOccurred()) | ||
} else { | ||
g.Expect(md.ValidateCreate()).To(gomega.Succeed()) | ||
g.Expect(md.ValidateUpdate(nil)).To(gomega.Succeed()) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/* | ||
Copyright 2019 The Kubernetes Authors. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package v1alpha3 | ||
|
||
import ( | ||
"fmt" | ||
|
||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/labels" | ||
runtime "k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/util/validation/field" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/webhook" | ||
) | ||
|
||
func (m *MachineSet) SetupWebhookWithManager(mgr ctrl.Manager) error { | ||
return ctrl.NewWebhookManagedBy(mgr). | ||
For(m). | ||
Complete() | ||
} | ||
|
||
// +kubebuilder:webhook:verbs=create;update,path=/validate-cluster-x-k8s-io-v1alpha3-machineset,mutating=false,failurePolicy=fail,groups=cluster.x-k8s.io,resources=machinesets,versions=v1alpha3,name=validation.machineset.cluster.x-k8s.io | ||
|
||
var _ webhook.Validator = &MachineSet{} | ||
|
||
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type | ||
func (m *MachineSet) ValidateCreate() error { | ||
return m.validate() | ||
} | ||
|
||
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type | ||
func (m *MachineSet) ValidateUpdate(old runtime.Object) error { | ||
return m.validate() | ||
} | ||
|
||
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type | ||
func (m *MachineSet) ValidateDelete() error { | ||
return nil | ||
} | ||
|
||
func (m *MachineSet) validate() error { | ||
var allErrs field.ErrorList | ||
selector, err := metav1.LabelSelectorAsSelector(&m.Spec.Selector) | ||
if err != nil { | ||
allErrs = append( | ||
allErrs, | ||
field.Invalid(field.NewPath("spec", "selector"), m.Spec.Selector, err.Error()), | ||
) | ||
} else if !selector.Matches(labels.Set(m.Spec.Template.Labels)) { | ||
allErrs = append( | ||
allErrs, | ||
field.Invalid( | ||
field.NewPath("spec", "template", "labels"), | ||
m.Spec.Template.Labels, | ||
fmt.Sprintf("must match spec.selector %q", selector.String()), | ||
), | ||
) | ||
} | ||
|
||
if len(allErrs) == 0 { | ||
return nil | ||
} | ||
|
||
return apierrors.NewInvalid(GroupVersion.WithKind("MachineSet").GroupKind(), m.Name, allErrs) | ||
} |
Oops, something went wrong.