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  }