Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: harvester/network-controller-harvester
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: a91751ea5352e9135e6fb9efa89ddd6dd1af2bf6
Choose a base ref
..
head repository: harvester/network-controller-harvester
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 9c6f97394ae5215eaae72c47bcb56433e293a596
Choose a head ref
Showing with 118 additions and 23 deletions.
  1. +74 −3 pkg/controller/manager/node/controller.go
  2. +5 −4 pkg/webhook/nad/mutator.go
  3. +39 −16 pkg/webhook/nad/validator.go
77 changes: 74 additions & 3 deletions pkg/controller/manager/node/controller.go
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import (
mapset "github.com/deckarep/golang-set/v2"
ctlcorev1 "github.com/rancher/wrangler/pkg/generated/controllers/core/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/klog"

@@ -23,19 +24,24 @@ type Handler struct {
nodeClient ctlcorev1.NodeClient
vcCache ctlnetworkv1.VlanConfigCache
vcClient ctlnetworkv1.VlanConfigClient
vsCache ctlnetworkv1.VlanStatusCache
vsClient ctlnetworkv1.VlanStatusClient
lmCache ctlnetworkv1.LinkMonitorCache
lmClient ctlnetworkv1.LinkMonitorClient
}

func Register(ctx context.Context, management *config.Management) error {
nodes := management.CoreFactory.Core().V1().Node()
vcs := management.HarvesterNetworkFactory.Network().V1beta1().VlanConfig()
vss := management.HarvesterNetworkFactory.Network().V1beta1().VlanStatus()
lms := management.HarvesterNetworkFactory.Network().V1beta1().LinkMonitor()

h := Handler{
nodeClient: nodes,
vcCache: vcs.Cache(),
vcClient: vcs,
vsCache: vss.Cache(),
vsClient: vss,
lmCache: lms.Cache(),
lmClient: lms,
}
@@ -76,7 +82,14 @@ func (h Handler) OnRemove(key string, node *corev1.Node) (*corev1.Node, error) {

klog.Infof("node %s is removed", node.Name)

return node, h.clearLinkStatus(node.Name)
if err := h.removeNodeFromVlanConfig(node.Name); err != nil {
return nil, err
}
if err := h.clearLinkStatus(node.Name); err != nil {
return nil, err
}

return node, nil
}

func (h Handler) updateMatchedNodeAnnotation(vc *networkv1.VlanConfig, node *corev1.Node) error {
@@ -86,8 +99,10 @@ func (h Handler) updateMatchedNodeAnnotation(vc *networkv1.VlanConfig, node *cor
}

s := mapset.NewSet[string]()
if err := s.UnmarshalJSON([]byte(vc.Annotations[utils.KeyMatchedNodes])); err != nil {
return err
if vc.Annotations != nil && vc.Annotations[utils.KeyMatchedNodes] != "" {
if err := s.UnmarshalJSON([]byte(vc.Annotations[utils.KeyMatchedNodes])); err != nil {
return err
}
}

newSet := s.Clone()
@@ -154,3 +169,59 @@ func (h Handler) clearLinkStatus(nodeName string) error {

return nil
}

// remove the node from the matched node list of the vlan config and the related vlan status
func (h Handler) removeNodeFromVlanConfig(nodeName string) error {
vcs, err := h.vcCache.List(labels.Everything())
if err != nil {
return err
}

for _, vc := range vcs {
if err := h.removeNodeFromOneVlanConfig(vc, nodeName); err != nil {
return err
}
}

return nil
}

func (h Handler) removeNodeFromOneVlanConfig(vc *networkv1.VlanConfig, nodeName string) error {
if vc.Annotations == nil || vc.Annotations[utils.KeyMatchedNodes] == "" {
return nil
}

s := mapset.NewSet[string]()
if err := s.UnmarshalJSON([]byte(vc.Annotations[utils.KeyMatchedNodes])); err != nil {
return err
}

if s.Contains(nodeName) {
s.Remove(nodeName)
bytes, err := s.MarshalJSON()
if err != nil {
return err
}
vcCopy := vc.DeepCopy()
vcCopy.Annotations[utils.KeyMatchedNodes] = string(bytes)
if _, err := h.vcClient.Update(vcCopy); err != nil {
return err
}

vss, err := h.vsCache.List(labels.Set{
utils.KeyVlanConfigLabel: vc.Name,
utils.KeyNodeLabel: nodeName,
}.AsSelector())
if err != nil {
return err
}
if len(vss) == 0 {
return nil
}
if err := h.vsClient.Delete(vss[0].Name, &metav1.DeleteOptions{}); err != nil {
return err
}
}

return nil
}
9 changes: 5 additions & 4 deletions pkg/webhook/nad/mutator.go
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ package nad
import (
"encoding/json"
"fmt"
"reflect"
"strconv"

"github.com/harvester/webhook/pkg/types"
@@ -37,10 +38,6 @@ func (m *Mutator) Update(_ *types.Request, oldObj, newObj runtime.Object) (types
if newNad.DeletionTimestamp != nil {
return nil, nil
}
// ignore the update if the config is not being updated
if oldNad.Spec.Config == newNad.Spec.Config {
return nil, nil
}

oldNetconf, newNetconf := &utils.NetConf{}, &utils.NetConf{}
if err := json.Unmarshal([]byte(oldNad.Spec.Config), oldNetconf); err != nil {
@@ -49,6 +46,10 @@ func (m *Mutator) Update(_ *types.Request, oldObj, newObj runtime.Object) (types
if err := json.Unmarshal([]byte(newNad.Spec.Config), newNetconf); err != nil {
return nil, fmt.Errorf(updateErr, newNad.Namespace, newNad.Name, err)
}
// ignore the update if the config is not being updated
if reflect.DeepEqual(oldNetconf, newNetconf) {
return nil, nil
}

patch, err := m.ensureLabels(newNad, oldNetconf, newNetconf)
if err != nil {
55 changes: 39 additions & 16 deletions pkg/webhook/nad/validator.go
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ package nad
import (
"encoding/json"
"fmt"
"reflect"
"strings"

ctlkubevirtv1 "github.com/harvester/harvester/pkg/generated/controllers/kubevirt.io/v1"
@@ -37,9 +38,15 @@ func NewNadValidator(vmiCache ctlkubevirtv1.VirtualMachineInstanceCache) *Valida
func (v *Validator) Create(_ *types.Request, newObj runtime.Object) error {
nad := newObj.(*cniv1.NetworkAttachmentDefinition)

if err := v.checkNadConfig(nad.Spec.Config); err != nil {
if err := v.checkRoute(nad.Annotations[utils.KeyNetworkRoute]); err != nil {
return fmt.Errorf(createErr, nad.Namespace, nad.Name, err)
} else if err := v.checkRoute(nad.Annotations[utils.KeyNetworkRoute]); err != nil {
}

conf, err := encodeConfig(nad.Spec.Config)
if err != nil {
return fmt.Errorf(createErr, nad.Namespace, nad.Name, err)
}
if err := v.checkNadConfig(conf); err != nil {
return fmt.Errorf(createErr, nad.Namespace, nad.Name, err)
}

@@ -54,15 +61,24 @@ func (v *Validator) Update(_ *types.Request, oldObj, newObj runtime.Object) erro
if newNad.DeletionTimestamp != nil {
return nil
}
// ignore the update if the config and route are not being updated
if newNad.Spec.Config == oldNad.Spec.Config &&
newNad.Annotations[utils.KeyNetworkRoute] == oldNad.Annotations[utils.KeyNetworkRoute] {
return nil

if err := v.checkRoute(newNad.Annotations[utils.KeyNetworkRoute]); err != nil {
return fmt.Errorf(updateErr, newNad.Namespace, newNad.Name, err)
}

if err := v.checkNadConfig(newNad.Spec.Config); err != nil {
newConf, err := encodeConfig(newNad.Spec.Config)
if err != nil {
return fmt.Errorf(updateErr, newNad.Namespace, newNad.Name, err)
}
oldConf, err := encodeConfig(oldNad.Spec.Config)
if err != nil {
return fmt.Errorf(updateErr, newNad.Namespace, newNad.Name, err)
} else if err := v.checkRoute(newNad.Annotations[utils.KeyNetworkRoute]); err != nil {
}
// skip the update if the config is not changed
if reflect.DeepEqual(newConf, oldConf) {
return nil
}
if err := v.checkNadConfig(newConf); err != nil {
return fmt.Errorf(updateErr, newNad.Namespace, newNad.Name, err)
}

@@ -83,17 +99,11 @@ func (v *Validator) Delete(_ *types.Request, oldObj runtime.Object) error {
return nil
}

func (v *Validator) checkNadConfig(config string) error {
if config == "" {
func (v *Validator) checkNadConfig(bridgeConf *utils.NetConf) error {
if bridgeConf == nil {
return fmt.Errorf("config is empty")
}

var bridgeConf = &utils.NetConf{}
err := json.Unmarshal([]byte(config), &bridgeConf)
if err != nil {
return fmt.Errorf("unmarshal %s failed, error: %w", config, err)
}

// The VLAN value of untagged network will be empty or number 0.
if bridgeConf.Vlan < 0 || bridgeConf.Vlan > 4094 {
return fmt.Errorf("VLAN ID must >=0 and <=4094")
@@ -147,3 +157,16 @@ func (v *Validator) Resource() types.Resource {
},
}
}

func encodeConfig(config string) (*utils.NetConf, error) {
conf := &utils.NetConf{}
if config == "" {
return conf, nil
}

if err := json.Unmarshal([]byte(config), &conf); err != nil {
return nil, fmt.Errorf("unmarshal config %s failed: %w", config, err)
}

return conf, nil
}