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

support exclusive eni in shared eni #682

Merged
merged 4 commits into from
Sep 10, 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
6 changes: 6 additions & 0 deletions charts/terway/templates/daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ spec:
command:
- /bin/init.sh
env:
- name: TERWAY_DAEMON_MODE
value: "{{.Values.daemonMode}}"
- name: K8S_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: DISABLE_POLICY
valueFrom:
configMapKeyRef:
Expand Down
3 changes: 1 addition & 2 deletions cmd/terway-cli/cni.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
"strings"

"github.com/Jeffail/gabs/v2"
"github.com/docker/docker/pkg/parsers/kernel"
"github.com/spf13/cobra"
utilfeature "k8s.io/apiserver/pkg/util/feature"
cliflag "k8s.io/component-base/cli/flag"
Expand Down Expand Up @@ -71,7 +70,7 @@
func processCNIConfig(cmd *cobra.Command, args []string) error {
flag.Parse()

_checkKernelVersion = kernel.CheckKernelVersion
_checkKernelVersion = checkKernelVersion

Check warning on line 73 in cmd/terway-cli/cni.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/cni.go#L73

Added line #L73 was not covered by tests

_switchDataPathV2 = switchDataPathV2

Expand Down
5 changes: 5 additions & 0 deletions cmd/terway-cli/cni_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"errors"
"fmt"

"github.com/docker/docker/pkg/parsers/kernel"
"github.com/vishvananda/netlink"
utilfeature "k8s.io/apiserver/pkg/util/feature"

Expand All @@ -25,3 +26,7 @@
_, err := netlink.LinkByName("cilium_net")
return errors.As(err, &netlink.LinkNotFoundError{})
}

func checkKernelVersion(k, major, minor int) bool {
return kernel.CheckKernelVersion(k, major, minor)

Check warning on line 31 in cmd/terway-cli/cni_linux.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/cni_linux.go#L30-L31

Added lines #L30 - L31 were not covered by tests
}
4 changes: 4 additions & 0 deletions cmd/terway-cli/cni_unsupport.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ package main
func switchDataPathV2() bool {
return true
}

func checkKernelVersion(k, major, minor int) bool {
return false
}
2 changes: 1 addition & 1 deletion cmd/terway-cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ var (
)

func init() {
rootCmd.AddCommand(listCmd, showCmd, mappingCmd, executeCmd, metadataCmd, cniCmd)
rootCmd.AddCommand(listCmd, showCmd, mappingCmd, executeCmd, metadataCmd, cniCmd, nodeconfigCmd)
}

func main() {
Expand Down
173 changes: 173 additions & 0 deletions cmd/terway-cli/node.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package main

import (
"context"
"fmt"
"os"
"time"

"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
k8sClient "sigs.k8s.io/controller-runtime/pkg/client"

"github.com/AliyunContainerService/terway/pkg/utils/nodecap"
"github.com/AliyunContainerService/terway/pkg/version"
"github.com/AliyunContainerService/terway/types"
"github.com/AliyunContainerService/terway/types/daemon"
)

const eniOnlyCNI = `{
"cniVersion": "0.4.0",
"name": "terway-chainer",
"plugins": [
{
"capabilities": {
"bandwidth": true
},
"host_stack_cidrs": [
"169.254.20.10/32"
],
"type": "terway"
}
]
}`

const cniFilePath = "/etc/cni/net.d/10-terway.conflist"
const nodeCapabilitiesFile = "/var/run/eni/node_capabilities"

type Task struct {
Name string
Func func(cmd *cobra.Command, args []string) error
}

var tasks = []Task{
{
Name: "get eni config",
Func: getENIConfig,
},
{
Name: "eniOnly",
Func: overrideCNI,
},
{
Name: "dual stack",
Func: dualStack,
},
}

var nodeconfigCmd = &cobra.Command{
Use: "nodeconfig",
SilenceUsage: true,
Run: func(cmd *cobra.Command, args []string) {
for _, task := range tasks {
err := task.Func(cmd, args)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "task: %serror: %v\n", task.Name, err)
os.Exit(1)

Check warning on line 68 in cmd/terway-cli/node.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/node.go#L63-L68

Added lines #L63 - L68 were not covered by tests
}
}
},
}

