Skip to content

Commit

Permalink
libnetwork: extend API to support NetworkUpdate
Browse files Browse the repository at this point in the history
New features in netavark/aardvark allows users to update network dns
servers and all the containers attached to those networks uses updated
resolvers.

Following PR adds support in `libnetwork` to support

* Add support for higher level `NetworkUpdate` API
* Add support for `Update` exec call which can invoke netavark with a
  new update option added here: containers/netavark#503

Signed-off-by: Aditya R <[email protected]>
  • Loading branch information
flouthoc committed Dec 6, 2022
1 parent 24d7820 commit db42c65
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 9 deletions.
4 changes: 4 additions & 0 deletions libnetwork/cni/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import (
"github.com/sirupsen/logrus"
)

func (n *cniNetwork) NetworkUpdate(name string, options types.NetworkUpdateOptions) error {
return fmt.Errorf("NetworkUpdate is not supported for backend CNI: %w", types.ErrInvalidArg)
}

// NetworkCreate will take a partial filled Network and fill the
// missing fields. It creates the Network and returns the full Network.
func (n *cniNetwork) NetworkCreate(net types.Network) (types.Network, error) {
Expand Down
70 changes: 61 additions & 9 deletions libnetwork/netavark/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"net"
"os"
"path/filepath"
"reflect"
"strconv"
"time"

Expand All @@ -19,6 +20,65 @@ import (
"github.com/containers/storage/pkg/stringid"
)

func sliceRemoveDuplicates(strList []string) []string {
list := make([]string, 0, len(strList))
for _, item := range strList {
if !util.StringInSlice(item, list) {
list = append(list, item)
}
}
return list
}

func (n *netavarkNetwork) commitNetwork(network *types.Network) error {
confPath := filepath.Join(n.networkConfigDir, network.Name+".json")
f, err := os.Create(confPath)
if err != nil {
return err
}
defer f.Close()
enc := json.NewEncoder(f)
enc.SetIndent("", " ")
err = enc.Encode(network)
if err != nil {
return err
}
return nil
}

func (n *netavarkNetwork) NetworkUpdate(name string, options types.NetworkUpdateOptions) error {
n.lock.Lock()
defer n.lock.Unlock()
err := n.loadNetworks()
if err != nil {
return err
}
network, err := n.getNetwork(name)
if err != nil {
return err
}
networkDNSServersBefore := network.NetworkDNSServers
networkDNSServersAfter := []string{}
for _, server := range networkDNSServersBefore {
if util.StringInSlice(server, options.RemoveDNSServers) {
continue
}
networkDNSServersAfter = append(networkDNSServersAfter, server)
}
networkDNSServersAfter = append(networkDNSServersAfter, options.AddDNSServers...)
networkDNSServersAfter = sliceRemoveDuplicates(networkDNSServersAfter)
network.NetworkDNSServers = networkDNSServersAfter
if reflect.DeepEqual(networkDNSServersBefore, networkDNSServersAfter) {
return nil
}
err = n.commitNetwork(network)
if err != nil {
return err
}

return n.execUpdate(network.Name, network.NetworkDNSServers)
}

// NetworkCreate will take a partial filled Network and fill the
// missing fields. It creates the Network and returns the full Network.
func (n *netavarkNetwork) NetworkCreate(net types.Network) (types.Network, error) {
Expand Down Expand Up @@ -158,15 +218,7 @@ func (n *netavarkNetwork) networkCreate(newNetwork *types.Network, defaultNet bo
newNetwork.Created = time.Now()

if !defaultNet {
confPath := filepath.Join(n.networkConfigDir, newNetwork.Name+".json")
f, err := os.Create(confPath)
if err != nil {
return nil, err
}
defer f.Close()
enc := json.NewEncoder(f)
enc.SetIndent("", " ")
err = enc.Encode(newNetwork)
err = n.commitNetwork(newNetwork)
if err != nil {
return nil, err
}
Expand Down
73 changes: 73 additions & 0 deletions libnetwork/netavark/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"path/filepath"
"time"

"github.com/containers/common/libnetwork/netavark"
"github.com/containers/common/libnetwork/types"
"github.com/containers/common/libnetwork/util"
. "github.com/onsi/ginkgo/v2"
Expand Down Expand Up @@ -618,6 +619,78 @@ var _ = Describe("Config", func() {
Expect(network1.Internal).To(BeTrue())
})

It("update NetworkDNSServers AddDNSServers", func() {
libpodNet, err := netavark.NewNetworkInterface(&netavark.InitConfig{
NetworkConfigDir: networkConfDir,
NetworkRunDir: networkConfDir,
NetavarkBinary: "true",
})
if err != nil {
Fail("Failed to create NewNetavarkNetworkInterface")
}
network := types.Network{
NetworkDNSServers: []string{"8.8.8.8", "3.3.3.3"},
DNSEnabled: true,
Name: "test-network",
}
network1, err := libpodNet.NetworkCreate(network)
Expect(err).To(BeNil())
Expect(network1.NetworkDNSServers).To(Equal([]string{"8.8.8.8", "3.3.3.3"}))
err = libpodNet.NetworkUpdate("test-network", types.NetworkUpdateOptions{AddDNSServers: []string{"8.8.8.8", "3.3.3.3", "7.7.7.7"}})
Expect(err).To(BeNil())
testNetwork, err := libpodNet.NetworkInspect("test-network")
Expect(err).To(BeNil())
Expect(testNetwork.NetworkDNSServers).To(Equal([]string{"8.8.8.8", "3.3.3.3", "7.7.7.7"}))
})

It("update NetworkDNSServers RemoveDNSServers", func() {
libpodNet, err := netavark.NewNetworkInterface(&netavark.InitConfig{
NetworkConfigDir: networkConfDir,
NetworkRunDir: networkConfDir,
NetavarkBinary: "true",
})
if err != nil {
Fail("Failed to create NewNetavarkNetworkInterface")
}
network := types.Network{
NetworkDNSServers: []string{"8.8.8.8", "3.3.3.3"},
DNSEnabled: true,
Name: "test-network",
}
network1, err := libpodNet.NetworkCreate(network)
Expect(err).To(BeNil())
Expect(network1.NetworkDNSServers).To(Equal([]string{"8.8.8.8", "3.3.3.3"}))
err = libpodNet.NetworkUpdate("test-network", types.NetworkUpdateOptions{RemoveDNSServers: []string{"3.3.3.3"}})
Expect(err).To(BeNil())
testNetwork, err := libpodNet.NetworkInspect("test-network")
Expect(err).To(BeNil())
Expect(testNetwork.NetworkDNSServers).To(Equal([]string{"8.8.8.8"}))
})

It("update NetworkDNSServers Add and Remove DNSServers", func() {
libpodNet, err := netavark.NewNetworkInterface(&netavark.InitConfig{
NetworkConfigDir: networkConfDir,
NetworkRunDir: networkConfDir,
NetavarkBinary: "true",
})
if err != nil {
Fail("Failed to create NewNetavarkNetworkInterface")
}
network := types.Network{
NetworkDNSServers: []string{"8.8.8.8", "3.3.3.3"},
DNSEnabled: true,
Name: "test-network",
}
network1, err := libpodNet.NetworkCreate(network)
Expect(err).To(BeNil())
Expect(network1.NetworkDNSServers).To(Equal([]string{"8.8.8.8", "3.3.3.3"}))
err = libpodNet.NetworkUpdate("test-network", types.NetworkUpdateOptions{RemoveDNSServers: []string{"3.3.3.3"}, AddDNSServers: []string{"7.7.7.7"}})
Expect(err).To(BeNil())
testNetwork, err := libpodNet.NetworkInspect("test-network")
Expect(err).To(BeNil())
Expect(testNetwork.NetworkDNSServers).To(Equal([]string{"8.8.8.8", "7.7.7.7"}))
})

It("create network with NetworDNSServers", func() {
network := types.Network{
NetworkDNSServers: []string{"8.8.8.8", "3.3.3.3"},
Expand Down
6 changes: 6 additions & 0 deletions libnetwork/netavark/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"encoding/json"
"fmt"
"strconv"
"strings"

"github.com/containers/common/libnetwork/internal/util"
"github.com/containers/common/libnetwork/types"
Expand All @@ -18,6 +19,11 @@ type netavarkOptions struct {
Networks map[string]*types.Network `json:"network_info"`
}

func (n *netavarkNetwork) execUpdate(networkName string, networkDNSServers []string) error {
retErr := n.execNetavark([]string{"update", networkName, "--network-dns-servers", strings.Join(networkDNSServers, ",")}, nil, nil)
return retErr
}

// Setup will setup the container network namespace. It returns
// a map of StatusBlocks, the key is the network name.
func (n *netavarkNetwork) Setup(namespacePath string, options types.SetupOptions) (map[string]types.StatusBlock, error) {
Expand Down
10 changes: 10 additions & 0 deletions libnetwork/types/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ type ContainerNetwork interface {
// NetworkCreate will take a partial filled Network and fill the
// missing fields. It creates the Network and returns the full Network.
NetworkCreate(Network) (Network, error)
// NetworkUpdate will take network name and ID and updates network DNS Servers.
NetworkUpdate(nameOrID string, options NetworkUpdateOptions) error
// NetworkRemove will remove the Network with the given name or ID.
NetworkRemove(nameOrID string) error
// NetworkList will return all known Networks. Optionally you can
Expand Down Expand Up @@ -70,6 +72,14 @@ type Network struct {
IPAMOptions map[string]string `json:"ipam_options,omitempty"`
}

// NetworkOptions for a given container.
type NetworkUpdateOptions struct {
// List of custom DNS server for podman's DNS resolver.
// Priority order will be kept as defined by user in the configuration.
AddDNSServers []string `json:"add_dns_servers,omitempty"`
RemoveDNSServers []string `json:"remove_dns_servers,omitempty"`
}

// IPNet is used as custom net.IPNet type to add Marshal/Unmarshal methods.
type IPNet struct {
net.IPNet
Expand Down

0 comments on commit db42c65

Please sign in to comment.