Skip to content

Commit

Permalink
feat(v2 upgrade/webhook): validate data engine upgrade related fields
Browse files Browse the repository at this point in the history
Longhorn 9104

Signed-off-by: Derek Su <[email protected]>
  • Loading branch information
derekbit committed Nov 25, 2024
1 parent bef8b75 commit 4a0aa27
Show file tree
Hide file tree
Showing 8 changed files with 465 additions and 27 deletions.
24 changes: 24 additions & 0 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ const (
LonghornLabelManagedBy = "managed-by"
LonghornLabelSnapshotForCloningVolume = "for-cloning-volume"
LonghornLabelBackingImageDataSource = "backing-image-data-source"
LonghornLabelDataEngineUpgradeManager = "data-engine-upgrade-manager"
LonghornLabelNodeDataEngineUpgrade = "node-data-engine-upgrade"
LonghornLabelBackupVolume = "backup-volume"
LonghornLabelRecurringJob = "job"
LonghornLabelRecurringJobGroup = "job-group"
Expand Down Expand Up @@ -1265,3 +1267,25 @@ func MergeStringMaps(baseMap, overwriteMap map[string]string) map[string]string
}
return result
}

func GenerateNodeDataEngineUpgradeName(prefix, nodeID string) string {
return prefix + "-" + nodeID + "-" + util.RandomID()
}

func GetDataEngineUpgradeManagerLabels() map[string]string {
labels := GetBaseLabelsForSystemManagedComponent()
labels[GetLonghornLabelComponentKey()] = LonghornLabelDataEngineUpgradeManager
return labels
}

func GetNodeDataEngineUpgradeLabels(upgradeManagerID, nodeID string) map[string]string {
labels := GetBaseLabelsForSystemManagedComponent()
labels[GetLonghornLabelComponentKey()] = LonghornLabelNodeDataEngineUpgrade
if upgradeManagerID != "" {
labels[GetLonghornLabelKey(LonghornLabelDataEngineUpgradeManager)] = upgradeManagerID
}
if nodeID != "" {
labels[GetLonghornLabelKey(LonghornLabelNode)] = nodeID
}
return labels
}
74 changes: 74 additions & 0 deletions webhook/resources/dataengineupgrademanager/mutator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package dataengineupgrademanager

import (
"fmt"

"github.com/pkg/errors"

"k8s.io/apimachinery/pkg/runtime"

admissionregv1 "k8s.io/api/admissionregistration/v1"

"github.com/longhorn/longhorn-manager/datastore"
"github.com/longhorn/longhorn-manager/types"
"github.com/longhorn/longhorn-manager/webhook/admission"
"github.com/longhorn/longhorn-manager/webhook/common"

longhorn "github.com/longhorn/longhorn-manager/k8s/pkg/apis/longhorn/v1beta2"
werror "github.com/longhorn/longhorn-manager/webhook/error"
)

type dataEngineUpgradeManagerMutator struct {
admission.DefaultMutator
ds *datastore.DataStore
}

func NewMutator(ds *datastore.DataStore) admission.Mutator {
return &dataEngineUpgradeManagerMutator{ds: ds}
}

func (u *dataEngineUpgradeManagerMutator) Resource() admission.Resource {
return admission.Resource{
Name: "dataengineupgrademanagers",
Scope: admissionregv1.NamespacedScope,
APIGroup: longhorn.SchemeGroupVersion.Group,
APIVersion: longhorn.SchemeGroupVersion.Version,
ObjectType: &longhorn.DataEngineUpgradeManager{},
OperationTypes: []admissionregv1.OperationType{
admissionregv1.Create,
},
}
}

func (u *dataEngineUpgradeManagerMutator) Create(request *admission.Request, newObj runtime.Object) (admission.PatchOps, error) {
return mutate(newObj)
}