var eniCfg *daemon.Config

func getENIConfig(cmd *cobra.Command, args []string) error {
var err error
eniCfg, err = daemon.GetConfigFromFileWithMerge("/etc/eni/eni.json", nil)
if err != nil {
return err

Check warning on line 80 in cmd/terway-cli/node.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/node.go#L76-L80

Added lines #L76 - L80 were not covered by tests
}

fmt.Printf("eni config: %+v\n", eniCfg)
return nil

Check warning on line 84 in cmd/terway-cli/node.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/node.go#L83-L84

Added lines #L83 - L84 were not covered by tests
}

func overrideCNI(cmd *cobra.Command, args []string) error {
restConfig := ctrl.GetConfigOrDie()
restConfig.UserAgent = version.UA
c, err := k8sClient.New(restConfig, k8sClient.Options{
Scheme: types.Scheme,
Mapper: types.NewRESTMapper(),
})
if err != nil {
return err

Check warning on line 95 in cmd/terway-cli/node.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/node.go#L87-L95

Added lines #L87 - L95 were not covered by tests
}

node := &corev1.Node{}
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel()

Check warning on line 100 in cmd/terway-cli/node.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/node.go#L98-L100

Added lines #L98 - L100 were not covered by tests

nodeName := os.Getenv("K8S_NODE_NAME")

Check warning on line 102 in cmd/terway-cli/node.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/node.go#L102

Added line #L102 was not covered by tests

err = c.Get(ctx, k8sClient.ObjectKey{
Name: nodeName,
}, node, &k8sClient.GetOptions{
Raw: &metav1.GetOptions{
ResourceVersion: "0",
},
})
if err != nil {
return fmt.Errorf("get node %s error: %w", nodeName, err)

Check warning on line 112 in cmd/terway-cli/node.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/node.go#L104-L112

Added lines #L104 - L112 were not covered by tests
}

store := nodecap.NewFileNodeCapabilities(nodeCapabilitiesFile)
return setExclusiveMode(store, node.Labels, cniFilePath)

Check warning on line 116 in cmd/terway-cli/node.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/node.go#L115-L116

Added lines #L115 - L116 were not covered by tests
}

func setExclusiveMode(store nodecap.NodeCapabilitiesStore, labels map[string]string, cniPath string) error {
err := store.Load()
if err != nil {
return err

Check warning on line 122 in cmd/terway-cli/node.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/node.go#L122

Added line #L122 was not covered by tests
}
now := types.NodeExclusiveENIMode(labels)

switch store.Get(nodecap.NodeCapabilityExclusiveENI) {
case string(types.ExclusiveENIOnly):
if now != types.ExclusiveENIOnly {
return fmt.Errorf("exclusive eni mode changed")
}
case string(types.ExclusiveDefault):
if now != types.ExclusiveDefault {
return fmt.Errorf("exclusive eni mode changed")

Check warning on line 133 in cmd/terway-cli/node.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/node.go#L131-L133

Added lines #L131 - L133 were not covered by tests
}
case "":
// empty for new node, or rebooted
}

store.Set(nodecap.NodeCapabilityExclusiveENI, string(now))
err = store.Save()
if err != nil {
return err

Check warning on line 142 in cmd/terway-cli/node.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/node.go#L142

Added line #L142 was not covered by tests
}

// write cni config
if now == types.ExclusiveENIOnly {
err = os.WriteFile(cniPath, []byte(eniOnlyCNI), 0644)
if err != nil {
return err

Check warning on line 149 in cmd/terway-cli/node.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/node.go#L149

Added line #L149 was not covered by tests
}
}
return nil
}

