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

cni: allow users to set CNI args in job spec #23538

Merged
merged 6 commits into from
Jul 12, 2024
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
3 changes: 3 additions & 0 deletions .changelog/23538.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
cni: allow users to input CNI args in job specification
```
6 changes: 5 additions & 1 deletion api/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ type DNSConfig struct {
Searches []string `mapstructure:"searches" hcl:"searches,optional"`
Options []string `mapstructure:"options" hcl:"options,optional"`
}
type CNIConfig struct {
Args map[string]string `hcl:"args,optional"`
}

// NetworkResource is used to describe required network
// resources of a given task.
Expand All @@ -160,7 +163,8 @@ type NetworkResource struct {
// XXX Deprecated. Please do not use. The field will be removed in Nomad
// 0.13 and is only being kept to allow any references to be removed before
// then.
MBits *int `hcl:"mbits,optional"`
MBits *int `hcl:"mbits,optional"`
CNI *CNIConfig `hcl:"cni,block"`
}

// COMPAT(0.13)
Expand Down
16 changes: 16 additions & 0 deletions client/allocrunner/networking_cni.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,18 @@ const (
ConsulIPTablesConfigEnvVar = "CONSUL_IPTABLES_CONFIG"
)

// Adds user inputted custom CNI args to cniArgs map
func addCustomCNIArgs(networks []*structs.NetworkResource, cniArgs map[string]string) {
for _, net := range networks {
if net.CNI == nil {
continue
}
for k, v := range net.CNI.Args {
cniArgs[k] = v
}
}
}

// Setup calls the CNI plugins with the add action
func (c *cniNetworkConfigurator) Setup(ctx context.Context, alloc *structs.Allocation, spec *drivers.NetworkIsolationSpec) (*structs.AllocNetworkStatus, error) {
if err := c.ensureCNIInitialized(); err != nil {
Expand All @@ -114,6 +126,10 @@ func (c *cniNetworkConfigurator) Setup(ctx context.Context, alloc *structs.Alloc
"IgnoreUnknown": "true",
}

tg := alloc.Job.LookupTaskGroup(alloc.TaskGroup)

addCustomCNIArgs(tg.Networks, cniArgs)

portMaps := getPortMapping(alloc, c.ignorePortMappingHostIP)

tproxyArgs, err := c.setupTransparentProxyArgs(alloc, spec, portMaps)
Expand Down
47 changes: 47 additions & 0 deletions client/allocrunner/networking_cni_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,53 @@ func TestCNI_cniToAllocNet_Invalid(t *testing.T) {
require.Nil(t, allocNet)
}

func TestCNI_addCustomCNIArgs(t *testing.T) {
gulducat marked this conversation as resolved.
Show resolved Hide resolved
ci.Parallel(t)
cniArgs := map[string]string{
"default": "yup",
}

networkWithArgs := []*structs.NetworkResource{{
CNI: &structs.CNIConfig{
Args: map[string]string{
"first_arg": "example",
"new_arg": "example_2",
},
},
}}
networkWithoutArgs := []*structs.NetworkResource{{
Mode: "bridge",
}}
testCases := []struct {
name string
network []*structs.NetworkResource
expectMap map[string]string
}{
{
name: "cni args not specified",
network: networkWithoutArgs,
expectMap: map[string]string{
"default": "yup",
},
}, {
name: "cni args specified",
network: networkWithArgs,
expectMap: map[string]string{
"default": "yup",
"first_arg": "example",
"new_arg": "example_2",
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
addCustomCNIArgs(tc.network, cniArgs)
test.Eq(t, tc.expectMap, cniArgs)

})
}
}

func TestCNI_setupTproxyArgs(t *testing.T) {
ci.Parallel(t)

Expand Down
7 changes: 7 additions & 0 deletions client/taskenv/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
// Current interoperable fields:
// - Hostname
// - DNS
// - CNI
func InterpolateNetworks(taskEnv *TaskEnv, networks structs.Networks) structs.Networks {

// Guard against not having a valid taskEnv. This can be the case if the
Expand All @@ -32,6 +33,12 @@ func InterpolateNetworks(taskEnv *TaskEnv, networks structs.Networks) structs.Ne
interpolated[i].DNS.Searches = taskEnv.ParseAndReplace(interpolated[i].DNS.Searches)
interpolated[i].DNS.Options = taskEnv.ParseAndReplace(interpolated[i].DNS.Options)
}
if interpolated[i].CNI != nil {
for k, v := range interpolated[i].CNI.Args {
interpolated[i].CNI.Args[k] = taskEnv.ReplaceEnv(v)

}
}
}

return interpolated
Expand Down
24 changes: 24 additions & 0 deletions client/taskenv/network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,30 @@ func Test_InterpolateNetworks(t *testing.T) {
},
name: "interpolated dns servers",
},
{
inputTaskEnv: testEnv,
inputNetworks: structs.Networks{
{
CNI: &structs.CNIConfig{
Args: map[string]string{
"static": "example",
"second": "${foo}-opt",
},
},
},
},
expectedOutputNetworks: structs.Networks{
{
CNI: &structs.CNIConfig{
Args: map[string]string{
"static": "example",
"second": "bar-opt",
},
},
},
},
name: "interpolated and non-interpolated cni args",
},
}

for _, tc := range testCases {
Expand Down
5 changes: 5 additions & 0 deletions command/agent/job_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -1553,6 +1553,11 @@ func ApiNetworkResourceToStructs(in []*api.NetworkResource) []*structs.NetworkRe
Options: nw.DNS.Options,
}
}
if nw.CNI != nil {
out[i].CNI = &structs.CNIConfig{
Args: nw.CNI.Args,
}
}

if l := len(nw.DynamicPorts); l != 0 {
out[i].DynamicPorts = make([]structs.Port, l)
Expand Down
32 changes: 32 additions & 0 deletions nomad/structs/cni_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package structs

import (
"maps"
)

type CNIConfig struct {
Args map[string]string
}

func (d *CNIConfig) Copy() *CNIConfig {
if d == nil {
return nil
}
newMap := make(map[string]string)
for k, v := range d.Args {
newMap[k] = v
}
Comment on lines +18 to +21
Copy link
Member

Choose a reason for hiding this comment

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

There's a maps.Clone helper for this.

return &CNIConfig{
Args: newMap,
}
}

func (d *CNIConfig) Equal(o *CNIConfig) bool {
if d == nil || o == nil {
return d == o
}
return maps.Equal(d.Args, o.Args)
}
28 changes: 28 additions & 0 deletions nomad/structs/cni_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package structs

import (
"github.com/hashicorp/nomad/ci"
"github.com/shoenig/test/must"
"testing"
)

func TestCNIConfig_Equal(t *testing.T) {
ci.Parallel(t)

must.Equal[*CNIConfig](t, nil, nil)
must.NotEqual[*CNIConfig](t, nil, new(CNIConfig))
must.NotEqual[*CNIConfig](t, nil, &CNIConfig{Args: map[string]string{"first": "second"}})

must.StructEqual(t, &CNIConfig{
Args: map[string]string{
"arg": "example_1",
"new_arg": "example_2",
},
}, []must.Tweak[*CNIConfig]{{
Field: "Args",
Apply: func(c *CNIConfig) { c.Args = map[string]string{"different": "arg"} },
}})
}
19 changes: 19 additions & 0 deletions nomad/structs/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -2663,6 +2663,10 @@ func (n *NetworkResource) Diff(other *NetworkResource, contextual bool) *ObjectD
diff.Objects = append(diff.Objects, dnsDiff)
}

if cniDiff := n.CNI.Diff(other.CNI, contextual); cniDiff != nil {
diff.Objects = append(diff.Objects, cniDiff)
}

return diff
}

Expand Down Expand Up @@ -2706,6 +2710,21 @@ func (d *DNSConfig) Diff(other *DNSConfig, contextual bool) *ObjectDiff {
return diff
}

// Diff returns a diff of two CNIConfig structs
func (d *CNIConfig) Diff(other *CNIConfig, contextual bool) *ObjectDiff {
if d == nil {
d = &CNIConfig{}
}
if other == nil {
other = &CNIConfig{}
}
if d.Equal(other) {
return nil
}

return primitiveObjectDiff(d.Args, other.Args, nil, "CNIConfig", contextual)
}

func disconectStrategyDiffs(old, new *DisconnectStrategy, contextual bool) *ObjectDiff {
diff := &ObjectDiff{Type: DiffTypeNone, Name: "Disconnect"}
var oldDisconnectFlat, newDisconnectFlat map[string]string
Expand Down
Loading