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  }