func mutate(newObj runtime.Object) (admission.PatchOps, error) {
upgradeManager, ok := newObj.(*longhorn.DataEngineUpgradeManager)
if !ok {
return nil, werror.NewInvalidError(fmt.Sprintf("%v is not a *longhorn.DataEngineUpgradeManager", newObj), "")
}
var patchOps admission.PatchOps

longhornLabels := types.GetDataEngineUpgradeManagerLabels()
patchOp, err := common.GetLonghornLabelsPatchOp(upgradeManager, longhornLabels, nil)
if err != nil {
err := errors.Wrapf(err, "failed to get label patch for upgradeManager %v", upgradeManager.Name)
return nil, werror.NewInvalidError(err.Error(), "")
}
if patchOp != "" {
patchOps = append(patchOps, patchOp)
}

patchOp, err = common.GetLonghornFinalizerPatchOpIfNeeded(upgradeManager)
if err != nil {
err := errors.Wrapf(err, "failed to get finalizer patch for dataEngineUpgradeManager %v", upgradeManager.Name)
return nil, werror.NewInvalidError(err.Error(), "")
}
if patchOp != "" {
patchOps = append(patchOps, patchOp)
}

return patchOps, nil
}
79 changes: 79 additions & 0 deletions webhook/resources/dataengineupgrademanager/validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package dataengineupgrademanager

import (
"fmt"
"reflect"

"k8s.io/apimachinery/pkg/runtime"

admissionregv1 "k8s.io/api/admissionregistration/v1"

"github.com/longhorn/longhorn-manager/datastore"
"github.com/longhorn/longhorn-manager/webhook/admission"

longhorn "github.com/longhorn/longhorn-manager/k8s/pkg/apis/longhorn/v1beta2"
werror "github.com/longhorn/longhorn-manager/webhook/error"
)

type dataEngineUpgradeManagerValidator struct {
admission.DefaultValidator
ds *datastore.DataStore
}

func NewValidator(ds *datastore.DataStore) admission.Validator {
return &dataEngineUpgradeManagerValidator{ds: ds}
}

func (u *dataEngineUpgradeManagerValidator) Resource() admission.Resource {
return admission.Resource{
Name: "dataengineupgrademanagers",
Scope: admissionregv1.NamespacedScope,
APIGroup: longhorn.SchemeGroupVersion.Group,
APIVersion: longhorn.SchemeGroupVersion.Version,
ObjectType: &longhorn.DataEngineUpgradeManager{},
OperationTypes: []admissionregv1.OperationType{
admissionregv1.Create,
admissionregv1.Update,
},
}
}

func (u *dataEngineUpgradeManagerValidator) Create(request *admission.Request, newObj runtime.Object) error {
upgradeManager, ok := newObj.(*longhorn.DataEngineUpgradeManager)
if !ok {
return werror.NewInvalidError(fmt.Sprintf("%v is not a *longhorn.DataEngineUpgradeManager", newObj), "")
}

if upgradeManager.Spec.DataEngine != longhorn.DataEngineTypeV2 {
err := fmt.Errorf("data engine %v is not supported", upgradeManager.Spec.DataEngine)
return werror.NewInvalidError(err.Error(), "spec.dataEngine")
}

return nil
}

func (u *dataEngineUpgradeManagerValidator) Update(request *admission.Request, oldObj runtime.Object, newObj runtime.Object) error {
oldUpgradeManager, ok := oldObj.(*longhorn.DataEngineUpgradeManager)
if !ok {
return werror.NewInvalidError(fmt.Sprintf("%v is not a *longhorn.DataEngineUpgradeManager", oldObj), "")
}
newUpgradeManager, ok := newObj.(*longhorn.DataEngineUpgradeManager)
if !ok {
return werror.NewInvalidError(fmt.Sprintf("%v is not a *longhorn.DataEngineUpgradeManager", newObj), "")
}

if newUpgradeManager.Spec.DataEngine != longhorn.DataEngineTypeV2 {
err := fmt.Errorf("data engine %v is not supported", newUpgradeManager.Spec.DataEngine)
return werror.NewInvalidError(err.Error(), "spec.dataEngine")
}

if oldUpgradeManager.Spec.DataEngine != newUpgradeManager.Spec.DataEngine {
return werror.NewInvalidError("spec.dataEngine field is immutable", "spec.dataEngine")
}

if !reflect.DeepEqual(oldUpgradeManager.Spec.Nodes, newUpgradeManager.Spec.Nodes) {
return werror.NewInvalidError("nodes field is immutable", "spec.nodes")
}

return nil
}
74 changes: 74 additions & 0 deletions webhook/resources/nodedataengineupgrade/mutator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package nodedataengineupgrade

