launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/provider/azure/environprovider.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package azure
     5  
     6  import (
     7  	"encoding/xml"
     8  	"fmt"
     9  	"io/ioutil"
    10  
    11  	"github.com/loggo/loggo"
    12  
    13  	"launchpad.net/juju-core/environs"
    14  	"launchpad.net/juju-core/environs/config"
    15  )
    16  
    17  // Register the Azure provider with Juju.
    18  func init() {
    19  	environs.RegisterProvider("azure", azureEnvironProvider{})
    20  }
    21  
    22  // Logger for the Azure provider.
    23  var logger = loggo.GetLogger("juju.provider.azure")
    24  
    25  type azureEnvironProvider struct{}
    26  
    27  // azureEnvironProvider implements EnvironProvider.
    28  var _ environs.EnvironProvider = (*azureEnvironProvider)(nil)
    29  
    30  // Open is specified in the EnvironProvider interface.
    31  func (prov azureEnvironProvider) Open(cfg *config.Config) (environs.Environ, error) {
    32  	logger.Debugf("opening environment %q.", cfg.Name())
    33  	// We can't return NewEnviron(cfg) directly here because otherwise,
    34  	// when err is not nil, we end up with a non-nil returned environ and
    35  	// this breaks the loop in cmd/jujud/upgrade.go:run() (see
    36  	// http://golang.org/doc/faq#nil_error for the gory details).
    37  	environ, err := NewEnviron(cfg)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  	return environ, nil
    42  }
    43  
    44  // Prepare is specified in the EnvironProvider interface.
    45  func (prov azureEnvironProvider) Prepare(cfg *config.Config) (environs.Environ, error) {
    46  	// TODO prepare environment as necessary
    47  	return prov.Open(cfg)
    48  }
    49  
    50  // PublicAddress is specified in the EnvironProvider interface.
    51  func (prov azureEnvironProvider) PublicAddress() (string, error) {
    52  	config, err := parseWALAConfig()
    53  	if err != nil {
    54  		logger.Errorf("error parsing Windows Azure Linux Agent config file (%q): %v", _WALAConfigPath, err)
    55  		return "", err
    56  	}
    57  	return config.getDeploymentFQDN(), nil
    58  }
    59  
    60  // PrivateAddress is specified in the EnvironProvider interface.
    61  func (prov azureEnvironProvider) PrivateAddress() (string, error) {
    62  	config, err := parseWALAConfig()
    63  	if err != nil {
    64  		logger.Errorf("error parsing Windows Azure Linux Agent config file (%q): %v", _WALAConfigPath, err)
    65  		return "", err
    66  	}
    67  	return config.getInternalIP(), nil
    68  }
    69  
    70  // The XML Windows Azure Linux Agent (WALA) is the agent which runs on all
    71  // the Linux Azure VMs.  The hostname of the VM is the service name and the
    72  // juju instanceId is (by design), the deployment's name.
    73  //
    74  // See https://github.com/windows-azure/walinuxagent for more details.
    75  //
    76  // Here is an example content of such a config file:
    77  // <?xml version="1.0" encoding="utf-8"?>
    78  // <SharedConfig version="1.0.0.0" goalStateIncarnation="1">
    79  //   <Deployment name="b6de4c4c7d4a49c39270e0c57481fd9b" guid="{495985a8-8e5a-49aa-826f-d1f7f51045b6}" incarnation="0">
    80  //    <Service name="gwaclmachineex95rsek" guid="{00000000-0000-0000-0000-000000000000}" />
    81  //    <ServiceInstance name="b6de4c4c7d4a49c39270e0c57481fd9b.0" guid="{9806cac7-e566-42b8-9ecb-de8da8f69893}" />
    82  //  [...]
    83  //  <Instances>
    84  //    <Instance id="gwaclroleldc1o5p" address="10.76.200.59">
    85  //      [...]
    86  //    </Instance>
    87  //  </Instances>
    88  //  </Deployment>
    89  // </SharedConfig>
    90  
    91  // Structures used to parse the XML Windows Azure Linux Agent (WALA)
    92  // configuration file.
    93  
    94  type WALASharedConfig struct {
    95  	XMLName    xml.Name       `xml:"SharedConfig"`
    96  	Deployment WALADeployment `xml:"Deployment"`
    97  	Instances  []WALAInstance `xml:"Instances>Instance"`
    98  }
    99  
   100  // getDeploymentName returns the deployment name referenced by the
   101  // configuration.
   102  // Confusingly, this is stored in the 'name' attribute of the 'Service'
   103  // element.
   104  func (config *WALASharedConfig) getDeploymentName() string {
   105  	return config.Deployment.Service.Name
   106  }
   107  
   108  // getDeploymentFQDN returns the FQDN of this deployment.
   109  // The hostname is taken from the 'name' attribute of the Service element
   110  // embedded in the Deployment element.  The domain name is Azure's fixed
   111  // domain name: 'cloudapp.net'.
   112  func (config *WALASharedConfig) getDeploymentFQDN() string {
   113  	return fmt.Sprintf("%s.%s", config.getDeploymentName(), AZURE_DOMAIN_NAME)
   114  }
   115  
   116  // getInternalIP returns the internal IP for this deployment.
   117  // The internalIP is the internal IP of the only instance in this deployment.
   118  func (config *WALASharedConfig) getInternalIP() string {
   119  	return config.Instances[0].Address
   120  }
   121  
   122  type WALADeployment struct {
   123  	Name    string                `xml:"name,attr"`
   124  	Service WALADeploymentService `xml:"Service"`
   125  }
   126  
   127  type WALADeploymentService struct {
   128  	Name string `xml:"name,attr"`
   129  }
   130  
   131  type WALAInstance struct {
   132  	Address string `xml:"address,attr"`
   133  }
   134  
   135  // Path to the WALA configuration file.
   136  var _WALAConfigPath = "/var/lib/waagent/SharedConfig.xml"
   137  
   138  func parseWALAConfig() (*WALASharedConfig, error) {
   139  	data, err := ioutil.ReadFile(_WALAConfigPath)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	config := &WALASharedConfig{}
   144  	err = xml.Unmarshal(data, config)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  	return config, nil
   149  }