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  }