v2networking enable commands

Enable the networking commands for v2.

Signed-off-by: baude <bbaude@redhat.com>
This commit is contained in:
baude
2020-04-30 11:02:59 -05:00
parent c31bf2e976
commit e88a418528
12 changed files with 667 additions and 8 deletions

View File

@ -9,6 +9,7 @@ import (
_ "github.com/containers/libpod/cmd/podman/healthcheck"
_ "github.com/containers/libpod/cmd/podman/images"
_ "github.com/containers/libpod/cmd/podman/manifest"
_ "github.com/containers/libpod/cmd/podman/networks"
_ "github.com/containers/libpod/cmd/podman/pods"
"github.com/containers/libpod/cmd/podman/registry"
_ "github.com/containers/libpod/cmd/podman/system"

View File

@ -0,0 +1,81 @@
package network
import (
"fmt"
"net"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/network"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
var (
networkCreateDescription = `create CNI networks for containers and pods`
networkCreateCommand = &cobra.Command{
Use: "create [flags] [NETWORK]",
Short: "network create",
Long: networkCreateDescription,
RunE: networkCreate,
Example: `podman network create podman1`,
Annotations: map[string]string{
registry.ParentNSRequired: "",
},
}
)
var (
networkCreateOptions entities.NetworkCreateOptions
)
func networkCreateFlags(flags *pflag.FlagSet) {
flags.StringVarP(&networkCreateOptions.Driver, "driver", "d", "bridge", "driver to manage the network")
flags.IPVar(&networkCreateOptions.Gateway, "gateway", nil, "IPv4 or IPv6 gateway for the subnet")
flags.BoolVar(&networkCreateOptions.Internal, "internal", false, "restrict external access from this network")
flags.IPNetVar(&networkCreateOptions.Range, "ip-range", net.IPNet{}, "allocate container IP from range")
flags.StringVar(&networkCreateOptions.MacVLAN, "macvlan", "", "create a Macvlan connection based on this device")
// TODO not supported yet
//flags.StringVar(&networkCreateOptions.IPamDriver, "ipam-driver", "", "IP Address Management Driver")
// TODO enable when IPv6 is working
//flags.BoolVar(&networkCreateOptions.IPV6, "IPv6", false, "enable IPv6 networking")
flags.IPNetVar(&networkCreateOptions.Subnet, "subnet", net.IPNet{}, "subnet in CIDR format")
flags.BoolVar(&networkCreateOptions.DisableDNS, "disable-dns", false, "disable dns plugin")
}
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode},
Command: networkCreateCommand,
Parent: networkCmd,
})
flags := networkCreateCommand.Flags()
networkCreateFlags(flags)
}
func networkCreate(cmd *cobra.Command, args []string) error {
var (
name string
)
if err := network.IsSupportedDriver(networkCreateOptions.Driver); err != nil {
return err
}
if len(args) > 1 {
return errors.Errorf("only one network can be created at a time")
}
if len(args) > 0 && !libpod.NameRegex.MatchString(args[0]) {
return libpod.RegexError
}
if len(args) > 0 {
name = args[0]
}
response, err := registry.ContainerEngine().NetworkCreate(registry.Context(), name, networkCreateOptions)
if err != nil {
return err
}
fmt.Println(response.Filename)
return nil
}

View File

@ -0,0 +1,46 @@
package network
import (
"encoding/json"
"fmt"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/spf13/cobra"
)
var (
networkinspectDescription = `Inspect network`
networkinspectCommand = &cobra.Command{
Use: "inspect NETWORK [NETWORK...] [flags] ",
Short: "network inspect",
Long: networkinspectDescription,
RunE: networkInspect,
Example: `podman network inspect podman`,
Args: cobra.MinimumNArgs(1),
Annotations: map[string]string{
registry.ParentNSRequired: "",
},
}
)
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode},
Command: networkinspectCommand,
Parent: networkCmd,
})
}
func networkInspect(cmd *cobra.Command, args []string) error {
responses, err := registry.ContainerEngine().NetworkInspect(registry.Context(), args, entities.NetworkInspectOptions{})
if err != nil {
return err
}
b, err := json.MarshalIndent(responses, "", " ")
if err != nil {
return err
}
fmt.Println(string(b))
return nil
}

