github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/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(opts.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  	flags.SetAnnotation("attachable", "version", []string{"1.25"})
    62  
    63  	flags.StringVar(&opts.ipamDriver, "ipam-driver", "default", "IP Address Management Driver")
    64  	flags.StringSliceVar(&opts.ipamSubnet, "subnet", []string{}, "Subnet in CIDR format that represents a network segment")
    65  	flags.StringSliceVar(&opts.ipamIPRange, "ip-range", []string{}, "Allocate container ip from a sub-range")
    66  	flags.StringSliceVar(&opts.ipamGateway, "gateway", []string{}, "IPv4 or IPv6 Gateway for the master subnet")
    67  
    68  	flags.Var(&opts.ipamAux, "aux-address", "Auxiliary IPv4 or IPv6 addresses used by Network driver")
    69  	flags.Var(&opts.ipamOpt, "ipam-opt", "Set IPAM driver specific options")
    70  
    71  	return cmd
    72  }
    73  
    74  func runCreate(dockerCli *command.DockerCli, opts createOptions) error {
    75  	client := dockerCli.Client()
    76  
    77  	ipamCfg, err := consolidateIpam(opts.ipamSubnet, opts.ipamIPRange, opts.ipamGateway, opts.ipamAux.GetAll())
    78  	if err != nil {
    79  		return err
    80  	}
    81  
    82  	// Construct network create request body
    83  	nc := types.NetworkCreate{
    84  		Driver:  opts.driver,
    85  		Options: opts.driverOpts.GetAll(),
    86  		IPAM: &network.IPAM{
    87  			Driver:  opts.ipamDriver,
    88  			Config:  ipamCfg,
    89  			Options: opts.ipamOpt.GetAll(),
    90  		},
    91  		CheckDuplicate: true,
    92  		Internal:       opts.internal,
    93  		EnableIPv6:     opts.ipv6,
    94  		Attachable:     opts.attachable,
    95  		Labels:         runconfigopts.ConvertKVStringsToMap(opts.labels.GetAll()),
    96  	}
    97  
    98  	resp, err := client.NetworkCreate(context.Background(), opts.name, nc)
    99  	if err != nil {
   100  		return err
   101  	}
   102  	fmt.Fprintf(dockerCli.Out(), "%s\n", resp.ID)
   103  	return nil
   104  }
   105  
   106  // Consolidates the ipam configuration as a group from different related configurations
   107  // user can configure network with multiple non-overlapping subnets and hence it is
   108  // possible to correlate the various related parameters and consolidate them.
   109  // consolidateIpam consolidates subnets, ip-ranges, gateways and auxiliary addresses into
   110  // structured ipam data.
   111  func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) {
   112  	if len(subnets) < len(ranges) || len(subnets) < len(gateways) {
   113  		return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet")
   114  	}
   115  	iData := map[string]*network.IPAMConfig{}
   116  
   117  	// Populate non-overlapping subnets into consolidation map
   118  	for _, s := range subnets {
   119  		for k := range iData {
   120  			ok1, err := subnetMatches(s, k)
   121  			if err != nil {
   122  				return nil, err
   123  			}
   124  			ok2, err := subnetMatches(k, s)
   125  			if err != nil {
   126  				return nil, err
   127  			}
   128  			if ok1 || ok2 {
   129  				return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported")
   130  			}
   131  		}
   132  		iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}}
   133  	}
   134  
   135  	// Validate and add valid ip ranges
   136  	for _, r := range ranges {
   137  		match := false
   138  		for _, s := range subnets {
   139  			ok, err := subnetMatches(s, r)
   140  			if err != nil {
   141  				return nil, err
   142  			}
   143  			if !ok {
   144  				continue
   145  			}
   146  			if iData[s].IPRange != "" {
   147  				return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s)
   148  			}
   149  			d := iData[s]
   150  			d.IPRange = r
   151  			match = true
   152  		}
   153  		if !match {
   154  			return nil, fmt.Errorf("no matching subnet for range %s", r)
   155  		}
   156  	}
   157  
   158  	// Validate and add valid gateways
   159  	for _, g := range gateways {
   160  		match := false
   161  		for _, s := range subnets {
   162  			ok, err := subnetMatches(s, g)
   163  			if err != nil {
   164  				return nil, err
   165  			}
   166  			if !ok {
   167  				continue
   168  			}
   169  			if iData[s].Gateway != "" {
   170  				return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s)
   171  			}
   172  			d := iData[s]
   173  			d.Gateway = g
   174  			match = true
   175  		}
   176  		if !match {
   177  			return nil, fmt.Errorf("no matching subnet for gateway %s", g)
   178  		}
   179  	}
   180  
   181  	// Validate and add aux-addresses
   182  	for key, aa := range auxaddrs {
   183  		match := false
   184  		for _, s := range subnets {
   185  			ok, err := subnetMatches(s, aa)
   186  			if err != nil {
   187  				return nil, err
   188  			}
   189  			if !ok {
   190  				continue
   191  			}
   192  			iData[s].AuxAddress[key] = aa
   193  			match = true
   194  		}
   195  		if !match {
   196  			return nil, fmt.Errorf("no matching subnet for aux-address %s", aa)
   197  		}
   198  	}
   199  
   200  	idl := []network.IPAMConfig{}
   201  	for _, v := range iData {
   202  		idl = append(idl, *v)
   203  	}
   204  	return idl, nil
   205  }
   206  
   207  func subnetMatches(subnet, data string) (bool, error) {
   208  	var (
   209  		ip net.IP
   210  	)
   211  
   212  	_, s, err := net.ParseCIDR(subnet)
   213  	if err != nil {
   214  		return false, fmt.Errorf("Invalid subnet %s : %v", s, err)
   215  	}
   216  
   217  	if strings.Contains(data, "/") {
   218  		ip, _, err = net.ParseCIDR(data)
   219  		if err != nil {
   220  			return false, fmt.Errorf("Invalid cidr %s : %v", data, err)
   221  		}
   222  	} else {
   223  		ip = net.ParseIP(data)
   224  	}
   225  
   226  	return s.Contains(ip), nil
   227  }