github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/uniter/storage.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package uniter
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"gopkg.in/juju/names.v2"
     9  
    10  	"github.com/juju/juju/apiserver/common"
    11  	"github.com/juju/juju/apiserver/common/storagecommon"
    12  	"github.com/juju/juju/apiserver/facade"
    13  	"github.com/juju/juju/apiserver/params"
    14  	"github.com/juju/juju/state"
    15  	"github.com/juju/juju/state/watcher"
    16  )
    17  
    18  // StorageAPI provides access to the Storage API facade.
    19  type StorageAPI struct {
    20  	st         storageStateInterface
    21  	resources  facade.Resources
    22  	accessUnit common.GetAuthFunc
    23  }
    24  
    25  // newStorageAPI creates a new server-side Storage API facade.
    26  func newStorageAPI(
    27  	st storageStateInterface,
    28  	resources facade.Resources,
    29  	accessUnit common.GetAuthFunc,
    30  ) (*StorageAPI, error) {
    31  
    32  	return &StorageAPI{
    33  		st:         st,
    34  		resources:  resources,
    35  		accessUnit: accessUnit,
    36  	}, nil
    37  }
    38  
    39  // UnitStorageAttachments returns the IDs of storage attachments for a collection of units.
    40  func (s *StorageAPI) UnitStorageAttachments(args params.Entities) (params.StorageAttachmentIdsResults, error) {
    41  	canAccess, err := s.accessUnit()
    42  	if err != nil {
    43  		return params.StorageAttachmentIdsResults{}, err
    44  	}
    45  	result := params.StorageAttachmentIdsResults{
    46  		Results: make([]params.StorageAttachmentIdsResult, len(args.Entities)),
    47  	}
    48  	for i, entity := range args.Entities {
    49  		storageAttachmentIds, err := s.getOneUnitStorageAttachmentIds(canAccess, entity.Tag)
    50  		if err == nil {
    51  			result.Results[i].Result = params.StorageAttachmentIds{
    52  				storageAttachmentIds,
    53  			}
    54  		}
    55  		result.Results[i].Error = common.ServerError(err)
    56  	}
    57  	return result, nil
    58  }
    59  
    60  func (s *StorageAPI) getOneUnitStorageAttachmentIds(canAccess common.AuthFunc, unitTag string) ([]params.StorageAttachmentId, error) {
    61  	tag, err := names.ParseUnitTag(unitTag)
    62  	if err != nil || !canAccess(tag) {
    63  		return nil, common.ErrPerm
    64  	}
    65  	stateStorageAttachments, err := s.st.UnitStorageAttachments(tag)
    66  	if errors.IsNotFound(err) {
    67  		return nil, common.ErrPerm
    68  	} else if err != nil {
    69  		return nil, err
    70  	}
    71  	result := make([]params.StorageAttachmentId, len(stateStorageAttachments))
    72  	for i, stateStorageAttachment := range stateStorageAttachments {
    73  		result[i] = params.StorageAttachmentId{
    74  			UnitTag:    unitTag,
    75  			StorageTag: stateStorageAttachment.StorageInstance().String(),
    76  		}
    77  	}
    78  	return result, nil
    79  }
    80  
    81  // DestroyUnitStorageAttachments marks each storage attachment of the
    82  // specified units as Dying.
    83  func (s *StorageAPI) DestroyUnitStorageAttachments(args params.Entities) (params.ErrorResults, error) {
    84  	canAccess, err := s.accessUnit()
    85  	if err != nil {
    86  		return params.ErrorResults{}, err
    87  	}
    88  	result := params.ErrorResults{
    89  		Results: make([]params.ErrorResult, len(args.Entities)),
    90  	}
    91  	one := func(tag string) error {
    92  		unitTag, err := names.ParseUnitTag(tag)
    93  		if err != nil {
    94  			return err
    95  		}
    96  		if !canAccess(unitTag) {
    97  			return common.ErrPerm
    98  		}
    99  		return s.st.DestroyUnitStorageAttachments(unitTag)
   100  	}
   101  	for i, entity := range args.Entities {
   102  		err := one(entity.Tag)
   103  		result.Results[i].Error = common.ServerError(err)
   104  	}
   105  	return result, nil
   106  }
   107  
   108  // StorageAttachments returns the storage attachments with the specified tags.
   109  func (s *StorageAPI) StorageAttachments(args params.StorageAttachmentIds) (params.StorageAttachmentResults, error) {
   110  	canAccess, err := s.accessUnit()
   111  	if err != nil {
   112  		return params.StorageAttachmentResults{}, err
   113  	}
   114  	result := params.StorageAttachmentResults{
   115  		Results: make([]params.StorageAttachmentResult, len(args.Ids)),
   116  	}
   117  	for i, id := range args.Ids {
   118  		storageAttachment, err := s.getOneStorageAttachment(canAccess, id)
   119  		if err == nil {
   120  			result.Results[i].Result = storageAttachment
   121  		}
   122  		result.Results[i].Error = common.ServerError(err)
   123  	}
   124  	return result, nil
   125  }
   126  
   127  // StorageAttachmentLife returns the lifecycle state of the storage attachments
   128  // with the specified tags.
   129  func (s *StorageAPI) StorageAttachmentLife(args params.StorageAttachmentIds) (params.LifeResults, error) {
   130  	canAccess, err := s.accessUnit()
   131  	if err != nil {
   132  		return params.LifeResults{}, err
   133  	}
   134  	result := params.LifeResults{
   135  		Results: make([]params.LifeResult, len(args.Ids)),
   136  	}
   137  	for i, id := range args.Ids {
   138  		stateStorageAttachment, err := s.getOneStateStorageAttachment(canAccess, id)
   139  		if err == nil {
   140  			life := stateStorageAttachment.Life()
   141  			result.Results[i].Life = params.Life(life.String())
   142  		}
   143  		result.Results[i].Error = common.ServerError(err)
   144  	}
   145  	return result, nil
   146  }
   147  
   148  func (s *StorageAPI) getOneStorageAttachment(canAccess common.AuthFunc, id params.StorageAttachmentId) (params.StorageAttachment, error) {
   149  	stateStorageAttachment, err := s.getOneStateStorageAttachment(canAccess, id)
   150  	if err != nil {
   151  		return params.StorageAttachment{}, err
   152  	}
   153  	return s.fromStateStorageAttachment(stateStorageAttachment)
   154  }
   155  
   156  func (s *StorageAPI) getOneStateStorageAttachment(canAccess common.AuthFunc, id params.StorageAttachmentId) (state.StorageAttachment, error) {
   157  	unitTag, err := names.ParseUnitTag(id.UnitTag)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	if !canAccess(unitTag) {
   162  		return nil, common.ErrPerm
   163  	}
   164  	storageTag, err := names.ParseStorageTag(id.StorageTag)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  	return s.st.StorageAttachment(storageTag, unitTag)
   169  }
   170  
   171  func (s *StorageAPI) fromStateStorageAttachment(stateStorageAttachment state.StorageAttachment) (params.StorageAttachment, error) {
   172  	machineTag, err := s.st.UnitAssignedMachine(stateStorageAttachment.Unit())
   173  	if err != nil {
   174  		return params.StorageAttachment{}, err
   175  	}
   176  	info, err := storagecommon.StorageAttachmentInfo(s.st, stateStorageAttachment, machineTag)
   177  	if err != nil {
   178  		return params.StorageAttachment{}, err
   179  	}
   180  	stateStorageInstance, err := s.st.StorageInstance(stateStorageAttachment.StorageInstance())
   181  	if err != nil {
   182  		return params.StorageAttachment{}, err
   183  	}
   184  	return params.StorageAttachment{
   185  		stateStorageAttachment.StorageInstance().String(),
   186  		stateStorageInstance.Owner().String(),
   187  		stateStorageAttachment.Unit().String(),
   188  		params.StorageKind(stateStorageInstance.Kind()),
   189  		info.Location,
   190  		params.Life(stateStorageAttachment.Life().String()),
   191  	}, nil
   192  }
   193  
   194  // WatchUnitStorageAttachments creates watchers for a collection of units,
   195  // each of which can be used to watch for lifecycle changes to the corresponding
   196  // unit's storage attachments.
   197  func (s *StorageAPI) WatchUnitStorageAttachments(args params.Entities) (params.StringsWatchResults, error) {
   198  	canAccess, err := s.accessUnit()
   199  	if err != nil {
   200  		return params.StringsWatchResults{}, err
   201  	}
   202  	results := params.StringsWatchResults{
   203  		Results: make([]params.StringsWatchResult, len(args.Entities)),
   204  	}
   205  	for i, entity := range args.Entities {
   206  		result, err := s.watchOneUnitStorageAttachments(entity.Tag, canAccess)
   207  		if err == nil {
   208  			results.Results[i] = result
   209  		}
   210  		results.Results[i].Error = common.ServerError(err)
   211  	}
   212  	return results, nil
   213  }
   214  
   215  func (s *StorageAPI) watchOneUnitStorageAttachments(tag string, canAccess func(names.Tag) bool) (params.StringsWatchResult, error) {
   216  	nothing := params.StringsWatchResult{}
   217  	unitTag, err := names.ParseUnitTag(tag)
   218  	if err != nil || !canAccess(unitTag) {
   219  		return nothing, common.ErrPerm
   220  	}
   221  	watch := s.st.WatchStorageAttachments(unitTag)
   222  	if changes, ok := <-watch.Changes(); ok {
   223  		return params.StringsWatchResult{
   224  			StringsWatcherId: s.resources.Register(watch),
   225  			Changes:          changes,
   226  		}, nil
   227  	}
   228  	return nothing, watcher.EnsureErr(watch)
   229  }
   230  
   231  // WatchStorageAttachments creates watchers for a collection of storage
   232  // attachments, each of which can be used to watch changes to storage
   233  // attachment info.
   234  func (s *StorageAPI) WatchStorageAttachments(args params.StorageAttachmentIds) (params.NotifyWatchResults, error) {
   235  	canAccess, err := s.accessUnit()
   236  	if err != nil {
   237  		return params.NotifyWatchResults{}, err
   238  	}
   239  	results := params.NotifyWatchResults{
   240  		Results: make([]params.NotifyWatchResult, len(args.Ids)),
   241  	}
   242  	for i, id := range args.Ids {
   243  		result, err := s.watchOneStorageAttachment(id, canAccess)
   244  		if err == nil {
   245  			results.Results[i] = result
   246  		}
   247  		results.Results[i].Error = common.ServerError(err)
   248  	}
   249  	return results, nil
   250  }
   251  
   252  func (s *StorageAPI) watchOneStorageAttachment(id params.StorageAttachmentId, canAccess func(names.Tag) bool) (params.NotifyWatchResult, error) {
   253  	// Watching a storage attachment is implemented as watching the
   254  	// underlying volume or filesystem attachment. The only thing
   255  	// we don't necessarily see in doing this is the lifecycle state
   256  	// changes, but these may be observed by using the
   257  	// WatchUnitStorageAttachments watcher.
   258  	nothing := params.NotifyWatchResult{}
   259  	unitTag, err := names.ParseUnitTag(id.UnitTag)
   260  	if err != nil || !canAccess(unitTag) {
   261  		return nothing, common.ErrPerm
   262  	}
   263  	storageTag, err := names.ParseStorageTag(id.StorageTag)
   264  	if err != nil {
   265  		return nothing, err
   266  	}
   267  	machineTag, err := s.st.UnitAssignedMachine(unitTag)
   268  	if err != nil {
   269  		return nothing, err
   270  	}
   271  	watch, err := storagecommon.WatchStorageAttachment(s.st, storageTag, machineTag, unitTag)
   272  	if err != nil {
   273  		return nothing, errors.Trace(err)
   274  	}
   275  	if _, ok := <-watch.Changes(); ok {
   276  		return params.NotifyWatchResult{
   277  			NotifyWatcherId: s.resources.Register(watch),
   278  		}, nil
   279  	}
   280  	return nothing, watcher.EnsureErr(watch)
   281  }
   282  
   283  // RemoveStorageAttachments removes the specified storage
   284  // attachments from state.
   285  func (s *StorageAPI) RemoveStorageAttachments(args params.StorageAttachmentIds) (params.ErrorResults, error) {
   286  	canAccess, err := s.accessUnit()
   287  	if err != nil {
   288  		return params.ErrorResults{}, err
   289  	}
   290  	results := params.ErrorResults{
   291  		Results: make([]params.ErrorResult, len(args.Ids)),
   292  	}
   293  	for i, id := range args.Ids {
   294  		err := s.removeOneStorageAttachment(id, canAccess)
   295  		if err != nil {
   296  			results.Results[i].Error = common.ServerError(err)
   297  		}
   298  	}
   299  	return results, nil
   300  }
   301  
   302  func (s *StorageAPI) removeOneStorageAttachment(id params.StorageAttachmentId, canAccess func(names.Tag) bool) error {
   303  	unitTag, err := names.ParseUnitTag(id.UnitTag)
   304  	if err != nil {
   305  		return err
   306  	}
   307  	if !canAccess(unitTag) {
   308  		return common.ErrPerm
   309  	}
   310  	storageTag, err := names.ParseStorageTag(id.StorageTag)
   311  	if err != nil {
   312  		return err
   313  	}
   314  	return s.st.RemoveStorageAttachment(storageTag, unitTag)
   315  }
   316  
   317  // AddUnitStorage validates and creates additional storage instances for units.
   318  // Failures on an individual storage instance do not block remaining
   319  // instances from being processed.
   320  func (a *StorageAPI) AddUnitStorage(
   321  	args params.StoragesAddParams,
   322  ) (params.ErrorResults, error) {
   323  	canAccess, err := a.accessUnit()
   324  	if err != nil {
   325  		return params.ErrorResults{}, err
   326  	}
   327  	if len(args.Storages) == 0 {
   328  		return params.ErrorResults{}, nil
   329  	}
   330  
   331  	serverErr := func(err error) params.ErrorResult {
   332  		return params.ErrorResult{common.ServerError(err)}
   333  	}
   334  
   335  	storageErr := func(err error, s, u string) params.ErrorResult {
   336  		return serverErr(errors.Annotatef(err, "adding storage %v for %v", s, u))
   337  	}
   338  
   339  	result := make([]params.ErrorResult, len(args.Storages))
   340  	for i, one := range args.Storages {
   341  		u, err := accessUnitTag(one.UnitTag, canAccess)
   342  		if err != nil {
   343  			result[i] = serverErr(err)
   344  			continue
   345  		}
   346  
   347  		cons, err := a.st.UnitStorageConstraints(u)
   348  		if err != nil {
   349  			result[i] = serverErr(err)
   350  			continue
   351  		}
   352  
   353  		oneCons, err := validConstraints(one, cons)
   354  		if err != nil {
   355  			result[i] = storageErr(err, one.StorageName, one.UnitTag)
   356  			continue
   357  		}
   358  
   359  		err = a.st.AddStorageForUnit(u, one.StorageName, oneCons)
   360  		if err != nil {
   361  			result[i] = storageErr(err, one.StorageName, one.UnitTag)
   362  		}
   363  	}
   364  	return params.ErrorResults{Results: result}, nil
   365  }
   366  
   367  func validConstraints(
   368  	p params.StorageAddParams,
   369  	cons map[string]state.StorageConstraints,
   370  ) (state.StorageConstraints, error) {
   371  	emptyCons := state.StorageConstraints{}
   372  
   373  	result, ok := cons[p.StorageName]
   374  	if !ok {
   375  		return emptyCons, errors.NotFoundf("storage %q", p.StorageName)
   376  	}
   377  
   378  	onlyCount := params.StorageConstraints{Count: p.Constraints.Count}
   379  	if p.Constraints != onlyCount {
   380  		return emptyCons, errors.New("only count can be specified")
   381  	}
   382  
   383  	if p.Constraints.Count == nil || *p.Constraints.Count == 0 {
   384  		return emptyCons, errors.New("count must be specified")
   385  	}
   386  
   387  	result.Count = *p.Constraints.Count
   388  	return result, nil
   389  }
   390  
   391  func accessUnitTag(tag string, canAccess func(names.Tag) bool) (names.UnitTag, error) {
   392  	u, err := names.ParseUnitTag(tag)
   393  	if err != nil {
   394  		return names.UnitTag{}, errors.Annotatef(err, "parsing unit tag %v", tag)
   395  	}
   396  	if !canAccess(u) {
   397  		return names.UnitTag{}, common.ErrPerm
   398  	}
   399  	return u, nil
   400  }