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