Files
podman/pkg/adapter/network.go
baude 2f6b8b94e8 enable dnsplugin for network create
when users create a new network and the dnsname plugin can be found by
podman, we will enable container name resolution on the new network.
there is an option to opt *out* as well.

tests cannot be added until we solve the packaging portion of the
dnsname plugin.

Signed-off-by: baude <bbaude@redhat.com>
2019-10-28 12:52:30 -05:00

265 lines
7.9 KiB
Go

// +build !remoteclient
package adapter
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"text/tabwriter"
cniversion "github.com/containernetworking/cni/pkg/version"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/pkg/network"
"github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
)
func getCNIConfDir(r *LocalRuntime) (string, error) {
config, err := r.GetConfig()
if err != nil {
return "", err
}
configPath := config.CNIConfigDir
if len(config.CNIConfigDir) < 1 {
configPath = network.CNIConfigDir
}
return configPath, nil
}
// NetworkList displays summary information about CNI networks
func (r *LocalRuntime) NetworkList(cli *cliconfig.NetworkListValues) error {
cniConfigPath, err := getCNIConfDir(r)
if err != nil {
return err
}
networks, err := network.LoadCNIConfsFromDir(cniConfigPath)
if err != nil {
return err
}
// quiet means we only print the network names
if cli.Quiet {
for _, cniNetwork := range networks {
fmt.Println(cniNetwork.Name)
}
return nil
}
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
if _, err := fmt.Fprintln(w, "NAME\tVERSION\tPLUGINS"); err != nil {
return err
}
for _, cniNetwork := range networks {
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\n", cniNetwork.Name, cniNetwork.CNIVersion, network.GetCNIPlugins(cniNetwork)); err != nil {
return err
}
}
return w.Flush()
}
// NetworkInspect displays the raw CNI configuration for one
// or more CNI networks
func (r *LocalRuntime) NetworkInspect(cli *cliconfig.NetworkInspectValues) error {
var (
rawCNINetworks []map[string]interface{}
)
for _, name := range cli.InputArgs {
b, err := network.ReadRawCNIConfByName(name)
if err != nil {
return err
}
rawList := make(map[string]interface{})
if err := json.Unmarshal(b, &rawList); err != nil {
return fmt.Errorf("error parsing configuration list: %s", err)
}
rawCNINetworks = append(rawCNINetworks, rawList)
}
out, err := json.MarshalIndent(rawCNINetworks, "", "\t")
if err != nil {
return err
}
fmt.Printf("%s\n", out)
return nil
}
// NetworkRemove deletes one or more CNI networks
func (r *LocalRuntime) NetworkRemove(ctx context.Context, cli *cliconfig.NetworkRmValues) ([]string, map[string]error, error) {
var (
networkRmSuccesses []string
lastError error
)
networkRmErrors := make(map[string]error)
for _, name := range cli.InputArgs {
containers, err := r.GetAllContainers()
if err != nil {
return networkRmSuccesses, networkRmErrors, err
}
if err := r.removeNetwork(ctx, name, containers, cli.Force); err != nil {
if lastError != nil {
networkRmErrors[name] = lastError
}
lastError = err
} else {
networkRmSuccesses = append(networkRmSuccesses, fmt.Sprintf("Deleted: %s\n", name))
}
}
return networkRmSuccesses, networkRmErrors, lastError
}
// removeNetwork removes a single network and its containers given a force bool
func (r *LocalRuntime) removeNetwork(ctx context.Context, name string, containers []*Container, force bool) error {
cniPath, err := network.GetCNIConfigPathByName(name)
if err != nil {
return err
}
// We need to iterate containers looking to see if they belong to the given network
for _, c := range containers {
if util.StringInSlice(name, c.Config().Networks) {
// if user passes force, we nuke containers
if force {
if err := r.RemoveContainer(ctx, c.Container, true, true); err != nil {
return err
}
} else {
// Without the the force option, we return an error
return errors.Errorf("%q has associated containers with it. use -f to forcibly delete containers", name)
}
}
}
// Before we delete the configuration file, we need to make sure we can read and parse
// it to get the network interface name so we can remove that too
interfaceName, err := network.GetInterfaceNameFromConfig(cniPath)
if err != nil {
return errors.Wrapf(err, "failed to find network interface name in %q", cniPath)
}
liveNetworkNames, err := network.GetLiveNetworkNames()
if err != nil {
return errors.Wrapf(err, "failed to get live network names")
}
if util.StringInSlice(interfaceName, liveNetworkNames) {
if err := network.RemoveInterface(interfaceName); err != nil {
return errors.Wrapf(err, "failed to delete the network interface %q", interfaceName)
}
}
// Remove the configuration file
if err := os.Remove(cniPath); err != nil {
return errors.Wrapf(err, "failed to remove network configuration file %q", cniPath)
}
return nil
}
// NetworkCreate creates a CNI network
func (r *LocalRuntime) NetworkCreate(cli *cliconfig.NetworkCreateValues) (string, error) {
isGateway := true
ipMasq := true
subnet := &cli.Network
ipRange := cli.IPRange
runtimeConfig, err := r.GetConfig()
if err != nil {
return "", err
}
// if range is provided, make sure it is "in" network
if cli.IsSet("subnet") {
// if network is provided, does it conflict with existing CNI or live networks
err = network.ValidateUserNetworkIsAvailable(subnet)
} else {
// if no network is provided, figure out network
subnet, err = network.GetFreeNetwork()
}
if err != nil {
return "", err
}
gateway := cli.Gateway
if gateway == nil {
// if no gateway is provided, provide it as first ip of network
gateway = network.CalcGatewayIP(subnet)
}
// if network is provided and if gateway is provided, make sure it is "in" network
if cli.IsSet("subnet") && cli.IsSet("gateway") {
if !subnet.Contains(gateway) {
return "", errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String())
}
}
if cli.Internal {
isGateway = false
ipMasq = false
}
// if a range is given, we need to ensure it is "in" the network range.
if cli.IsSet("ip-range") {
if !cli.IsSet("subnet") {
return "", errors.New("you must define a subnet range to define an ip-range")
}
firstIP, err := network.FirstIPInSubnet(&cli.IPRange)
if err != nil {
return "", err
}
lastIP, err := network.LastIPInSubnet(&cli.IPRange)
if err != nil {
return "", err
}
if !subnet.Contains(firstIP) || !subnet.Contains(lastIP) {
return "", errors.Errorf("the ip range %s does not fall within the subnet range %s", cli.IPRange.String(), subnet.String())
}
}
bridgeDeviceName, err := network.GetFreeDeviceName()
if err != nil {
return "", err
}
// If no name is given, we give the name of the bridge device
name := bridgeDeviceName
if len(cli.InputArgs) > 0 {
name = cli.InputArgs[0]
netNames, err := network.GetNetworkNamesFromFileSystem()
if err != nil {
return "", err
}
if util.StringInSlice(name, netNames) {
return "", errors.Errorf("the network name %s is already used", name)
}
}
ncList := network.NewNcList(name, cniversion.Current())
var plugins []network.CNIPlugins
var routes []network.IPAMRoute
defaultRoute, err := network.NewIPAMDefaultRoute()
if err != nil {
return "", err
}
routes = append(routes, defaultRoute)
ipamConfig, err := network.NewIPAMHostLocalConf(subnet, routes, ipRange, gateway)
if err != nil {
return "", err
}
// TODO need to iron out the role of isDefaultGW and IPMasq
bridge := network.NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, ipamConfig)
plugins = append(plugins, bridge)
plugins = append(plugins, network.NewPortMapPlugin())
plugins = append(plugins, network.NewFirewallPlugin())
// if we find the dnsname plugin, we add configuration for it
if network.HasDNSNamePlugin(runtimeConfig.CNIPluginDir) && !cli.DisableDNS {
// Note: in the future we might like to allow for dynamic domain names
plugins = append(plugins, network.NewDNSNamePlugin(network.DefaultPodmanDomainName))
}
ncList["plugins"] = plugins
b, err := json.MarshalIndent(ncList, "", " ")
if err != nil {
return "", err
}
cniConfigPath, err := getCNIConfDir(r)
if err != nil {
return "", err
}
cniPathName := filepath.Join(cniConfigPath, fmt.Sprintf("%s.conflist", name))
err = ioutil.WriteFile(cniPathName, b, 0644)
return cniPathName, err
}