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  }