133
cmd/podman/networks/list.go Normal file
View File

@ -0,0 +1,133 @@
package network
import (
"encoding/json"
"fmt"
"html/template"
"io"
"os"
"strings"
"github.com/containers/libpod/cmd/podman/validate"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/network"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
var (
networklistDescription = `List networks`
networklistCommand = &cobra.Command{
Use: "ls",
Args: validate.NoArgs,
Short: "network list",
Long: networklistDescription,
RunE: networkList,
Example: `podman network list`,
Annotations: map[string]string{
registry.ParentNSRequired: "",
},
}
)
var (
networkListOptions entities.NetworkListOptions
headers string = "NAME\tVERSION\tPLUGINS\n"
defaultListRow string = "{{.Name}}\t{{.Version}}\t{{.Plugins}}\n"
)
func networkListFlags(flags *pflag.FlagSet) {
// TODO enable filters based on something
//flags.StringSliceVarP(&networklistCommand.Filter, "filter", "f", []string{}, "Pause all running containers")
flags.StringVarP(&networkListOptions.Format, "format", "f", "", "Pretty-print containers to JSON or using a Go template")
flags.BoolVarP(&networkListOptions.Quiet, "quiet", "q", false, "display only names")
}
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode},
Command: networklistCommand,
Parent: networkCmd,
})
flags := networklistCommand.Flags()
networkListFlags(flags)
}
func networkList(cmd *cobra.Command, args []string) error {
var (
w io.Writer = os.Stdout
nlprs []NetworkListPrintReports
)
responses, err := registry.ContainerEngine().NetworkList(registry.Context(), networkListOptions)
if err != nil {
return err
}
// quiet means we only print the network names
if networkListOptions.Quiet {
return quietOut(responses)
}
if networkListOptions.Format == "json" {
return jsonOut(responses)
}
for _, r := range responses {
nlprs = append(nlprs, NetworkListPrintReports{r})
}
row := networkListOptions.Format
if len(row) < 1 {
row = defaultListRow
}
if !strings.HasSuffix(row, "\n") {
row += "\n"
}
format := "{{range . }}" + row + "{{end}}"
if !cmd.Flag("format").Changed {
format = headers + format
}
tmpl, err := template.New("listNetworks").Parse(format)
if err != nil {
return err
}
if err := tmpl.Execute(w, nlprs); err != nil {
return err
}
if flusher, ok := w.(interface{ Flush() error }); ok {
return flusher.Flush()
}
return nil
}
func quietOut(responses []*entities.NetworkListReport) error {
for _, r := range responses {
fmt.Println(r.Name)
}
return nil
}
func jsonOut(responses []*entities.NetworkListReport) error {
b, err := json.MarshalIndent(responses, "", " ")
if err != nil {
return err
}
fmt.Println(string(b))
return nil
}
type NetworkListPrintReports struct {
*entities.NetworkListReport
}
func (n NetworkListPrintReports) Version() string {
return n.CNIVersion
}
func (n NetworkListPrintReports) Plugins() string {
return network.GetCNIPlugins(n.NetworkConfigList)
}

View File

@ -1,4 +1,4 @@
package images
package network
import (
"github.com/containers/libpod/cmd/podman/registry"
@ -9,7 +9,7 @@ import (
var (
// Command: podman _network_
cmd = &cobra.Command{
networkCmd = &cobra.Command{
Use: "network",
Short: "Manage networks",
Long: "Manage networks",
@ -18,12 +18,9 @@ var (
}
)
// TODO add the following to main.go to get networks back onto the
// command list.
// _ "github.com/containers/libpod/cmd/podman/networks"
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode},
Command: cmd,
Command: networkCmd,
})
}

63
cmd/podman/networks/rm.go Normal file
View File

