mirror of
https://github.com/containers/podman.git
synced 2025-05-24 18:46:26 +08:00

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>
265 lines
7.9 KiB
Go
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
|
|
}
|