File: run.go

package info (click to toggle)
golang-github-containers-common 0.64.2%2Bds1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,528 kB
  • sloc: makefile: 130; sh: 102
file content (197 lines) | stat: -rw-r--r-- 5,695 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
//go:build linux || freebsd

package netavark

import (
	"encoding/json"
	"fmt"
	"slices"
	"strconv"
	"strings"

	"github.com/containers/common/libnetwork/internal/util"
	"github.com/containers/common/libnetwork/types"
	"github.com/sirupsen/logrus"
)

type netavarkOptions struct {
	types.NetworkOptions
	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, ",")}, false, 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, retErr error) {
	n.lock.Lock()
	defer n.lock.Unlock()
	err := n.loadNetworks()
	if err != nil {
		return nil, err
	}

	err = util.ValidateSetupOptions(n, namespacePath, options)
	if err != nil {
		return nil, err
	}

	// allocate IPs in the IPAM db
	err = n.allocIPs(&options.NetworkOptions)
	if err != nil {
		return nil, err
	}
	defer func() {
		// In case the setup failed for whatever reason podman will not start the
		// container so we must free the allocated ips again to not leak them.
		if retErr != nil {
			if err := n.deallocIPs(&options.NetworkOptions); err != nil {
				logrus.Error(err)
			}
		}
	}()

	netavarkOpts, needPlugin, err := n.convertNetOpts(options.NetworkOptions)
	if err != nil {
		return nil, fmt.Errorf("failed to convert net opts: %w", err)
	}

	// Warn users if one or more networks have dns enabled
	// but aardvark-dns binary is not configured
	for _, network := range netavarkOpts.Networks {
		if network != nil && network.DNSEnabled && n.aardvarkBinary == "" {
			// this is not a fatal error we can still use container without dns
			logrus.Warnf("aardvark-dns binary not found, container dns will not be enabled")
			break
		}
	}

	// trace output to get the json
	if logrus.IsLevelEnabled(logrus.TraceLevel) {
		b, err := json.Marshal(&netavarkOpts)
		if err != nil {
			return nil, err
		}
		// show the full netavark command so we can easily reproduce errors from the cli
		logrus.Tracef("netavark command: printf '%s' | %s setup %s", string(b), n.netavarkBinary, namespacePath)
	}

	result := map[string]types.StatusBlock{}
	setup := func() error {
		return n.execNetavark([]string{"setup", namespacePath}, needPlugin, netavarkOpts, &result)
	}

	if n.rootlessNetns != nil {
		err = n.rootlessNetns.Setup(len(options.Networks), setup)
	} else {
		err = setup()
	}
	if err != nil {
		return nil, err
	}

	// make sure that the result makes sense
	if len(result) != len(options.Networks) {
		logrus.Errorf("unexpected netavark result: %v", result)
		return nil, fmt.Errorf("unexpected netavark result length, want (%d), got (%d) networks", len(options.Networks), len(result))
	}

	return result, err
}

// Teardown will teardown the container network namespace.
func (n *netavarkNetwork) Teardown(namespacePath string, options types.TeardownOptions) error {
	n.lock.Lock()
	defer n.lock.Unlock()
	err := n.loadNetworks()
	if err != nil {
		return err
	}

	// get IPs from the IPAM db
	err = n.getAssignedIPs(&options.NetworkOptions)
	if err != nil {
		// when there is an error getting the ips we should still continue
		// to call teardown for netavark to prevent leaking network interfaces
		logrus.Error(err)
	}

	netavarkOpts, needPlugin, err := n.convertNetOpts(options.NetworkOptions)
	if err != nil {
		return fmt.Errorf("failed to convert net opts: %w", err)
	}

	var retErr error
	teardown := func() error {
		return n.execNetavark([]string{"teardown", namespacePath}, needPlugin, netavarkOpts, nil)
	}

	if n.rootlessNetns != nil {
		retErr = n.rootlessNetns.Teardown(len(options.Networks), teardown)
	} else {
		retErr = teardown()
	}

	// when netavark returned an error we still free the used ips
	// otherwise we could end up in a state where block the ips forever
	err = n.deallocIPs(&netavarkOpts.NetworkOptions)
	if err != nil {
		if retErr != nil {
			logrus.Error(err)
		} else {
			retErr = err
		}
	}

	return retErr
}

func (n *netavarkNetwork) getCommonNetavarkOptions(needPlugin bool) []string {
	opts := []string{"--config", n.networkRunDir, "--rootless=" + strconv.FormatBool(n.networkRootless), "--aardvark-binary=" + n.aardvarkBinary}
	// to allow better backwards compat we only add the new netavark option when really needed
	if needPlugin {
		// Note this will require a netavark with https://github.com/containers/netavark/pull/509
		for _, dir := range n.pluginDirs {
			opts = append(opts, "--plugin-directory", dir)
		}
	}
	return opts
}

func (n *netavarkNetwork) convertNetOpts(opts types.NetworkOptions) (*netavarkOptions, bool, error) {
	netavarkOptions := netavarkOptions{
		NetworkOptions: opts,
		Networks:       make(map[string]*types.Network, len(opts.Networks)),
	}

	needsPlugin := false

	for network := range opts.Networks {
		net, err := n.getNetwork(network)
		if err != nil {
			return nil, false, err
		}
		netavarkOptions.Networks[network] = net
		if !slices.Contains(builtinDrivers, net.Driver) {
			needsPlugin = true
		}
	}
	return &netavarkOptions, needsPlugin, nil
}

func (n *netavarkNetwork) RunInRootlessNetns(toRun func() error) error {
	if n.rootlessNetns == nil {
		return types.ErrNotRootlessNetns
	}
	return n.rootlessNetns.Run(n.lock, toRun)
}

func (n *netavarkNetwork) RootlessNetnsInfo() (*types.RootlessNetnsInfo, error) {
	if n.rootlessNetns == nil {
		return nil, types.ErrNotRootlessNetns
	}
	return n.rootlessNetns.Info(), nil
}