@ -0,0 +1,63 @@
package network
import (
"fmt"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/cmd/podman/utils"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
var (
networkrmDescription = `Remove networks`
networkrmCommand = &cobra.Command{
Use: "rm [flags] NETWORK [NETWORK...]",
Short: "network rm",
Long: networkrmDescription,
RunE: networkRm,
Example: `podman network rm podman`,
Args: cobra.MinimumNArgs(1),
Annotations: map[string]string{
registry.ParentNSRequired: "",
},
}
)
var (
networkRmOptions entities.NetworkRmOptions
)
func networkRmFlags(flags *pflag.FlagSet) {
flags.BoolVarP(&networkRmOptions.Force, "force", "f", false, "remove any containers using network")
}
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode},
Command: networkrmCommand,
Parent: networkCmd,
})
flags := networkrmCommand.Flags()
networkRmFlags(flags)
}
func networkRm(cmd *cobra.Command, args []string) error {
var (
errs utils.OutputErrors
)
responses, err := registry.ContainerEngine().NetworkRm(registry.Context(), args, networkRmOptions)
if err != nil {
return err
}
for _, r := range responses {
if r.Err == nil {
fmt.Println(r.Name)
} else {
errs = append(errs, r.Err)
}
}
return errs.PrintErrors()
}

View File

@ -44,6 +44,10 @@ type ContainerEngine interface {
GenerateSystemd(ctx context.Context, nameOrID string, opts GenerateSystemdOptions) (*GenerateSystemdReport, error)
HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error)
Info(ctx context.Context) (*define.Info, error)
NetworkCreate(ctx context.Context, name string, options NetworkCreateOptions) (*NetworkCreateReport, error)
NetworkInspect(ctx context.Context, namesOrIds []string, options NetworkInspectOptions) ([]NetworkInspectReport, error)
NetworkList(ctx context.Context, options NetworkListOptions) ([]*NetworkListReport, error)
NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error)
PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error)
PodExists(ctx context.Context, nameOrId string) (*BoolReport, error)
PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error)

View File

@ -0,0 +1,52 @@
package entities
import (
"net"
"github.com/containernetworking/cni/libcni"
)
// NetworkListOptions describes options for listing networks in cli
type NetworkListOptions struct {
Format string
Quiet bool
}
// NetworkListReport describes the results from listing networks
type NetworkListReport struct {
*libcni.NetworkConfigList
}
// NetworkInspectOptions describes options for inspect networks
type NetworkInspectOptions struct {
}
// NetworkInspectReport describes the results from inspect networks
type NetworkInspectReport map[string]interface{}
// NetworkRmOptions describes options for removing networks
type NetworkRmOptions struct {
Force bool
}
//NetworkRmReport describes the results of network removal
type NetworkRmReport struct {
Name string
Err error
}
// NetworkCreateOptions describes options to create a network
type NetworkCreateOptions struct {
DisableDNS bool
Driver string
Gateway net.IP
Internal bool
MacVLAN string
Range net.IPNet
Subnet net.IPNet
}
// NetworkCreateReport describes a created network for the cli
type NetworkCreateReport struct {
Filename string
}

View File

