Skip to content

Commit

Permalink
policy,init: do not start policy in exclusive eni mode
Browse files Browse the repository at this point in the history
Signed-off-by: l1b0k <[email protected]>
  • Loading branch information
l1b0k committed Sep 10, 2024
1 parent 392456e commit b6733c9
Show file tree
Hide file tree
Showing 14 changed files with 606 additions and 52 deletions.
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 @@ import (
"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 @@ var cniCmd = &cobra.Command{
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 @@ import (
"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 @@ func switchDataPathV2() bool {
_, 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 @@ func (b *NetworkServiceBuilder) InitK8S() *NetworkServiceBuilder {
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 @@ func stackTriger() {

// 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
Loading

0 comments on commit b6733c9

Please sign in to comment.