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

Introduce controller code #5

Merged
merged 4 commits into from
Aug 22, 2022
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
4 changes: 4 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
run:
skip-dirs:
- pkg/annotations

linters-settings:
dupl:
threshold: 100
Expand Down
5 changes: 0 additions & 5 deletions controller/networks.go

This file was deleted.

15 changes: 0 additions & 15 deletions controller/networks_test.go

This file was deleted.

54 changes: 54 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,57 @@
module github.com/maiqueb/multus-dynamic-networks-controller

go 1.18

require (
github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.3.0
github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.17.0
k8s.io/api v0.24.4
k8s.io/apimachinery v0.24.4
k8s.io/client-go v0.24.4
k8s.io/klog/v2 v2.60.1
)

require (
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful v2.10.0+incompatible // indirect
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/go-logr/logr v1.2.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.5 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.5 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/pkg/errors v0.9.1 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
)
652 changes: 652 additions & 0 deletions go.sum

Large diffs are not rendered by default.

136 changes: 136 additions & 0 deletions pkg/annotations/network-selection-elements.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Copyright (c) 2017 Intel Corporation
//
// 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 annotations code looted from
// https://github.com/k8snetworkplumbingwg/multus-cni/blob/549808011920e6c6f0dd4b78a75250d865e7c1c9/pkg/k8sclient/k8sclient.go
package annotations

import (
"encoding/json"
"fmt"
"net"
"regexp"
"strings"

"k8s.io/klog/v2"

nadv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
)

func ParsePodNetworkAnnotations(podNetworks, defaultNamespace string) ([]*nadv1.NetworkSelectionElement, error) {
var networks []*nadv1.NetworkSelectionElement

klog.V(5).Infof("parsePodNetworkAnnotation: %s, %s", podNetworks, defaultNamespace)
if podNetworks == "" {
return nil, fmt.Errorf("parsePodNetworkAnnotation: pod annotation does not have \"network\" as key")
}

if strings.ContainsAny(podNetworks, "[{\"") {
if err := json.Unmarshal([]byte(podNetworks), &networks); err != nil {
return nil, fmt.Errorf("parsePodNetworkAnnotation: failed to parse pod Network Attachment Selection Annotation JSON format: %v", err)
}
} else {
// Comma-delimited list of network attachment object names
for _, item := range strings.Split(podNetworks, ",") {
// Remove leading and trailing whitespace.
item = strings.TrimSpace(item)

// Parse network name (i.e. <namespace>/<network name>@<ifname>)
netNsName, networkName, netIfName, err := parsePodNetworkObjectName(item)
if err != nil {
return nil, fmt.Errorf("parsePodNetworkAnnotation: %v", err)
}

networks = append(networks, &nadv1.NetworkSelectionElement{
Name: networkName,
Namespace: netNsName,
InterfaceRequest: netIfName,
})
}
}

for _, n := range networks {
if n.Namespace == "" {
n.Namespace = defaultNamespace
}
if n.MacRequest != "" {
// validate MAC address
if _, err := net.ParseMAC(n.MacRequest); err != nil {
return nil, fmt.Errorf("parsePodNetworkAnnotation: failed to mac: %v", err)
}
}
if n.InfinibandGUIDRequest != "" {
// validate GUID address
if _, err := net.ParseMAC(n.InfinibandGUIDRequest); err != nil {
return nil, fmt.Errorf("parsePodNetworkAnnotation: failed to validate infiniband GUID: %v", err)
}
}
if n.IPRequest != nil {
for _, ip := range n.IPRequest {
// validate IP address
if strings.Contains(ip, "/") {
if _, _, err := net.ParseCIDR(ip); err != nil {
return nil, fmt.Errorf("failed to parse CIDR %q: %v", ip, err)
}
} else if net.ParseIP(ip) == nil {
return nil, fmt.Errorf("failed to parse IP address %q", ip)
}
}
}
}

return networks, nil
}

