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