Skip to content
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions azure-ipam/const.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import (
"time"
)

const (
pluginName = "azure-ipam"
cnsBaseURL = "" // fallback to default http://localhost:10090
cnsReqTimeout = 15 * time.Second
)

// plugin specific error codes
// https://www.cni.dev/docs/spec/#error
const (
ErrCreateIPConfigRequest uint = iota + 100
ErrRequestIPConfigFromCNS
ErrProcessIPConfigResponse
)
82 changes: 82 additions & 0 deletions azure-ipam/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
module github.com/Azure/azure-container-networking/azure-ipam
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you try running a go mod tidy from within the azure-ipam directory? When I tried it, I got this error:

go: finding module for package github.com/containernetworking/cni/pkg/types/current
github.com/Azure/azure-container-networking/azure-ipam imports
        github.com/Azure/azure-container-networking/cns/client imports
        github.com/Azure/azure-container-networking/cns/restserver imports
        github.com/Azure/azure-container-networking/cns/ipamclient imports
        github.com/Azure/azure-container-networking/ipam tested by
        github.com/Azure/azure-container-networking/ipam.test imports
        github.com/containernetworking/cni/pkg/types/current: module github.com/containernetworking/cni@latest found (v1.1.1), but does not contain package github.com/containernetworking/cni/pkg/types/current

Actually, do we even need azure-ipam to be a go module? It seems like there's already a go.mod at the repository root.

Copy link
Collaborator

