github.com/olljanat/moby@v1.13.1/cli/command/network/create.go (about)

     1  package network
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"strings"
     7  
     8  	"golang.org/x/net/context"
     9  
    10  	"github.com/docker/docker/api/types"
    11  	"github.com/docker/docker/api/types/network"
    12  	"github.com/docker/docker/cli"
    13  	"github.com/docker/docker/cli/command"
    14  	"github.com/docker/docker/opts"
    15  	runconfigopts "github.com/docker/docker/runconfig/opts"
    16  	"github.com/spf13/cobra"
    17  )
    18  
    19  type createOptions struct {
    20  	name       string
    21  	driver     string
    22  	driverOpts opts.MapOpts
    23  	labels     opts.ListOpts
    24  	internal   bool
    25  	ipv6       bool
    26  	attachable bool
    27  
    28  	ipamDriver  string
    29  	ipamSubnet  []string
    30  	ipamIPRange []string
    31  	ipamGateway []string
    32  	ipamAux     opts.MapOpts
    33  	ipamOpt     opts.MapOpts
    34  }
    35  
    36  func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
    37  	opts := createOptions{
    38  		driverOpts: *opts.NewMapOpts(nil, nil),
    39  		labels:     opts.NewListOpts(runconfigopts.ValidateEnv),
    40  		ipamAux:    *opts.NewMapOpts(nil, nil),
    41  		ipamOpt:    *opts.NewMapOpts(nil, nil),
    42  	}
    43  
    44  	cmd := &cobra.Command{
    45  		Use:   "create [OPTIONS] NETWORK",
    46  		Short: "Create a network",
    47  		Args:  cli.ExactArgs(1),
    48  		RunE: func(cmd *cobra.Command, args []string) error {
    49  			opts.name = args[0]
    50  			return runCreate(dockerCli, opts)
    51  		},
    52  	}
    53  
    54  	flags := cmd.Flags()
    55  	flags.StringVarP(&opts.driver, "driver", "d", "bridge", "Driver to manage the Network")
    56  	flags.VarP(&opts.driverOpts, "opt", "o", "Set driver specific options")
    57  	flags.Var(&opts.labels, "label", "Set metadata on a network")
    58  	flags.BoolVar(&opts.internal, "internal", false, "Restrict external access to the network")
    59  	flags.BoolVar(&opts.ipv6, "ipv6", false, "Enable IPv6 networking")
    60  	flags.BoolVar(&opts.attachable, "attachable", false, "Enable manual container attachment")
    61  
    62  	flags.StringVar(&opts.ipamDriver, "ipam-driver", "default", "IP Address Management Driver")
    63  	flags.StringSliceVar(&opts.ipamSubnet, "subnet", []string{}, "Subnet in CIDR format that represents a network segment")
    64  	flags.StringSliceVar(&opts.ipamIPRange, "ip-range", []string{}, "Allocate container ip from a sub-range")
    65  	flags.StringSliceVar(&opts.ipamGateway, "gateway", []string{}, "IPv4 or IPv6 Gateway for the master subnet")
    66  
    67  	flags.Var(&opts.ipamAux, "aux-address", "Auxiliary IPv4 or IPv6 addresses used by Network driver")
    68  	flags.Var(&opts.ipamOpt, "ipam-opt", "Set IPAM driver specific options")
    69  
    70  	return cmd
    71  }
    72  
    73  func runCreate(dockerCli *command.DockerCli, opts createOptions) error {
    74  	client := dockerCli.Client()
    75  
    76  	ipamCfg, err := consolidateIpam(opts.ipamSubnet, opts.ipamIPRange, opts.ipamGateway, opts.ipamAux.GetAll())
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	// Construct network create request body
    82  	nc := types.NetworkCreate{
    83  		Driver:  opts.driver,
    84  		Options: opts.driverOpts.GetAll(),
    85  		IPAM: &network.IPAM{
    86  			Driver:  opts.ipamDriver,
    87  			Config:  ipamCfg,
    88  			Options: opts.ipamOpt.GetAll(),
    89  		},
    90  		CheckDuplicate: true,
    91  		Internal:       opts.internal,
    92  		EnableIPv6:     opts.ipv6,
    93  		Attachable:     opts.attachable,
    94  		Labels:         runconfigopts.ConvertKVStringsToMap(opts.labels.GetAll()),
    95  	}
    96  
    97  	resp, err := client.NetworkCreate(context.Background(), opts.name, nc)
    98  	if err != nil {
    99  		return err
   100  	}
   101  	fmt.Fprintf(dockerCli.Out(), "%s\n", resp.ID)
   102  	return nil
   103  }
   104  
   105  // Consolidates the ipam configuration as a group from different related configurations
   106  // user can configure network with multiple non-overlapping subnets and hence it is
   107  // possible to correlate the various related parameters and consolidate them.
   108  // consoidateIpam consolidates subnets, ip-ranges, gateways and auxiliary addresses into
   109  // structured ipam data.
   110  func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) {
   111  	if len(subnets) < len(ranges) || len(subnets) < len(gateways) {
   112  		return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet")
   113  	}
   114  	iData := map[string]*network.IPAMConfig{}
   115  
   116  	// Populate non-overlapping subnets into consolidation map
   117  	for _, s := range subnets {
   118  		for k := range iData {
   119  			ok1, err := subnetMatches(s, k)
   120  			if err != nil {
   121  				return nil, err
   122  			}
   123  			ok2, err := subnetMatches(k, s)
   124  			if err != nil {
   125  				return nil, err
   126  			}
   127  			if ok1 || ok2 {
   128  				return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported")
   129  			}
   130  		}
   131  		iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}}
   132  	}
   133  
   134  	// Validate and add valid ip ranges
   135  	for _, r := range ranges {
   136  		match := false
   137  		for _, s := range subnets {
   138  			ok, err := subnetMatches(s, r)
   139  			if err != nil {
   140  				return nil, err
   141  			}
   142  			if !ok {
   143  				continue
   144  			}
   145  			if iData[s].IPRange != "" {
   146  				return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s)
   147  			}
   148  			d := iData[s]
   149  			d.IPRange = r
   150  			match = true
   151  		}
   152  		if !match {
   153  			return nil, fmt.Errorf("no matching subnet for range %s", r)
   154  		}
   155  	}
   156  
   157  	// Validate and add valid gateways
   158  	for _, g := range gateways {
   159  		match := false
   160  		for _, s := range subnets {
   161  			ok, err := subnetMatches(s, g)
   162  			if err != nil {
   163  				return nil, err
   164  			}
   165  			if !ok {
   166  				continue
   167  			}
   168  			if iData[s].Gateway != "" {
   169  				return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s)
   170  			}
   171  			d := iData[s]
   172  			d.Gateway = g
   173  			match = true
   174  		}
   175  		if !match {
   176  			return nil, fmt.Errorf("no matching subnet for gateway %s", g)
   177  		}
   178  	}
   179  
   180  	// Validate and add aux-addresses
   181  	for key, aa := range auxaddrs {
   182  		match := false
   183  		for _, s := range subnets {
   184  			ok, err := subnetMatches(s, aa)
   185  			if err != nil {
   186  				return nil, err
   187  			}
   188  			if !ok {
   189  				continue
   190  			}
   191  			iData[s].AuxAddress[key] = aa
   192  			match = true
   193  		}
   194  		if !match {
   195  			return nil, fmt.Errorf("no matching subnet for aux-address %s", aa)
   196  		}
   197  	}
   198  
   199  	idl := []network.IPAMConfig{}
   200  	for _, v := range iData {
   201  		idl = append(idl, *v)
   202  	}
   203  	return idl, nil
   204  }
   205  
   206  func subnetMatches(subnet, data string) (bool, error) {
   207  	var (
   208  		ip net.IP
   209  	)
   210  
   211  	_, s, err := net.ParseCIDR(subnet)
   212  	if err != nil {
   213  		return false, fmt.Errorf("Invalid subnet %s : %v", s, err)
   214  	}
   215  
   216  	if strings.Contains(data, "/") {
   217  		ip, _, err = net.ParseCIDR(data)
   218  		if err != nil {
   219  			return false, fmt.Errorf("Invalid cidr %s : %v", data, err)
   220  		}
   221  	} else {
   222  		ip = net.ParseIP(data)
   223  	}
   224  
   225  	return s.Contains(ip), nil
   226  }