From 2f98c526d1118b2ba8da2bab05624cc9e2d8f113 Mon Sep 17 00:00:00 2001 From: Swagat Bora <sbora@amazon.com> Date: Wed, 9 Oct 2024 17:32:46 +0000 Subject: [PATCH] feat: implementation of enable_icc option + code refactor Signed-off-by: Swagat Bora <sbora@amazon.com> --- go.mod | 3 +- go.sum | 2 + internal/service/network/bridge_driver.go | 375 ++++++++++++++++++ .../service/network/bridge_driver_test.go | 123 ++++++ internal/service/network/create.go | 177 ++------- internal/service/network/create_test.go | 145 ++++++- internal/service/network/remove.go | 44 ++ mocks/mocks_backend/nerdctlimagesvc.go | 2 +- mocks/mocks_container/containersvc.go | 2 +- mocks/mocks_network/bridge_driver.go | 150 +++++++ mocks/mocks_network/iptables.go | 120 ++++++ 11 files changed, 998 insertions(+), 145 deletions(-) create mode 100644 internal/service/network/bridge_driver.go create mode 100644 internal/service/network/bridge_driver_test.go create mode 100644 mocks/mocks_network/bridge_driver.go create mode 100644 mocks/mocks_network/iptables.go diff --git a/go.mod b/go.mod index 207d507a..38c0cdea 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/containerd/platforms v0.2.1 github.com/containerd/typeurl/v2 v2.2.0 github.com/containernetworking/cni v1.2.2 + github.com/coreos/go-iptables v0.7.0 github.com/coreos/go-systemd/v22 v22.5.0 github.com/distribution/reference v0.6.0 github.com/docker/cli v26.0.0+incompatible @@ -63,7 +64,6 @@ require ( github.com/containerd/typeurl v1.0.3-0.20220422153119-7f6e6d160d67 // indirect github.com/containernetworking/plugins v1.4.1 // indirect github.com/containers/ocicrypt v1.1.10 // indirect - github.com/coreos/go-iptables v0.7.0 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/djherbis/times v1.6.0 // indirect github.com/docker/docker-credential-helpers v0.8.1 // indirect @@ -138,6 +138,7 @@ require ( go.opentelemetry.io/otel/trace v1.30.0 // indirect golang.org/x/crypto v0.27.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/mod v0.20.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/term v0.24.0 // indirect golang.org/x/text v0.18.0 // indirect diff --git a/go.sum b/go.sum index 21c7f4b1..05aaf7c4 100644 --- a/go.sum +++ b/go.sum @@ -358,6 +358,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/internal/service/network/bridge_driver.go b/internal/service/network/bridge_driver.go new file mode 100644 index 00000000..f6ae1dec --- /dev/null +++ b/internal/service/network/bridge_driver.go @@ -0,0 +1,375 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package network + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strconv" + + "github.com/containerd/nerdctl/pkg/lockutil" + "github.com/containerd/nerdctl/pkg/netutil" + "github.com/coreos/go-iptables/iptables" + "github.com/runfinch/finch-daemon/api/types" + "github.com/runfinch/finch-daemon/internal/backend" + "github.com/runfinch/finch-daemon/pkg/flog" +) + +const ( + FinchICCLabel = "finch.network.bridge.enable_icc" + + BridgeICCOption = "com.docker.network.bridge.enable_icc" + BridgeHostBindingIpv4Option = "com.docker.network.bridge.host_binding_ipv4" + BridgeNameOption = "com.docker.network.bridge.name" +) + +//go:generate mockgen --destination=../../../mocks/mocks_network/bridge_driver.go -package=mocks_network -mock_names BridgeDriverOperations=BridgeDriver . BridgeDriverOperations +type BridgeDriverOperations interface { + HandleCreateOptions(request types.NetworkCreateRequest, options netutil.CreateOptions) (netutil.CreateOptions, error) + HandlePostCreate(net *netutil.NetworkConfig) (string, error) + SetBridgeName(net *netutil.NetworkConfig, bridge string) error + GetBridgeName(net *netutil.NetworkConfig) (string, error) + GetNetworkByBridgeName(bridge string) (*netutil.NetworkConfig, error) + DisableICC(bridgeIface string, insert bool) error + SetICCDisabled() + ICCDisabled() bool +} + +// IPTablesWrapper is an interface that wraps the methods of iptables.IPTables +// to help with mock +// +//go:generate mockgen --destination=../../../mocks/mocks_network/iptables.go -package=mocks_network . IPTablesWrapper +type IPTablesWrapper interface { + ChainExists(table, chain string) (bool, error) + NewChain(table, chain string) error + Insert(table, chain string, pos int, rulespec ...string) error + Append(table, chain string, rulespec ...string) error + DeleteIfExists(table, chain string, rulespec ...string) error +} + +// IPTablesWrapperImpl implements IPTablesWrapper +// that delegates to an actual iptables.IPTables instance. +type IPTablesWrapperImpl struct { + ipt *iptables.IPTables +} + +func NewIPTablesWrapper() IPTablesWrapper { + iptables, _ := iptables.NewWithProtocol(iptables.ProtocolIPv4) + return &IPTablesWrapperImpl{ipt: iptables} +} + +func (i *IPTablesWrapperImpl) ChainExists(table, chain string) (bool, error) { + return i.ipt.ChainExists(table, chain) +} + +func (i *IPTablesWrapperImpl) NewChain(table, chain string) error { + return i.ipt.NewChain(table, chain) +} + +func (i *IPTablesWrapperImpl) Insert(table, chain string, pos int, rulespec ...string) error { + return i.ipt.Insert(table, chain, pos, rulespec...) +} + +func (i *IPTablesWrapperImpl) Append(table, chain string, rulespec ...string) error { + return i.ipt.Append(table, chain, rulespec...) +} + +func (i *IPTablesWrapperImpl) DeleteIfExists(table, chain string, rulespec ...string) error { + return i.ipt.DeleteIfExists(table, chain, rulespec...) +} + +type bridgeDriver struct { + bridgeName string + disableICC bool + netClient backend.NerdctlNetworkSvc + logger flog.Logger + ipt IPTablesWrapper +} + +var _ BridgeDriverOperations = (*bridgeDriver)(nil) + +var NewBridgeDriver = func(netClient backend.NerdctlNetworkSvc, logger flog.Logger) BridgeDriverOperations { + return &bridgeDriver{ + netClient: netClient, + logger: logger, + ipt: NewIPTablesWrapper(), + } +} + +// handleBridgeDriverOptions filters unsupported options for the bridge driver. +func (bd *bridgeDriver) HandleCreateOptions(request types.NetworkCreateRequest, options netutil.CreateOptions) (netutil.CreateOptions, error) { + // enable_icc, host_binding_ipv4, and bridge name network options are not supported by nerdctl. + // So we must filter out any unsupported options which would prevent the network from being created and accept the defaults. + filterUnsupportedOptions := func(original map[string]string) map[string]string { + opts := map[string]string{} + for k, v := range original { + switch k { + case BridgeHostBindingIpv4Option: + if v != "0.0.0.0" { + bd.logger.Warnf("network option com.docker.network.bridge.host_binding_ipv4 is set to %s, but it must be 0.0.0.0", v) + } + case BridgeICCOption: + iccOption, err := strconv.ParseBool(v) //t + if err != nil { + bd.logger.Warnf("invalid value for com.docker.network.bridge.enable_icc: %s", v) + } + if !iccOption { + bd.SetICCDisabled() + } + + case BridgeNameOption: + bd.bridgeName = v + default: + opts[k] = v + } + } + return opts + } + + options.Options = filterUnsupportedOptions(request.Options) + + if bd.ICCDisabled() { + // Append a label when enable_icc is set to false. This is used for clean up during network removal. + options.Labels = append(options.Labels, FinchICCLabel+"=false") + } + + // Return the modified options + return options, nil +} + +func (bd *bridgeDriver) HandlePostCreate(net *netutil.NetworkConfig) (string, error) { + // Handle bridge driver post create actions + var warning string + var err error + bridgeName := bd.bridgeName + if bridgeName != "" { + // Since nerdctl currently does not support custom bridge names, + // we explicitly override bridge name in the conflist file for the network that was just created + if err = bd.SetBridgeName(net, bridgeName); err != nil { + warning = fmt.Sprintf("Failed to set network bridge name %s: %s", bridgeName, err) + } + } + + if bd.ICCDisabled() { + // Handle "enable_icc=false" option if set (bd.disableICC is true) + // By default, CNI allows connectivity between containers attached to the same bridge. + // If "com.docker.network.bridge.enable_icc" option is explicitly set to false, + // we disable inter-container connectivity by applying iptable rules + // If "com.docker.network.bridge.enable_icc=true" is set, it is considered a noop + if bridgeName == "" { + bridgeName, err = bd.GetBridgeName(net) + if err != nil { + return "", fmt.Errorf("failed to get bridge name to enable inter-container connectivity: %w ", err) + } + } + err = bd.DisableICC(bridgeName, true) + if err != nil { + return "", fmt.Errorf("failed to disable inter-container connectivity: %w", err) + } + } + + return warning, nil +} + +// setBridgeName will override the bridge name in an existing CNI config file for a network. +func (bd *bridgeDriver) SetBridgeName(net *netutil.NetworkConfig, bridge string) error { + return lockutil.WithDirLock(bd.netClient.NetconfPath(), func() error { + // first, make sure that the bridge name is not used by any of the existing bridge networks + bridgeNet, err := bd.GetNetworkByBridgeName(bridge) + if err != nil { + return err + } + if bridgeNet != nil { + return fmt.Errorf("bridge name %s already in use by network %s", bridge, bridgeNet.Name) + } + + // load the CNI config file and set bridge name + configFilename := bd.getConfigPathForNetworkName(net.Name) + configFile, err := os.Open(configFilename) + if err != nil { + return err + } + defer configFile.Close() + var netJSON interface{} + if err = json.NewDecoder(configFile).Decode(&netJSON); err != nil { + return err + } + netMap, ok := netJSON.(map[string]interface{}) + if !ok { + return fmt.Errorf("network config file %s is not a valid map", configFilename) + } + plugins, ok := netMap["plugins"] + if !ok { + return fmt.Errorf("could not find plugins in network config file %s", configFilename) + } + pluginsMap, ok := plugins.([]interface{}) + if !ok { + return fmt.Errorf("could not parse plugins in network config file %s", configFilename) + } + for _, plugin := range pluginsMap { + pluginMap, ok := plugin.(map[string]interface{}) + if !ok { + continue + } + if pluginMap["type"] == "bridge" { + pluginMap["bridge"] = bridge + data, err := json.MarshalIndent(netJSON, "", " ") + if err != nil { + return err + } + return os.WriteFile(configFilename, data, 0o644) + } + } + return fmt.Errorf("bridge plugin not found in network config file %s", configFilename) + }) +} + +func (bd *bridgeDriver) GetBridgeName(net *netutil.NetworkConfig) (string, error) { + var bridgeName string + err := lockutil.WithDirLock(bd.netClient.NetconfPath(), func() error { + configFilename := bd.getConfigPathForNetworkName(net.Name) + configFile, err := os.Open(configFilename) + if err != nil { + return err + } + defer configFile.Close() + + var netJSON interface{} + if err = json.NewDecoder(configFile).Decode(&netJSON); err != nil { + return err + } + + netMap, ok := netJSON.(map[string]interface{}) + if !ok { + return fmt.Errorf("network config file %s is not a valid map", configFilename) + } + + plugins, ok := netMap["plugins"] + if !ok { + return fmt.Errorf("could not find plugins in network config file %s", configFilename) + } + + pluginsMap, ok := plugins.([]interface{}) + if !ok { + return fmt.Errorf("could not parse plugins in network config file %s", configFilename) + } + + for _, plugin := range pluginsMap { + pluginMap, ok := plugin.(map[string]interface{}) + if !ok { + continue + } + if pluginMap["type"] == "bridge" { + bridge, ok := pluginMap["bridge"].(string) + if !ok { + return fmt.Errorf("bridge name in config file %s is not a string", configFilename) + } + bridgeName = bridge + return nil + } + } + + return fmt.Errorf("bridge plugin not found in network config file %s", configFilename) + }) + + if err != nil { + return "", err + } + + return bridgeName, nil +} + +type bridgePlugin struct { + Type string `json:"type"` + Bridge string `json:"bridge"` +} + +func (bd *bridgeDriver) GetNetworkByBridgeName(bridge string) (*netutil.NetworkConfig, error) { + networks, err := bd.netClient.FilterNetworks(func(*netutil.NetworkConfig) bool { + return true + }) + if err != nil { + return nil, err + } + for _, network := range networks { + for _, plugin := range network.Plugins { + if plugin.Network.Type != "bridge" { + continue + } + var bridgeJSON bridgePlugin + if err = json.Unmarshal(plugin.Bytes, &bridgeJSON); err != nil { + continue + } + if bridgeJSON.Bridge == bridge { + return network, nil + } + } + } + return nil, nil +} + +func (bd *bridgeDriver) SetICCDisabled() { + bd.disableICC = true +} + +func (bd *bridgeDriver) ICCDisabled() bool { + return bd.disableICC +} + +func (bd *bridgeDriver) DisableICC(bridgeIface string, insert bool) error { + filterTable := "filter" + isolateChain := "FINCH-ISOLATE-CHAIN" + + if bd.ipt == nil { + return fmt.Errorf("iptables is not initialized") + } + + // Check if the FINCH-ISOLATE-CHAIN already exists + exists, err := bd.ipt.ChainExists(filterTable, isolateChain) + if err != nil { + return fmt.Errorf("failed to check if %s chain exists: %v", isolateChain, err) + } + + if !exists { + // Create and setup the FINCH-ISOLATE-CHAIN chain if it doesn't exist + err = bd.ipt.NewChain(filterTable, isolateChain) + if err != nil { + return fmt.Errorf("failed to create %s chain: %v", isolateChain, err) + } + // Add a rule to the FORWARD chain that jumps to the FINCH-ISOLATE-CHAIN for all packets + jumpRule := []string{"-j", isolateChain} + err = bd.ipt.Insert(filterTable, "FORWARD", 1, jumpRule...) + if err != nil { + return fmt.Errorf("failed to add %s jump rule to FORWARD chain: %v", isolateChain, err) + } + // In the FINCH-ISOLATE-CHAIN, add a rule to return to the FORWARD chain if no match + returnRule := []string{"-j", "RETURN"} + err = bd.ipt.Append(filterTable, isolateChain, returnRule...) + if err != nil { + return fmt.Errorf("failed to add RETURN rule to DOCKER-ISOLATE chain: %v", err) + } + } + + // In the FINCH-ISOLATE-CHAIN, add or remove the DROP rule for packets from and to the same bridge + dropRule := []string{"-i", bridgeIface, "-o", bridgeIface, "-j", "DROP"} + if insert { + err = bd.ipt.Insert(filterTable, isolateChain, 1, dropRule...) + if err != nil { + return fmt.Errorf("failed to add DROP rule to %s chain: %v", isolateChain, err) + } + } else { + err = bd.ipt.DeleteIfExists(filterTable, isolateChain, dropRule...) + if err != nil { + return fmt.Errorf("failed to remove DROP rule from %s chain: %v", isolateChain, err) + } + } + return nil +} + +// From https://github.com/containerd/nerdctl/blob/v1.5.0/pkg/netutil/netutil.go#L186-L188 +func (bd *bridgeDriver) getConfigPathForNetworkName(netName string) string { + return filepath.Join(bd.netClient.NetconfPath(), "nerdctl-"+netName+".conflist") +} diff --git a/internal/service/network/bridge_driver_test.go b/internal/service/network/bridge_driver_test.go new file mode 100644 index 00000000..93b3e3a0 --- /dev/null +++ b/internal/service/network/bridge_driver_test.go @@ -0,0 +1,123 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package network + +import ( + "fmt" + + "github.com/golang/mock/gomock" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/runfinch/finch-daemon/mocks/mocks_network" +) + +var _ = Describe("bridgeDriver DisableICC", func() { + var ( + mockController *gomock.Controller + mockIpt *mocks_network.MockIPTablesWrapper + driver *bridgeDriver + bridgeIface string + ) + + BeforeEach(func() { + mockController = gomock.NewController(GinkgoT()) + mockIpt = mocks_network.NewMockIPTablesWrapper(mockController) + driver = &bridgeDriver{ipt: mockIpt} + bridgeIface = "br-mock" + }) + + Context("FINCH-ISOLATE-CHAIN chain does not exist", func() { + BeforeEach(func() { + // FINCH-ISOLATE-CHAIN does not exist initially + mockIpt.EXPECT().ChainExists("filter", "FINCH-ISOLATE-CHAIN").Return(false, nil) + + // NewChain method should be called + mockIpt.EXPECT().NewChain("filter", "FINCH-ISOLATE-CHAIN").Return(nil) + + // Expect a rule to be added to FORWARD chain that jumps to FINCH-ISOLATE-CHAIN + mockIpt.EXPECT().Insert("filter", "FORWARD", 1, "-j", "FINCH-ISOLATE-CHAIN").Return(nil) + // Expect a RETURN rule to be appended in FINCH-ISOLATE-CHAIN + mockIpt.EXPECT().Append("filter", "FINCH-ISOLATE-CHAIN", "-j", "RETURN").Return(nil) + + // Expect the second Insert call to FINCH-ISOLATE-CHAIN for the DROP rule + mockIpt.EXPECT().Insert("filter", "FINCH-ISOLATE-CHAIN", 1, "-i", bridgeIface, "-o", bridgeIface, "-j", "DROP").Return(nil) + }) + + It("should create and set up the FINCH-ISOLATE-CHAIN", func() { + err := driver.DisableICC(bridgeIface, true) + Expect(err).ShouldNot(HaveOccurred()) + }) + }) + + Context("FINCH-ISOLATE-CHAIN exists", func() { + BeforeEach(func() { + mockIpt.EXPECT().ChainExists("filter", "FINCH-ISOLATE-CHAIN").Return(true, nil) + }) + + When("insert set to true", func() { + BeforeEach(func() { + // Expect the DROP rule to be inserted for packets from and to the same bridge + mockIpt.EXPECT().Insert("filter", "FINCH-ISOLATE-CHAIN", 1, "-i", bridgeIface, "-o", bridgeIface, "-j", "DROP").Return(nil) + }) + + It("should add the DROP rule to the FINCH-ISOLATE-CHAIN", func() { + err := driver.DisableICC(bridgeIface, true) + Expect(err).ShouldNot(HaveOccurred()) + }) + }) + + When("insert set to false", func() { + BeforeEach(func() { + // Expect the DROP rule to be removed for packets from and to the same bridge + mockIpt.EXPECT().DeleteIfExists("filter", "FINCH-ISOLATE-CHAIN", "-i", bridgeIface, "-o", bridgeIface, "-j", "DROP").Return(nil) + }) + + It("should remove the DROP rule from the FINCH-ISOLATE-CHAIN", func() { + err := driver.DisableICC(bridgeIface, false) + Expect(err).ShouldNot(HaveOccurred()) + }) + }) + }) + + Context("when iptables returns an error", func() { + It("should return an error if ChainExists fails", func() { + mockIpt.EXPECT().ChainExists("filter", "FINCH-ISOLATE-CHAIN").Return(false, fmt.Errorf("iptables error")) + + err := driver.DisableICC(bridgeIface, true) + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("failed to check if FINCH-ISOLATE-CHAIN chain exists")) + }) + + It("should return an error if NewChain fails", func() { + mockIpt.EXPECT().ChainExists("filter", "FINCH-ISOLATE-CHAIN").Return(false, nil) + + mockIpt.EXPECT().NewChain("filter", "FINCH-ISOLATE-CHAIN").Return(fmt.Errorf("iptables error")) + + err := driver.DisableICC(bridgeIface, true) + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("failed to create FINCH-ISOLATE-CHAIN chain")) + }) + + It("should return an error if Insert fails while adding DROP rule", func() { + mockIpt.EXPECT().ChainExists("filter", "FINCH-ISOLATE-CHAIN").Return(true, nil) + + mockIpt.EXPECT().Insert("filter", "FINCH-ISOLATE-CHAIN", 1, "-i", bridgeIface, "-o", bridgeIface, "-j", "DROP").Return(fmt.Errorf("iptables error")) + + err := driver.DisableICC(bridgeIface, true) + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("failed to add DROP rule")) + }) + + It("should return an error if Delete fails while removing DROP rule", func() { + mockIpt.EXPECT().ChainExists("filter", "FINCH-ISOLATE-CHAIN").Return(true, nil) + + // Simulate an error while removing the DROP rule + mockIpt.EXPECT().DeleteIfExists("filter", "FINCH-ISOLATE-CHAIN", "-i", bridgeIface, "-o", bridgeIface, "-j", "DROP").Return(fmt.Errorf("iptables error")) + + err := driver.DisableICC(bridgeIface, false) + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("failed to remove DROP rule")) + }) + }) +}) diff --git a/internal/service/network/create.go b/internal/service/network/create.go index 031718d3..1933acfe 100644 --- a/internal/service/network/create.go +++ b/internal/service/network/create.go @@ -5,13 +5,9 @@ package network import ( "context" - "encoding/json" "fmt" - "os" - "path/filepath" "strings" - "github.com/containerd/nerdctl/pkg/lockutil" "github.com/containerd/nerdctl/pkg/netutil" "github.com/runfinch/finch-daemon/api/types" @@ -21,49 +17,28 @@ import ( // Create implements the logic to turn a network create request to the back-end nerdctl create network calls. func (s *service) Create(ctx context.Context, request types.NetworkCreateRequest) (types.NetworkCreateResponse, error) { - // enable_ip_masquerade, host_binding_ipv4, and bridge name network options are not supported by nerdctl. - // So we must filter out any unsupported options which would prevent the network from being created and accept the defaults. - bridge := "" - filterUnsupportedOptions := func(original map[string]string) map[string]string { - options := map[string]string{} - for k, v := range original { - switch k { - case "com.docker.network.bridge.enable_ip_masquerade": - // must be true - if v != "true" { - s.logger.Warnf("network option com.docker.network.bridge.enable_ip_masquerade is set to %s, but it must be true", v) - } - case "com.docker.network.bridge.host_binding_ipv4": - // must be 0.0.0.0 - if v != "0.0.0.0" { - s.logger.Warnf("network option com.docker.network.bridge.host_binding_ipv4 is set to %s, but it must be 0.0.0.0", v) - } - case "com.docker.network.bridge.enable_icc": - s.logger.Warnf("network option com.docker.network.bridge.enable_icc is not currently supported in nerdctl", v) - case "com.docker.network.bridge.name": - bridge = v - default: - options[k] = v - } + var bridgeDriver BridgeDriverOperations + var err error + + createOptionsFrom := func(request types.NetworkCreateRequest) (netutil.CreateOptions, error) { + // Default to "bridge" driver if request does not specify a driver + driver := request.Driver + if driver == "" { + driver = "bridge" } - return options - } - createOptionsFrom := func(r types.NetworkCreateRequest) netutil.CreateOptions { options := netutil.CreateOptions{ - Name: r.Name, - Driver: "bridge", + Name: request.Name, + Driver: driver, IPAMDriver: "default", - IPAMOptions: r.IPAM.Options, - Options: filterUnsupportedOptions(r.Options), - Labels: maputility.Flatten(r.Labels, maputility.KeyEqualsValueFormat), + IPAMOptions: request.IPAM.Options, + Labels: maputility.Flatten(request.Labels, maputility.KeyEqualsValueFormat), } - if r.Driver != "" { - options.Driver = r.Driver - } - if r.IPAM.Driver != "" { - options.IPAMDriver = r.IPAM.Driver + + if request.IPAM.Driver != "" { + options.IPAMDriver = request.IPAM.Driver } + if len(request.IPAM.Config) != 0 { options.Subnets = []string{} if subnet, ok := request.IPAM.Config[0]["Subnet"]; ok { @@ -76,7 +51,18 @@ func (s *service) Create(ctx context.Context, request types.NetworkCreateRequest options.Gateway = gateway } } - return options + + // Handle driver-specific options + switch driver { + case "bridge": + bridgeDriver = NewBridgeDriver(s.netClient, s.logger) + options, err = bridgeDriver.HandleCreateOptions(request, options) + return options, err + default: + options.Options = request.Options + } + + return options, nil } if config, err := s.getNetwork(request.Name); err == nil { @@ -92,8 +78,13 @@ func (s *service) Create(ctx context.Context, request types.NetworkCreateRequest return response, nil } - net, err := s.netClient.CreateNetwork(createOptionsFrom(request)) - warning := "" + options, err := createOptionsFrom(request) + if err != nil { + return types.NetworkCreateResponse{}, err + } + + // Create network + net, err := s.netClient.CreateNetwork(options) if err != nil && strings.Contains(err.Error(), "unsupported cni driver") { return types.NetworkCreateResponse{}, errdefs.NewNotFound(errPluginNotFound) } else if err != nil { @@ -104,11 +95,12 @@ func (s *service) Create(ctx context.Context, request types.NetworkCreateRequest return types.NetworkCreateResponse{}, errNetworkIDNotFound } - // Since nerdctl currently does not support custom bridge names, - // we explicitly override bridge name in the conflist file for the network that was just created - if bridge != "" { - if err = s.setBridgeName(net, bridge); err != nil { - warning = fmt.Sprintf("Failed to set network bridge name %s: %s", bridge, err) + // Handle post network create actions + warning := "" + if bridgeDriver != nil { + warning, err = bridgeDriver.HandlePostCreate(net) + if err != nil { + return types.NetworkCreateResponse{}, err } } @@ -117,90 +109,3 @@ func (s *service) Create(ctx context.Context, request types.NetworkCreateRequest Warning: warning, }, nil } - -// setBridgeName will override the bridge name in an existing CNI config file for a network. -func (s *service) setBridgeName(net *netutil.NetworkConfig, bridge string) error { - return lockutil.WithDirLock(s.netClient.NetconfPath(), func() error { - // first, make sure that the bridge name is not used by any of the existing bridge networks - bridgeNet, err := s.getNetworkByBridgeName(bridge) - if err != nil { - return err - } - if bridgeNet != nil { - return fmt.Errorf("bridge name %s already in use by network %s", bridge, bridgeNet.Name) - } - - // load the CNI config file and set bridge name - configFilename := s.getConfigPathForNetworkName(net.Name) - configFile, err := os.Open(configFilename) - if err != nil { - return err - } - defer configFile.Close() - var netJSON interface{} - if err = json.NewDecoder(configFile).Decode(&netJSON); err != nil { - return err - } - netMap, ok := netJSON.(map[string]interface{}) - if !ok { - return fmt.Errorf("network config file %s is not a valid map", configFilename) - } - plugins, ok := netMap["plugins"] - if !ok { - return fmt.Errorf("could not find plugins in network config file %s", configFilename) - } - pluginsMap, ok := plugins.([]interface{}) - if !ok { - return fmt.Errorf("could not parse plugins in network config file %s", configFilename) - } - for _, plugin := range pluginsMap { - pluginMap, ok := plugin.(map[string]interface{}) - if !ok { - continue - } - if pluginMap["type"] == "bridge" { - pluginMap["bridge"] = bridge - data, err := json.MarshalIndent(netJSON, "", " ") - if err != nil { - return err - } - return os.WriteFile(configFilename, data, 0o644) - } - } - return fmt.Errorf("bridge plugin not found in network config file %s", configFilename) - }) -} - -// From https://github.com/containerd/nerdctl/blob/v1.5.0/pkg/netutil/netutil.go#L186-L188 -func (s *service) getConfigPathForNetworkName(netName string) string { - return filepath.Join(s.netClient.NetconfPath(), "nerdctl-"+netName+".conflist") -} - -type bridgePlugin struct { - Type string `json:"type"` - Bridge string `json:"bridge"` -} - -func (s *service) getNetworkByBridgeName(bridge string) (*netutil.NetworkConfig, error) { - networks, err := s.netClient.FilterNetworks(func(*netutil.NetworkConfig) bool { - return true - }) - if err != nil { - return nil, err - } - for _, network := range networks { - for _, plugin := range network.Plugins { - if plugin.Network.Type != "bridge" { - continue - } - var bridgeJSON bridgePlugin - if err = json.Unmarshal(plugin.Bytes, &bridgeJSON); err != nil { - continue - } - if bridgeJSON.Bridge == bridge { - return network, nil - } - } - } - return nil, nil -} diff --git a/internal/service/network/create_test.go b/internal/service/network/create_test.go index edcd302e..294fe6c1 100644 --- a/internal/service/network/create_test.go +++ b/internal/service/network/create_test.go @@ -6,6 +6,7 @@ package network import ( "context" "errors" + "fmt" "github.com/containerd/nerdctl/pkg/netutil" "github.com/golang/mock/gomock" @@ -14,8 +15,11 @@ import ( "github.com/runfinch/finch-daemon/api/handlers/network" "github.com/runfinch/finch-daemon/api/types" + "github.com/runfinch/finch-daemon/internal/backend" "github.com/runfinch/finch-daemon/mocks/mocks_backend" "github.com/runfinch/finch-daemon/mocks/mocks_logger" + "github.com/runfinch/finch-daemon/mocks/mocks_network" + "github.com/runfinch/finch-daemon/pkg/flog" ) var _ = Describe("Network Service Create Network Implementation", func() { @@ -25,12 +29,13 @@ var _ = Describe("Network Service Create Network Implementation", func() { ) var ( - ctx context.Context - mockController *gomock.Controller - cdClient *mocks_backend.MockContainerdClient - ncNetClient *mocks_backend.MockNerdctlNetworkSvc - logger *mocks_logger.Logger - service network.Service + ctx context.Context + mockController *gomock.Controller + cdClient *mocks_backend.MockContainerdClient + ncNetClient *mocks_backend.MockNerdctlNetworkSvc + logger *mocks_logger.Logger + service network.Service + mockBridgeDriver *mocks_network.BridgeDriver ) BeforeEach(func() { @@ -40,6 +45,7 @@ var _ = Describe("Network Service Create Network Implementation", func() { ncNetClient = mocks_backend.NewMockNerdctlNetworkSvc(mockController) logger = mocks_logger.NewLogger(mockController) service = NewService(cdClient, ncNetClient, logger) + mockBridgeDriver = mocks_network.NewBridgeDriver(mockController) }) When("a create network call is successful", func() { @@ -336,4 +342,131 @@ var _ = Describe("Network Service Create Network Implementation", func() { }) }) }) + + Context("ICC configuration", func() { + When("com.docker.network.bridge.enable_icc is set to true in the request", func() { + It("should not change default behavior", func() { + request := types.NewCreateNetworkRequest( + networkName, + types.WithOptions(map[string]string{ + "com.docker.network.bridge.enable_icc": "true", + }), + ) + + ncNetClient.EXPECT().FilterNetworks(gomock.Any()).Return([]*netutil.NetworkConfig{}, nil) + logger.EXPECT().Debugf(gomock.Any(), gomock.Any()) + + nid := networkID + ncNetClient.EXPECT().CreateNetwork(gomock.Any()).DoAndReturn(func(actual netutil.CreateOptions) (*netutil.NetworkConfig, error) { + // Check if the label exists + checkLabel := "com.docker.network.bridge.enable_icc=true" + labelExists := false + for _, label := range actual.Labels { + if label == checkLabel { + labelExists = true + break + } + } + + // Expect that DisableICC is not called + mockBridgeDriver.EXPECT().DisableICC(gomock.Any(), gomock.Any()).Times(0) + + Expect(labelExists).To(BeFalse(), fmt.Sprintf("Label '%s' should not exist in Labels", checkLabel)) + + return &netutil.NetworkConfig{NerdctlID: &nid}, nil + }) + + response, err := service.Create(ctx, *request) + Expect(err).ShouldNot(HaveOccurred()) + Expect(response.ID).Should(Equal(networkID)) + }) + }) + + When("ICC is not specified in the request", func() { + It("should use the default setting in the create options and not set any icc labels", func() { + request := types.NewCreateNetworkRequest(networkName) + + ncNetClient.EXPECT().FilterNetworks(gomock.Any()).Return([]*netutil.NetworkConfig{}, nil) + logger.EXPECT().Debugf(gomock.Any(), gomock.Any()) + + nid := networkID + ncNetClient.EXPECT().CreateNetwork(gomock.Any()).DoAndReturn(func(actual netutil.CreateOptions) (*netutil.NetworkConfig, error) { + // Check if the label exists + expectedLabel := "com.docker.network.bridge.enable_icc=true" + labelExists := false + for _, label := range actual.Labels { + if label == expectedLabel { + labelExists = true + break + } + } + + Expect(labelExists).To(BeFalse(), fmt.Sprintf("Label '%s' should not exist in Labels", expectedLabel)) + + return &netutil.NetworkConfig{NerdctlID: &nid}, nil + }) + + response, err := service.Create(ctx, *request) + Expect(err).ShouldNot(HaveOccurred()) + Expect(response.ID).Should(Equal(networkID)) + }) + }) + + When("com.docker.network.bridge.enable_icc is set to false in the request", func() { + It("should set ICC to false in the create options", func() { + request := types.NewCreateNetworkRequest( + networkName, + types.WithOptions(map[string]string{ + BridgeICCOption: "false", + }), + ) + + ncNetClient.EXPECT().FilterNetworks(gomock.Any()).Return([]*netutil.NetworkConfig{}, nil) + logger.EXPECT().Debugf(gomock.Any(), gomock.Any()) + + nid := networkID + ncNetClient.EXPECT().CreateNetwork(gomock.Any()).DoAndReturn(func(actual netutil.CreateOptions) (*netutil.NetworkConfig, error) { + // Check if the label exists + expectedLabel := FinchICCLabel + "=false" + labelExists := false + for _, label := range actual.Labels { + if label == expectedLabel { + labelExists = true + break + } + } + + Expect(labelExists).To(BeTrue(), fmt.Sprintf("Label '%s' should exist in Labels", expectedLabel)) + + return &netutil.NetworkConfig{NerdctlID: &nid}, nil + }) + + originalDriverFunc := NewBridgeDriver + NewBridgeDriver = func(netClient backend.NerdctlNetworkSvc, logger flog.Logger) BridgeDriverOperations { + return mockBridgeDriver + } + defer func() { NewBridgeDriver = originalDriverFunc }() + + // Set up expectations for mockBridgeDriver + mockBridgeDriver.EXPECT().HandleCreateOptions(gomock.Any(), gomock.Any()).DoAndReturn( + func(request types.NetworkCreateRequest, options netutil.CreateOptions) (netutil.CreateOptions, error) { + // Mock the behavior for BridgeICCOption set to false + // Remove the option from the options map + fmt.Println("HandleCreateOptions called with options:", options) + delete(options.Options, BridgeICCOption) + options.Labels = append(options.Labels, FinchICCLabel+"=false") + return options, nil + }).AnyTimes() + + mockBridgeDriver.EXPECT().HandlePostCreate(gomock.Any()).Return("", nil).AnyTimes() + mockBridgeDriver.EXPECT().ICCDisabled().DoAndReturn(func() bool { + return true + }).AnyTimes() + + response, err := service.Create(ctx, *request) + Expect(err).ShouldNot(HaveOccurred()) + Expect(response.ID).Should(Equal(networkID)) + }) + }) + }) }) diff --git a/internal/service/network/remove.go b/internal/service/network/remove.go index 674bb49e..e9c3a6cf 100644 --- a/internal/service/network/remove.go +++ b/internal/service/network/remove.go @@ -7,6 +7,7 @@ import ( "context" "fmt" + "github.com/containerd/nerdctl/pkg/netutil" "github.com/runfinch/finch-daemon/pkg/errdefs" ) @@ -25,8 +26,51 @@ func (s *service) Remove(ctx context.Context, networkId string) error { if value, ok := usedNetworkInfo[net.Name]; ok { return errdefs.NewForbidden(fmt.Errorf("network %q is in use by container %q", networkId, value)) } + if net.File == "" { return errdefs.NewForbidden(fmt.Errorf("%s is a pre-defined network and cannot be removed", networkId)) } + + // Perform additional workflow based on the assigned network labels + if err := s.handleNetworkLabels(net); err != nil { + return fmt.Errorf("failed to handle nerdctl label: %w", err) + } + return s.netClient.RemoveNetwork(net) } + +func (s *service) handleNetworkLabels(net *netutil.NetworkConfig) error { + if net.NerdctlLabels == nil { + return nil + } + + for key, value := range *net.NerdctlLabels { + switch key { + case FinchICCLabel: + if err := s.handleEnableICCOption(net, value); err != nil { + return fmt.Errorf("error handling %s label: %w", BridgeICCOption, err) + } + } + } + return nil +} + +func (s *service) handleEnableICCOption(net *netutil.NetworkConfig, value string) error { + if value != "false" { + // for some reason the label value got modified. + // we will still try to remove the iptable rules. + // iptable.DeleteIfExists is used to ignore non-existent errors + s.logger.Warnf("unexpected value for %s label: %s", BridgeICCOption, value) + } + // Remove iptable rules set for disabling ICC for the network bridge + bridgeDriver := NewBridgeDriver(s.netClient, s.logger) + bridgeName, err := bridgeDriver.GetBridgeName(net) + if err != nil { + return fmt.Errorf("unable to get bridge name: %w", err) + } + err = bridgeDriver.DisableICC(bridgeName, false) + if err != nil { + return fmt.Errorf("unable to remove ICC disable rule: %w", err) + } + return nil +} diff --git a/mocks/mocks_backend/nerdctlimagesvc.go b/mocks/mocks_backend/nerdctlimagesvc.go index 018db5da..e4121ae2 100644 --- a/mocks/mocks_backend/nerdctlimagesvc.go +++ b/mocks/mocks_backend/nerdctlimagesvc.go @@ -10,12 +10,12 @@ import ( reflect "reflect" images "github.com/containerd/containerd/images" - platforms "github.com/containerd/platforms" remotes "github.com/containerd/containerd/remotes" docker "github.com/containerd/containerd/remotes/docker" imgutil "github.com/containerd/nerdctl/pkg/imgutil" dockerconfigresolver "github.com/containerd/nerdctl/pkg/imgutil/dockerconfigresolver" dockercompat "github.com/containerd/nerdctl/pkg/inspecttypes/dockercompat" + platforms "github.com/containerd/platforms" gomock "github.com/golang/mock/gomock" v1 "github.com/opencontainers/image-spec/specs-go/v1" ) diff --git a/mocks/mocks_container/containersvc.go b/mocks/mocks_container/containersvc.go index be39abd1..7b80ef56 100644 --- a/mocks/mocks_container/containersvc.go +++ b/mocks/mocks_container/containersvc.go @@ -191,7 +191,7 @@ func (m *MockService) Restart(arg0 context.Context, arg1 string, arg2 time.Durat ret0, _ := ret[0].(error) return ret0 } - + // Restart indicates an expected call of Restart. func (mr *MockServiceMockRecorder) Restart(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() diff --git a/mocks/mocks_network/bridge_driver.go b/mocks/mocks_network/bridge_driver.go new file mode 100644 index 00000000..ad2f551f --- /dev/null +++ b/mocks/mocks_network/bridge_driver.go @@ -0,0 +1,150 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/runfinch/finch-daemon/internal/service/network (interfaces: BridgeDriverOperations) + +// Package mocks_network is a generated GoMock package. +package mocks_network + +import ( + reflect "reflect" + + netutil "github.com/containerd/nerdctl/pkg/netutil" + gomock "github.com/golang/mock/gomock" + types "github.com/runfinch/finch-daemon/api/types" +) + +// BridgeDriver is a mock of BridgeDriverOperations interface. +type BridgeDriver struct { + ctrl *gomock.Controller + recorder *BridgeDriverMockRecorder +} + +// BridgeDriverMockRecorder is the mock recorder for BridgeDriver. +type BridgeDriverMockRecorder struct { + mock *BridgeDriver +} + +// NewBridgeDriver creates a new mock instance. +func NewBridgeDriver(ctrl *gomock.Controller) *BridgeDriver { + mock := &BridgeDriver{ctrl: ctrl} + mock.recorder = &BridgeDriverMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *BridgeDriver) EXPECT() *BridgeDriverMockRecorder { + return m.recorder +} + +// DisableICC mocks base method. +func (m *BridgeDriver) DisableICC(arg0 string, arg1 bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DisableICC", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// DisableICC indicates an expected call of DisableICC. +func (mr *BridgeDriverMockRecorder) DisableICC(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisableICC", reflect.TypeOf((*BridgeDriver)(nil).DisableICC), arg0, arg1) +} + +// GetBridgeName mocks base method. +func (m *BridgeDriver) GetBridgeName(arg0 *netutil.NetworkConfig) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBridgeName", arg0) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBridgeName indicates an expected call of GetBridgeName. +func (mr *BridgeDriverMockRecorder) GetBridgeName(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBridgeName", reflect.TypeOf((*BridgeDriver)(nil).GetBridgeName), arg0) +} + +// GetNetworkByBridgeName mocks base method. +func (m *BridgeDriver) GetNetworkByBridgeName(arg0 string) (*netutil.NetworkConfig, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNetworkByBridgeName", arg0) + ret0, _ := ret[0].(*netutil.NetworkConfig) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetNetworkByBridgeName indicates an expected call of GetNetworkByBridgeName. +func (mr *BridgeDriverMockRecorder) GetNetworkByBridgeName(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetworkByBridgeName", reflect.TypeOf((*BridgeDriver)(nil).GetNetworkByBridgeName), arg0) +} + +// HandleCreateOptions mocks base method. +func (m *BridgeDriver) HandleCreateOptions(arg0 types.NetworkCreateRequest, arg1 netutil.CreateOptions) (netutil.CreateOptions, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HandleCreateOptions", arg0, arg1) + ret0, _ := ret[0].(netutil.CreateOptions) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HandleCreateOptions indicates an expected call of HandleCreateOptions. +func (mr *BridgeDriverMockRecorder) HandleCreateOptions(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleCreateOptions", reflect.TypeOf((*BridgeDriver)(nil).HandleCreateOptions), arg0, arg1) +} + +// HandlePostCreate mocks base method. +func (m *BridgeDriver) HandlePostCreate(arg0 *netutil.NetworkConfig) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HandlePostCreate", arg0) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HandlePostCreate indicates an expected call of HandlePostCreate. +func (mr *BridgeDriverMockRecorder) HandlePostCreate(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandlePostCreate", reflect.TypeOf((*BridgeDriver)(nil).HandlePostCreate), arg0) +} + +// ICCDisabled mocks base method. +func (m *BridgeDriver) ICCDisabled() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ICCDisabled") + ret0, _ := ret[0].(bool) + return ret0 +} + +// ICCDisabled indicates an expected call of ICCDisabled. +func (mr *BridgeDriverMockRecorder) ICCDisabled() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ICCDisabled", reflect.TypeOf((*BridgeDriver)(nil).ICCDisabled)) +} + +// SetBridgeName mocks base method. +func (m *BridgeDriver) SetBridgeName(arg0 *netutil.NetworkConfig, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetBridgeName", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetBridgeName indicates an expected call of SetBridgeName. +func (mr *BridgeDriverMockRecorder) SetBridgeName(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBridgeName", reflect.TypeOf((*BridgeDriver)(nil).SetBridgeName), arg0, arg1) +} + +// SetICCDisabled mocks base method. +func (m *BridgeDriver) SetICCDisabled() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetICCDisabled") +} + +// SetICCDisabled indicates an expected call of SetICCDisabled. +func (mr *BridgeDriverMockRecorder) SetICCDisabled() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetICCDisabled", reflect.TypeOf((*BridgeDriver)(nil).SetICCDisabled)) +} diff --git a/mocks/mocks_network/iptables.go b/mocks/mocks_network/iptables.go new file mode 100644 index 00000000..87a922da --- /dev/null +++ b/mocks/mocks_network/iptables.go @@ -0,0 +1,120 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/runfinch/finch-daemon/internal/service/network (interfaces: IPTablesWrapper) + +// Package mocks_network is a generated GoMock package. +package mocks_network + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockIPTablesWrapper is a mock of IPTablesWrapper interface. +type MockIPTablesWrapper struct { + ctrl *gomock.Controller + recorder *MockIPTablesWrapperMockRecorder +} + +// MockIPTablesWrapperMockRecorder is the mock recorder for MockIPTablesWrapper. +type MockIPTablesWrapperMockRecorder struct { + mock *MockIPTablesWrapper +} + +// NewMockIPTablesWrapper creates a new mock instance. +func NewMockIPTablesWrapper(ctrl *gomock.Controller) *MockIPTablesWrapper { + mock := &MockIPTablesWrapper{ctrl: ctrl} + mock.recorder = &MockIPTablesWrapperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIPTablesWrapper) EXPECT() *MockIPTablesWrapperMockRecorder { + return m.recorder +} + +// Append mocks base method. +func (m *MockIPTablesWrapper) Append(arg0, arg1 string, arg2 ...string) error { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Append", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Append indicates an expected call of Append. +func (mr *MockIPTablesWrapperMockRecorder) Append(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Append", reflect.TypeOf((*MockIPTablesWrapper)(nil).Append), varargs...) +} + +// ChainExists mocks base method. +func (m *MockIPTablesWrapper) ChainExists(arg0, arg1 string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ChainExists", arg0, arg1) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ChainExists indicates an expected call of ChainExists. +func (mr *MockIPTablesWrapperMockRecorder) ChainExists(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainExists", reflect.TypeOf((*MockIPTablesWrapper)(nil).ChainExists), arg0, arg1) +} + +// DeleteIfExists mocks base method. +func (m *MockIPTablesWrapper) DeleteIfExists(arg0, arg1 string, arg2 ...string) error { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteIfExists", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteIfExists indicates an expected call of DeleteIfExists. +func (mr *MockIPTablesWrapperMockRecorder) DeleteIfExists(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteIfExists", reflect.TypeOf((*MockIPTablesWrapper)(nil).DeleteIfExists), varargs...) +} + +// Insert mocks base method. +func (m *MockIPTablesWrapper) Insert(arg0, arg1 string, arg2 int, arg3 ...string) error { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1, arg2} + for _, a := range arg3 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Insert", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Insert indicates an expected call of Insert. +func (mr *MockIPTablesWrapperMockRecorder) Insert(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockIPTablesWrapper)(nil).Insert), varargs...) +} + +// NewChain mocks base method. +func (m *MockIPTablesWrapper) NewChain(arg0, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewChain", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// NewChain indicates an expected call of NewChain. +func (mr *MockIPTablesWrapperMockRecorder) NewChain(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewChain", reflect.TypeOf((*MockIPTablesWrapper)(nil).NewChain), arg0, arg1) +}