import (
"fmt"

"github.com/pkg/errors"

"k8s.io/apimachinery/pkg/runtime"

admissionregv1 "k8s.io/api/admissionregistration/v1"

"github.com/longhorn/longhorn-manager/datastore"
"github.com/longhorn/longhorn-manager/types"
"github.com/longhorn/longhorn-manager/webhook/admission"
"github.com/longhorn/longhorn-manager/webhook/common"

longhorn "github.com/longhorn/longhorn-manager/k8s/pkg/apis/longhorn/v1beta2"
werror "github.com/longhorn/longhorn-manager/webhook/error"
)

type nodeDataEngineUpgradeMutator struct {
admission.DefaultMutator
ds *datastore.DataStore
}

func NewMutator(ds *datastore.DataStore) admission.Mutator {
return &nodeDataEngineUpgradeMutator{ds: ds}
}

func (u *nodeDataEngineUpgradeMutator) Resource() admission.Resource {
return admission.Resource{
Name: "nodedataengineupgrades",
Scope: admissionregv1.NamespacedScope,
APIGroup: longhorn.SchemeGroupVersion.Group,
APIVersion: longhorn.SchemeGroupVersion.Version,
ObjectType: &longhorn.NodeDataEngineUpgrade{},
OperationTypes: []admissionregv1.OperationType{
admissionregv1.Create,
},
}
}

func (u *nodeDataEngineUpgradeMutator) Create(request *admission.Request, newObj runtime.Object) (admission.PatchOps, error) {
return mutate(newObj)
}

func mutate(newObj runtime.Object) (admission.PatchOps, error) {
nodeUpgrade, ok := newObj.(*longhorn.NodeDataEngineUpgrade)
if !ok {
return nil, werror.NewInvalidError(fmt.Sprintf("%v is not a *longhorn.NodeDataEngineUpgrade", newObj), "")
}
var patchOps admission.PatchOps

longhornLabels := types.GetNodeDataEngineUpgradeLabels(nodeUpgrade.Spec.DataEngineUpgradeManager, nodeUpgrade.Spec.NodeID)
patchOp, err := common.GetLonghornLabelsPatchOp(nodeUpgrade, longhornLabels, nil)
if err != nil {
err := errors.Wrapf(err, "failed to get label patch for nodeUpgrade %v", nodeUpgrade.Name)
return nil, werror.NewInvalidError(err.Error(), "")
}
if patchOp != "" {
patchOps = append(patchOps, patchOp)
}

patchOp, err = common.GetLonghornFinalizerPatchOpIfNeeded(nodeUpgrade)
if err != nil {
err := errors.Wrapf(err, "failed to get finalizer patch for nodeDataEngineUpgrade %v", nodeUpgrade.Name)
return nil, werror.NewInvalidError(err.Error(), "")
}
if patchOp != "" {
patchOps = append(patchOps, patchOp)
}

return patchOps, nil
}
95 changes: 95 additions & 0 deletions webhook/resources/nodedataengineupgrade/validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package nodedataengineupgrade

