github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/modelgeneration/modelgeneration.go (about)

     1  // Copyright 2019 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package modelgeneration
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/loggo"
     9  	"gopkg.in/juju/names.v2"
    10  
    11  	"github.com/juju/juju/apiserver/common"
    12  	"github.com/juju/juju/apiserver/facade"
    13  	"github.com/juju/juju/apiserver/params"
    14  	"github.com/juju/juju/permission"
    15  )
    16  
    17  var logger = loggo.GetLogger("juju.apiserver.modelgeneration")
    18  
    19  // ModelGenerationAPI implements the ModelGeneration interface and is the concrete implementation
    20  // of the API endpoint.
    21  type ModelGenerationAPI struct {
    22  	check             *common.BlockChecker
    23  	authorizer        facade.Authorizer
    24  	apiUser           names.UserTag
    25  	isControllerAdmin bool
    26  	model             GenerationModel
    27  }
    28  
    29  // NewModelGenerationFacade provides the signature required for facade registration.
    30  func NewModelGenerationFacade(ctx facade.Context) (*ModelGenerationAPI, error) {
    31  	authorizer := ctx.Auth()
    32  	st := &modelGenerationStateShim{State: ctx.State()}
    33  	model, err := st.Model()
    34  	if err != nil {
    35  		return nil, errors.Trace(err)
    36  	}
    37  	return NewModelGenerationAPI(st, authorizer, model)
    38  }
    39  
    40  // NewModelGenerationAPI creates a new API endpoint for dealing with model generations.
    41  func NewModelGenerationAPI(
    42  	st ModelGenerationState,
    43  	authorizer facade.Authorizer,
    44  	m GenerationModel,
    45  ) (*ModelGenerationAPI, error) {
    46  	if !authorizer.AuthClient() {
    47  		return nil, common.ErrPerm
    48  	}
    49  	// Since we know this is a user tag (because AuthClient is true),
    50  	// we just do the type assertion to the UserTag.
    51  	apiUser, _ := authorizer.GetAuthTag().(names.UserTag)
    52  	// Pretty much all of the user manager methods have special casing for admin
    53  	// users, so look once when we start and remember if the user is an admin.
    54  	isAdmin, err := authorizer.HasPermission(permission.SuperuserAccess, st.ControllerTag())
    55  	if err != nil {
    56  		return nil, errors.Trace(err)
    57  	}
    58  
    59  	return &ModelGenerationAPI{
    60  		authorizer:        authorizer,
    61  		isControllerAdmin: isAdmin,
    62  		apiUser:           apiUser,
    63  		model:             m,
    64  	}, nil
    65  }
    66  
    67  func (m *ModelGenerationAPI) hasAdminAccess(modelTag names.ModelTag) (bool, error) {
    68  	canWrite, err := m.authorizer.HasPermission(permission.AdminAccess, modelTag)
    69  	if errors.IsNotFound(err) {
    70  		return false, nil
    71  	}
    72  	return canWrite, err
    73  }
    74  
    75  // AddGeneration adds a 'next' generation to the given model.
    76  func (m *ModelGenerationAPI) AddGeneration(arg params.Entity) (params.ErrorResult, error) {
    77  	result := params.ErrorResult{}
    78  	modelTag, err := names.ParseModelTag(arg.Tag)
    79  	if err != nil {
    80  		return result, errors.Trace(err)
    81  	}
    82  	isModelAdmin, err := m.hasAdminAccess(modelTag)
    83  	if !isModelAdmin && !m.isControllerAdmin {
    84  		return result, common.ErrPerm
    85  	}
    86  
    87  	result.Error = common.ServerError(m.model.AddGeneration())
    88  	return result, nil
    89  }
    90  
    91  // HasNextGeneration returns a true result if the input model has a "next"
    92  // generation that has not yet been completed.
    93  func (m *ModelGenerationAPI) HasNextGeneration(arg params.Entity) (params.BoolResult, error) {
    94  	result := params.BoolResult{}
    95  	modelTag, err := names.ParseModelTag(arg.Tag)
    96  	if err != nil {
    97  		return result, errors.Trace(err)
    98  	}
    99  	isModelAdmin, err := m.hasAdminAccess(modelTag)
   100  	if !isModelAdmin && !m.isControllerAdmin {
   101  		return result, common.ErrPerm
   102  	}
   103  
   104  	if has, err := m.model.HasNextGeneration(); err != nil {
   105  		result.Error = common.ServerError(err)
   106  	} else {
   107  		result.Result = has
   108  	}
   109  	return result, nil
   110  }
   111  
   112  // AdvanceGeneration, adds the provided unit(s) and/or application(s) to
   113  // the "next" generation.  If the generation can auto complete, it is
   114  // made the "current" generation.
   115  func (m *ModelGenerationAPI) AdvanceGeneration(arg params.AdvanceGenerationArg) (params.AdvanceGenerationResult, error) {
   116  	modelTag, err := names.ParseModelTag(arg.Model.Tag)
   117  	if err != nil {
   118  		return params.AdvanceGenerationResult{}, errors.Trace(err)
   119  	}
   120  	isModelAdmin, err := m.hasAdminAccess(modelTag)
   121  	if !isModelAdmin && !m.isControllerAdmin {
   122  		return params.AdvanceGenerationResult{}, common.ErrPerm
   123  	}
   124  
   125  	generation, err := m.model.NextGeneration()
   126  	if err != nil {
   127  		return params.AdvanceGenerationResult{}, errors.Trace(err)
   128  	}
   129  
   130  	results := params.ErrorResults{
   131  		Results: make([]params.ErrorResult, len(arg.Entities)),
   132  	}
   133  	for i, entity := range arg.Entities {
   134  		tag, err := names.ParseTag(entity.Tag)
   135  		if err != nil {
   136  			results.Results[i].Error = common.ServerError(err)
   137  			continue
   138  		}
   139  		switch tag.Kind() {
   140  		case names.ApplicationTagKind:
   141  			results.Results[i].Error = common.ServerError(generation.AssignAllUnits(tag.Id()))
   142  		case names.UnitTagKind:
   143  			results.Results[i].Error = common.ServerError(generation.AssignUnit(tag.Id()))
   144  		default:
   145  			results.Results[i].Error = common.ServerError(
   146  				errors.Errorf("expected names.UnitTag or names.ApplicationTag, got %T", tag))
   147  		}
   148  		if err := generation.Refresh(); err != nil {
   149  			return params.AdvanceGenerationResult{AdvanceResults: results}, errors.Trace(err)
   150  		}
   151  	}
   152  	result := params.AdvanceGenerationResult{AdvanceResults: results}
   153  
   154  	// Complete the generation if possible.
   155  	completed, err := generation.AutoComplete()
   156  	result.CompleteResult = params.BoolResult{
   157  		Result: completed,
   158  		Error:  common.ServerError(err),
   159  	}
   160  	return result, nil
   161  }
   162  
   163  // CancelGeneration cancels the 'next' generation if cancel
   164  // criteria are met.
   165  func (m *ModelGenerationAPI) CancelGeneration(arg params.Entity) (params.ErrorResult, error) {
   166  	result := params.ErrorResult{}
   167  	modelTag, err := names.ParseModelTag(arg.Tag)
   168  	if err != nil {
   169  		return result, errors.Trace(err)
   170  	}
   171  	isModelAdmin, err := m.hasAdminAccess(modelTag)
   172  	if !isModelAdmin && !m.isControllerAdmin {
   173  		return result, common.ErrPerm
   174  	}
   175  
   176  	generation, err := m.model.NextGeneration()
   177  	if err != nil {
   178  		return result, errors.Trace(err)
   179  	}
   180  	result.Error = common.ServerError(generation.MakeCurrent())
   181  	return result, nil
   182  }