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 }