func dualStack(cmd *cobra.Command, args []string) error {
store := nodecap.NewFileNodeCapabilities(nodeCapabilitiesFile)

Check warning on line 156 in cmd/terway-cli/node.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/node.go#L155-L156

Added lines #L155 - L156 were not covered by tests

val := ""
switch eniCfg.IPStack {
case "dual", "ipv6":
val = "true"
default:
val = "false"

Check warning on line 163 in cmd/terway-cli/node.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/node.go#L158-L163

Added lines #L158 - L163 were not covered by tests
}

err := store.Load()
if err != nil {
return err

Check warning on line 168 in cmd/terway-cli/node.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/node.go#L166-L168

Added lines #L166 - L168 were not covered by tests
}

store.Set(nodecap.NodeCapabilityIPv6, val)
return store.Save()

Check warning on line 172 in cmd/terway-cli/node.go

View check run for this annotation

Codecov / codecov/patch

cmd/terway-cli/node.go#L171-L172

Added lines #L171 - L172 were not covered by tests
}
94 changes: 94 additions & 0 deletions cmd/terway-cli/node_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package main

import (
"os"
"testing"

"github.com/stretchr/testify/assert"

"github.com/AliyunContainerService/terway/pkg/utils/nodecap"
)

func TestExclusiveModeNewNode(t *testing.T) {
tempFile, err := os.CreateTemp("", "test_node_capabilities")
assert.NoError(t, err)
defer os.Remove(tempFile.Name())

store := nodecap.NewFileNodeCapabilities(tempFile.Name())
labels := map[string]string{"k8s.aliyun.com/exclusive-mode-eni-type": "eniOnly"}
cniPath := tempFile.Name() + "_cni_config"

err = setExclusiveMode(store, labels, cniPath)
assert.NoError(t, err)
assert.Equal(t, "eniOnly", store.Get(nodecap.NodeCapabilityExclusiveENI))
}

func TestExclusiveModeDoesNotChangeWhenAlreadySet(t *testing.T) {
tempFile, err := os.CreateTemp("", "test_node_capabilities")
assert.NoError(t, err)
defer os.Remove(tempFile.Name())

err = os.WriteFile(tempFile.Name(), []byte("cni_exclusive_eni = eniOnly"), 0644)
assert.NoError(t, err)

store := nodecap.NewFileNodeCapabilities(tempFile.Name())
labels := map[string]string{"k8s.aliyun.com/exclusive-mode-eni-type": "eniOnly"}
cniPath := tempFile.Name() + "_cni_config"

err = setExclusiveMode(store, labels, cniPath)
assert.NoError(t, err)
assert.Equal(t, "eniOnly", store.Get(nodecap.NodeCapabilityExclusiveENI))
}

func TestExclusiveModeFailsWhenChangedFromExclusive(t *testing.T) {
tempFile, err := os.CreateTemp("", "test_node_capabilities")
assert.NoError(t, err)
defer os.Remove(tempFile.Name())

err = os.WriteFile(tempFile.Name(), []byte("cni_exclusive_eni = eniOnly"), 0644)
assert.NoError(t, err)

store := nodecap.NewFileNodeCapabilities(tempFile.Name())
labels := map[string]string{"k8s.aliyun.com/exclusive-mode-eni-type": "default"}
cniPath := tempFile.Name() + "_cni_config"

err = setExclusiveMode(store, labels, cniPath)
assert.Error(t, err)
assert.Contains(t, err.Error(), "exclusive eni mode changed")
}

func TestExclusiveModeWritesCNIConfigWhenSet(t *testing.T) {
tempFile, err := os.CreateTemp("", "test_node_capabilities")
assert.NoError(t, err)
defer os.Remove(tempFile.Name())

store := nodecap.NewFileNodeCapabilities(tempFile.Name())
labels := map[string]string{"k8s.aliyun.com/exclusive-mode-eni-type": "eniOnly"}
cniPath := tempFile.Name() + "_cni_config"
defer os.Remove(cniPath)

err = setExclusiveMode(store, labels, cniPath)
assert.NoError(t, err)

content, err := os.ReadFile(cniPath)
assert.NoError(t, err)
assert.Contains(t, string(content), `"type": "terway"`)
}

