Skip to content

Commit

Permalink
Add cni plugin (#4)
Browse files Browse the repository at this point in the history
* Add macvtap-cni production code

The ConfigureMacvtap interface code is located in the
`netlink` util pkg.

The following helper functions are also made available on
the `netlink` pkg.
  - configureArp
  - renameMacvtapIface

All the configuration is performed within a single netns.Do
instruction.

Interface renaming is performed in the target netns, thus
reducing the risk of interface name collision.

Signed-off-by: Miguel Duarte Barroso <[email protected]>

* Use init containers to install the cni plugin

The init-container installs the macvtap-cni plugin into the
host, and afterwards, the macvtap container listens to device
plugin requests, and operates on them.

This way, we can achieve macvtap cni & dp co-existence within the
same container.

Signed-off-by: Miguel Duarte Barroso <[email protected]>

* Add unit test to check the correct behavior of the CNI plugin

Signed-off-by: Miguel Duarte Barroso <[email protected]>

* workaround: remove flag breaking unit tests

The -logtostderr flag is somehow preventing the cni unit tests
from running.

Can't figure this one out; this is just to show I'm not insane.

Signed-off-by: Miguel Duarte Barroso <[email protected]>
  • Loading branch information
maiqueb authored Feb 25, 2020
1 parent ff2d786 commit fde0a6f
Show file tree
Hide file tree
Showing 71 changed files with 13,988 additions and 81 deletions.
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
CNI_MOUNT_PATH ?= /opt/cni/bin

IMAGE_REGISTRY ?= quay.io/kubevirt
IMAGE_NAME ?= macvtap-cni
IMAGE_TAG ?= latest
Expand Down Expand Up @@ -75,10 +77,10 @@ test/unit:
@echo "You are not root, run this target as root please"
exit 1
fi
go test ./cmd/... ./pkg/... -v -logtostderr --ginkgo.v
go test ./cmd/... ./pkg/... -v --ginkgo.v

manifests:
IMAGE_REGISTRY=$(IMAGE_REGISTRY) IMAGE_NAME=$(IMAGE_NAME) IMAGE_TAG=$(IMAGE_TAG) ./hack/generate-manifests.sh
IMAGE_REGISTRY=$(IMAGE_REGISTRY) IMAGE_NAME=$(IMAGE_NAME) IMAGE_TAG=$(IMAGE_TAG) CNI_MOUNT_PATH=$(CNI_MOUNT_PATH) ./hack/generate-manifests.sh

vendor:
go mod tidy
Expand Down
7 changes: 4 additions & 3 deletions cmd/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ FROM golang:1.13 as builder
ENV GOPATH=/go
WORKDIR /go/src/github.com/kubevirt/macvtap-cni
COPY . .
RUN GOOS=linux CGO_ENABLED=0 go build -o /macvtap github.com/kubevirt/macvtap-cni/cmd/deviceplugin
RUN GOOS=linux CGO_ENABLED=0 go build -o /macvtap-deviceplugin github.com/kubevirt/macvtap-cni/cmd/deviceplugin
RUN GOOS=linux CGO_ENABLED=0 go build -o /macvtap-cni github.com/kubevirt/macvtap-cni/cmd/cni

FROM registry.access.redhat.com/ubi8/ubi-minimal
COPY --from=builder /macvtap /macvtap
ENTRYPOINT [ "./macvtap", "-v", "3", "-logtostderr"]
COPY --from=builder /macvtap-deviceplugin /macvtap-deviceplugin
COPY --from=builder /macvtap-cni /macvtap-cni
12 changes: 12 additions & 0 deletions cmd/cni/macvtap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package main

import (
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/version"
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
macvtap_cni "github.com/kubevirt/macvtap-cni/pkg/cni"
)

func main() {
skel.PluginMain(macvtap_cni.CmdAdd, macvtap_cni.CmdCheck, macvtap_cni.CmdDel, version.All, bv.BuildString("macvtap"))
}
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@ module github.com/kubevirt/macvtap-cni
go 1.13

require (
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/client9/misspell v0.3.4 // indirect
github.com/containernetworking/cni v0.7.1
github.com/containernetworking/plugins v0.8.5
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/kubevirt/device-plugin-manager v1.14.0
github.com/onsi/ginkgo v1.11.0
github.com/onsi/gomega v1.8.1
github.com/vishvananda/netlink v1.1.0
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 // indirect
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc
golang.org/x/tools v0.0.0-20200117220505-0cba7a3a9ee9
google.golang.org/grpc v1.26.0
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc // indirect
k8s.io/client-go v0.0.0
k8s.io/kubernetes v0.0.0-00010101000000-000000000000
)
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ github.com/containernetworking/plugins v0.8.5 h1:pCvEMrFf7yzJI8+/D/7jkvE96KD52b7
github.com/containernetworking/plugins v0.8.5/go.mod h1:UZ2539umj8djuRQmBxuazHeJbYrLV8BSBejkk+she6o=
github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38=
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
github.com/coreos/go-oidc v0.0.0-20180117170138-065b426bd416/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-semver v0.0.0-20180108230905-e214231b295a/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
Expand Down Expand Up @@ -239,6 +240,7 @@ github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uY
github.com/robfig/cron v0.0.0-20170309132418-df38d32658d8/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
github.com/russross/blackfriday v0.0.0-20151117072312-300106c228d5/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 h1:2c1EFnZHIPCW8qKWgHMH/fX2PkSabFc5mrVzfUNdg5U=
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/seccomp/libseccomp-golang v0.0.0-20150813023252-1b506fc7c24e/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
Expand Down
2 changes: 2 additions & 0 deletions hack/generate-manifests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

set -ex

CNI_MOUNT_PATH=${CNI_MOUNT_PATH} # the default is stored in Makefile
IMAGE_REGISTRY=${IMAGE_REGISTRY} # the default is stored in Makefile
IMAGE_NAME=${IMAGE_NAME} # the default is stored in Makefile
IMAGE_TAG=${IMAGE_TAG} # the default is store in Makefile
Expand All @@ -14,5 +15,6 @@ for template in templates/*.in; do
-e "s#{{ .ImageRegistry }}#${IMAGE_REGISTRY}#g" \
-e "s#{{ .ImageName }}#${IMAGE_NAME}#g" \
-e "s#{{ .ImageTag }}#${IMAGE_TAG}#g" \
-e "s#{{ .CniMountPath }}#${CNI_MOUNT_PATH}#g" \
${template} > ${DESTINATION}/${name}
done
14 changes: 14 additions & 0 deletions manifests/macvtap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,24 @@ spec:
envFrom:
- configMapRef:
name: macvtap-deviceplugin-config
command: [ "/macvtap-deviceplugin", "-v", "3", "-logtostderr"]
volumeMounts:
- name: deviceplugin
mountPath: /var/lib/kubelet/device-plugins
initContainers:
- name: install-cni
image: quay.io/kubevirt/macvtap-cni:latest
securityContext:
privileged: true
command: ['cp', '/macvtap-cni', '/host/opt/cni/bin/macvtap']
volumeMounts:
- name: cni
mountPath: /host/opt/cni/bin
mountPropagation: Bidirectional
volumes:
- name: deviceplugin
hostPath:
path: /var/lib/kubelet/device-plugins
- name: cni
hostPath:
path: /opt/cni/bin
13 changes: 13 additions & 0 deletions pkg/cni/cni_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package cni_test

import (
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func TestCni(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Cni Suite")
}
143 changes: 143 additions & 0 deletions pkg/cni/plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Copyright 2019 CNI authors
//
// 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 cni

import (
"encoding/json"
"fmt"
"net"
"runtime"

"github.com/kubevirt/macvtap-cni/pkg/util"

"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"

"github.com/containernetworking/plugins/pkg/ip"
"github.com/containernetworking/plugins/pkg/ns"
)

// A NetConf structure represents a Multus network attachment definition configuration
type NetConf struct {
types.NetConf
DeviceID string `json:"deviceID"`
MTU int `json:"mtu,omitempty"`
}

// EnvArgs structure represents inputs sent from each VMI via environment variables
type EnvArgs struct {
types.CommonArgs
MAC types.UnmarshallableString `json:"mac,omitempty"`
}

func init() {
// this ensures that main runs only on main thread (thread group leader).
// since namespace ops (unshare, setns) are done for a single thread, we
// must ensure that the goroutine does not jump from OS thread to thread
runtime.LockOSThread()
}

func loadConf(bytes []byte) (NetConf, string, error) {
n := NetConf{}
if err := json.Unmarshal(bytes, &n); err != nil {
return n, "", fmt.Errorf("failed to load netconf: %v", err)
}

return n, n.CNIVersion, nil
}

func getEnvArgs(envArgsString string) (EnvArgs, error) {
e := EnvArgs{}
err := types.LoadArgs(envArgsString, &e)
if err != nil {
return e, err
}
return e, nil
}

// CmdAdd - CNI interface
func CmdAdd(args *skel.CmdArgs) error {
var err error
netConf, cniVersion, err := loadConf(args.StdinData)
if err != nil {
return err
}

envArgs, err := getEnvArgs(args.Args)
if err != nil {
return err
}

var mac *net.HardwareAddr = nil
if envArgs.MAC != "" {
aMac, err := net.ParseMAC(string(envArgs.MAC))
mac = &aMac
if err != nil {
return err
}
}

netns, err := ns.GetNS(args.Netns)
if err != nil {
return fmt.Errorf("failed to open netns %q: %v", netns, err)
}

// Delete link if err to avoid link leak in this ns
defer func() {
netns.Close()
if err != nil {
util.LinkDelete(netConf.DeviceID)
}
}()

macvtapInterface, err := util.ConfigureInterface(netConf.DeviceID, args.IfName, mac, netConf.MTU, netns)
if err != nil {
return err
}

result := &current.Result{
CNIVersion: cniVersion,
Interfaces: []*current.Interface{macvtapInterface},
}

return types.PrintResult(result, cniVersion)
}

// CmdDel - CNI plugin Interface
func CmdDel(args *skel.CmdArgs) error {
if args.Netns == "" {
return nil
}

// There is a netns so try to clean up. Delete can be called multiple times
// so don't return an error if the device is already removed.
err := ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {

if err := ip.DelLinkByName(args.IfName); err != nil {
if err != ip.ErrLinkNotFound {
return err
}
}
return nil
})

return err
}

// CmdCheck - CNI plugin Interface
func CmdCheck(args *skel.CmdArgs) error {
return nil
}
Loading

0 comments on commit fde0a6f

Please sign in to comment.