import (
"fmt"

"k8s.io/apimachinery/pkg/runtime"

admissionregv1 "k8s.io/api/admissionregistration/v1"

"github.com/longhorn/longhorn-manager/datastore"
"github.com/longhorn/longhorn-manager/webhook/admission"

longhorn "github.com/longhorn/longhorn-manager/k8s/pkg/apis/longhorn/v1beta2"
werror "github.com/longhorn/longhorn-manager/webhook/error"
)

type nodeDataEngineUpgradeValidator struct {
admission.DefaultValidator
ds *datastore.DataStore
}

func NewValidator(ds *datastore.DataStore) admission.Validator {
return &nodeDataEngineUpgradeValidator{ds: ds}
}

func (u *nodeDataEngineUpgradeValidator) Resource() admission.Resource {
return admission.Resource{
Name: "nodedataengineupgrades",
Scope: admissionregv1.NamespacedScope,
APIGroup: longhorn.SchemeGroupVersion.Group,
APIVersion: longhorn.SchemeGroupVersion.Version,
ObjectType: &longhorn.NodeDataEngineUpgrade{},
OperationTypes: []admissionregv1.OperationType{
admissionregv1.Create,
admissionregv1.Update,
},
}
}

func (u *nodeDataEngineUpgradeValidator) Create(request *admission.Request, newObj runtime.Object) error {
nodeUpgrade, ok := newObj.(*longhorn.NodeDataEngineUpgrade)
if !ok {
return werror.NewInvalidError(fmt.Sprintf("%v is not a *longhorn.NodeDataEngineUpgrade", newObj), "")
}

if nodeUpgrade.Spec.NodeID == "" {
return werror.NewInvalidError("nodeID is required", "spec.nodeID")
}

if nodeUpgrade.Spec.DataEngine != longhorn.DataEngineTypeV2 {
err := fmt.Errorf("data engine %v is not supported", nodeUpgrade.Spec.DataEngine)
return werror.NewInvalidError(err.Error(), "spec.dataEngine")
}

if nodeUpgrade.Spec.InstanceManagerImage == "" {
err := fmt.Errorf("instanceManagerImage is required")
return werror.NewInvalidError(err.Error(), "spec.instanceManagerImage")
}

if nodeUpgrade.Spec.DataEngineUpgradeManager == "" {
err := fmt.Errorf("dataEngineUpgradeManager is required")
return werror.NewInvalidError(err.Error(), "spec.dataEngineUpgradeManager")
}

return nil
}

func (u *nodeDataEngineUpgradeValidator) Update(request *admission.Request, oldObj runtime.Object, newObj runtime.Object) error {
oldNodeUpgrade, ok := oldObj.(*longhorn.NodeDataEngineUpgrade)
if !ok {
return werror.NewInvalidError(fmt.Sprintf("%v is not a *longhorn.NodeDataEngineUpgrade", oldObj), "")
}
newNodeUpgrade, ok := newObj.(*longhorn.NodeDataEngineUpgrade)
if !ok {
return werror.NewInvalidError(fmt.Sprintf("%v is not a *longhorn.NodeDataEngineUpgrade", newObj), "")
}

if oldNodeUpgrade.Spec.NodeID != newNodeUpgrade.Spec.NodeID {
return werror.NewInvalidError("nodeID field is immutable", "spec.nodeID")
}

if oldNodeUpgrade.Spec.DataEngine != newNodeUpgrade.Spec.DataEngine {
return werror.NewInvalidError("dataEngine field is immutable", "spec.dataEngine")
}

if oldNodeUpgrade.Spec.InstanceManagerImage != newNodeUpgrade.Spec.InstanceManagerImage {
return werror.NewInvalidError("instanceManagerImage field is immutable", "spec.instanceManagerImage")
}

if oldNodeUpgrade.Spec.DataEngineUpgradeManager != newNodeUpgrade.Spec.DataEngineUpgradeManager {
return werror.NewInvalidError("dataEngineUpgradeManager field is immutable", "spec.dataEngineUpgradeManager")
}

return nil
}
Loading

0 comments on commit 4a0aa27

Please sign in to comment.