@rbtr rbtr Jun 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want it to be a separate module; I've been trying to move the functionally distinct binaries in this monorepo to modules for a while so that they can be released and versioned separately (it's not super clear from reading the mod proposal, but you can tag path/v#.#.# and go understands that as the submodule version).
As for your build error - multimodule workspaces are new in go1.18 and with nested modules require that you have a go.work in the root module. (there is a make workspace target that should set this up but I wouldn't rely on it yet)


go 1.18

require (
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.7.1
go.uber.org/zap v1.21.0
)

require (
code.cloudfoundry.org/clock v1.0.0 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Microsoft/go-winio v0.4.17 // indirect
github.com/Microsoft/hcsshim v0.8.23 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/billgraziano/dpapi v0.4.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/containerd/cgroups v1.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful v2.9.5+incompatible // 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/gofrs/uuid v4.2.0+incompatible // 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.8 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/imdario/mergo v0.3.12 // 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/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/microsoft/ApplicationInsights-Go v0.4.4 // 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/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.12.2 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.opencensus.io v0.23.0 // indirect
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // 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.28.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0 // indirect
k8s.io/api v0.24.1 // indirect
k8s.io/apimachinery v0.24.1 // indirect
k8s.io/client-go v0.24.1 // indirect
k8s.io/klog/v2 v2.60.1 // indirect
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
sigs.k8s.io/controller-runtime v0.12.1 // 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.3.0 // indirect
)

require (
github.com/Azure/azure-container-networking v1.4.27
github.com/containernetworking/cni v1.1.1
github.com/containernetworking/plugins v1.1.1
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
)
866 changes: 866 additions & 0 deletions azure-ipam/go.sum

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions azure-ipam/internal/buildinfo/buildinfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package buildinfo

// this will be populate by the Go compiler via
// the -ldflags, which insert dynamic information
// into the binary at build time
var Version string
173 changes: 173 additions & 0 deletions azure-ipam/ipam.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package main

import (
"context"
"encoding/json"
"io"
"net"

"github.com/Azure/azure-container-networking/azure-ipam/internal/buildinfo"
"github.com/Azure/azure-container-networking/azure-ipam/ipconfig"
"github.com/Azure/azure-container-networking/cns"
cniSkel "github.com/containernetworking/cni/pkg/skel"
cniTypes "github.com/containernetworking/cni/pkg/types"
types100 "github.com/containernetworking/cni/pkg/types/100"
"github.com/pkg/errors"
"go.uber.org/zap"
)

// IPAMPlugin is the struct for the delegated azure-ipam plugin
// https://www.cni.dev/docs/spec/#section-4-plugin-delegation
type IPAMPlugin struct {
Name string
Version string
Options map[string]interface{}
logger *zap.Logger
cnsClient cnsClient
out io.Writer // indicate the output channel for the plugin
}

type cnsClient interface {
RequestIPAddress(context.Context, cns.IPConfigRequest) (*cns.IPConfigResponse, error)
ReleaseIPAddress(context.Context, cns.IPConfigRequest) error
}

// NewPlugin constructs a new IPAM plugin instance with given logger and CNS client
func NewPlugin(logger *zap.Logger, c cnsClient, out io.Writer) (*IPAMPlugin, error) {
plugin := &IPAMPlugin{
Name: pluginName,
Version: buildinfo.Version,
logger: logger,
out: out,
cnsClient: c,
}
return plugin, nil
}

//
// CNI implementation
// https://github.com/containernetworking/cni/blob/master/SPEC.md
//

// CmdAdd handles CNI add commands.
func (p *IPAMPlugin) CmdAdd(args *cniSkel.CmdArgs) error {
p.logger.Info("ADD called", zap.Any("args", args))

// Parsing network conf
nwCfg, err := parseNetConf(args.StdinData)
if err != nil {
p.logger.Error("Failed to parse CNI network config from stdin", zap.Error(err), zap.Any("argStdinData", args.StdinData))
return cniTypes.NewError(cniTypes.ErrDecodingFailure, err.Error(), "failed to parse CNI network config from stdin")
}
p.logger.Debug("Parsed network config", zap.Any("netconf", nwCfg))

// Create ip config request from args
req, err := ipconfig.CreateIPConfigReq(args)
if err != nil {
p.logger.Error("Failed to create CNS IP config request", zap.Error(err))
return cniTypes.NewError(ErrCreateIPConfigRequest, err.Error(), "failed to create CNS IP config request")
}
p.logger.Debug("Created CNS IP config request", zap.Any("request", req))

p.logger.Debug("Making request to CNS")
// if this fails, the caller plugin should execute again with cmdDel before returning error.
// https://www.cni.dev/docs/spec/#delegated-plugin-execution-procedure
resp, err := p.cnsClient.RequestIPAddress(context.TODO(), req)
if err != nil {
p.logger.Error("Failed to request IP address from CNS", zap.Error(err), zap.Any("request", req))
return cniTypes.NewError(ErrRequestIPConfigFromCNS, err.Error(), "failed to request IP address from CNS")
}
p.logger.Debug("Received CNS IP config response", zap.Any("response", resp))

// Get Pod IP and gateway IP from ip config response
podIPNet, gwIP, err := ipconfig.ProcessIPConfigResp(resp)
if err != nil {
p.logger.Error("Failed to interpret CNS IPConfigResponse", zap.Error(err), zap.Any("response", resp))
return cniTypes.NewError(ErrProcessIPConfigResponse, err.Error(), "failed to interpret CNS IPConfigResponse")
}
p.logger.Debug("Parsed pod IP and gateway IP", zap.String("podIPNet", podIPNet.String()), zap.String("gwIP", gwIP.String()))

cniResult := &types100.Result{
IPs: []*types100.IPConfig{
{
Address: net.IPNet{
IP: net.ParseIP(podIPNet.Addr().String()),
Mask: net.CIDRMask(podIPNet.Bits(), 32), // nolint
},
Gateway: net.ParseIP(gwIP.String()),
},
},
Routes: []*cniTypes.Route{
{
Dst: net.IPNet{
IP: net.IPv4zero,
Mask: net.IPv4Mask(0, 0, 0, 0),
},
GW: net.ParseIP(gwIP.String()),
},
},
}

// Get versioned result
versionedCniResult, err := cniResult.GetAsVersion(nwCfg.CNIVersion)
if err != nil {
p.logger.Error("Failed to interpret CNI result with netconf CNI version", zap.Error(err), zap.Any("cniVersion", nwCfg.CNIVersion))
return cniTypes.NewError(cniTypes.ErrIncompatibleCNIVersion, err.Error(), "failed to interpret CNI result with netconf CNI version")
}

p.logger.Info("ADD success", zap.Any("result", versionedCniResult))

// Write result to output channel
err = versionedCniResult.PrintTo(p.out)
if err != nil {
p.logger.Error("Failed to print CNI result to output channel", zap.Error(err), zap.Any("result", versionedCniResult))
return cniTypes.NewError(cniTypes.ErrIOFailure, err.Error(), "failed to print CNI result to output channel")
}

return nil
}

// CmdDel handles CNI delete commands.
func (p *IPAMPlugin) CmdDel(args *cniSkel.CmdArgs) error {
p.logger.Info("DEL called", zap.Any("args", args))

// Create ip config request from args
req, err := ipconfig.CreateIPConfigReq(args)
if err != nil {
p.logger.Error("Failed to create CNS IP config request", zap.Error(err))
return cniTypes.NewError(cniTypes.ErrTryAgainLater, err.Error(), "failed to create CNS IP config request")
}
p.logger.Debug("Created CNS IP config request", zap.Any("request", req))

p.logger.Debug("Making request to CNS")
// cnsClient enforces it own timeout
if err := p.cnsClient.ReleaseIPAddress(context.TODO(), req); err != nil {
p.logger.Error("Failed to release IP address from CNS", zap.Error(err), zap.Any("request", req))
return cniTypes.NewError(cniTypes.ErrTryAgainLater, err.Error(), "failed to release IP address from CNS")
}

p.logger.Info("DEL success")

return nil
}

// CmdCheck handles CNI check command - not implemented
func (p *IPAMPlugin) CmdCheck(args *cniSkel.CmdArgs) error {
p.logger.Info("CHECK called")
return nil
}

// Parse network config from given byte array
func parseNetConf(b []byte) (*cniTypes.NetConf, error) {
netConf := &cniTypes.NetConf{}
err := json.Unmarshal(b, netConf)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal net conf")
}

if netConf.CNIVersion == "" {
netConf.CNIVersion = "0.2.0" // default CNI version
}

return netConf, nil
}
Loading