func TestExclusiveModeDoesNotWriteCNIConfigWhenNotSet(t *testing.T) {
tempFile, err := os.CreateTemp("", "test_node_capabilities")
assert.NoError(t, err)
defer os.Remove(tempFile.Name())

store := nodecap.NewFileNodeCapabilities(tempFile.Name())
labels := map[string]string{"k8s.aliyun.com/exclusive-mode-eni-type": "default"}
cniPath := tempFile.Name() + "_cni_config"
defer os.Remove(cniPath)

err = setExclusiveMode(store, labels, cniPath)
assert.NoError(t, err)

_, err = os.ReadFile(cniPath)
assert.Error(t, err)
assert.True(t, os.IsNotExist(err))
}
5 changes: 5 additions & 0 deletions daemon/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@
b.err = fmt.Errorf("error init k8s: %w", err)
return b
}

if types.NodeExclusiveENIMode(b.service.k8s.Node().Labels) == types.ExclusiveENIOnly {
b.service.daemonMode = daemon.ModeENIOnly

Check warning on line 113 in daemon/builder.go

View check run for this annotation

Codecov / codecov/patch

daemon/builder.go#L112-L113

Added lines #L112 - L113 were not covered by tests
}

return b
}

Expand Down
6 changes: 5 additions & 1 deletion daemon/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@

// Run terway daemon
func Run(ctx context.Context, socketFilePath, debugSocketListen, configFilePath, daemonMode string) error {
// init.sh already create the /var/run/eni
err := os.MkdirAll(filepath.Dir(lockFile), 0700)
if err != nil {
return err

Check warning on line 73 in daemon/server.go

View check run for this annotation

Codecov / codecov/patch

daemon/server.go#L71-L73

Added lines #L71 - L73 were not covered by tests
}

lock, err := filemutex.New(lockFile)
if err != nil {
return fmt.Errorf("error create lock file: %s, %w", lockFile, err)
Expand Down
13 changes: 10 additions & 3 deletions init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ set -o nounset

# install CNIs
cp -f /usr/bin/terway /opt/cni/bin/
cp -f /usr/bin/cilium-cni /opt/cni/bin/
chmod +x /opt/cni/bin/terway
chmod +x /opt/cni/bin/cilium-cni

if [ "$TERWAY_DAEMON_MODE" != "VPC" ]; then
cp -f /usr/bin/cilium-cni /opt/cni/bin/
chmod +x /opt/cni/bin/cilium-cni
fi

# init cni config
cp /tmp/eni/eni_conf /etc/eni/eni.json

terway-cli cni /tmp/eni/10-terway.conflist /tmp/eni/10-terway.conf --output /etc/cni/net.d/10-terway.conflist
terway-cli nodeconfig

node_capabilities=/var/run/eni/node_capabilities
if [ ! -f "$node_capabilities" ]; then
Expand Down Expand Up @@ -42,4 +46,7 @@ cp $node_capabilities /var-run-eni/node_capabilities

sysctl -w net.ipv4.conf.eth0.rp_filter=0
modprobe sch_htb || true
chroot /host sh -c "systemctl disable eni.service; rm -f /etc/udev/rules.d/75-persistent-net-generator.rules /lib/udev/rules.d/60-net.rules /lib/udev/rules.d/61-eni.rules /lib/udev/write_net_rules"

if [ "$TERWAY_DAEMON_MODE" != "VPC" ]; then
chroot /host sh -c "systemctl disable eni.service; rm -f /etc/udev/rules.d/75-persistent-net-generator.rules /lib/udev/rules.d/60-net.rules /lib/udev/rules.d/61-eni.rules /lib/udev/write_net_rules"
fi
5 changes: 5 additions & 0 deletions pkg/apis/crds/network.alibabacloud.com_podenis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ spec:
eni:
description: ENI eni info
properties:
attachmentOptions:
properties:
trunk:
type: boolean
type: object
id:
type: string
mac:
Expand Down
Loading
Loading