func parsePodNetworkObjectName(podnetwork string) (string, string, string, error) {
var netNsName string
var netIfName string
var networkName string

klog.V(5).Infof("parsePodNetworkObjectName: %s", podnetwork)
slashItems := strings.Split(podnetwork, "/")
if len(slashItems) == 2 {
netNsName = strings.TrimSpace(slashItems[0])
networkName = slashItems[1]
} else if len(slashItems) == 1 {
networkName = slashItems[0]
} else {
return "", "", "", fmt.Errorf("parsePodNetworkObjectName: Invalid network object (failed at '/')")
}

atItems := strings.Split(networkName, "@")
networkName = strings.TrimSpace(atItems[0])
if len(atItems) == 2 {
netIfName = strings.TrimSpace(atItems[1])
} else if len(atItems) != 1 {
return "", "", "", fmt.Errorf("parsePodNetworkObjectName: Invalid network object (failed at '@')")
}

// Check and see if each item matches the specification for valid attachment name.
// "Valid attachment names must be comprised of units of the DNS-1123 label format"
// [a-z0-9]([-a-z0-9]*[a-z0-9])?
// And we allow at (@), and forward slash (/) (units separated by commas)
// It must start and end alphanumerically.
allItems := []string{netNsName, networkName, netIfName}
expr := regexp.MustCompile("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$")
for i := range allItems {
matched := expr.MatchString(allItems[i])
if !matched && len([]rune(allItems[i])) > 0 {
return "", "", "", fmt.Errorf("parsePodNetworkObjectName: Failed to parse: one or more items did not match comma-delimited format (must consist of lower case alphanumeric characters). Must start and end with an alphanumeric character), mismatch @ '%v'", allItems[i])
}
}

klog.V(5).Infof("parsePodNetworkObjectName: parsed: %s, %s, %s", netNsName, networkName, netIfName)
return netNsName, networkName, netIfName, nil
}
91 changes: 91 additions & 0 deletions pkg/annotations/network-selection-elements_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package annotations

import (
v1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"strings"
"testing"
)

func TestController(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Annotation parsing suite")
}

var _ = Describe("Parsing annotations", func() {
const namespace = "ns1"

It("nil input", func() {
_, err := ParsePodNetworkAnnotations("", namespace)
Expect(err).To(MatchError("parsePodNetworkAnnotation: pod annotation does not have \"network\" as key"))
})

It("empty list input", func() {
Expect(ParsePodNetworkAnnotations("[]", namespace)).To(BeEmpty())
})

It("single network name", func() {
const networkName = "net1"
Expect(ParsePodNetworkAnnotations(networkName, namespace)).To(ConsistOf(newNetworkSelectionElement(networkName, namespace)))
})

It("comma separated list of network names", func() {
const (
networkName = "net1"
secondNetworkName = "net321"
)
Expect(
ParsePodNetworkAnnotations(networkSelectionElements(networkName, secondNetworkName), namespace),
).To(
ConsistOf(
newNetworkSelectionElement(networkName, namespace),
newNetworkSelectionElement(secondNetworkName, namespace)))
})

It("comma separated list of network names with interface names", func() {
const (
networkAndInterfaceNamingPair = "net1@eth1"
secondNetworkAndInterfaceNamingPair = "net321@eth2"
)
Expect(
ParsePodNetworkAnnotations(
networkSelectionElements(
networkAndInterfaceNamingPair,
secondNetworkAndInterfaceNamingPair),
namespace),
).To(
ConsistOf(
newNetworkSelectionElementWithIface("net1", "eth1", namespace),
newNetworkSelectionElementWithIface("net321", "eth2", namespace)))
})

It("network selection element specified in JSON", func() {
const networkSelectionElementsString = "[\n { \"name\" : \"macvlan-conf-1\" },\n { \"name\" : \"macvlan-conf-2\", \"interface\": \"ens4\" }\n ]"
Expect(
ParsePodNetworkAnnotations(networkSelectionElementsString, namespace),
).To(
ConsistOf(
newNetworkSelectionElement("macvlan-conf-1", namespace),
newNetworkSelectionElementWithIface("macvlan-conf-2", "ens4", namespace)))
})
})

func networkSelectionElements(networkNames ...string) string {
return strings.Join(networkNames, ",")
}

func newNetworkSelectionElement(networkName string, namespace string) *v1.NetworkSelectionElement {
return &v1.NetworkSelectionElement{
Name: networkName,
Namespace: namespace,
}
}

func newNetworkSelectionElementWithIface(networkName string, ifaceName string, namespace string) *v1.NetworkSelectionElement {
return &v1.NetworkSelectionElement{
Name: networkName,
Namespace: namespace,
InterfaceRequest: ifaceName,
}
}
Loading