Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vip controller and CRD #3

Merged
merged 4 commits into from
Jul 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,16 @@ resources:
defaulting: true
validation: true
webhookVersion: v1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: nordix.org
group: meridio
kind: Vip
path: github.com/nordix/meridio-operator/api/v1alpha1
version: v1alpha1
webhooks:
validation: true
webhookVersion: v1
version: "3"
10 changes: 8 additions & 2 deletions api/v1alpha1/trench_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
Expand All @@ -30,8 +31,6 @@ type TrenchSpec struct {

// ConfigMapName
ConfigMapName string `json:"configMapName,omitempty"`
mandydydy marked this conversation as resolved.
Show resolved Hide resolved
// VIPs
VIPs []string `json:"vips,omitempty"`
}

// TrenchStatus defines the observed state of Trench
Expand Down Expand Up @@ -61,6 +60,13 @@ type TrenchList struct {
Items []Trench `json:"items"`
}

func (r *Trench) GroupResource() schema.GroupResource {
return schema.GroupResource{
Group: r.GroupVersionKind().Group,
Resource: r.GroupVersionKind().Kind,
}
}

func init() {
SchemeBuilder.Register(&Trench{}, &TrenchList{})
}
15 changes: 1 addition & 14 deletions api/v1alpha1/trench_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ limitations under the License.
package v1alpha1

import (
"github.com/vishvananda/netlink"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand Down Expand Up @@ -74,9 +73,7 @@ func (r *Trench) ValidateDelete() error {

func (r *Trench) validateTrench() error {
var allErrs field.ErrorList
if err := r.validateVIPs(); err != nil {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("vips"), r.Spec.VIPs, err.Error()))
}

if len(allErrs) == 0 {
return nil
}
Expand All @@ -85,13 +82,3 @@ func (r *Trench) validateTrench() error {
schema.GroupKind{Group: "meridio.nordix.org", Kind: "Trench"},
r.Name, allErrs)
}

func (r *Trench) validateVIPs() error {
for _, vip := range r.Spec.VIPs {
_, err := netlink.ParseIPNet(vip)
if err != nil {
return err
}
}
return nil
}
78 changes: 78 additions & 0 deletions api/v1alpha1/vip_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
Copyright 2021.

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 v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
type StatusPhase string

var (
NoPhase StatusPhase
PhaseAccepted StatusPhase = "accepted"
PhaseRejected StatusPhase = "rejected"
)

// VipSpec defines the desired state of Vip
type VipSpec struct {
Address string `json:"address,omitempty"`
}

// VipStatus defines the observed state of Vip
type VipStatus struct {
// if the vip is accepted or rejected by controller
Status StatusPhase `json:"status,omitempty"`
// the reason why vip is rejected
Message string `json:"message,omitempty"`
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="Address",type=string,JSONPath=`.spec.address`
//+kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.status`
// Vip is the Schema for the vips API
type Vip struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec VipSpec `json:"spec,omitempty"`
Status VipStatus `json:"status,omitempty"`
}

//+kubebuilder:object:root=true

// VipList contains a list of Vip
type VipList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Vip `json:"items"`
}

func (r *Vip) GroupResource() schema.GroupResource {
return schema.GroupResource{
Group: r.GroupVersionKind().Group,
Resource: r.GroupVersionKind().Kind,
}
}

func init() {
SchemeBuilder.Register(&Vip{}, &VipList{})
}
130 changes: 130 additions & 0 deletions api/v1alpha1/vip_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
Copyright 2021.

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 v1alpha1

import (
"fmt"
"net"

apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)

// log is for logging in this package.
var viplog = logf.Log.WithName("vip-resource")

func (r *Vip) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
}

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!

// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
//+kubebuilder:webhook:path=/validate-meridio-nordix-org-v1alpha1-vip,mutating=false,failurePolicy=fail,sideEffects=None,groups=meridio.nordix.org,resources=vips,verbs=create;update,versions=v1alpha1,name=vvip.kb.io,admissionReviewVersions={v1,v1beta1}

var _ webhook.Validator = &Vip{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *Vip) ValidateCreate() error {
viplog.Info("validate create", "name", r.Name)

return r.validateVip()
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *Vip) ValidateUpdate(old runtime.Object) error {
viplog.Info("validate update", "name", r.Name)
err := r.validateLabelUpdate(old)
if err != nil {
return err
}
return r.validateVip()
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *Vip) ValidateDelete() error {
viplog.Info("validate delete", "name", r.Name)

return nil
}

func (r *Vip) validateVip() error {
var allErrs field.ErrorList
if err := r.validateAddresses(); err != nil {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("address"), r.Spec.Address, err.Error()))
}

if err := r.validateLabels(); err != nil {
allErrs = append(allErrs, field.Invalid(field.NewPath("metadata").Child("labels"), r.ObjectMeta.Labels, err.Error()))
}

if len(allErrs) == 0 {
return nil
}

return apierrors.NewInvalid(
schema.GroupKind{Group: "meridio.nordix.org", Kind: "Vip"},
r.Name, allErrs)
}

func (r *Vip) validateAddresses() error {
_, _, err := net.ParseCIDR(r.Spec.Address)
if err != nil {
return err
}
ip, ipnet, err := net.ParseCIDR(r.Spec.Address)
if err != nil {
return err
}
// ipv4 cidr validation for alpha
if ip.To4() != nil && ipnet.Mask.String() != net.CIDRMask(32, 32).String() {
return fmt.Errorf("only /32 address is supported for ipv4 vips")
}
// ipv6 cidr validation for alpha
if ip.To4() == nil && ipnet.Mask.String() != net.CIDRMask(128, 128).String() {
return fmt.Errorf("only /128 address is supported for ipv6 vips")
}
return nil
}

func (r *Vip) validateLabels() error {
if _, ok := r.ObjectMeta.Labels["trench"]; !ok {
return fmt.Errorf("vip must have a trench label")
}
return nil
}

func (r *Vip) validateLabelUpdate(old runtime.Object) error {
vipOld, ok := old.(*Vip)
if !ok {
return apierrors.NewBadRequest(fmt.Sprintf("expected a vip got got a %T", vipOld))
}
trenchNew := r.ObjectMeta.Labels["trench"]
trenchOld := vipOld.ObjectMeta.Labels["trench"]
if trenchNew != trenchOld {
return apierrors.NewForbidden(r.GroupResource(),
r.Name, field.Forbidden(field.NewPath("metadata", "labels", "trench"), "update on vip label trench is forbidden"))
}
return nil
}
6 changes: 6 additions & 0 deletions api/v1alpha1/webhook_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ var _ = BeforeSuite(func() {
err = admissionv1beta1.AddToScheme(scheme)
Expect(err).NotTo(HaveOccurred())

err = admissionv1beta1.AddToScheme(scheme)
Expect(err).NotTo(HaveOccurred())

//+kubebuilder:scaffold:scheme

k8sClient, err = client.New(cfg, client.Options{Scheme: scheme})
Expand All @@ -103,6 +106,9 @@ var _ = BeforeSuite(func() {
err = (&Trench{}).SetupWebhookWithManager(mgr)
Expect(err).NotTo(HaveOccurred())

err = (&Vip{}).SetupWebhookWithManager(mgr)
Expect(err).NotTo(HaveOccurred())

//+kubebuilder:scaffold:webhook

go func() {
Expand Down
Loading