@ -0,0 +1,258 @@
package abi
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"path/filepath"
cniversion "github.com/containernetworking/cni/pkg/version"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/network"
"github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
)
func getCNIConfDir(r *libpod.Runtime) (string, error) {
config, err := r.GetConfig()
if err != nil {
return "", err
}
configPath := config.Network.NetworkConfigDir
if len(config.Network.NetworkConfigDir) < 1 {
configPath = network.CNIConfigDir
}
return configPath, nil
}
func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) {
var reports []*entities.NetworkListReport
cniConfigPath, err := getCNIConfDir(ic.Libpod)
if err != nil {
return nil, err
}
networks, err := network.LoadCNIConfsFromDir(cniConfigPath)
if err != nil {
return nil, err
}
for _, n := range networks {
reports = append(reports, &entities.NetworkListReport{NetworkConfigList: n})
}
return reports, nil
}
func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) {
var (
rawCNINetworks []entities.NetworkInspectReport
)
for _, name := range namesOrIds {
rawList, err := network.InspectNetwork(name)
if err != nil {
return nil, err
}
rawCNINetworks = append(rawCNINetworks, rawList)
}
return rawCNINetworks, nil
}
func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) {
var reports []*entities.NetworkRmReport
for _, name := range namesOrIds {
report := entities.NetworkRmReport{Name: name}
containers, err := ic.Libpod.GetAllContainers()
if err != nil {
return reports, 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 !options.Force {
// Without the force option, we return an error
return reports, errors.Errorf("%q has associated containers with it. Use -f to forcibly delete containers", name)
}
if err := ic.Libpod.RemoveContainer(ctx, c, true, true); err != nil {
return reports, err
}
}
}
if err := network.RemoveNetwork(name); err != nil {
report.Err = err
}
reports = append(reports, &report)
}
return reports, nil
}
func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, options entities.NetworkCreateOptions) (*entities.NetworkCreateReport, error) {
var (
err error
fileName string
)
if len(options.MacVLAN) > 0 {
fileName, err = createMacVLAN(ic.Libpod, name, options)
} else {
fileName, err = createBridge(ic.Libpod, name, options)
}
if err != nil {
return nil, err
}
return &entities.NetworkCreateReport{Filename: fileName}, nil
}
// createBridge creates a CNI network
func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) {
isGateway := true
ipMasq := true
subnet := &options.Subnet
ipRange := options.Range
runtimeConfig, err := r.GetConfig()
if err != nil {
return "", err
}
// if range is provided, make sure it is "in" network
if subnet.IP != nil {
// 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 := options.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 options.Subnet.IP != nil && options.Gateway != nil {
if !subnet.Contains(gateway) {
return "", errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String())
}
}
if options.Internal {
isGateway = false
ipMasq = false
}
// if a range is given, we need to ensure it is "in" the network range.
if options.Range.IP != nil {
if options.Subnet.IP == nil {
return "", errors.New("you must define a subnet range to define an ip-range")
}
firstIP, err := network.FirstIPInSubnet(&options.Range)
if err != nil {
return "", err
}
lastIP, err := network.LastIPInSubnet(&options.Range)
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", options.Range.String(), subnet.String())
}
}
bridgeDeviceName, err := network.GetFreeDeviceName()
if err != nil {
return "", err
}
if len(name) > 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)
}
} else {
// If no name is given, we give the name of the bridge device
name = bridgeDeviceName
}
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.Network.CNIPluginDirs) && !options.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
}
func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) {
var (
plugins []network.CNIPlugins
)
liveNetNames, err := network.GetLiveNetworkNames()
if err != nil {
return "", err
}
// Make sure the host-device exists
if !util.StringInSlice(options.MacVLAN, liveNetNames) {
return "", errors.Errorf("failed to find network interface %q", options.MacVLAN)
}
if len(name) > 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)
}
} else {
name, err = network.GetFreeDeviceName()
if err != nil {
return "", err
}
}
ncList := network.NewNcList(name, cniversion.Current())
macvlan := network.NewMacVLANPlugin(options.MacVLAN)
plugins = append(plugins, macvlan)
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
}

View File

@ -0,0 +1,23 @@
package tunnel
import (
"context"
"errors"
"github.com/containers/libpod/pkg/domain/entities"
)
func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) {
return nil, errors.New("not implemented")
}
func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) {
return nil, errors.New("not implemented")
}
func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) {
return nil, errors.New("not implemented")
}
func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, options entities.NetworkCreateOptions) (*entities.NetworkCreateReport, error) {
return nil, errors.New("not implemented")
}

View File

@ -76,7 +76,6 @@ var _ = Describe("Podman network create", func() {
)
BeforeEach(func() {
Skip(v2fail)
SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
@ -141,6 +140,7 @@ var _ = Describe("Podman network create", func() {
})
It("podman network create with name and subnet", func() {
Skip(v2fail)
var (
results []network.NcList
)

View File

@ -34,7 +34,6 @@ var _ = Describe("Podman network", func() {
)
BeforeEach(func() {
Skip(v2fail)
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
@ -159,6 +158,7 @@ var _ = Describe("Podman network", func() {
})
It("podman inspect container single CNI network", func() {
Skip(v2fail)
SkipIfRootless()
netName := "testNetSingleCNI"
network := podmanTest.Podman([]string{"network", "create", "--subnet", "10.50.50.0/24", netName})
@ -190,6 +190,7 @@ var _ = Describe("Podman network", func() {
})
It("podman inspect container two CNI networks", func() {
Skip(v2fail)
SkipIfRootless()
netName1 := "testNetTwoCNI1"
network1 := podmanTest.Podman([]string{"network", "create", "--subnet", "10.50.51.0/25", netName1})