diff --git a/cmd/apply.go b/cmd/apply.go index 9eb0e19..d8e9cf2 100644 --- a/cmd/apply.go +++ b/cmd/apply.go @@ -64,6 +64,11 @@ var applyCmd = &cobra.Command{ if err != nil { return err } + + err = RunPostScript(netns, values.PostScript) + if err != nil { + return err + } } return nil }, @@ -206,3 +211,19 @@ func SetupVethDevices(netns string, devices map[string]config.VethDevice) error func init() { rootCmd.AddCommand(applyCmd) } + +func RunPostScript(netns string, script string) error { + if script == "" { + return nil + } + + n := ip.IntoNetns(netns) + slog.Info("run post script", "netns", netns, "script", script) + out, err := n.ExecuteCommand(script) + if err != nil { + return err + } + slog.Debug("post script output", "netns", netns, "script", script, "output", out) + + return nil +} diff --git a/config/config.go b/config/config.go index 6d52a21..a61103c 100644 --- a/config/config.go +++ b/config/config.go @@ -35,6 +35,7 @@ type Netns struct { Ethernets map[string]Ethernet `yaml:"ethernets,omitempty"` DummyDevices map[string]Ethernet `yaml:"dummy-devices,omitempty"` VethDevices map[string]VethDevice `yaml:"veth-devices,omitempty"` + PostScript string `yaml:"post-script,omitempty"` } type Ethernet struct { diff --git a/iproute2/base.go b/iproute2/base.go index 4121959..56bc01e 100644 --- a/iproute2/base.go +++ b/iproute2/base.go @@ -24,14 +24,25 @@ package iproute2 import ( "encoding/json" "fmt" + "io" "os/exec" "strings" "syscall" ) type BaseCommand struct { - path string - prependArgs []string + path string + prepend []string +} + +func (b *BaseCommand) run(args ...string) error { + _, err := b.runIpCommand(args...) + return err +} + +func (b *BaseCommand) runIpCommand(args ...string) (string, error) { + cmd := append([]string{b.path}, args...) + return b.runCommand(cmd, nil) } type Error struct { @@ -39,25 +50,35 @@ type Error struct { Message string } -func (b *BaseCommand) execute(args ...string) error { - _, err := b.executeWithOutput(args...) - return err -} - func (e *Error) Error() string { msg := strings.TrimRight(e.Message, "\n") return fmt.Sprintf("%s (exit status: %d)", msg, e.ExitStatus) } -func (b *BaseCommand) executeWithOutput(args ...string) (string, error) { - cmdArgs := append(b.prependArgs, args...) +func (b *BaseCommand) runCommand(cmd []string, input *string) (string, error) { + if b.prepend != nil { + cmd = append(b.prepend, cmd...) + } + path := cmd[0] + args := cmd[1:] if logger != nil { - logger.Debug("exec", "cmd", b.path, "args", cmdArgs) + logger.Debug("exec", "cmd", path, "args", args) + } + + c := exec.Command(path, args...) + if input != nil { + stdin, err := c.StdinPipe() + if err != nil { + return "", err + } + go func() { + defer stdin.Close() + io.WriteString(stdin, *input) + }() } - cmd := exec.Command(b.path, cmdArgs...) - stdout, err := cmd.Output() + stdout, err := c.Output() if err != nil { exitErr, _ := err.(*exec.ExitError) status, _ := exitErr.Sys().(syscall.WaitStatus) @@ -72,11 +93,11 @@ func (b *BaseCommand) executeWithOutput(args ...string) (string, error) { func (b *BaseCommand) AddLink(name string, linkType string, options ...string) error { args := append([]string{"link", "add", name, "type", linkType}, options...) - return b.execute(args...) + return b.run(args...) } func (b *BaseCommand) DelLink(name string) error { - return b.execute("link", "del", name) + return b.run("link", "del", name) } func (b *BaseCommand) AddDummyDevice(name string) error { @@ -88,23 +109,23 @@ func (b *BaseCommand) AddVethDevice(name string, peerName string) error { } func (b *BaseCommand) SetLinkUp(name string) error { - return b.execute("link", "set", "dev", name, "up") + return b.run("link", "set", "dev", name, "up") } func (b *BaseCommand) AddAddress(name string, address string) error { - return b.execute("address", "add", address, "dev", name) + return b.run("address", "add", address, "dev", name) } func (b *BaseCommand) DelAddress(name string, address string) error { - return b.execute("address", "del", address, "dev", name) + return b.run("address", "del", address, "dev", name) } func (b *BaseCommand) AddRoute(name string, to string, via string) error { - return b.execute("route", "add", to, "via", via, "dev", name) + return b.run("route", "add", to, "via", via, "dev", name) } func (b *BaseCommand) DelRoute(name string, to string, via string) error { - return b.execute("route", "del", to, "via", via, "dev", name) + return b.run("route", "del", to, "via", via, "dev", name) } func (i *IpCmd) InNetns() bool { @@ -143,7 +164,7 @@ type InterfaceInfo struct { type Addresses []InterfaceInfo func (b *BaseCommand) ListAddresses() (*Addresses, error) { - data, err := b.executeWithOutput("-json", "address", "show") + data, err := b.runIpCommand("-json", "address", "show") if err != nil { return nil, err } @@ -175,7 +196,7 @@ type Link struct { type Links []Link func (b *BaseCommand) ListLinks() (*Links, error) { - data, err := b.executeWithOutput("-json", "link", "show") + data, err := b.runIpCommand("-json", "link", "show") if err != nil { return nil, err } diff --git a/iproute2/iproute2.go b/iproute2/iproute2.go index 236204f..e812b75 100644 --- a/iproute2/iproute2.go +++ b/iproute2/iproute2.go @@ -46,7 +46,7 @@ func New(path string) *IpCmd { } func (i *IpCmd) AddNetns(name string) error { - return i.execute("netns", "add", name) + return i.run("netns", "add", name) } func (i *IpCmd) DelNetns(name string) error { @@ -63,11 +63,11 @@ func (i *IpCmd) DelNetns(name string) error { return fmt.Errorf("netns %s has running processes: %s", name, strings.Join(pidStrs, ", ")) } - return i.execute("netns", "del", name) + return i.run("netns", "del", name) } func (i *IpCmd) ListNetns() []string { - data, _ := i.executeWithOutput("netns", "list") + data, _ := i.runIpCommand("netns", "list") var netns []string for _, line := range strings.Split(data, "\n") { @@ -79,11 +79,11 @@ func (i *IpCmd) ListNetns() []string { } func (i *IpCmd) SetNetns(name string, netns string) error { - return i.execute("link", "set", name, "netns", netns) + return i.run("link", "set", name, "netns", netns) } func (i *IpCmd) ListNetnsProcesses(netns string) ([]int, error) { - out, err := i.executeWithOutput("netns", "pids", netns) + out, err := i.runIpCommand("netns", "pids", netns) if err != nil { return nil, err } @@ -116,8 +116,8 @@ func (i *IpCmd) IntoNetns(netns string) *IpCmdWithNetns { ip := IpCmdWithNetns{ netns: netns, BaseCommand: BaseCommand{ - path: i.path, - prependArgs: []string{"netns", "exec", netns, i.path}, + path: i.path, + prepend: []string{i.path, "netns", "exec", netns}, }, } return &ip @@ -136,6 +136,7 @@ func (i *IpCmdWithNetns) Netns() string { return i.netns } -func (i *IpCmdWithNetns) ExecuteCommand(args ...string) (string, error) { - return i.executeWithOutput(args...) +func (i *IpCmdWithNetns) ExecuteCommand(cmd string) (string, error) { + shell := []string{"/bin/bash"} + return i.runCommand(shell, &cmd) } diff --git a/sample/netnsplan.yaml b/sample/netnsplan.yaml index deb2ca2..819f3f3 100644 --- a/sample/netnsplan.yaml +++ b/sample/netnsplan.yaml @@ -1,15 +1,18 @@ netns: sample1: ethernets: - eth0: - addresses: - - 192.168.10.1 eth1: addresses: - 192.168.20.1 + route: + - to: default + via: 192.168.20.254 + post-script: | + sysctl --system + iptables-restore /etc/iptables/rules.v4 sample2: ethernets: - eth0: + eth2: addresses: - 172.16.10.1 dummy-devices: