Skip to content
This repository has been archived by the owner on Nov 8, 2023. It is now read-only.

Write the v1alpha2 webhook and enable it #102

Merged
merged 8 commits into from
Apr 3, 2023
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
2 changes: 1 addition & 1 deletion api/v1alpha1/edgex_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ type EdgeXStatus struct {
//+kubebuilder:printcolumn:name="ReadyService",type="integer",JSONPath=".status.serviceReadyReplicas",description="The Ready Service Replica."
//+kubebuilder:printcolumn:name="Deployment",type="integer",JSONPath=".status.deploymentReplicas",description="The Deployment Replica."
//+kubebuilder:printcolumn:name="ReadyDeployment",type="integer",JSONPath=".status.deploymentReadyReplicas",description="The Ready Deployment Replica."
//+kubebuilder:deprecatedversion:warning="device.openyurt.io/v1alpha1 EdgeX will be deprecated in future; use device.openyurt.io/v1alpha2 EdgeX"
//+kubebuilder:deprecatedversion:warning="device.openyurt.io/v1alpha1 EdgeX will be deprecated in future; use device.openyurt.io/v1alpha2 EdgeX; v1alpha1 EdgeX.Spec.ServiceType only support ClusterIP"

// EdgeX is the Schema for the edgexes API
type EdgeX struct {
Expand Down
3 changes: 2 additions & 1 deletion config/crd/bases/device.openyurt.io_edgexes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ spec:
type: integer
deprecated: true
deprecationWarning: device.openyurt.io/v1alpha1 EdgeX will be deprecated in future;
use device.openyurt.io/v1alpha2 EdgeX
use device.openyurt.io/v1alpha2 EdgeX; v1alpha1 EdgeX.Spec.ServiceType only
support ClusterIP
name: v1alpha1
schema:
openAPIV3Schema:
Expand Down
14 changes: 8 additions & 6 deletions config/webhook/manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,20 @@ metadata:
name: mutating-webhook-configuration
webhooks:
- admissionReviewVersions:
- v2
- v1
clientConfig:
service:
name: webhook-service
namespace: system
path: /mutate-device-openyurt-io-v1alpha1-edgex
path: /mutate-device-openyurt-io-v1alpha2-edgex
failurePolicy: Fail
name: medgex.kb.io
name: medgex.kb.io.v1alpha2
rules:
- apiGroups:
- device.openyurt.io
apiVersions:
- v1alpha1
- v1alpha2
operations:
- CREATE
- UPDATE
Expand All @@ -35,19 +36,20 @@ metadata:
name: validating-webhook-configuration
webhooks:
- admissionReviewVersions:
- v2
- v1
clientConfig:
service:
name: webhook-service
namespace: system
path: /validate-device-openyurt-io-v1alpha1-edgex
path: /validate-device-openyurt-io-v1alpha2-edgex
failurePolicy: Fail
name: vedgex.kb.io
name: vedgex.kb.io.v1alpha2
rules:
- apiGroups:
- device.openyurt.io
apiVersions:
- v1alpha1
- v1alpha2
operations:
- CREATE
- UPDATE
Expand Down
20 changes: 2 additions & 18 deletions controllers/utils/fieldindexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,35 +20,18 @@ import (
"context"
"sync"

"github.com/openyurtio/yurt-edgex-manager/api/v1alpha1"
"github.com/openyurtio/yurt-edgex-manager/api/v1alpha2"

"sigs.k8s.io/controller-runtime/pkg/client"
)

const (
IndexerPathForNodepool = "spec.poolname"
IndexerPathForNodepool = "spec.poolName"
)

var registerOnce sync.Once

func RegisterFieldIndexers(fi client.FieldIndexer) error {
var err error
registerOnce.Do(func() {
// register the fieldIndexer for device
if err = fi.IndexField(context.TODO(), &v1alpha1.EdgeX{}, IndexerPathForNodepool, func(rawObj client.Object) []string {
edgex, ok := rawObj.(*v1alpha1.EdgeX)
if ok {
return []string{edgex.Spec.PoolName}
}
return []string{}
}); err != nil {
return
}
})
if err != nil {
return err
}
registerOnce.Do(func() {
// register the fieldIndexer for device
if err = fi.IndexField(context.TODO(), &v1alpha2.EdgeX{}, IndexerPathForNodepool, func(rawObj client.Object) []string {
Expand All @@ -61,5 +44,6 @@ func RegisterFieldIndexers(fi client.FieldIndexer) error {
return
}
})

return err
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/openyurtio/api v0.0.0-20220907024010-e5bfc9cc1b4b
github.com/pkg/errors v0.9.1
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/api v0.24.1
k8s.io/apimachinery v0.24.1
k8s.io/client-go v0.24.1
Expand Down
19 changes: 16 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import (
"os"

util "github.com/openyurtio/yurt-edgex-manager/controllers/utils"
edgexwebhook "github.com/openyurtio/yurt-edgex-manager/pkg/webhook/edgex"
edgexwebhookv1alpha2 "github.com/openyurtio/yurt-edgex-manager/pkg/webhook/edgex"
edgexwebhookv1alpha1 "github.com/openyurtio/yurt-edgex-manager/pkg/webhook/edgex/v1alpha1"

// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
Expand All @@ -48,6 +49,7 @@ var (
setupLog = ctrl.Log.WithName("setup")
securityFile = "EdgeXConfig/config.json"
nosectyFile = "EdgeXConfig/config-nosecty.json"
manifestPath = "EdgeXConfig/manifest.yaml"
//go:embed EdgeXConfig
edgeXconfig embed.FS
)
Expand Down Expand Up @@ -148,10 +150,21 @@ func main() {
}

if enableWebhook {
if err = (&edgexwebhook.EdgeXHandler{Client: mgr.GetClient()}).SetupWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "EdgeX")
manifestContent, err := edgeXconfig.ReadFile(manifestPath)
if err != nil {
setupLog.Error(err, "File to open the embed EdgeX manifest config")
os.Exit(1)
}
if err = (&edgexwebhookv1alpha2.EdgeXHandler{Client: mgr.GetClient(), ManifestContent: manifestContent}).SetupWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook v1alpha2", "webhook", "EdgeX")
os.Exit(1)
}

if err = (&edgexwebhookv1alpha1.EdgeXHandler{Client: mgr.GetClient()}).SetupWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook v1alpha1", "webhook", "EdgeX")
os.Exit(1)
}

} else {
setupLog.Info("webhook disabled")
}
Expand Down
126 changes: 88 additions & 38 deletions pkg/webhook/edgex/edgex_webhook.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
/*
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.
Expand All @@ -19,89 +16,127 @@ package edgex
import (
"context"
"fmt"
"strings"

unitv1alpha1 "github.com/openyurtio/api/apps/v1alpha1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/validation/field"

apierrors "k8s.io/apimachinery/pkg/api/errors"
"gopkg.in/yaml.v2"

"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook"

"github.com/openyurtio/yurt-edgex-manager/api/v1alpha1"
apierrors "k8s.io/apimachinery/pkg/api/errors"

"github.com/openyurtio/yurt-edgex-manager/api/v1alpha2"
util "github.com/openyurtio/yurt-edgex-manager/controllers/utils"
)

type Manifest struct {
Updated string `yaml:"updated"`
Count int `yaml:"count"`
LatestVersion string `yaml:"latestVersion"`
Versions []string `yaml:"versions"`
}

func NewManifest() *Manifest {
manifest := &Manifest{
Updated: "false",
Count: 0,
LatestVersion: "",
Versions: make([]string, 0),
}
return manifest
}

// SetupWebhookWithManager sets up Cluster webhooks.
func (webhook *EdgeXHandler) SetupWebhookWithManager(mgr ctrl.Manager) error {

err := webhook.initManifest(webhook.ManifestContent)
if err != nil {
return err
}

return ctrl.NewWebhookManagedBy(mgr).
For(&v1alpha1.EdgeX{}).
For(&v1alpha2.EdgeX{}).
WithDefaulter(webhook).
WithValidator(webhook).
Complete()
}

func (webhook *EdgeXHandler) initManifest(manifestContent []byte) error {

err := yaml.Unmarshal(manifestContent, &manifest)
if err != nil {
return fmt.Errorf("Error manifest edgeX configuration file %w", err)
}
return nil
}

var manifest = NewManifest()

//+kubebuilder:rbac:groups=apps.openyurt.io,resources=nodepools,verbs=list;watch

// Cluster implements a validating and defaulting webhook for Cluster.
type EdgeXHandler struct {
Client client.Client
Client client.Client
ManifestContent []byte
}

//+kubebuilder:webhook:path=/mutate-device-openyurt-io-v1alpha1-edgex,mutating=true,failurePolicy=fail,sideEffects=None,groups=device.openyurt.io,resources=edgexes,verbs=create;update,versions=v1alpha1,name=medgex.kb.io,admissionReviewVersions=v1
//+kubebuilder:webhook:path=/mutate-device-openyurt-io-v1alpha2-edgex,mutating=true,failurePolicy=fail,sideEffects=None,groups=device.openyurt.io,resources=edgexes,verbs=create;update,versions={"v1alpha2"},name=medgex.kb.io.v1alpha2,admissionReviewVersions={"v2", "v1"}

var _ webhook.CustomDefaulter = &EdgeXHandler{}

//+kubebuilder:webhook:path=/validate-device-openyurt-io-v1alpha1-edgex,mutating=false,failurePolicy=fail,sideEffects=None,groups=device.openyurt.io,resources=edgexes,verbs=create;update,versions=v1alpha1,name=vedgex.kb.io,admissionReviewVersions=v1
//+kubebuilder:webhook:path=/validate-device-openyurt-io-v1alpha2-edgex,mutating=false,failurePolicy=fail,sideEffects=None,groups=device.openyurt.io,resources=edgexes,verbs=create;update,versions={"v1alpha2"},name=vedgex.kb.io.v1alpha2,admissionReviewVersions={"v2", "v1"}

var _ webhook.CustomValidator = &EdgeXHandler{}

// Default satisfies the defaulting webhook interface.
func (webhook *EdgeXHandler) Default(ctx context.Context, obj runtime.Object) error {
edgex, ok := obj.(*v1alpha1.EdgeX)
edgex, ok := obj.(*v1alpha2.EdgeX)
if !ok {
return apierrors.NewBadRequest(fmt.Sprintf("expected a EdgeX but got a %T", obj))
}
//set the default version

if edgex.Spec.Version == "" {
edgex.Spec.Version = "jakarta"
}
//set the default ServiceType
if edgex.Spec.ServiceType == "" {
edgex.Spec.ServiceType = corev1.ServiceTypeClusterIP
edgex.Spec.Version = manifest.LatestVersion
}

return nil
}

// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type.
func (webhook *EdgeXHandler) ValidateCreate(ctx context.Context, obj runtime.Object) error {
edgex, ok := obj.(*v1alpha1.EdgeX)
edgex, ok := obj.(*v1alpha2.EdgeX)
if !ok {
return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", obj))
}

if allErrs := webhook.validate(ctx, edgex); len(allErrs) > 0 {
return apierrors.NewInvalid(v1alpha1.GroupVersion.WithKind("EdgeX").GroupKind(), edgex.Name, allErrs)
return apierrors.NewInvalid(v1alpha2.GroupVersion.WithKind("EdgeX").GroupKind(), edgex.Name, allErrs)
}

return nil
}

// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type.
func (webhook *EdgeXHandler) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error {
newEdgex, ok := newObj.(*v1alpha1.EdgeX)
newEdgex, ok := newObj.(*v1alpha2.EdgeX)
if !ok {
return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", newObj))
return apierrors.NewBadRequest(fmt.Sprintf("expected a new Cluster but got a %T", newObj))
}
oldEdgex, ok := oldObj.(*v1alpha1.EdgeX)

oldEdgex, ok := oldObj.(*v1alpha2.EdgeX)
if !ok {
return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", oldObj))
return apierrors.NewBadRequest(fmt.Sprintf("expected a old Cluster but got a %T", newObj))
}

newErrorList := webhook.validate(ctx, newEdgex)
oldErrorList := webhook.validate(ctx, oldEdgex)

if allErrs := append(newErrorList, oldErrorList...); len(allErrs) > 0 {
return apierrors.NewInvalid(v1alpha1.GroupVersion.WithKind("EdgeX").GroupKind(), newEdgex.Name, allErrs)
return apierrors.NewInvalid(v1alpha2.GroupVersion.WithKind("EdgeX").GroupKind(), newEdgex.Name, allErrs)
}
return nil
}
Expand All @@ -112,13 +147,32 @@ func (webhook *EdgeXHandler) ValidateDelete(_ context.Context, obj runtime.Objec
}

// validate validates a EdgeX
func (webhook *EdgeXHandler) validate(ctx context.Context, edgex *v1alpha1.EdgeX) field.ErrorList {
func (webhook *EdgeXHandler) validate(ctx context.Context, edgex *v1alpha2.EdgeX) field.ErrorList {

// verify the version
// if !(edgex.Spec.Version == "jakarta" || edgex.Spec.Version == "hanoi") {
// return field.ErrorList{
// field.Invalid(field.NewPath("spec", "version"), edgex.Spec.Version, "must be one of jakarta, hanoi"),
// }
// }
if specErrs := webhook.validateEdgeXSpec(edgex); specErrs != nil {
return specErrs
}
// verify that the poolname nodepool
if nodePoolErrs := webhook.validateEdgeXWithNodePools(ctx, edgex); nodePoolErrs != nil {
return nodePoolErrs
}
return nil
}

func (webhook *EdgeXHandler) validateEdgeXSpec(edgex *v1alpha2.EdgeX) field.ErrorList {
for _, version := range manifest.Versions {
if edgex.Spec.Version == version {
return nil
}
}

return field.ErrorList{
field.Invalid(field.NewPath("spec", "version"), edgex.Spec.Version, "must be one of"+strings.Join(manifest.Versions, ",")),
}
}

func (webhook *EdgeXHandler) validateEdgeXWithNodePools(ctx context.Context, edgex *v1alpha2.EdgeX) field.ErrorList {
// verify that the poolname is a right nodepool name
nodePools := &unitv1alpha1.NodePoolList{}
if err := webhook.Client.List(ctx, nodePools); err != nil {
Expand All @@ -139,7 +193,7 @@ func (webhook *EdgeXHandler) validate(ctx context.Context, edgex *v1alpha1.EdgeX
}
}
// verify that no other edgex in the nodepool
var edgexes v1alpha1.EdgeXList
var edgexes v1alpha2.EdgeXList
listOptions := client.MatchingFields{util.IndexerPathForNodepool: edgex.Spec.PoolName}
if err := webhook.Client.List(ctx, &edgexes, listOptions); err != nil {
return field.ErrorList{
Expand All @@ -153,11 +207,7 @@ func (webhook *EdgeXHandler) validate(ctx context.Context, edgex *v1alpha1.EdgeX
}
}
}
// verify the ServiceType
if !(edgex.Spec.ServiceType == corev1.ServiceTypeClusterIP || edgex.Spec.ServiceType == corev1.ServiceTypeNodePort) {
return field.ErrorList{
field.Invalid(field.NewPath("spec", "serviceType"), edgex.Spec.ServiceType, "must be one of ClusterIP, NodePort"),
}
}

return nil

}
Loading