github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/address.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"net"
     8  	"reflect"
     9  	"strconv"
    10  
    11  	"github.com/juju/errors"
    12  	statetxn "github.com/juju/txn"
    13  	"gopkg.in/mgo.v2/bson"
    14  	"gopkg.in/mgo.v2/txn"
    15  
    16  	"github.com/juju/juju/network"
    17  )
    18  
    19  // controllerAddresses returns the list of internal addresses of the state
    20  // server machines.
    21  func (st *State) controllerAddresses() ([]string, error) {
    22  	ssState := st
    23  	model, err := st.ControllerModel()
    24  	if err != nil {
    25  		return nil, errors.Trace(err)
    26  	}
    27  	if st.ModelTag() != model.ModelTag() {
    28  		// We are not using the controller model, so get one.
    29  		logger.Debugf("getting a controller state connection, current env: %s", st.ModelTag())
    30  		ssState, err = st.ForModel(model.ModelTag())
    31  		if err != nil {
    32  			return nil, errors.Trace(err)
    33  		}
    34  		defer ssState.Close()
    35  		logger.Debugf("ssState env: %s", ssState.ModelTag())
    36  	}
    37  
    38  	type addressMachine struct {
    39  		Addresses []address
    40  	}
    41  	var allAddresses []addressMachine
    42  	// TODO(rog) 2013/10/14 index machines on jobs.
    43  	machines, closer := ssState.getCollection(machinesC)
    44  	defer closer()
    45  	err = machines.Find(bson.D{{"jobs", JobManageModel}}).All(&allAddresses)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	if len(allAddresses) == 0 {
    50  		return nil, errors.New("no controller machines found")
    51  	}
    52  	apiAddrs := make([]string, 0, len(allAddresses))
    53  	for _, addrs := range allAddresses {
    54  		naddrs := networkAddresses(addrs.Addresses)
    55  		addr, ok := network.SelectControllerAddress(naddrs, false)
    56  		if ok {
    57  			apiAddrs = append(apiAddrs, addr.Value)
    58  		}
    59  	}
    60  	if len(apiAddrs) == 0 {
    61  		return nil, errors.New("no controller machines with addresses found")
    62  	}
    63  	return apiAddrs, nil
    64  }
    65  
    66  func appendPort(addrs []string, port int) []string {
    67  	newAddrs := make([]string, len(addrs))
    68  	for i, addr := range addrs {
    69  		newAddrs[i] = net.JoinHostPort(addr, strconv.Itoa(port))
    70  	}
    71  	return newAddrs
    72  }
    73  
    74  // Addresses returns the list of cloud-internal addresses that
    75  // can be used to connect to the state.
    76  func (st *State) Addresses() ([]string, error) {
    77  	addrs, err := st.controllerAddresses()
    78  	if err != nil {
    79  		return nil, errors.Trace(err)
    80  	}
    81  	config, err := st.ControllerConfig()
    82  	if err != nil {
    83  		return nil, errors.Trace(err)
    84  	}
    85  	return appendPort(addrs, config.StatePort()), nil
    86  }
    87  
    88  // APIAddressesFromMachines returns the list of cloud-internal addresses that
    89  // can be used to connect to the state API server.
    90  // This method will be deprecated when API addresses are
    91  // stored independently in their own document.
    92  func (st *State) APIAddressesFromMachines() ([]string, error) {
    93  	addrs, err := st.controllerAddresses()
    94  	if err != nil {
    95  		return nil, errors.Trace(err)
    96  	}
    97  	config, err := st.ControllerConfig()
    98  	if err != nil {
    99  		return nil, errors.Trace(err)
   100  	}
   101  	return appendPort(addrs, config.APIPort()), nil
   102  }
   103  
   104  const apiHostPortsKey = "apiHostPorts"
   105  
   106  type apiHostPortsDoc struct {
   107  	APIHostPorts [][]hostPort `bson:"apihostports"`
   108  	TxnRevno     int64        `bson:"txn-revno"`
   109  }
   110  
   111  // SetAPIHostPorts sets the addresses of the API server instances.
   112  // Each server is represented by one element in the top level slice.
   113  func (st *State) SetAPIHostPorts(netHostsPorts [][]network.HostPort) error {
   114  	controllers, closer := st.getCollection(controllersC)
   115  	defer closer()
   116  	doc := apiHostPortsDoc{
   117  		APIHostPorts: fromNetworkHostsPorts(netHostsPorts),
   118  	}
   119  	buildTxn := func(attempt int) ([]txn.Op, error) {
   120  		var existingDoc apiHostPortsDoc
   121  		err := controllers.Find(bson.D{{"_id", apiHostPortsKey}}).One(&existingDoc)
   122  		if err != nil {
   123  			return nil, err
   124  		}
   125  		op := txn.Op{
   126  			C:  controllersC,
   127  			Id: apiHostPortsKey,
   128  			Assert: bson.D{{
   129  				"txn-revno", existingDoc.TxnRevno,
   130  			}},
   131  		}
   132  		hostPorts := networkHostsPorts(existingDoc.APIHostPorts)
   133  		if !hostsPortsEqual(netHostsPorts, hostPorts) {
   134  			op.Update = bson.D{{
   135  				"$set", bson.D{{"apihostports", doc.APIHostPorts}},
   136  			}}
   137  		} else {
   138  			return nil, statetxn.ErrNoOperations
   139  		}
   140  		return []txn.Op{op}, nil
   141  	}
   142  	if err := st.run(buildTxn); err != nil {
   143  		return errors.Annotate(err, "cannot set API addresses")
   144  	}
   145  	logger.Debugf("setting API hostPorts: %v", netHostsPorts)
   146  	return nil
   147  }
   148  
   149  // APIHostPorts returns the API addresses as set by SetAPIHostPorts.
   150  func (st *State) APIHostPorts() ([][]network.HostPort, error) {
   151  	var doc apiHostPortsDoc
   152  	controllers, closer := st.getCollection(controllersC)
   153  	defer closer()
   154  	err := controllers.Find(bson.D{{"_id", apiHostPortsKey}}).One(&doc)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	return networkHostsPorts(doc.APIHostPorts), nil
   159  }
   160  
   161  // address represents the location of a machine, including metadata
   162  // about what kind of location the address describes.
   163  //
   164  // TODO(dimitern) Make sure we integrate this with other networking
   165  // stuff at some point. We want to use juju-specific network names
   166  // that point to existing documents in the networks collection.
   167  type address struct {
   168  	Value       string `bson:"value"`
   169  	AddressType string `bson:"addresstype"`
   170  	Scope       string `bson:"networkscope,omitempty"`
   171  	Origin      string `bson:"origin,omitempty"`
   172  	SpaceName   string `bson:"spacename,omitempty"`
   173  }
   174  
   175  // Origin specifies where an address comes from, whether it was reported by a
   176  // provider or by a machine.
   177  type Origin string
   178  
   179  const (
   180  	// Address origin unknown.
   181  	OriginUnknown Origin = ""
   182  	// Address comes from a provider.
   183  	OriginProvider Origin = "provider"
   184  	// Address comes from a machine.
   185  	OriginMachine Origin = "machine"
   186  )
   187  
   188  // fromNetworkAddress is a convenience helper to create a state type
   189  // out of the network type, here for Address with a given Origin.
   190  func fromNetworkAddress(netAddr network.Address, origin Origin) address {
   191  	return address{
   192  		Value:       netAddr.Value,
   193  		AddressType: string(netAddr.Type),
   194  		Scope:       string(netAddr.Scope),
   195  		Origin:      string(origin),
   196  		SpaceName:   string(netAddr.SpaceName),
   197  	}
   198  }
   199  
   200  // networkAddress is a convenience helper to return the state type
   201  // as network type, here for Address.
   202  func (addr *address) networkAddress() network.Address {
   203  	return network.Address{
   204  		Value:     addr.Value,
   205  		Type:      network.AddressType(addr.AddressType),
   206  		Scope:     network.Scope(addr.Scope),
   207  		SpaceName: network.SpaceName(addr.SpaceName),
   208  	}
   209  }
   210  
   211  // fromNetworkAddresses is a convenience helper to create a state type
   212  // out of the network type, here for a slice of Address with a given origin.
   213  func fromNetworkAddresses(netAddrs []network.Address, origin Origin) []address {
   214  	addrs := make([]address, len(netAddrs))
   215  	for i, netAddr := range netAddrs {
   216  		addrs[i] = fromNetworkAddress(netAddr, origin)
   217  	}
   218  	return addrs
   219  }
   220  
   221  // networkAddresses is a convenience helper to return the state type
   222  // as network type, here for a slice of Address.
   223  func networkAddresses(addrs []address) []network.Address {
   224  	netAddrs := make([]network.Address, len(addrs))
   225  	for i, addr := range addrs {
   226  		netAddrs[i] = addr.networkAddress()
   227  	}
   228  	return netAddrs
   229  }
   230  
   231  // hostPort associates an address with a port. See also network.HostPort,
   232  // from/to which this is transformed.
   233  //
   234  // TODO(dimitern) Make sure we integrate this with other networking
   235  // stuff at some point. We want to use juju-specific network names
   236  // that point to existing documents in the networks collection.
   237  type hostPort struct {
   238  	Value       string `bson:"value"`
   239  	AddressType string `bson:"addresstype"`
   240  	Scope       string `bson:"networkscope,omitempty"`
   241  	Port        int    `bson:"port"`
   242  	SpaceName   string `bson:"spacename,omitempty"`
   243  }
   244  
   245  // fromNetworkHostPort is a convenience helper to create a state type
   246  // out of the network type, here for HostPort.
   247  func fromNetworkHostPort(netHostPort network.HostPort) hostPort {
   248  	return hostPort{
   249  		Value:       netHostPort.Value,
   250  		AddressType: string(netHostPort.Type),
   251  		Scope:       string(netHostPort.Scope),
   252  		Port:        netHostPort.Port,
   253  		SpaceName:   string(netHostPort.SpaceName),
   254  	}
   255  }
   256  
   257  // networkHostPort is a convenience helper to return the state type
   258  // as network type, here for HostPort.
   259  func (hp *hostPort) networkHostPort() network.HostPort {
   260  	return network.HostPort{
   261  		Address: network.Address{
   262  			Value:     hp.Value,
   263  			Type:      network.AddressType(hp.AddressType),
   264  			Scope:     network.Scope(hp.Scope),
   265  			SpaceName: network.SpaceName(hp.SpaceName),
   266  		},
   267  		Port: hp.Port,
   268  	}
   269  }
   270  
   271  // fromNetworkHostsPorts is a helper to create a state type
   272  // out of the network type, here for a nested slice of HostPort.
   273  func fromNetworkHostsPorts(netHostsPorts [][]network.HostPort) [][]hostPort {
   274  	hsps := make([][]hostPort, len(netHostsPorts))
   275  	for i, netHostPorts := range netHostsPorts {
   276  		hsps[i] = make([]hostPort, len(netHostPorts))
   277  		for j, netHostPort := range netHostPorts {
   278  			hsps[i][j] = fromNetworkHostPort(netHostPort)
   279  		}
   280  	}
   281  	return hsps
   282  }
   283  
   284  // networkHostsPorts is a convenience helper to return the state type
   285  // as network type, here for a nested slice of HostPort.
   286  func networkHostsPorts(hsps [][]hostPort) [][]network.HostPort {
   287  	netHostsPorts := make([][]network.HostPort, len(hsps))
   288  	for i, hps := range hsps {
   289  		netHostsPorts[i] = make([]network.HostPort, len(hps))
   290  		for j, hp := range hps {
   291  			netHostsPorts[i][j] = hp.networkHostPort()
   292  		}
   293  	}
   294  	return netHostsPorts
   295  }
   296  
   297  // addressEqual checks that two slices of network addresses are equal.
   298  func addressesEqual(a, b []network.Address) bool {
   299  	return reflect.DeepEqual(a, b)
   300  }
   301  
   302  // hostsPortsEqual checks that two arrays of network hostports are equal.
   303  func hostsPortsEqual(a, b [][]network.HostPort) bool {
   304  	return reflect.DeepEqual(a, b)
   305  }