github.com/camronlevanger/libcompose@v0.4.1-0.20180423130544-6bb86d53fa21/docker/network/network.go (about) 1 package network 2 3 import ( 4 "fmt" 5 "reflect" 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/client" 13 "github.com/docker/libcompose/config" 14 "github.com/docker/libcompose/yaml" 15 ) 16 17 // Network holds attributes and method for a network definition in compose 18 type Network struct { 19 client client.NetworkAPIClient 20 name string 21 projectName string 22 driver string 23 driverOptions map[string]string 24 ipam config.Ipam 25 external bool 26 } 27 28 func (n *Network) fullName() string { 29 name := n.projectName + "_" + n.name 30 if n.external { 31 name = n.name 32 } 33 return name 34 } 35 36 // Inspect inspect the current network 37 func (n *Network) Inspect(ctx context.Context) (types.NetworkResource, error) { 38 return n.client.NetworkInspect(ctx, n.fullName(), types.NetworkInspectOptions{ 39 Verbose: true, 40 }) 41 } 42 43 // Remove removes the current network (from docker engine) 44 func (n *Network) Remove(ctx context.Context) error { 45 if n.external { 46 fmt.Printf("Network %s is external, skipping", n.fullName()) 47 return nil 48 } 49 fmt.Printf("Removing network %q\n", n.fullName()) 50 return n.client.NetworkRemove(ctx, n.fullName()) 51 } 52 53 // EnsureItExists make sure the network exists and return an error if it does not exists 54 // and cannot be created. 55 func (n *Network) EnsureItExists(ctx context.Context) error { 56 networkResource, err := n.Inspect(ctx) 57 if n.external { 58 if client.IsErrNotFound(err) { 59 // FIXME(vdemeester) introduce some libcompose error type 60 return fmt.Errorf("Network %s declared as external, but could not be found. Please create the network manually using docker network create %s and try again", n.fullName(), n.fullName()) 61 } 62 return err 63 } 64 if err != nil && client.IsErrNotFound(err) { 65 return n.create(ctx) 66 } 67 if n.driver != "" && networkResource.Driver != n.driver { 68 return fmt.Errorf("Network %q needs to be recreated - driver has changed", n.fullName()) 69 } 70 if len(n.driverOptions) != 0 && !reflect.DeepEqual(networkResource.Options, n.driverOptions) { 71 return fmt.Errorf("Network %q needs to be recreated - options have changed", n.fullName()) 72 } 73 return err 74 } 75 76 func (n *Network) create(ctx context.Context) error { 77 fmt.Printf("Creating network %q with driver %q\n", n.fullName(), n.driver) 78 _, err := n.client.NetworkCreate(ctx, n.fullName(), types.NetworkCreate{ 79 Driver: n.driver, 80 Options: n.driverOptions, 81 IPAM: convertToAPIIpam(n.ipam), 82 }) 83 return err 84 } 85 86 func convertToAPIIpam(ipam config.Ipam) *network.IPAM { 87 ipamConfigs := []network.IPAMConfig{} 88 for _, config := range ipam.Config { 89 ipamConfigs = append(ipamConfigs, network.IPAMConfig{ 90 Subnet: config.Subnet, 91 IPRange: config.IPRange, 92 Gateway: config.Gateway, 93 AuxAddress: config.AuxAddress, 94 }) 95 } 96 return &network.IPAM{ 97 Driver: ipam.Driver, 98 Config: ipamConfigs, 99 } 100 } 101 102 // NewNetwork creates a new network from the specified name and config. 103 func NewNetwork(projectName, name string, config *config.NetworkConfig, client client.NetworkAPIClient) *Network { 104 networkName := name 105 if config.External.External { 106 networkName = config.External.Name 107 } 108 return &Network{ 109 client: client, 110 name: networkName, 111 projectName: projectName, 112 driver: config.Driver, 113 driverOptions: config.DriverOpts, 114 external: config.External.External, 115 ipam: config.Ipam, 116 } 117 } 118 119 // Networks holds a list of network 120 type Networks struct { 121 networks []*Network 122 networkEnabled bool 123 } 124 125 // Initialize make sure network exists if network is enabled 126 func (n *Networks) Initialize(ctx context.Context) error { 127 if !n.networkEnabled { 128 return nil 129 } 130 for _, network := range n.networks { 131 err := network.EnsureItExists(ctx) 132 if err != nil { 133 return err 134 } 135 } 136 return nil 137 } 138 139 // Remove removes networks (clean-up) 140 func (n *Networks) Remove(ctx context.Context) error { 141 if !n.networkEnabled { 142 return nil 143 } 144 for _, network := range n.networks { 145 err := network.Remove(ctx) 146 if err != nil { 147 return err 148 } 149 } 150 return nil 151 } 152 153 // NetworksFromServices creates a new Networks struct based on networks configurations and 154 // services configuration. If a network is defined but not used by any service, it will return 155 // an error along the Networks. 156 func NetworksFromServices(cli client.NetworkAPIClient, projectName string, networkConfigs map[string]*config.NetworkConfig, services *config.ServiceConfigs, networkEnabled bool) (*Networks, error) { 157 var err error 158 networks := make([]*Network, 0, len(networkConfigs)) 159 networkNames := map[string]*yaml.Network{} 160 for _, serviceName := range services.Keys() { 161 serviceConfig, _ := services.Get(serviceName) 162 if serviceConfig.NetworkMode != "" || serviceConfig.Networks == nil || len(serviceConfig.Networks.Networks) == 0 { 163 continue 164 } 165 for _, network := range serviceConfig.Networks.Networks { 166 if network.Name != "default" { 167 if _, ok := networkConfigs[network.Name]; !ok { 168 return nil, fmt.Errorf(`Service "%s" uses an undefined network "%s"`, serviceName, network.Name) 169 } 170 } 171 networkNames[network.Name] = network 172 } 173 } 174 if len(networkConfigs) == 0 { 175 network := NewNetwork(projectName, "default", &config.NetworkConfig{ 176 Driver: "bridge", 177 }, cli) 178 networks = append(networks, network) 179 } 180 for name, config := range networkConfigs { 181 network := NewNetwork(projectName, name, config, cli) 182 networks = append(networks, network) 183 } 184 if len(networkNames) != len(networks) { 185 unused := []string{} 186 for name := range networkConfigs { 187 if name == "default" { 188 continue 189 } 190 if _, ok := networkNames[name]; !ok { 191 unused = append(unused, name) 192 } 193 } 194 if len(unused) != 0 { 195 err = fmt.Errorf("Some networks were defined but are not used by any service: %v", strings.Join(unused, " ")) 196 } 197 } 198 return &Networks{ 199 networks: networks, 200 networkEnabled: networkEnabled, 201 }, err 202 }