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