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