Use CNI to configure container networks
Use CNI to configure networks for containers for which we create new network namespaces. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com> Closes: #700 Approved by: rhatdan
This commit is contained in:
parent
aa5cf3115e
commit
00fafcf9cb
3
Makefile
3
Makefile
|
@ -8,11 +8,12 @@ GO := go
|
|||
|
||||
GIT_COMMIT := $(if $(shell git rev-parse --short HEAD),$(shell git rev-parse --short HEAD),$(error "git failed"))
|
||||
BUILD_INFO := $(if $(shell date +%s),$(shell date +%s),$(error "date failed"))
|
||||
CNI_COMMIT := $(if $(shell sed -e '\,github.com/containernetworking/cni, !d' -e 's,.* ,,g' vendor.conf),$(shell sed -e '\,github.com/containernetworking/cni, !d' -e 's,.* ,,g' vendor.conf),$(error "sed failed"))
|
||||
|
||||
RUNC_COMMIT := c5ec25487693612aed95673800863e134785f946
|
||||
LIBSECCOMP_COMMIT := release-2.3
|
||||
|
||||
LDFLAGS := -ldflags '-X main.gitCommit=${GIT_COMMIT} -X main.buildInfo=${BUILD_INFO}'
|
||||
LDFLAGS := -ldflags '-X main.gitCommit=${GIT_COMMIT} -X main.buildInfo=${BUILD_INFO} -X main.cniVersion=${CNI_COMMIT}'
|
||||
|
||||
all: buildah imgtype docs
|
||||
|
||||
|
|
61
buildah.go
61
buildah.go
|
@ -67,6 +67,37 @@ func (p PullPolicy) String() string {
|
|||
return fmt.Sprintf("unrecognized policy %d", p)
|
||||
}
|
||||
|
||||
// NetworkConfigurationPolicy takes the value NetworkDefault, NetworkDisabled,
|
||||
// or NetworkEnabled.
|
||||
type NetworkConfigurationPolicy int
|
||||
|
||||
const (
|
||||
// NetworkDefault is one of the values that BuilderOptions.ConfigureNetwork
|
||||
// can take, signalling that the default behavior should be used.
|
||||
NetworkDefault NetworkConfigurationPolicy = iota
|
||||
// NetworkDisabled is one of the values that BuilderOptions.ConfigureNetwork
|
||||
// can take, signalling that network interfaces should NOT be configured for
|
||||
// newly-created network namespaces.
|
||||
NetworkDisabled
|
||||
// NetworkEnabled is one of the values that BuilderOptions.ConfigureNetwork
|
||||
// can take, signalling that network interfaces should be configured for
|
||||
// newly-created network namespaces.
|
||||
NetworkEnabled
|
||||
)
|
||||
|
||||
// String formats a NetworkConfigurationPolicy as a string.
|
||||
func (p NetworkConfigurationPolicy) String() string {
|
||||
switch p {
|
||||
case NetworkDefault:
|
||||
return "NetworkDefault"
|
||||
case NetworkDisabled:
|
||||
return "NetworkDisabled"
|
||||
case NetworkEnabled:
|
||||
return "NetworkEnabled"
|
||||
}
|
||||
return fmt.Sprintf("unknown NetworkConfigurationPolicy %d", p)
|
||||
}
|
||||
|
||||
// Builder objects are used to represent containers which are being used to
|
||||
// build images. They also carry potential updates which will be applied to
|
||||
// the image's configuration when the container's contents are used to build an
|
||||
|
@ -118,6 +149,18 @@ type Builder struct {
|
|||
|
||||
// NamespaceOptions controls how we set up the namespaces for processes that we run in the container.
|
||||
NamespaceOptions NamespaceOptions
|
||||
// ConfigureNetwork controls whether or not network interfaces and
|
||||
// routing are configured for a new network namespace (i.e., when not
|
||||
// joining another's namespace and not just using the host's
|
||||
// namespace), effectively deciding whether or not the process has a
|
||||
// usable network.
|
||||
ConfigureNetwork NetworkConfigurationPolicy
|
||||
// CNIPluginPath is the location of CNI plugin helpers, if they should be
|
||||
// run from a location other than the default location.
|
||||
CNIPluginPath string
|
||||
// CNIConfigDir is the location of CNI configuration files, if the files in
|
||||
// the default configuration directory shouldn't be used.
|
||||
CNIConfigDir string
|
||||
// ID mapping options to use when running processes in the container with non-host user namespaces.
|
||||
IDMappingOptions IDMappingOptions
|
||||
|
||||
|
@ -142,6 +185,9 @@ type BuilderInfo struct {
|
|||
Docker docker.V2Image
|
||||
DefaultMountsFilePath string
|
||||
NamespaceOptions NamespaceOptions
|
||||
ConfigureNetwork string
|
||||
CNIPluginPath string
|
||||
CNIConfigDir string
|
||||
IDMappingOptions IDMappingOptions
|
||||
}
|
||||
|
||||
|
@ -164,6 +210,9 @@ func GetBuildInfo(b *Builder) BuilderInfo {
|
|||
Docker: b.Docker,
|
||||
DefaultMountsFilePath: b.DefaultMountsFilePath,
|
||||
NamespaceOptions: b.NamespaceOptions,
|
||||
ConfigureNetwork: fmt.Sprintf("%v", b.ConfigureNetwork),
|
||||
CNIPluginPath: b.CNIPluginPath,
|
||||
CNIConfigDir: b.CNIConfigDir,
|
||||
IDMappingOptions: b.IDMappingOptions,
|
||||
}
|
||||
}
|
||||
|
@ -262,6 +311,18 @@ type BuilderOptions struct {
|
|||
// NamespaceOptions controls how we set up namespaces for processes that
|
||||
// we might need to run using the container's root filesystem.
|
||||
NamespaceOptions NamespaceOptions
|
||||
// ConfigureNetwork controls whether or not network interfaces and
|
||||
// routing are configured for a new network namespace (i.e., when not
|
||||
// joining another's namespace and not just using the host's
|
||||
// namespace), effectively deciding whether or not the process has a
|
||||
// usable network.
|
||||
ConfigureNetwork NetworkConfigurationPolicy
|
||||
// CNIPluginPath is the location of CNI plugin helpers, if they should be
|
||||
// run from a location other than the default location.
|
||||
CNIPluginPath string
|
||||
// CNIConfigDir is the location of CNI configuration files, if the files in
|
||||
// the default configuration directory shouldn't be used.
|
||||
CNIConfigDir string
|
||||
// ID mapping options to use if we're setting up our own user namespace.
|
||||
IDMappingOptions *IDMappingOptions
|
||||
|
||||
|
|
|
@ -167,7 +167,7 @@ func budCmd(c *cli.Context) error {
|
|||
logrus.Debugf("build caching not enabled so --rm flag has no effect")
|
||||
}
|
||||
|
||||
namespaceOptions, err := parseNamespaceOptions(c)
|
||||
namespaceOptions, networkPolicy, err := parseNamespaceOptions(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error parsing namespace-related options")
|
||||
}
|
||||
|
@ -191,6 +191,9 @@ func budCmd(c *cli.Context) error {
|
|||
OutputFormat: format,
|
||||
SystemContext: systemContext,
|
||||
NamespaceOptions: namespaceOptions,
|
||||
ConfigureNetwork: networkPolicy,
|
||||
CNIPluginPath: c.String("cni-plugin-path"),
|
||||
CNIConfigDir: c.String("cni-config-dir"),
|
||||
IDMappingOptions: idmappingOptions,
|
||||
CommonBuildOpts: commonOpts,
|
||||
DefaultMountsFilePath: c.GlobalString("default-mounts-file"),
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -137,8 +138,9 @@ func parseUserOptions(c *cli.Context) string {
|
|||
return c.String("user")
|
||||
}
|
||||
|
||||
func parseNamespaceOptions(c *cli.Context) (namespaceOptions buildah.NamespaceOptions, err error) {
|
||||
func parseNamespaceOptions(c *cli.Context) (namespaceOptions buildah.NamespaceOptions, networkPolicy buildah.NetworkConfigurationPolicy, err error) {
|
||||
options := make(buildah.NamespaceOptions, 0, 7)
|
||||
policy := buildah.NetworkDefault
|
||||
for _, what := range []string{string(specs.IPCNamespace), "net", string(specs.PIDNamespace), string(specs.UTSNamespace)} {
|
||||
if c.IsSet(what) {
|
||||
how := c.String(what)
|
||||
|
@ -159,8 +161,27 @@ func parseNamespaceOptions(c *cli.Context) (namespaceOptions buildah.NamespaceOp
|
|||
Host: true,
|
||||
})
|
||||
default:
|
||||
if what == specs.NetworkNamespace {
|
||||
if how == "none" {
|
||||
options.AddOrReplace(buildah.NamespaceOption{
|
||||
Name: what,
|
||||
})
|
||||
policy = buildah.NetworkDisabled
|
||||
logrus.Debugf("setting network to disabled")
|
||||
break
|
||||
}
|
||||
if !filepath.IsAbs(how) {
|
||||
options.AddOrReplace(buildah.NamespaceOption{
|
||||
Name: what,
|
||||
Path: how,
|
||||
})
|
||||
policy = buildah.NetworkEnabled
|
||||
logrus.Debugf("setting network configuration to %q", how)
|
||||
break
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(how); err != nil {
|
||||
return nil, errors.Wrapf(err, "error checking for %s namespace at %q", what, how)
|
||||
return nil, buildah.NetworkDefault, errors.Wrapf(err, "error checking for %s namespace at %q", what, how)
|
||||
}
|
||||
logrus.Debugf("setting %q namespace to %q", what, how)
|
||||
options.AddOrReplace(buildah.NamespaceOption{
|
||||
|
@ -170,7 +191,7 @@ func parseNamespaceOptions(c *cli.Context) (namespaceOptions buildah.NamespaceOp
|
|||
}
|
||||
}
|
||||
}
|
||||
return options, nil
|
||||
return options, policy, nil
|
||||
}
|
||||
|
||||
func parseIDMappingOptions(c *cli.Context) (usernsOptions buildah.NamespaceOptions, idmapOptions *buildah.IDMappingOptions, err error) {
|
||||
|
|
|
@ -112,7 +112,7 @@ func fromCmd(c *cli.Context) error {
|
|||
}
|
||||
}
|
||||
|
||||
namespaceOptions, err := parseNamespaceOptions(c)
|
||||
namespaceOptions, networkPolicy, err := parseNamespaceOptions(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error parsing namespace-related options")
|
||||
}
|
||||
|
@ -131,6 +131,9 @@ func fromCmd(c *cli.Context) error {
|
|||
SystemContext: systemContext,
|
||||
DefaultMountsFilePath: c.GlobalString("default-mounts-file"),
|
||||
NamespaceOptions: namespaceOptions,
|
||||
ConfigureNetwork: networkPolicy,
|
||||
CNIPluginPath: c.String("cni-plugin-path"),
|
||||
CNIConfigDir: c.String("cni-config-dir"),
|
||||
IDMappingOptions: idmappingOptions,
|
||||
CommonBuildOpts: commonOpts,
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ func runCmd(c *cli.Context) error {
|
|||
}
|
||||
|
||||
user := parseUserOptions(c)
|
||||
namespaceOptions, err := parseNamespaceOptions(c)
|
||||
namespaceOptions, networkPolicy, err := parseNamespaceOptions(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error parsing namespace-related options")
|
||||
}
|
||||
|
@ -101,6 +101,9 @@ func runCmd(c *cli.Context) error {
|
|||
Args: runtimeFlags,
|
||||
User: user,
|
||||
NamespaceOptions: namespaceOptions,
|
||||
ConfigureNetwork: networkPolicy,
|
||||
CNIPluginPath: c.String("cni-plugin-path"),
|
||||
CNIConfigDir: c.String("cni-config-dir"),
|
||||
}
|
||||
|
||||
if c.IsSet("tty") {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
cniversion "github.com/containernetworking/cni/pkg/version"
|
||||
ispecs "github.com/opencontainers/image-spec/specs-go"
|
||||
rspecs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/projectatomic/buildah"
|
||||
|
@ -14,8 +15,9 @@ import (
|
|||
|
||||
//Overwritten at build time
|
||||
var (
|
||||
gitCommit string
|
||||
buildInfo string
|
||||
gitCommit string
|
||||
buildInfo string
|
||||
cniVersion string
|
||||
)
|
||||
|
||||
//Function to get and print info for version command
|
||||
|
@ -27,15 +29,17 @@ func versionCmd(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Version: ", buildah.Version)
|
||||
fmt.Println("Go Version: ", runtime.Version())
|
||||
fmt.Println("Image Spec: ", ispecs.Version)
|
||||
fmt.Println("Runtime Spec: ", rspecs.Version)
|
||||
fmt.Println("Git Commit: ", gitCommit)
|
||||
fmt.Println("Version: ", buildah.Version)
|
||||
fmt.Println("Go Version: ", runtime.Version())
|
||||
fmt.Println("Image Spec: ", ispecs.Version)
|
||||
fmt.Println("Runtime Spec: ", rspecs.Version)
|
||||
fmt.Println("CNI Spec: ", cniversion.Current())
|
||||
fmt.Println("libcni Version: ", cniVersion)
|
||||
fmt.Println("Git Commit: ", gitCommit)
|
||||
|
||||
//Prints out the build time in readable format
|
||||
fmt.Println("Built: ", time.Unix(buildTime, 0).Format(time.ANSIC))
|
||||
fmt.Println("OS/Arch: ", runtime.GOOS+"/"+runtime.GOARCH)
|
||||
fmt.Println("Built: ", time.Unix(buildTime, 0).Format(time.ANSIC))
|
||||
fmt.Println("OS/Arch: ", runtime.GOOS+"/"+runtime.GOARCH)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -371,6 +371,8 @@ return 1
|
|||
--build-arg
|
||||
--cert-dir
|
||||
--cgroup-parent
|
||||
--cni-config-dir
|
||||
--cni-plugin-path
|
||||
--cpu-period
|
||||
--cpu-quota
|
||||
--cpu-shares
|
||||
|
@ -439,6 +441,8 @@ return 1
|
|||
"
|
||||
|
||||
local options_with_args="
|
||||
--cni-config-dir
|
||||
--cni-plugin-path
|
||||
--hostname
|
||||
--ipc
|
||||
--net
|
||||
|
@ -704,6 +708,8 @@ return 1
|
|||
--authfile
|
||||
--cert-dir
|
||||
--cgroup-parent
|
||||
--cni-config-dir
|
||||
--cni-plugin-path
|
||||
--cpu-period
|
||||
--cpu-quota
|
||||
--cpu-shares
|
||||
|
|
|
@ -60,9 +60,21 @@ Path to cgroups under which the cgroup for the container will be created. If the
|
|||
**--compress**
|
||||
|
||||
This option is added to be aligned with other containers CLIs.
|
||||
Buildah doesn't communicate with a daemon or a remote server.
|
||||
Buildah doesn't send a copy of the context directory to a daemon or a remote server.
|
||||
Thus, compressing the data before sending it is irrelevant to Buildah.
|
||||
|
||||
**--cni-config-dir**=*directory*
|
||||
|
||||
Location of CNI configuration files which will dictate which plugins will be
|
||||
used to configure network interfaces and routing for containers created for
|
||||
handling `RUN` instructions, if those containers will be run in their own
|
||||
network namespaces, and networking is not disabled.
|
||||
|
||||
**--cni-plugin-path**=*directory[:directory[:directory[...]]]*
|
||||
|
||||
List of directories in which the CNI plugins which will be used for configuring
|
||||
network namespaces can be found.
|
||||
|
||||
**--cpu-period**=*0*
|
||||
|
||||
Limit the CPU CFS (Completely Fair Scheduler) period
|
||||
|
|
|
@ -57,6 +57,18 @@ The default certificates directory is _/etc/containers/certs.d_.
|
|||
|
||||
Path to cgroups under which the cgroup for the container will be created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups will be created if they do not already exist.
|
||||
|
||||
**--cni-config-dir**=*directory*
|
||||
|
||||
Location of CNI configuration files which will dictate which plugins will be
|
||||
used to configure network interfaces and routing when the container is
|
||||
subsequently used for `buildah run`, if processes to be started will be run in
|
||||
their own network namespaces, and networking is not disabled.
|
||||
|
||||
**--cni-plugin-path**=*directory[:directory[:directory[...]]]*
|
||||
|
||||
List of directories in which the CNI plugins which will be used for configuring
|
||||
network namespaces can be found.
|
||||
|
||||
**--cpu-period**=*0*
|
||||
|
||||
Limit the CPU CFS (Completely Fair Scheduler) period
|
||||
|
|
|
@ -14,6 +14,18 @@ the *buildah config* command. To execute *buildah run* within an
|
|||
interactive shell, specify the --tty option.
|
||||
|
||||
## OPTIONS
|
||||
**--cni-config-dir**=*directory*
|
||||
|
||||
Location of CNI configuration files which will dictate which plugins will be
|
||||
used to configure network interfaces and routing inside the running container,
|
||||
if the container will be run in its own network namespace, and networking is
|
||||
not disabled.
|
||||
|
||||
**--cni-plugin-path**=*directory[:directory[:directory[...]]]*
|
||||
|
||||
List of directories in which the CNI plugins which will be used for configuring
|
||||
network namespaces can be found.
|
||||
|
||||
**--hostname**
|
||||
Set the hostname inside of the running container.
|
||||
|
||||
|
|
|
@ -111,6 +111,18 @@ type BuildOptions struct {
|
|||
// NamespaceOptions controls how we set up namespaces processes that we
|
||||
// might need when handling RUN instructions.
|
||||
NamespaceOptions []buildah.NamespaceOption
|
||||
// ConfigureNetwork controls whether or not network interfaces and
|
||||
// routing are configured for a new network namespace (i.e., when not
|
||||
// joining another's namespace and not just using the host's
|
||||
// namespace), effectively deciding whether or not the process has a
|
||||
// usable network.
|
||||
ConfigureNetwork buildah.NetworkConfigurationPolicy
|
||||
// CNIPluginPath is the location of CNI plugin helpers, if they should be
|
||||
// run from a location other than the default location.
|
||||
CNIPluginPath string
|
||||
// CNIConfigDir is the location of CNI configuration files, if the files in
|
||||
// the default configuration directory shouldn't be used.
|
||||
CNIConfigDir string
|
||||
// ID mapping options to use if we're setting up our own user namespace
|
||||
// when handling RUN instructions.
|
||||
IDMappingOptions *buildah.IDMappingOptions
|
||||
|
@ -161,6 +173,9 @@ type Executor struct {
|
|||
volumeCacheInfo map[string]os.FileInfo
|
||||
reportWriter io.Writer
|
||||
namespaceOptions []buildah.NamespaceOption
|
||||
configureNetwork buildah.NetworkConfigurationPolicy
|
||||
cniPluginPath string
|
||||
cniConfigDir string
|
||||
idmappingOptions *buildah.IDMappingOptions
|
||||
commonBuildOptions *buildah.CommonBuildOptions
|
||||
defaultMountsFilePath string
|
||||
|
@ -421,17 +436,21 @@ func (b *Executor) Run(run imagebuilder.Run, config docker.Config) error {
|
|||
return errors.Errorf("no build container available")
|
||||
}
|
||||
options := buildah.RunOptions{
|
||||
Hostname: config.Hostname,
|
||||
Runtime: b.runtime,
|
||||
Args: b.runtimeArgs,
|
||||
Mounts: convertMounts(b.transientMounts),
|
||||
Env: config.Env,
|
||||
User: config.User,
|
||||
WorkingDir: config.WorkingDir,
|
||||
Entrypoint: config.Entrypoint,
|
||||
Cmd: config.Cmd,
|
||||
NetworkDisabled: config.NetworkDisabled,
|
||||
Quiet: b.quiet,
|
||||
Hostname: config.Hostname,
|
||||
Runtime: b.runtime,
|
||||
Args: b.runtimeArgs,
|
||||
Mounts: convertMounts(b.transientMounts),
|
||||
Env: config.Env,
|
||||
User: config.User,
|
||||
WorkingDir: config.WorkingDir,
|
||||
Entrypoint: config.Entrypoint,
|
||||
Cmd: config.Cmd,
|
||||
Quiet: b.quiet,
|
||||
}
|
||||
if config.NetworkDisabled {
|
||||
options.ConfigureNetwork = buildah.NetworkDisabled
|
||||
} else {
|
||||
options.ConfigureNetwork = buildah.NetworkEnabled
|
||||
}
|
||||
|
||||
args := run.Args
|
||||
|
@ -498,6 +517,9 @@ func NewExecutor(store storage.Store, options BuildOptions) (*Executor, error) {
|
|||
err: options.Err,
|
||||
reportWriter: options.ReportWriter,
|
||||
namespaceOptions: options.NamespaceOptions,
|
||||
configureNetwork: options.ConfigureNetwork,
|
||||
cniPluginPath: options.CNIPluginPath,
|
||||
cniConfigDir: options.CNIConfigDir,
|
||||
idmappingOptions: options.IDMappingOptions,
|
||||
commonBuildOptions: options.CommonBuildOpts,
|
||||
defaultMountsFilePath: options.DefaultMountsFilePath,
|
||||
|
@ -548,6 +570,9 @@ func (b *Executor) Prepare(ctx context.Context, ib *imagebuilder.Builder, node *
|
|||
ReportWriter: b.reportWriter,
|
||||
SystemContext: b.systemContext,
|
||||
NamespaceOptions: b.namespaceOptions,
|
||||
ConfigureNetwork: b.configureNetwork,
|
||||
CNIPluginPath: b.cniPluginPath,
|
||||
CNIConfigDir: b.cniConfigDir,
|
||||
IDMappingOptions: b.idmappingOptions,
|
||||
CommonBuildOpts: b.commonBuildOptions,
|
||||
DefaultMountsFilePath: b.defaultMountsFilePath,
|
||||
|
|
41
install.md
41
install.md
|
@ -17,6 +17,38 @@ encounters a `RUN` instruction, so you'll also need to build and install a compa
|
|||
[runc](https://github.com/opencontainers/runc) for Buildah to call for those cases. If Buildah is installed
|
||||
via a package manager such as yum, dnf or apt-get, runc will be installed as part of that process.
|
||||
|
||||
### CNI Requirement
|
||||
|
||||
When Buildah uses `runc` to run commands, it defaults to running those commands
|
||||
in the host's network namespace. If the command is being run in a separate
|
||||
user namespace, though, for example when ID mapping is used, then the command
|
||||
will also be run in a separate network namespace.
|
||||
|
||||
A newly-created network namespace starts with no network interfaces, so
|
||||
commands which are run in that namespace are effectively disconnected from the
|
||||
network unless additional setup is done. Buildah relies on the CNI
|
||||
[library](https://github.com/containernetworking/cni) and
|
||||
[plugins](https://github.com/containernetworking/plugins) to set up interfaces
|
||||
and routing for network namespaces.
|
||||
|
||||
If Buildah is installed via a package manager such as yum, dnf or apt-get, a
|
||||
package containing CNI plugins may be available (in Fedora, the package is
|
||||
named `containernetworking-cni`). If not, they will need to be installed,
|
||||
for example using:
|
||||
```
|
||||
git clone https://github.com/containernetworking/plugins
|
||||
( cd ./plugins; ./build.sh )
|
||||
mkdir -p /opt/cni/bin
|
||||
install -v ./plugins/bin/* /opt/cni/bin
|
||||
```
|
||||
|
||||
The CNI library needs to be configured so that it will know which plugins to
|
||||
call to set up namespaces. Usually, this configuration takes the form of one
|
||||
or more configuration files in the `/etc/cni/net.d` directory. A set of example
|
||||
configuration files is included in the
|
||||
[`docs/cni-examples`](https://github.com/projectatomic/buildah/tree/master/docs/cni-examples)
|
||||
directory of this source tree.
|
||||
|
||||
## Package Installation
|
||||
|
||||
Buildah is available on several software repositories and can be installed via a package manager such
|
||||
|
@ -66,7 +98,6 @@ In Fedora, you can use this command:
|
|||
|
||||
Then to install Buildah on Fedora follow the steps in this example:
|
||||
|
||||
|
||||
```
|
||||
mkdir ~/buildah
|
||||
cd ~/buildah
|
||||
|
@ -80,8 +111,8 @@ Then to install Buildah on Fedora follow the steps in this example:
|
|||
|
||||
### RHEL, CentOS
|
||||
|
||||
In RHEL and CentOS 7, ensure that you are subscribed to `rhel-7-server-rpms`,
|
||||
`rhel-7-server-extras-rpms`, and `rhel-7-server-optional-rpms`, then
|
||||
In RHEL and CentOS 7, ensure that you are subscribed to the `rhel-7-server-rpms`,
|
||||
`rhel-7-server-extras-rpms`, and `rhel-7-server-optional-rpms` repositories, then
|
||||
run this command:
|
||||
|
||||
```
|
||||
|
@ -103,7 +134,7 @@ run this command:
|
|||
skopeo-containers
|
||||
```
|
||||
|
||||
The build steps for Buildah on RHEL or CentOS are the same as Fedora, above.
|
||||
The build steps for Buildah on RHEL or CentOS are the same as for Fedora, above.
|
||||
|
||||
|
||||
### openSUSE
|
||||
|
@ -124,7 +155,7 @@ Currently openSUSE Leap 15 offers `go1.8` , while openSUSE Tumbleweed has `go1.9
|
|||
go-md2man
|
||||
```
|
||||
|
||||
The build steps for Buildah on SUSE / openSUSE are the same as Fedora, above.
|
||||
The build steps for Buildah on SUSE / openSUSE are the same as for Fedora, above.
|
||||
|
||||
|
||||
### Ubuntu
|
||||
|
|
3
new.go
3
new.go
|
@ -315,6 +315,9 @@ func newBuilder(ctx context.Context, store storage.Store, options BuilderOptions
|
|||
MountLabel: mountLabel,
|
||||
DefaultMountsFilePath: options.DefaultMountsFilePath,
|
||||
NamespaceOptions: namespaceOptions,
|
||||
ConfigureNetwork: options.ConfigureNetwork,
|
||||
CNIPluginPath: options.CNIPluginPath,
|
||||
CNIConfigDir: options.CNIConfigDir,
|
||||
IDMappingOptions: IDMappingOptions{
|
||||
HostUIDMapping: len(uidmap) == 0,
|
||||
HostGIDMapping: len(uidmap) == 0,
|
||||
|
|
|
@ -7,6 +7,7 @@ package cli
|
|||
import (
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/projectatomic/buildah"
|
||||
"github.com/projectatomic/buildah/util"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
|
@ -43,6 +44,16 @@ var (
|
|||
Name: string(specs.NetworkNamespace) + ", net",
|
||||
Usage: "'container', `path` of network namespace to join, or 'host'",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "cni-config-dir",
|
||||
Usage: "`directory` of CNI configuration files",
|
||||
Value: util.DefaultCNIConfigDir,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "cni-plugin-path",
|
||||
Usage: "`path` of CNI network plugins",
|
||||
Value: util.DefaultCNIPluginPath,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: string(specs.PIDNamespace),
|
||||
Usage: "'container', `path` of PID namespace to join, or 'host'",
|
||||
|
|
220
run.go
220
run.go
|
@ -17,6 +17,7 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containernetworking/cni/libcni"
|
||||
"github.com/containers/storage/pkg/ioutils"
|
||||
"github.com/containers/storage/pkg/reexec"
|
||||
"github.com/docker/docker/profiles/seccomp"
|
||||
|
@ -26,6 +27,7 @@ import (
|
|||
"github.com/opencontainers/runtime-tools/generate"
|
||||
"github.com/opencontainers/selinux/go-selinux/label"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectatomic/buildah/util"
|
||||
"github.com/projectatomic/libpod/pkg/secrets"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
|
@ -81,6 +83,10 @@ type NamespaceOption struct {
|
|||
// Path is the path of the namespace to attach our process to, if Host
|
||||
// is not set. If Host is not set and Path is also empty, a new
|
||||
// namespace will be created for the process that we're starting.
|
||||
// If Name is specs.NetworkNamespace, if Path doesn't look like an
|
||||
// absolute path, it is treated as a comma-separated list of CNI
|
||||
// configuration names which will be selected from among all of the CNI
|
||||
// network configurations which we find.
|
||||
Path string
|
||||
}
|
||||
|
||||
|
@ -120,13 +126,20 @@ type RunOptions struct {
|
|||
Cmd []string
|
||||
// Entrypoint is an override for the configured entry point.
|
||||
Entrypoint []string
|
||||
// NetworkDisabled skips configuration of network interfaces and routing
|
||||
// when setting up a new network namespace (i.e., when not joining another's
|
||||
// namespace and not just using the host's namespace), effectively leaving
|
||||
// the process without a usable network.
|
||||
NetworkDisabled bool
|
||||
// NamespaceOptions controls how we set up the namespaces for the process.
|
||||
NamespaceOptions NamespaceOptions
|
||||
// ConfigureNetwork controls whether or not network interfaces and
|
||||
// routing are configured for a new network namespace (i.e., when not
|
||||
// joining another's namespace and not just using the host's
|
||||
// namespace), effectively deciding whether or not the process has a
|
||||
// usable network.
|
||||
ConfigureNetwork NetworkConfigurationPolicy
|
||||
// CNIPluginPath is the location of CNI plugin helpers, if they should be
|
||||
// run from a location other than the default location.
|
||||
CNIPluginPath string
|
||||
// CNIConfigDir is the location of CNI configuration files, if the files in
|
||||
// the default configuration directory shouldn't be used.
|
||||
CNIConfigDir string
|
||||
// Terminal provides a way to specify whether or not the command should
|
||||
// be run with a pseudoterminal. By default (DefaultTerminal), a
|
||||
// terminal is used if os.Stdout is connected to a terminal, but that
|
||||
|
@ -497,9 +510,10 @@ func setupTerminal(g *generate.Generator, terminalPolicy TerminalPolicy) {
|
|||
}
|
||||
}
|
||||
|
||||
func setupNamespaces(g *generate.Generator, namespaceOptions NamespaceOptions, idmapOptions IDMappingOptions, networkDisabled bool) error {
|
||||
func setupNamespaces(g *generate.Generator, namespaceOptions NamespaceOptions, idmapOptions IDMappingOptions, policy NetworkConfigurationPolicy) (configureNetwork bool, configureNetworks []string, err error) {
|
||||
// Set namespace options in the container configuration.
|
||||
configureUserns := false
|
||||
specifiedNetwork := false
|
||||
for _, namespaceOption := range namespaceOptions {
|
||||
switch namespaceOption.Name {
|
||||
case string(specs.UserNamespace):
|
||||
|
@ -507,22 +521,32 @@ func setupNamespaces(g *generate.Generator, namespaceOptions NamespaceOptions, i
|
|||
if !namespaceOption.Host && namespaceOption.Path == "" {
|
||||
configureUserns = true
|
||||
}
|
||||
case specs.NetworkNamespace:
|
||||
specifiedNetwork = true
|
||||
configureNetwork = false
|
||||
if !namespaceOption.Host && (namespaceOption.Path == "" || !filepath.IsAbs(namespaceOption.Path)) {
|
||||
if namespaceOption.Path != "" && !filepath.IsAbs(namespaceOption.Path) {
|
||||
configureNetworks = strings.Split(namespaceOption.Path, ",")
|
||||
namespaceOption.Path = ""
|
||||
}
|
||||
configureNetwork = (policy != NetworkDisabled)
|
||||
}
|
||||
}
|
||||
if namespaceOption.Host {
|
||||
if err := g.RemoveLinuxNamespace(namespaceOption.Name); err != nil {
|
||||
return errors.Wrapf(err, "error removing %q namespace for run", namespaceOption.Name)
|
||||
return false, nil, errors.Wrapf(err, "error removing %q namespace for run", namespaceOption.Name)
|
||||
}
|
||||
} else if err := g.AddOrReplaceLinuxNamespace(namespaceOption.Name, namespaceOption.Path); err != nil {
|
||||
if namespaceOption.Path == "" {
|
||||
return errors.Wrapf(err, "error adding new %q namespace for run", namespaceOption.Name)
|
||||
return false, nil, errors.Wrapf(err, "error adding new %q namespace for run", namespaceOption.Name)
|
||||
}
|
||||
return errors.Wrapf(err, "error adding %q namespace %q for run", namespaceOption.Name, namespaceOption.Path)
|
||||
return false, nil, errors.Wrapf(err, "error adding %q namespace %q for run", namespaceOption.Name, namespaceOption.Path)
|
||||
}
|
||||
}
|
||||
// If we've got mappings, we're going to have to create a user namespace.
|
||||
if len(idmapOptions.UIDMap) > 0 || len(idmapOptions.GIDMap) > 0 || configureUserns {
|
||||
if err := g.AddOrReplaceLinuxNamespace(specs.UserNamespace, ""); err != nil {
|
||||
return errors.Wrapf(err, "error adding new %q namespace for run", string(specs.UserNamespace))
|
||||
return false, nil, errors.Wrapf(err, "error adding new %q namespace for run", string(specs.UserNamespace))
|
||||
}
|
||||
for _, m := range idmapOptions.UIDMap {
|
||||
g.AddLinuxUIDMapping(m.HostID, m.ContainerID, m.Size)
|
||||
|
@ -530,7 +554,7 @@ func setupNamespaces(g *generate.Generator, namespaceOptions NamespaceOptions, i
|
|||
if len(idmapOptions.UIDMap) == 0 {
|
||||
mappings, err := getProcIDMappings("/proc/self/uid_map")
|
||||
if err != nil {
|
||||
return err
|
||||
return false, nil, err
|
||||
}
|
||||
for _, m := range mappings {
|
||||
g.AddLinuxUIDMapping(m.ContainerID, m.ContainerID, m.Size)
|
||||
|
@ -542,28 +566,29 @@ func setupNamespaces(g *generate.Generator, namespaceOptions NamespaceOptions, i
|
|||
if len(idmapOptions.GIDMap) == 0 {
|
||||
mappings, err := getProcIDMappings("/proc/self/gid_map")
|
||||
if err != nil {
|
||||
return err
|
||||
return false, nil, err
|
||||
}
|
||||
for _, m := range mappings {
|
||||
g.AddLinuxGIDMapping(m.ContainerID, m.ContainerID, m.Size)
|
||||
}
|
||||
}
|
||||
if !networkDisabled {
|
||||
if !specifiedNetwork {
|
||||
if err := g.AddOrReplaceLinuxNamespace(specs.NetworkNamespace, ""); err != nil {
|
||||
return errors.Wrapf(err, "error adding new %q namespace for run", string(specs.NetworkNamespace))
|
||||
return false, nil, errors.Wrapf(err, "error adding new %q namespace for run", string(specs.NetworkNamespace))
|
||||
}
|
||||
configureNetwork = (policy != NetworkDisabled)
|
||||
}
|
||||
} else {
|
||||
if err := g.RemoveLinuxNamespace(specs.UserNamespace); err != nil {
|
||||
return errors.Wrapf(err, "error removing %q namespace for run", string(specs.UserNamespace))
|
||||
return false, nil, errors.Wrapf(err, "error removing %q namespace for run", string(specs.UserNamespace))
|
||||
}
|
||||
if !networkDisabled {
|
||||
if !specifiedNetwork {
|
||||
if err := g.RemoveLinuxNamespace(specs.NetworkNamespace); err != nil {
|
||||
return errors.Wrapf(err, "error removing %q namespace for run", string(specs.NetworkNamespace))
|
||||
return false, nil, errors.Wrapf(err, "error removing %q namespace for run", string(specs.NetworkNamespace))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return configureNetwork, configureNetworks, nil
|
||||
}
|
||||
|
||||
// Run runs the specified command in the container's root filesystem.
|
||||
|
@ -634,7 +659,14 @@ func (b *Builder) Run(command []string, options RunOptions) error {
|
|||
namespaceOptions := DefaultNamespaceOptions()
|
||||
namespaceOptions.AddOrReplace(b.NamespaceOptions...)
|
||||
namespaceOptions.AddOrReplace(options.NamespaceOptions...)
|
||||
if err = setupNamespaces(g, namespaceOptions, b.IDMappingOptions, options.NetworkDisabled); err != nil {
|
||||
|
||||
networkPolicy := options.ConfigureNetwork
|
||||
if networkPolicy == NetworkDefault {
|
||||
networkPolicy = b.ConfigureNetwork
|
||||
}
|
||||
|
||||
configureNetwork, configureNetworks, err := setupNamespaces(g, namespaceOptions, b.IDMappingOptions, networkPolicy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -694,25 +726,43 @@ func (b *Builder) Run(command []string, options RunOptions) error {
|
|||
if err != nil {
|
||||
return errors.Wrapf(err, "error resolving mountpoints for container")
|
||||
}
|
||||
return b.runUsingRuntimeSubproc(options, spec, mountPoint, path, Package+"-"+filepath.Base(path))
|
||||
|
||||
if options.CNIConfigDir == "" {
|
||||
options.CNIConfigDir = b.CNIConfigDir
|
||||
if b.CNIConfigDir == "" {
|
||||
options.CNIConfigDir = util.DefaultCNIConfigDir
|
||||
}
|
||||
}
|
||||
if options.CNIPluginPath == "" {
|
||||
options.CNIPluginPath = b.CNIPluginPath
|
||||
if b.CNIPluginPath == "" {
|
||||
options.CNIPluginPath = util.DefaultCNIPluginPath
|
||||
}
|
||||
}
|
||||
|
||||
return b.runUsingRuntimeSubproc(options, configureNetwork, configureNetworks, spec, mountPoint, path, Package+"-"+filepath.Base(path))
|
||||
}
|
||||
|
||||
type runUsingRuntimeSubprocOptions struct {
|
||||
Options RunOptions
|
||||
Spec *specs.Spec
|
||||
RootPath string
|
||||
BundlePath string
|
||||
ContainerName string
|
||||
Options RunOptions
|
||||
Spec *specs.Spec
|
||||
RootPath string
|
||||
BundlePath string
|
||||
ConfigureNetwork bool
|
||||
ConfigureNetworks []string
|
||||
ContainerName string
|
||||
}
|
||||
|
||||
func (b *Builder) runUsingRuntimeSubproc(options RunOptions, spec *specs.Spec, rootPath, bundlePath, containerName string) (err error) {
|
||||
func (b *Builder) runUsingRuntimeSubproc(options RunOptions, configureNetwork bool, configureNetworks []string, spec *specs.Spec, rootPath, bundlePath, containerName string) (err error) {
|
||||
var confwg sync.WaitGroup
|
||||
config, conferr := json.Marshal(runUsingRuntimeSubprocOptions{
|
||||
Options: options,
|
||||
Spec: spec,
|
||||
RootPath: rootPath,
|
||||
BundlePath: bundlePath,
|
||||
ContainerName: containerName,
|
||||
Options: options,
|
||||
Spec: spec,
|
||||
RootPath: rootPath,
|
||||
BundlePath: bundlePath,
|
||||
ConfigureNetwork: configureNetwork,
|
||||
ConfigureNetworks: configureNetworks,
|
||||
ContainerName: containerName,
|
||||
})
|
||||
if conferr != nil {
|
||||
return errors.Wrapf(conferr, "error encoding configuration for %q", runUsingRuntimeCommand)
|
||||
|
@ -773,7 +823,7 @@ func runUsingRuntimeMain() {
|
|||
os.Exit(1)
|
||||
}
|
||||
// Run the container, start to finish.
|
||||
status, err := runUsingRuntime(options.Options, options.Spec, options.RootPath, options.BundlePath, options.ContainerName)
|
||||
status, err := runUsingRuntime(options.Options, options.ConfigureNetwork, options.ConfigureNetworks, options.Spec, options.RootPath, options.BundlePath, options.ContainerName)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error running container: %v\n", err)
|
||||
os.Exit(1)
|
||||
|
@ -788,7 +838,7 @@ func runUsingRuntimeMain() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
func runUsingRuntime(options RunOptions, spec *specs.Spec, rootPath, bundlePath, containerName string) (wstatus unix.WaitStatus, err error) {
|
||||
func runUsingRuntime(options RunOptions, configureNetwork bool, configureNetworks []string, spec *specs.Spec, rootPath, bundlePath, containerName string) (wstatus unix.WaitStatus, err error) {
|
||||
// Write the runtime configuration.
|
||||
specbytes, err := json.Marshal(spec)
|
||||
if err != nil {
|
||||
|
@ -928,6 +978,16 @@ func runUsingRuntime(options RunOptions, spec *specs.Spec, rootPath, bundlePath,
|
|||
}
|
||||
}()
|
||||
|
||||
if configureNetwork {
|
||||
teardown, err := runConfigureNetwork(options, configureNetwork, configureNetworks, pid, containerName, spec.Process.Args)
|
||||
if teardown != nil {
|
||||
defer teardown()
|
||||
}
|
||||
if err != nil {
|
||||
return 1, err
|
||||
}
|
||||
}
|
||||
|
||||
if copyStdio {
|
||||
// We don't need the ends of the pipes that belong to the container.
|
||||
stdin.Close()
|
||||
|
@ -1006,6 +1066,100 @@ func runUsingRuntime(options RunOptions, spec *specs.Spec, rootPath, bundlePath,
|
|||
return wstatus, nil
|
||||
}
|
||||
|
||||
func runConfigureNetwork(options RunOptions, configureNetwork bool, configureNetworks []string, pid int, containerName string, command []string) (teardown func(), err error) {
|
||||
var netconf, undo []*libcni.NetworkConfigList
|
||||
// Scan for CNI configuration files.
|
||||
confdir := options.CNIConfigDir
|
||||
files, err := libcni.ConfFiles(confdir, []string{".conf"})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error finding CNI networking configuration files named *.conf in directory %q", confdir)
|
||||
}
|
||||
lists, err := libcni.ConfFiles(confdir, []string{".conflist"})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error finding CNI networking configuration list files named *.conflist in directory %q", confdir)
|
||||
}
|
||||
logrus.Debugf("CNI network configuration file list: %#v", append(files, lists...))
|
||||
// Read the CNI configuration files.
|
||||
for _, file := range files {
|
||||
nc, err := libcni.ConfFromFile(file)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error loading networking configuration from file %q for %v", file, command)
|
||||
}
|
||||
if len(configureNetworks) > 0 && nc.Network != nil && (nc.Network.Name == "" || !stringInSlice(nc.Network.Name, configureNetworks)) {
|
||||
if nc.Network.Name == "" {
|
||||
logrus.Debugf("configuration in %q has no name, skipping it", file)
|
||||
} else {
|
||||
logrus.Debugf("configuration in %q has name %q, skipping it", file, nc.Network.Name)
|
||||
}
|
||||
continue
|
||||
}
|
||||
cl, err := libcni.ConfListFromConf(nc)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error converting networking configuration from file %q for %v", file, command)
|
||||
}
|
||||
logrus.Debugf("using network configuration from %q", file)
|
||||
netconf = append(netconf, cl)
|
||||
}
|
||||
for _, list := range lists {
|
||||
cl, err := libcni.ConfListFromFile(list)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error loading networking configuration list from file %q for %v", list, command)
|
||||
}
|
||||
if len(configureNetworks) > 0 && (cl.Name == "" || !stringInSlice(cl.Name, configureNetworks)) {
|
||||
if cl.Name == "" {
|
||||
logrus.Debugf("configuration list in %q has no name, skipping it", list)
|
||||
} else {
|
||||
logrus.Debugf("configuration list in %q has name %q, skipping it", list, cl.Name)
|
||||
}
|
||||
continue
|
||||
}
|
||||
logrus.Debugf("using network configuration list from %q", list)
|
||||
netconf = append(netconf, cl)
|
||||
}
|
||||
// Make sure we can access the container's network namespace,
|
||||
// even after it exits, to successfully tear down the
|
||||
// interfaces. Ensure this by opening a handle to the network
|
||||
// namespace, and using our copy to both configure and
|
||||
// deconfigure it.
|
||||
netns := fmt.Sprintf("/proc/%d/ns/net", pid)
|
||||
netFD, err := unix.Open(netns, unix.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error opening network namespace for %v", command)
|
||||
}
|
||||
mynetns := fmt.Sprintf("/proc/%d/fd/%d", unix.Getpid(), netFD)
|
||||
// Build our search path for the plugins.
|
||||
pluginPaths := strings.Split(options.CNIPluginPath, string(os.PathListSeparator))
|
||||
cni := libcni.CNIConfig{Path: pluginPaths}
|
||||
// Configure the interfaces.
|
||||
rtconf := make(map[*libcni.NetworkConfigList]*libcni.RuntimeConf)
|
||||
teardown = func() {
|
||||
for _, nc := range undo {
|
||||
if err = cni.DelNetworkList(nc, rtconf[nc]); err != nil {
|
||||
logrus.Errorf("error cleaning up network %v for %v: %v", rtconf[nc].IfName, command, err)
|
||||
}
|
||||
}
|
||||
unix.Close(netFD)
|
||||
}
|
||||
for i, nc := range netconf {
|
||||
// Build the runtime config for use with this network configuration.
|
||||
rtconf[nc] = &libcni.RuntimeConf{
|
||||
ContainerID: containerName,
|
||||
NetNS: mynetns,
|
||||
IfName: fmt.Sprintf("if%d", i),
|
||||
Args: [][2]string{},
|
||||
CapabilityArgs: map[string]interface{}{},
|
||||
}
|
||||
// Bring it up.
|
||||
_, err := cni.AddNetworkList(nc, rtconf[nc])
|
||||
if err != nil {
|
||||
return teardown, errors.Wrapf(err, "error configuring network list %v for %v", rtconf[nc].IfName, command)
|
||||
}
|
||||
// Add it to the list of networks to take down when the container process exits.
|
||||
undo = append([]*libcni.NetworkConfigList{nc}, undo...)
|
||||
}
|
||||
return teardown, nil
|
||||
}
|
||||
|
||||
func runCopyStdio(stdio *sync.WaitGroup, copyStdio bool, stdioPipe [][]int, copyConsole bool, consoleListener *net.UnixListener, finishCopy []int, finishedCopy chan struct{}) {
|
||||
defer func() {
|
||||
unix.Close(finishCopy[0])
|
||||
|
|
9
util.go
9
util.go
|
@ -36,6 +36,15 @@ func copyStringSlice(s []string) []string {
|
|||
return t
|
||||
}
|
||||
|
||||
func stringInSlice(s string, slice []string) bool {
|
||||
for _, v := range slice {
|
||||
if v == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func convertStorageIDMaps(UIDMap, GIDMap []idtools.IDMap) ([]rspec.LinuxIDMapping, []rspec.LinuxIDMapping) {
|
||||
uidmap := make([]rspec.LinuxIDMapping, 0, len(UIDMap))
|
||||
gidmap := make([]rspec.LinuxIDMapping, 0, len(GIDMap))
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package util
|
||||
|
||||
const (
|
||||
// DefaultRuntime is the default command to use to run the container.
|
||||
DefaultRuntime = "runc"
|
||||
// DefaultCNIPluginPath is the default location of CNI plugin helpers.
|
||||
DefaultCNIPluginPath = "/usr/libexec/cni:/opt/cni/bin"
|
||||
// DefaultCNIConfigDir is the default location of CNI configuration files.
|
||||
DefaultCNIConfigDir = "/etc/cni/net.d"
|
||||
)
|
Loading…
Reference in New Issue