github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/credentialvalidator/backend.go (about)

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package credentialvalidator
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"gopkg.in/juju/names.v2"
     9  
    10  	jujucloud "github.com/juju/juju/cloud"
    11  	"github.com/juju/juju/state"
    12  )
    13  
    14  // Backend defines behavior that credential validator needs.
    15  type Backend interface {
    16  	// ModelUsesCredential determines if the model uses given cloud credential.
    17  	ModelUsesCredential(tag names.CloudCredentialTag) (bool, error)
    18  
    19  	// ModelCredential retrieves the cloud credential that a model uses.
    20  	ModelCredential() (*ModelCredential, error)
    21  
    22  	// WatchCredential returns a watcher that is keeping an eye on all changes to
    23  	// a given cloud credential.
    24  	WatchCredential(names.CloudCredentialTag) state.NotifyWatcher
    25  
    26  	// InvalidateModelCredential marks the cloud credential that a current model
    27  	// uses as invalid.
    28  	InvalidateModelCredential(reason string) error
    29  
    30  	// WatchModelCredential returns a watcher that is keeping an eye on what cloud credential a model uses.
    31  	WatchModelCredential() (state.NotifyWatcher, error)
    32  }
    33  
    34  func NewBackend(st StateAccessor) Backend {
    35  	return &backend{st}
    36  }
    37  
    38  type backend struct {
    39  	StateAccessor
    40  }
    41  
    42  // ModelUsesCredential implements Backend.ModelUsesCredential.
    43  func (b *backend) ModelUsesCredential(tag names.CloudCredentialTag) (bool, error) {
    44  	m, err := b.Model()
    45  	if err != nil {
    46  		return false, errors.Trace(err)
    47  	}
    48  	modelCredentialTag, exists := m.CloudCredential()
    49  	return exists && tag == modelCredentialTag, nil
    50  }
    51  
    52  // ModelCredential implements Backend.ModelCredential.
    53  func (b *backend) ModelCredential() (*ModelCredential, error) {
    54  	m, err := b.Model()
    55  	if err != nil {
    56  		return nil, errors.Trace(err)
    57  	}
    58  
    59  	modelCredentialTag, exists := m.CloudCredential()
    60  	result := &ModelCredential{Model: m.ModelTag(), Exists: exists}
    61  	if !exists {
    62  		// A model credential is not set, we must check if the model
    63  		// is on the cloud that requires a credential.
    64  		supportsEmptyAuth, err := b.cloudSupportsNoAuth(m.Cloud())
    65  		if err != nil {
    66  			return nil, errors.Trace(err)
    67  		}
    68  		result.Valid = supportsEmptyAuth
    69  		if !supportsEmptyAuth {
    70  			// TODO (anastasiamac 2018-11-12) Figure out how to notify the users here - maybe set a model status?...
    71  			logger.Warningf("model credential is not set for the model but the cloud requires it")
    72  		}
    73  		return result, nil
    74  	}
    75  
    76  	result.Credential = modelCredentialTag
    77  	credential, err := b.CloudCredential(modelCredentialTag)
    78  	if err != nil {
    79  		if !errors.IsNotFound(err) {
    80  			return nil, errors.Trace(err)
    81  		}
    82  		// In this situation, a model refers to a credential that does not exist in credentials collection.
    83  		// TODO (anastasiamac 2018-11-12) Figure out how to notify the users here - maybe set a model status?...
    84  		logger.Warningf("cloud credential reference is set for the model but the credential content is no longer on the controller")
    85  		result.Valid = false
    86  		return result, nil
    87  	}
    88  	result.Valid = credential.IsValid()
    89  	return result, nil
    90  }
    91  
    92  // WatchModelCredential implements Backend.WatchModelCredential.
    93  func (b *backend) WatchModelCredential() (state.NotifyWatcher, error) {
    94  	m, err := b.Model()
    95  	if err != nil {
    96  		return nil, errors.Trace(err)
    97  	}
    98  	return m.WatchModelCredential(), nil
    99  }
   100  
   101  func (b *backend) cloudSupportsNoAuth(cloudName string) (bool, error) {
   102  	cloud, err := b.Cloud(cloudName)
   103  	if err != nil {
   104  		return false, errors.Trace(err)
   105  	}
   106  	for _, authType := range cloud.AuthTypes {
   107  		if authType == jujucloud.EmptyAuthType {
   108  			return true, nil
   109  		}
   110  	}
   111  	return false, nil
   112  }
   113  
   114  // ModelCredential stores model's cloud credential information.
   115  type ModelCredential struct {
   116  	// Model is a model tag.
   117  	Model names.ModelTag
   118  
   119  	// Exists indicates whether the model has  a cloud credential.
   120  	// On some clouds, that only require "empty" auth, cloud credential
   121  	// is not needed for the models to function properly.
   122  	Exists bool
   123  
   124  	// Credential is a cloud credential tag.
   125  	Credential names.CloudCredentialTag
   126  
   127  	// Valid indicates that this model's cloud authentication is valid.
   128  	//
   129  	// If this model has a cloud credential setup,
   130  	// then this property indicates that this credential itself is valid.
   131  	//
   132  	// If this model has no cloud credential, then this property indicates
   133  	// whether or not it is valid for this model to have no credential.
   134  	// There are some clouds that do not require auth and, hence,
   135  	// models on these clouds do not require credentials.
   136  	//
   137  	// If a model is on the cloud that does require credential and
   138  	// the model's credential is not set, this property will be set to 'false'.
   139  	Valid bool
   140  }