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 }