diff --git a/Dockerfile b/Dockerfile index a6a681f..199d896 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:experimental # Build the manager binary -FROM golang:1.17 as builder +FROM golang:1.18 as builder WORKDIR /workspace diff --git a/README.md b/README.md index a719962..e2f3e27 100644 --- a/README.md +++ b/README.md @@ -34,16 +34,10 @@ Netris Operator managing resources samples are available in the `samples/` [fold | `v0.X` | `v2.9` | | `v1.X` | `v3.0` | | `v2.X` | `v3.1+` | + | `v3.X` | `v4.1+` | ## Features * Managing Netris Controller via CRD * Automatically creating `L4LB` resource for `type: load-balancer` services -* Integration with CNI: - | | | - | -----------| ---------------------| - | `Calico` | `Ready` | - | `Cilium` | *`In progress`* | - | `Flannel` | *`In progress`* | - - +* All CNIs are welcome diff --git a/api/v1alpha1/bgp_types.go b/api/v1alpha1/bgp_types.go index 4193511..102f154 100644 --- a/api/v1alpha1/bgp_types.go +++ b/api/v1alpha1/bgp_types.go @@ -89,7 +89,7 @@ type BGPMultihop struct { type BGPTransport struct { // +kubebuilder:validation:Enum=port;vnet Type string `json:"type,omitempty"` - // +kubebuilder:validation:Pattern=`(^[a-zA-Z0-9]+@[a-zA-Z0-9-]+$)|(^[a-zA-Z0-9]([-a-zA-Z0-9]*[a-z0-9])?(\.[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?)*$)` + // +kubebuilder:validation:Pattern=`(^[a-zA-Z0-9]+@[a-zA-Z0-9-]+$)|(^[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?)*$)` Name string `json:"name"` VlanID int `json:"vlanId,omitempty"` } diff --git a/api/v1alpha1/nat_types.go b/api/v1alpha1/nat_types.go index 9ff38a4..d626ef2 100644 --- a/api/v1alpha1/nat_types.go +++ b/api/v1alpha1/nat_types.go @@ -59,9 +59,7 @@ type NatSpec struct { // +kubebuilder:validation:Pattern=`(^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[12]\d|3[0-2]))?$)|(^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?(\/([1-9]|[1-5][0-9]|6[0-4]))?$)` DnatToIP string `json:"dnatToIp,omitempty"` - // +kubebuilder:validation:Minimum=1 - // +kubebuilder:validation:Maximum=65535 - DnatToPort int `json:"dnatToPort,omitempty"` + DnatToPort string `json:"dnatToPort,omitempty"` } // NatStatus defines the observed state of Nat @@ -85,7 +83,7 @@ type NatStatus struct { // +kubebuilder:printcolumn:name="SNATToIP",type=string,JSONPath=`.spec.snatToIp`,priority=1 // +kubebuilder:printcolumn:name="SNATToPool",type=string,JSONPath=`.spec.snatToPool`,priority=1 // +kubebuilder:printcolumn:name="DNATToIP",type=string,JSONPath=`.spec.dnatToIp`,priority=1 -// +kubebuilder:printcolumn:name="DNATToPort",type=integer,JSONPath=`.spec.dnatToPort`,priority=1 +// +kubebuilder:printcolumn:name="DNATToPort",type=string,JSONPath=`.spec.dnatToPort`,priority=1 // +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.status` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` diff --git a/api/v1alpha1/natmeta_types.go b/api/v1alpha1/natmeta_types.go index e0b6f26..194ab8e 100644 --- a/api/v1alpha1/natmeta_types.go +++ b/api/v1alpha1/natmeta_types.go @@ -45,7 +45,7 @@ type NatMetaSpec struct { SnatToIP string `json:"snatToIp,omitempty"` SnatToPool string `json:"snatToPool,omitempty"` DnatToIP string `json:"dnatToIp,omitempty"` - DnatToPort int `json:"dnatToPort,omitempty"` + DnatToPort string `json:"dnatToPort,omitempty"` } // NatMetaStatus defines the observed state of NatMeta diff --git a/api/v1alpha1/vnet_types.go b/api/v1alpha1/vnet_types.go index b0a8dca..3e71653 100644 --- a/api/v1alpha1/vnet_types.go +++ b/api/v1alpha1/vnet_types.go @@ -118,6 +118,8 @@ type VNetSwitchPort struct { // +kubebuilder:validation:Maximum=4094 VlanID int `json:"vlanId,omitempty"` State string `json:"state,omitempty"` + // +kubebuilder:validation:Enum=yes;no + Untagged string `json:"untagged,omitempty"` } func init() { diff --git a/api/v1alpha1/vnetmeta_types.go b/api/v1alpha1/vnetmeta_types.go index 561567a..235bbdb 100644 --- a/api/v1alpha1/vnetmeta_types.go +++ b/api/v1alpha1/vnetmeta_types.go @@ -52,12 +52,13 @@ type VNetMetaSite struct { // VNetMetaMember . type VNetMetaMember struct { - Name string `json:"name"` - Access bool `json:"access"` - ID int `json:"id"` - Lacp string `json:"lacp"` - State string `json:"state"` - Vlan string `json:"vlan"` + Name string `json:"name"` + Access bool `json:"access"` + ID int `json:"id"` + Lacp string `json:"lacp"` + State string `json:"state"` + Vlan string `json:"vlan"` + Untagged string `json:"untagged,omitempty"` } // VNetMetaGateway . diff --git a/config/crd/bases/k8s.netris.ai_bgps.yaml b/config/crd/bases/k8s.netris.ai_bgps.yaml index 270a877..9d34dba 100644 --- a/config/crd/bases/k8s.netris.ai_bgps.yaml +++ b/config/crd/bases/k8s.netris.ai_bgps.yaml @@ -139,7 +139,7 @@ spec: description: BGPTransport . properties: name: - pattern: (^[a-zA-Z0-9]+@[a-zA-Z0-9-]+$)|(^[a-zA-Z0-9]([-a-zA-Z0-9]*[a-z0-9])?(\.[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?)*$) + pattern: (^[a-zA-Z0-9]+@[a-zA-Z0-9-]+$)|(^[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?)*$) type: string type: enum: diff --git a/config/crd/bases/k8s.netris.ai_natmeta.yaml b/config/crd/bases/k8s.netris.ai_natmeta.yaml index 53688ef..07f0cc8 100644 --- a/config/crd/bases/k8s.netris.ai_natmeta.yaml +++ b/config/crd/bases/k8s.netris.ai_natmeta.yaml @@ -43,7 +43,7 @@ spec: dnatToIp: type: string dnatToPort: - type: integer + type: string dstAddress: type: string dstPort: diff --git a/config/crd/bases/k8s.netris.ai_nats.yaml b/config/crd/bases/k8s.netris.ai_nats.yaml index cd61776..67e70f7 100644 --- a/config/crd/bases/k8s.netris.ai_nats.yaml +++ b/config/crd/bases/k8s.netris.ai_nats.yaml @@ -56,7 +56,7 @@ spec: - jsonPath: .spec.dnatToPort name: DNATToPort priority: 1 - type: integer + type: string - jsonPath: .status.status name: Status type: string @@ -96,9 +96,7 @@ spec: pattern: (^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[12]\d|3[0-2]))?$)|(^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?(\/([1-9]|[1-5][0-9]|6[0-4]))?$) type: string dnatToPort: - maximum: 65535 - minimum: 1 - type: integer + type: string dstAddress: pattern: (^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[12]\d|3[0-2]))?$)|(^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?(\/([1-9]|[1-5][0-9]|6[0-4]))?$) type: string diff --git a/config/crd/bases/k8s.netris.ai_vnetmeta.yaml b/config/crd/bases/k8s.netris.ai_vnetmeta.yaml index dc8ef6d..ee3eac0 100644 --- a/config/crd/bases/k8s.netris.ai_vnetmeta.yaml +++ b/config/crd/bases/k8s.netris.ai_vnetmeta.yaml @@ -83,6 +83,8 @@ spec: type: string state: type: string + untagged: + type: string vlan: type: string required: diff --git a/config/crd/bases/k8s.netris.ai_vnets.yaml b/config/crd/bases/k8s.netris.ai_vnets.yaml index b5c300c..55ebc24 100644 --- a/config/crd/bases/k8s.netris.ai_vnets.yaml +++ b/config/crd/bases/k8s.netris.ai_vnets.yaml @@ -108,6 +108,11 @@ spec: type: string state: type: string + untagged: + enum: + - "yes" + - "no" + type: string vlanId: maximum: 4094 minimum: 2 diff --git a/controllers/api_handler.go b/controllers/api_handler.go index 148a9aa..2c3b428 100644 --- a/controllers/api_handler.go +++ b/controllers/api_handler.go @@ -24,7 +24,6 @@ import ( k8sv1alpha1 "github.com/netrisai/netris-operator/api/v1alpha1" "github.com/netrisai/netris-operator/configloader" "github.com/netrisai/netris-operator/netrisstorage" - "github.com/netrisai/netriswebapi/v2/types/vnet" ) func init() { @@ -36,7 +35,7 @@ func init() { func (r *VNetReconciler) getPortsMeta(portNames []k8sv1alpha1.VNetSwitchPort) ([]k8sv1alpha1.VNetMetaMember, error) { members := []k8sv1alpha1.VNetMetaMember{} - hwPorts := make(map[string]*vnet.VNetAddPort) + hwPorts := make(map[string]*k8sv1alpha1.VNetMetaMember) for _, port := range portNames { vlanID := "1" if port.VlanID > 1 { @@ -50,10 +49,18 @@ func (r *VNetReconciler) getPortsMeta(portNames []k8sv1alpha1.VNetSwitchPort) ([ } } - hwPorts[port.Name] = &vnet.VNetAddPort{ - Vlan: vlanID, - Lacp: "off", - State: state, + untagged := "" + if len(port.Untagged) > 0 { + if port.Untagged == "yes" || port.Untagged == "no" { + untagged = port.Untagged + } + } + + hwPorts[port.Name] = &k8sv1alpha1.VNetMetaMember{ + Vlan: vlanID, + Lacp: "off", + State: state, + Untagged: untagged, } } @@ -69,11 +76,12 @@ func (r *VNetReconciler) getPortsMeta(portNames []k8sv1alpha1.VNetSwitchPort) ([ for _, member := range hwPorts { members = append(members, k8sv1alpha1.VNetMetaMember{ - Name: member.Name, - Lacp: member.Lacp, - State: member.State, - ID: member.ID, - Vlan: member.Vlan, + Name: member.Name, + Lacp: member.Lacp, + State: member.State, + ID: member.ID, + Vlan: member.Vlan, + Untagged: member.Untagged, }) } return members, nil diff --git a/controllers/bgp_translations.go b/controllers/bgp_translations.go index 7778216..154d9e2 100644 --- a/controllers/bgp_translations.go +++ b/controllers/bgp_translations.go @@ -66,7 +66,7 @@ func (r *BGPReconciler) BGPToBGPMeta(bgp *k8sv1alpha1.BGP) (*k8sv1alpha1.BGPMeta } else if bgp.Spec.Transport.Name != "" { return nil, fmt.Errorf("coundn't find port %s", bgp.Spec.Transport.Name) } - vlanID = 1 + vlanID = -1 } else { vnets, err := r.Cred.VNet().Get() if err != nil { @@ -214,6 +214,11 @@ func BGPMetaToNetris(bgpMeta *k8sv1alpha1.BGPMeta) (*bgp.EBGPAdd, error) { hwID = "auto" } + var untagged bool = false + if bgpMeta.Spec.Vlan == -1 { + untagged = true + } + bgpAdd := &bgp.EBGPAdd{ AllowAsIn: bgpMeta.Spec.AllowasIn, BgpPassword: bgpMeta.Spec.BgpPassword, @@ -245,6 +250,7 @@ func BGPMetaToNetris(bgpMeta *k8sv1alpha1.BGPMeta) (*bgp.EBGPAdd, error) { Vlan: bgpMeta.Spec.Vlan, Weight: bgpMeta.Spec.Weight, Tags: []string{}, + Untagged: untagged, } return bgpAdd, nil @@ -418,8 +424,10 @@ func compareBGPMetaAPIEBGP(bgpMeta *k8sv1alpha1.BGPMeta, apiBGP *bgp.EBGP, u uni return false } if apiBGP.Vlan != bgpMeta.Spec.Vlan { - u.DebugLogger.Info("Vlan changed", "netrisValue", apiBGP.Vlan, "k8sValue", bgpMeta.Spec.Vlan) - return false + if bgpMeta.Spec.Vlan != -1 { + u.DebugLogger.Info("Vlan changed", "netrisValue", apiBGP.Vlan, "k8sValue", bgpMeta.Spec.Vlan) + return false + } } if apiBGP.Weight != bgpMeta.Spec.Weight { u.DebugLogger.Info("Weight changed", "netrisValue", apiBGP.Weight, "k8sValue", bgpMeta.Spec.Weight) diff --git a/controllers/nat_translations.go b/controllers/nat_translations.go index e1c3817..8d14219 100644 --- a/controllers/nat_translations.go +++ b/controllers/nat_translations.go @@ -18,7 +18,6 @@ package controllers import ( "fmt" - "strconv" "strings" k8sv1alpha1 "github.com/netrisai/netris-operator/api/v1alpha1" @@ -135,7 +134,7 @@ func NatMetaToNetris(natMeta *k8sv1alpha1.NatMeta) (*nat.NATw, error) { SnatToIP: natMeta.Spec.SnatToIP, SnatToPool: natMeta.Spec.SnatToPool, DnatToIP: natMeta.Spec.DnatToIP, - DnatToPort: strconv.Itoa(natMeta.Spec.DnatToPort), + DnatToPort: natMeta.Spec.DnatToPort, } return natAdd, nil @@ -157,7 +156,7 @@ func NatMetaToNetrisUpdate(natMeta *k8sv1alpha1.NatMeta) (*nat.NATw, error) { SnatToIP: natMeta.Spec.SnatToIP, SnatToPool: natMeta.Spec.SnatToPool, DnatToIP: natMeta.Spec.DnatToIP, - DnatToPort: strconv.Itoa(natMeta.Spec.DnatToPort), + DnatToPort: natMeta.Spec.DnatToPort, } return natAdd, nil diff --git a/controllers/subnet_translations.go b/controllers/subnet_translations.go index 1299cef..0b06a13 100644 --- a/controllers/subnet_translations.go +++ b/controllers/subnet_translations.go @@ -127,6 +127,7 @@ func SubnetMetaToNetris(subnetMeta *k8sv1alpha1.SubnetMeta) (*ipam.Subnet, error Purpose: subnetMeta.Spec.Purpose, DefaultGateway: subnetMeta.Spec.DefaultGateway, Sites: sites, + Tags: []string{}, } return subnetAdd, nil diff --git a/controllers/vnet_translations.go b/controllers/vnet_translations.go index 6efc960..e0116e9 100644 --- a/controllers/vnet_translations.go +++ b/controllers/vnet_translations.go @@ -126,22 +126,35 @@ func (r *VNetMetaReconciler) VnetMetaToNetris(vnetMeta *k8sv1alpha1.VNetMeta) (* sites := []vnet.VNetAddSite{} vlanid := vnetMeta.Spec.VlanID members := []vnet.VNetAddPort{} + vnetTypeOne := false for _, site := range vnetMeta.Spec.Sites { sites = append(sites, vnet.VNetAddSite{Name: site.Name}) } for _, port := range vnetMeta.Spec.Members { + accessMode := false + if port.Untagged == "yes" { + accessMode = true + } vID := vlanid - if (port.Vlan != "1" || vlanid == "") && vlanid != "auto" { + if (port.Vlan != "1" || vlanid == "") && vlanid != "auto" && vlanid != port.Vlan { vID = port.Vlan + vnetTypeOne = true + if vID == "1" { + accessMode = true + } + } + if vlanid != "" && port.Untagged != "no" { + accessMode = true } members = append(members, vnet.VNetAddPort{ - Name: port.Name, - Vlan: vID, - Lacp: port.Lacp, - State: "active", - ID: port.ID, + AccessMode: accessMode, + Name: port.Name, + Vlan: vID, + Lacp: port.Lacp, + State: "active", + ID: port.ID, }) } @@ -169,6 +182,14 @@ func (r *VNetMetaReconciler) VnetMetaToNetris(vnetMeta *k8sv1alpha1.VNetMeta) (* guestTenants = append(guestTenants, vnet.VNetAddTenant{Name: tenant}) } + var vlanidInterface any + + if vnetTypeOne { + vlanidInterface = 0 + } else { + vlanidInterface = vlanid + } + vnetAdd := &vnet.VNetAdd{ Name: vnetMeta.Spec.VnetName, Sites: sites, @@ -178,7 +199,7 @@ func (r *VNetMetaReconciler) VnetMetaToNetris(vnetMeta *k8sv1alpha1.VNetMeta) (* Gateways: apiGateways, Ports: members, NativeVlan: 1, - Vlan: vnetMeta.Spec.VlanID, + Vlan: vlanidInterface, Tags: []string{}, } @@ -192,22 +213,35 @@ func VnetMetaToNetrisUpdate(vnetMeta *k8sv1alpha1.VNetMeta) (*vnet.VNetUpdate, e sites := []vnet.VNetUpdateSite{} vlanid := vnetMeta.Spec.VlanID members := []vnet.VNetUpdatePort{} + vnetTypeOne := false for _, site := range vnetMeta.Spec.Sites { sites = append(sites, vnet.VNetUpdateSite{Name: site.Name}) } for _, port := range vnetMeta.Spec.Members { + accessMode := false + if port.Untagged == "yes" { + accessMode = true + } vID := vlanid - if (port.Vlan != "1" || vlanid == "") && vlanid != "auto" { + if (port.Vlan != "1" || vlanid == "") && vlanid != "auto" && vlanid != port.Vlan { vID = port.Vlan + vnetTypeOne = true + if vID == "1" { + accessMode = true + } + } + if vlanid != "" && port.Untagged != "no" { + accessMode = true } members = append(members, vnet.VNetUpdatePort{ - Name: port.Name, - Vlan: vID, - Lacp: port.Lacp, - State: "active", - ID: port.ID, + AccessMode: accessMode, + Name: port.Name, + Vlan: vID, + Lacp: port.Lacp, + State: "active", + ID: port.ID, }) } @@ -234,6 +268,14 @@ func VnetMetaToNetrisUpdate(vnetMeta *k8sv1alpha1.VNetMeta) (*vnet.VNetUpdate, e guestTenants = append(guestTenants, vnet.VNetUpdateGuestTenant{Name: tenant}) } + var vlanidInterface any + + if vnetTypeOne { + vlanidInterface = 0 + } else { + vlanidInterface = vlanid + } + vnetUpdate := &vnet.VNetUpdate{ Name: vnetMeta.Spec.VnetName, Sites: sites, @@ -242,7 +284,7 @@ func VnetMetaToNetrisUpdate(vnetMeta *k8sv1alpha1.VNetMeta) (*vnet.VNetUpdate, e Gateways: apiGateways, Ports: members, NativeVlan: 1, - Vlan: vnetMeta.Spec.VlanID, + Vlan: vlanidInterface, Tags: []string{}, } @@ -319,6 +361,40 @@ func compareVNetMetaAPIVnetMembers(vnetMetaMembers []k8sv1alpha1.VNetMetaMember, return len(changelog) <= 0 } +func compareVNetMetaAPIVnetMembersUntagged(vnetMetaSpec k8sv1alpha1.VNetMetaSpec, apiVnetMembers []vnet.VNetDetailedPort) bool { + type member struct { + Untagged string `diff:"untagged"` + } + + vnetMembers := []member{} + apiMembers := []member{} + vnetMetaMembers := vnetMetaSpec.Members + + for _, m := range vnetMetaMembers { + vnetMembers = append(vnetMembers, member{ + Untagged: m.Untagged, + }) + } + + for i, m := range apiVnetMembers { + untagged := "" + if m.AccessMode && len(vnetMembers[i].Untagged) == 0 { + untagged = "" + } else if m.AccessMode && len(vnetMembers[i].Untagged) > 0 { + untagged = "yes" + } else if !m.AccessMode && len(vnetMembers[i].Untagged) > 0 { + untagged = "no" + } else if !m.AccessMode && len(vnetMembers[i].Untagged) == 0 && vnetMetaSpec.VlanID != "0" && vnetMetaSpec.VlanID != "" { + untagged = "no" + } + apiMembers = append(apiMembers, member{ + Untagged: untagged, + }) + } + changelog, _ := diff.Diff(vnetMembers, apiMembers) + return len(changelog) <= 0 +} + func compareVNetMetaAPIVnetTenants(vnetMetaTenants []string, apiVnetTenants []vnet.VNetDetailedGuestTenant) bool { tenantList := []string{} for _, tenant := range apiVnetTenants { @@ -361,6 +437,10 @@ func compareVNetMetaAPIVnet(vnetMeta *k8sv1alpha1.VNetMeta, apiVnet *vnet.VNetDe return false } + if ok := compareVNetMetaAPIVnetMembersUntagged(vnetMeta.Spec, apiVnet.Ports); !ok { + return false + } + if vnetMeta.Spec.VnetName != apiVnet.Name { return false } diff --git a/deploy/charts/netris-operator/Chart.yaml b/deploy/charts/netris-operator/Chart.yaml index 6276fa4..bf66ab1 100644 --- a/deploy/charts/netris-operator/Chart.yaml +++ b/deploy/charts/netris-operator/Chart.yaml @@ -15,12 +15,12 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.1 +version: 2.0.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. -appVersion: v2.1.1 +appVersion: v3.0.0 home: https://github.com/netrisai/netris-operator icon: https://www.netris.ai/wp-content/uploads/2021/01/logo-300.png # [todo] Change url to permalink keywords: diff --git a/deploy/charts/netris-operator/crds/k8s.netris.ai_bgps.yaml b/deploy/charts/netris-operator/crds/k8s.netris.ai_bgps.yaml index 270a877..9d34dba 100644 --- a/deploy/charts/netris-operator/crds/k8s.netris.ai_bgps.yaml +++ b/deploy/charts/netris-operator/crds/k8s.netris.ai_bgps.yaml @@ -139,7 +139,7 @@ spec: description: BGPTransport . properties: name: - pattern: (^[a-zA-Z0-9]+@[a-zA-Z0-9-]+$)|(^[a-zA-Z0-9]([-a-zA-Z0-9]*[a-z0-9])?(\.[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?)*$) + pattern: (^[a-zA-Z0-9]+@[a-zA-Z0-9-]+$)|(^[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?)*$) type: string type: enum: diff --git a/deploy/charts/netris-operator/crds/k8s.netris.ai_natmeta.yaml b/deploy/charts/netris-operator/crds/k8s.netris.ai_natmeta.yaml index 53688ef..07f0cc8 100644 --- a/deploy/charts/netris-operator/crds/k8s.netris.ai_natmeta.yaml +++ b/deploy/charts/netris-operator/crds/k8s.netris.ai_natmeta.yaml @@ -43,7 +43,7 @@ spec: dnatToIp: type: string dnatToPort: - type: integer + type: string dstAddress: type: string dstPort: diff --git a/deploy/charts/netris-operator/crds/k8s.netris.ai_nats.yaml b/deploy/charts/netris-operator/crds/k8s.netris.ai_nats.yaml index cd61776..67e70f7 100644 --- a/deploy/charts/netris-operator/crds/k8s.netris.ai_nats.yaml +++ b/deploy/charts/netris-operator/crds/k8s.netris.ai_nats.yaml @@ -56,7 +56,7 @@ spec: - jsonPath: .spec.dnatToPort name: DNATToPort priority: 1 - type: integer + type: string - jsonPath: .status.status name: Status type: string @@ -96,9 +96,7 @@ spec: pattern: (^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[12]\d|3[0-2]))?$)|(^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?(\/([1-9]|[1-5][0-9]|6[0-4]))?$) type: string dnatToPort: - maximum: 65535 - minimum: 1 - type: integer + type: string dstAddress: pattern: (^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[12]\d|3[0-2]))?$)|(^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?(\/([1-9]|[1-5][0-9]|6[0-4]))?$) type: string diff --git a/deploy/charts/netris-operator/crds/k8s.netris.ai_vnetmeta.yaml b/deploy/charts/netris-operator/crds/k8s.netris.ai_vnetmeta.yaml index dc8ef6d..ee3eac0 100644 --- a/deploy/charts/netris-operator/crds/k8s.netris.ai_vnetmeta.yaml +++ b/deploy/charts/netris-operator/crds/k8s.netris.ai_vnetmeta.yaml @@ -83,6 +83,8 @@ spec: type: string state: type: string + untagged: + type: string vlan: type: string required: diff --git a/deploy/charts/netris-operator/crds/k8s.netris.ai_vnets.yaml b/deploy/charts/netris-operator/crds/k8s.netris.ai_vnets.yaml index b5c300c..55ebc24 100644 --- a/deploy/charts/netris-operator/crds/k8s.netris.ai_vnets.yaml +++ b/deploy/charts/netris-operator/crds/k8s.netris.ai_vnets.yaml @@ -108,6 +108,11 @@ spec: type: string state: type: string + untagged: + enum: + - "yes" + - "no" + type: string vlanId: maximum: 4094 minimum: 2 diff --git a/go.mod b/go.mod index 649364a..9eb96d6 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ module github.com/netrisai/netris-operator -go 1.17 +go 1.18 require ( github.com/go-logr/logr v0.1.0 github.com/kelseyhightower/envconfig v1.4.0 - github.com/netrisai/netriswebapi v0.0.0-20230404043848-3cf45f0ba07d + github.com/netrisai/netriswebapi v0.0.0-20231121004246-72bab01d3ef5 github.com/onsi/ginkgo v1.16.4 github.com/onsi/gomega v1.19.0 github.com/r3labs/diff/v2 v2.9.1 diff --git a/go.sum b/go.sum index 11e164f..c81a20d 100644 --- a/go.sum +++ b/go.sum @@ -242,6 +242,12 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/netrisai/netriswebapi v0.0.0-20230404043848-3cf45f0ba07d h1:w3bDLlD+UWOgn+AF1Qt/GN/XtpH1dZSyceVouvVeGVY= github.com/netrisai/netriswebapi v0.0.0-20230404043848-3cf45f0ba07d/go.mod h1:GLLz33Jc07/hIPwEYZDWEtNtHjX/QZjVzf9xLnfSiqs= +github.com/netrisai/netriswebapi v0.0.0-20230612115147-822e91521d5b h1:7DugRUR8D9zjSiLJGkRn/zPwRPWpJCdqk+63J97mA3w= +github.com/netrisai/netriswebapi v0.0.0-20230612115147-822e91521d5b/go.mod h1:GLLz33Jc07/hIPwEYZDWEtNtHjX/QZjVzf9xLnfSiqs= +github.com/netrisai/netriswebapi v0.0.0-20230612164134-c158192abfb2 h1:IEg2CAAmFDShUJS8vyiTPj3+aPOvryZ7T5HuN47as68= +github.com/netrisai/netriswebapi v0.0.0-20230612164134-c158192abfb2/go.mod h1:GLLz33Jc07/hIPwEYZDWEtNtHjX/QZjVzf9xLnfSiqs= +github.com/netrisai/netriswebapi v0.0.0-20231121004246-72bab01d3ef5 h1:H0MMXpuyLnpLPjJPMUmJcBmP8KKU1QYck9gSVEoDirM= +github.com/netrisai/netriswebapi v0.0.0-20231121004246-72bab01d3ef5/go.mod h1:GLLz33Jc07/hIPwEYZDWEtNtHjX/QZjVzf9xLnfSiqs= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= diff --git a/samples/nat.yaml b/samples/nat.yaml index 38debea..03192f1 100644 --- a/samples/nat.yaml +++ b/samples/nat.yaml @@ -28,7 +28,7 @@ spec: dstAddress: 203.0.113.193/32 dstPort: "8080" dnatToIp: 100.71.56.150/32 - dnatToPort: 80 + dnatToPort: "80" --- apiVersion: k8s.netris.ai/v1alpha1 kind: Nat