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