github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/azure/environprovider.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package azure 5 6 import ( 7 "github.com/Azure/go-autorest/autorest" 8 "github.com/juju/clock" 9 "github.com/juju/errors" 10 "github.com/juju/jsonschema" 11 "github.com/juju/loggo" 12 13 "github.com/juju/juju/environs" 14 "github.com/juju/juju/environs/config" 15 "github.com/juju/juju/environs/context" 16 "github.com/juju/juju/provider/azure/internal/azurestorage" 17 "github.com/juju/juju/provider/azure/internal/errorutils" 18 ) 19 20 const ( 21 // provider version 1 introduces the "common" deployment, 22 // which contains common resources such as the virtual 23 // network and network security group. 24 providerVersion1 = 1 25 26 currentProviderVersion = providerVersion1 27 ) 28 29 // Logger for the Azure provider. 30 var logger = loggo.GetLogger("juju.provider.azure") 31 32 // ProviderConfig contains configuration for the Azure providers. 33 type ProviderConfig struct { 34 // Sender is the autorest.Sender that will be used by Azure 35 // clients. If sender is nil, the default HTTP client sender 36 // will be used. 37 Sender autorest.Sender 38 39 // RequestInspector will be used to inspect Azure requests 40 // if it is non-nil. 41 RequestInspector autorest.PrepareDecorator 42 43 // NewStorageClient will be used to construct new storage 44 // clients. 45 NewStorageClient azurestorage.NewClientFunc 46 47 // RetryClock is used for retrying some operations, like 48 // waiting for deployments to complete. 49 // 50 // Retries due to rate-limiting are handled by the go-autorest 51 // package, which uses "time" directly. We cannot mock the 52 // waiting in that case. 53 RetryClock clock.Clock 54 55 // RandomWindowsAdminPassword is a function used to generate 56 // a random password for the Windows admin user. 57 RandomWindowsAdminPassword func() string 58 59 // GneerateSSHKey is a functio nused to generate a new SSH 60 // key pair for provisioning Linux machines. 61 GenerateSSHKey func(comment string) (private, public string, _ error) 62 63 // ServicePrincipalCreator is the interface used to create service principals. 64 ServicePrincipalCreator ServicePrincipalCreator 65 66 // AzureCLI is the interface the to Azure CLI (az) command. 67 AzureCLI AzureCLI 68 } 69 70 // Validate validates the Azure provider configuration. 71 func (cfg ProviderConfig) Validate() error { 72 if cfg.NewStorageClient == nil { 73 return errors.NotValidf("nil NewStorageClient") 74 } 75 if cfg.RetryClock == nil { 76 return errors.NotValidf("nil RetryClock") 77 } 78 if cfg.RandomWindowsAdminPassword == nil { 79 return errors.NotValidf("nil RandomWindowsAdminPassword") 80 } 81 if cfg.GenerateSSHKey == nil { 82 return errors.NotValidf("nil GenerateSSHKey") 83 } 84 if cfg.ServicePrincipalCreator == nil { 85 return errors.NotValidf("nil ServicePrincipalCreator") 86 } 87 if cfg.AzureCLI == nil { 88 return errors.NotValidf("nil AzureCLI") 89 } 90 return nil 91 } 92 93 type azureEnvironProvider struct { 94 environProviderCredentials 95 96 config ProviderConfig 97 } 98 99 // NewEnvironProvider returns a new EnvironProvider for Azure. 100 func NewEnvironProvider(config ProviderConfig) (*azureEnvironProvider, error) { 101 if err := config.Validate(); err != nil { 102 return nil, errors.Annotate(err, "validating environ provider configuration") 103 } 104 return &azureEnvironProvider{ 105 environProviderCredentials: environProviderCredentials{ 106 servicePrincipalCreator: config.ServicePrincipalCreator, 107 azureCLI: config.AzureCLI, 108 }, 109 config: config, 110 }, nil 111 } 112 113 // Version is part of the EnvironProvider interface. 114 func (prov *azureEnvironProvider) Version() int { 115 return currentProviderVersion 116 } 117 118 // Open is part of the EnvironProvider interface. 119 func (prov *azureEnvironProvider) Open(args environs.OpenParams) (environs.Environ, error) { 120 logger.Debugf("opening model %q", args.Config.Name()) 121 if err := validateCloudSpec(args.Cloud); err != nil { 122 return nil, errors.Annotate(err, "validating cloud spec") 123 } 124 environ, err := newEnviron(prov, args.Cloud, args.Config) 125 if err != nil { 126 return nil, errors.Annotate(err, "opening model") 127 } 128 return environ, nil 129 } 130 131 // CloudSchema returns the schema used to validate input for add-cloud. Since 132 // this provider does not support custom clouds, this always returns nil. 133 func (p azureEnvironProvider) CloudSchema() *jsonschema.Schema { 134 return nil 135 } 136 137 // Ping tests the connection to the cloud, to verify the endpoint is valid. 138 func (p azureEnvironProvider) Ping(ctx context.ProviderCallContext, endpoint string) error { 139 return errors.NotImplementedf("Ping") 140 } 141 142 // PrepareConfig is part of the EnvironProvider interface. 143 func (prov *azureEnvironProvider) PrepareConfig(args environs.PrepareConfigParams) (*config.Config, error) { 144 if err := validateCloudSpec(args.Cloud); err != nil { 145 return nil, errors.Annotate(err, "validating cloud spec") 146 } 147 // Set the default block-storage source. 148 attrs := make(map[string]interface{}) 149 if _, ok := args.Config.StorageDefaultBlockSource(); !ok { 150 attrs[config.StorageDefaultBlockSourceKey] = azureStorageProviderType 151 } 152 if len(attrs) == 0 { 153 return args.Config, nil 154 } 155 return args.Config.Apply(attrs) 156 } 157 158 func validateCloudSpec(spec environs.CloudSpec) error { 159 if err := spec.Validate(); err != nil { 160 return errors.Trace(err) 161 } 162 if spec.Credential == nil { 163 return errors.NotValidf("missing credential") 164 } 165 if authType := spec.Credential.AuthType(); authType != clientCredentialsAuthType { 166 return errors.NotSupportedf("%q auth-type", authType) 167 } 168 return nil 169 } 170 171 // verifyCredentials issues a cheap, non-modifying request to Azure to 172 // verify the configured credentials. If verification fails, a user-friendly 173 // error will be returned, and the original error will be logged at debug 174 // level. 175 var verifyCredentials = func(e *azureEnviron, ctx context.ProviderCallContext) error { 176 // TODO(axw) user-friendly error message 177 return errorutils.HandleCredentialError(e.authorizer.refresh(), ctx) 178 }