github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/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  	backend    backend
    21  	storage    storageAccess
    22  	resources  facade.Resources
    23  	accessUnit common.GetAuthFunc
    24  }
    25  
    26  // newStorageAPI creates a new server-side Storage API facade.
    27  func newStorageAPI(
    28  	backend backend,
    29  	storage storageAccess,
    30  	resources facade.Resources,
    31  	accessUnit common.GetAuthFunc,
    32  ) (*StorageAPI, error) {
    33  
    34  	return &StorageAPI{
    35  		backend:    backend,
    36  		storage:    storage,
    37  		resources:  resources,
    38  		accessUnit: accessUnit,
    39  	}, nil
    40  }
    41  
    42  // UnitStorageAttachments returns the IDs of storage attachments for a collection of units.
    43  func (s *StorageAPI) UnitStorageAttachments(args params.Entities) (params.StorageAttachmentIdsResults, error) {
    44  	canAccess, err := s.accessUnit()
    45  	if err != nil {
    46  		return params.StorageAttachmentIdsResults{}, err
    47  	}
    48  	result := params.StorageAttachmentIdsResults{
    49  		Results: make([]params.StorageAttachmentIdsResult, len(args.Entities)),
    50  	}
    51  	for i, entity := range args.Entities {
    52  		storageAttachmentIds, err := s.getOneUnitStorageAttachmentIds(canAccess, entity.Tag)
    53  		if err == nil {
    54  			result.Results[i].Result = params.StorageAttachmentIds{
    55  				storageAttachmentIds,
    56  			}
    57  		}
    58  		result.Results[i].Error = common.ServerError(err)
    59  	}
    60  	return result, nil
    61  }
    62  
    63  func (s *StorageAPI) getOneUnitStorageAttachmentIds(canAccess common.AuthFunc, unitTag string) ([]params.StorageAttachmentId, error) {
    64  	tag, err := names.ParseUnitTag(unitTag)
    65  	if err != nil || !canAccess(tag) {
    66  		return nil, common.ErrPerm
    67  	}
    68  	stateStorageAttachments, err := s.storage.UnitStorageAttachments(tag)
    69  	if errors.IsNotFound(err) {
    70  		return nil, common.ErrPerm
    71  	} else if err != nil {
    72  		return nil, err
    73  	}
    74  	result := make([]params.StorageAttachmentId, len(stateStorageAttachments))
    75  	for i, stateStorageAttachment := range stateStorageAttachments {
    76  		result[i] = params.StorageAttachmentId{
    77  			UnitTag:    unitTag,
    78  			StorageTag: stateStorageAttachment.StorageInstance().String(),
    79  		}
    80  	}
    81  	return result, nil
    82  }
    83  
    84  // DestroyUnitStorageAttachments marks each storage attachment of the
    85  // specified units as Dying.
    86  func (s *StorageAPI) DestroyUnitStorageAttachments(args params.Entities) (params.ErrorResults, error) {
    87  	canAccess, err := s.accessUnit()
    88  	if err != nil {
    89  		return params.ErrorResults{}, err
    90  	}
    91  	result := params.ErrorResults{
    92  		Results: make([]params.ErrorResult, len(args.Entities)),
    93  	}
    94  	one := func(tag string) error {
    95  		unitTag, err := names.ParseUnitTag(tag)
    96  		if err != nil {
    97  			return err
    98  		}
    99  		if !canAccess(unitTag) {
   100  			return common.ErrPerm
   101  		}
   102  		return s.storage.DestroyUnitStorageAttachments(unitTag)
   103  	}
   104  	for i, entity := range args.Entities {
   105  		err := one(entity.Tag)
   106  		result.Results[i].Error = common.ServerError(err)
   107  	}
   108  	return result, nil
   109  }
   110  
   111  // StorageAttachments returns the storage attachments with the specified tags.
   112  func (s *StorageAPI) StorageAttachments(args params.StorageAttachmentIds) (params.StorageAttachmentResults, error) {
   113  	canAccess, err := s.accessUnit()
   114  	if err != nil {
   115  		return params.StorageAttachmentResults{}, err
   116  	}
   117  	result := params.StorageAttachmentResults{
   118  		Results: make([]params.StorageAttachmentResult, len(args.Ids)),
   119  	}
   120  	for i, id := range args.Ids {
   121  		storageAttachment, err := s.getOneStorageAttachment(canAccess, id)
   122  		if err == nil {
   123  			result.Results[i].Result = storageAttachment
   124  		}
   125  		result.Results[i].Error = common.ServerError(err)
   126  	}
   127  	return result, nil
   128  }
   129  
   130  // StorageAttachmentLife returns the lifecycle state of the storage attachments
   131  // with the specified tags.
   132  func (s *StorageAPI) StorageAttachmentLife(args params.StorageAttachmentIds) (params.LifeResults, error) {
   133  	canAccess, err := s.accessUnit()
   134  	if err != nil {
   135  		return params.LifeResults{}, err
   136  	}
   137  	result := params.LifeResults{
   138  		Results: make([]params.LifeResult, len(args.Ids)),
   139  	}
   140  	for i, id := range args.Ids {
   141  		stateStorageAttachment, err := s.getOneStateStorageAttachment(canAccess, id)
   142  		if err == nil {
   143  			life := stateStorageAttachment.Life()
   144  			result.Results[i].Life = params.Life(life.String())
   145  		}
   146  		result.Results[i].Error = common.ServerError(err)
   147  	}
   148  	return result, nil
   149  }
   150  
   151  func (s *StorageAPI) getOneStorageAttachment(canAccess common.AuthFunc, id params.StorageAttachmentId) (params.StorageAttachment, error) {
   152  	stateStorageAttachment, err := s.getOneStateStorageAttachment(canAccess, id)
   153  	if err != nil {
   154  		return params.StorageAttachment{}, err
   155  	}
   156  	return s.fromStateStorageAttachment(stateStorageAttachment)
   157  }
   158  
   159  func (s *StorageAPI) getOneStateStorageAttachment(canAccess common.AuthFunc, id params.StorageAttachmentId) (state.StorageAttachment, error) {
   160  	unitTag, err := names.ParseUnitTag(id.UnitTag)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  	if !canAccess(unitTag) {
   165  		return nil, common.ErrPerm
   166  	}
   167  	storageTag, err := names.ParseStorageTag(id.StorageTag)
   168  	if err != nil {
   169  		return nil, err
   170  	}
   171  	return s.storage.StorageAttachment(storageTag, unitTag)
   172  }
   173  
   174  func (s *StorageAPI) fromStateStorageAttachment(stateStorageAttachment state.StorageAttachment) (params.StorageAttachment, error) {
   175  	var hostTag names.Tag
   176  	hostTag = stateStorageAttachment.Unit()
   177  	u, err := s.backend.Unit(hostTag.Id())
   178  	if err != nil {
   179  		return params.StorageAttachment{}, err
   180  	}
   181  	if u.ShouldBeAssigned() {
   182  		hostTag, err = unitAssignedMachine(s.backend, stateStorageAttachment.Unit())
   183  		if err != nil {
   184  			return params.StorageAttachment{}, err
   185  		}
   186  	}
   187  
   188  	info, err := storagecommon.StorageAttachmentInfo(
   189  		s.storage, s.storage.VolumeAccess(), s.storage.FilesystemAccess(), stateStorageAttachment, hostTag)
   190  	if err != nil {
   191  		return params.StorageAttachment{}, err
   192  	}
   193  	stateStorageInstance, err := s.storage.StorageInstance(stateStorageAttachment.StorageInstance())
   194  	if err != nil {
   195  		return params.StorageAttachment{}, err
   196  	}
   197  	var ownerTag string
   198  	if owner, ok := stateStorageInstance.Owner(); ok {
   199  		ownerTag = owner.String()
   200  	}
   201  	return params.StorageAttachment{
   202  		stateStorageAttachment.StorageInstance().String(),
   203  		ownerTag,
   204  		stateStorageAttachment.Unit().String(),
   205  		params.StorageKind(stateStorageInstance.Kind()),
   206  		info.Location,
   207  		params.Life(stateStorageAttachment.Life().String()),
   208  	}, nil
   209  }
   210  
   211  // WatchUnitStorageAttachments creates watchers for a collection of units,
   212  // each of which can be used to watch for lifecycle changes to the corresponding
   213  // unit's storage attachments.
   214  func (s *StorageAPI) WatchUnitStorageAttachments(args params.Entities) (params.StringsWatchResults, error) {
   215  	canAccess, err := s.accessUnit()
   216  	if err != nil {
   217  		return params.StringsWatchResults{}, err
   218  	}
   219  	results := params.StringsWatchResults{
   220  		Results: make([]params.StringsWatchResult, len(args.Entities)),
   221  	}
   222  	for i, entity := range args.Entities {
   223  		result, err := s.watchOneUnitStorageAttachments(entity.Tag, canAccess)
   224  		if err == nil {
   225  			results.Results[i] = result
   226  		}
   227  		results.Results[i].Error = common.ServerError(err)
   228  	}
   229  	return results, nil
   230  }
   231  
   232  func (s *StorageAPI) watchOneUnitStorageAttachments(tag string, canAccess func(names.Tag) bool) (params.StringsWatchResult, error) {
   233  	nothing := params.StringsWatchResult{}
   234  	unitTag, err := names.ParseUnitTag(tag)
   235  	if err != nil || !canAccess(unitTag) {
   236  		return nothing, common.ErrPerm
   237  	}
   238  	watch := s.storage.WatchStorageAttachments(unitTag)
   239  	if changes, ok := <-watch.Changes(); ok {
   240  		return params.StringsWatchResult{
   241  			StringsWatcherId: s.resources.Register(watch),
   242  			Changes:          changes,
   243  		}, nil
   244  	}
   245  	return nothing, watcher.EnsureErr(watch)
   246  }
   247  
   248  // WatchStorageAttachments creates watchers for a collection of storage
   249  // attachments, each of which can be used to watch changes to storage
   250  // attachment info.
   251  func (s *StorageAPI) WatchStorageAttachments(args params.StorageAttachmentIds) (params.NotifyWatchResults, error) {
   252  	canAccess, err := s.accessUnit()
   253  	if err != nil {
   254  		return params.NotifyWatchResults{}, err
   255  	}
   256  	results := params.NotifyWatchResults{
   257  		Results: make([]params.NotifyWatchResult, len(args.Ids)),
   258  	}
   259  	for i, id := range args.Ids {
   260  		result, err := s.watchOneStorageAttachment(id, canAccess)
   261  		if err == nil {
   262  			results.Results[i] = result
   263  		}
   264  		results.Results[i].Error = common.ServerError(err)
   265  	}
   266  	return results, nil
   267  }
   268  
   269  func (s *StorageAPI) watchOneStorageAttachment(id params.StorageAttachmentId, canAccess func(names.Tag) bool) (params.NotifyWatchResult, error) {
   270  	// Watching a storage attachment is implemented as watching the
   271  	// underlying volume or filesystem attachment. The only thing
   272  	// we don't necessarily see in doing this is the lifecycle state
   273  	// changes, but these may be observed by using the
   274  	// WatchUnitStorageAttachments watcher.
   275  	nothing := params.NotifyWatchResult{}
   276  	unitTag, err := names.ParseUnitTag(id.UnitTag)
   277  	if err != nil || !canAccess(unitTag) {
   278  		return nothing, common.ErrPerm
   279  	}
   280  	storageTag, err := names.ParseStorageTag(id.StorageTag)
   281  	if err != nil {
   282  		return nothing, err
   283  	}
   284  
   285  	var hostTag names.Tag
   286  	hostTag = unitTag
   287  	u, err := s.backend.Unit(unitTag.Id())
   288  	if err != nil {
   289  		return nothing, err
   290  	}
   291  	if u.ShouldBeAssigned() {
   292  		hostTag, err = unitAssignedMachine(s.backend, unitTag)
   293  		if err != nil {
   294  			return nothing, err
   295  		}
   296  	}
   297  	watch, err := watchStorageAttachment(
   298  		s.storage, s.storage.VolumeAccess(), s.storage.FilesystemAccess(), storageTag, hostTag, unitTag)
   299  	if err != nil {
   300  		return nothing, errors.Trace(err)
   301  	}
   302  	if _, ok := <-watch.Changes(); ok {
   303  		return params.NotifyWatchResult{
   304  			NotifyWatcherId: s.resources.Register(watch),
   305  		}, nil
   306  	}
   307  	return nothing, watcher.EnsureErr(watch)
   308  }
   309  
   310  // RemoveStorageAttachments removes the specified storage
   311  // attachments from state.
   312  func (s *StorageAPI) RemoveStorageAttachments(args params.StorageAttachmentIds) (params.ErrorResults, error) {
   313  	canAccess, err := s.accessUnit()
   314  	if err != nil {
   315  		return params.ErrorResults{}, err
   316  	}
   317  	results := params.ErrorResults{
   318  		Results: make([]params.ErrorResult, len(args.Ids)),
   319  	}
   320  	for i, id := range args.Ids {
   321  		err := s.removeOneStorageAttachment(id, canAccess)
   322  		if err != nil {
   323  			results.Results[i].Error = common.ServerError(err)
   324  		}
   325  	}
   326  	return results, nil
   327  }
   328  
   329  func (s *StorageAPI) removeOneStorageAttachment(id params.StorageAttachmentId, canAccess func(names.Tag) bool) error {
   330  	unitTag, err := names.ParseUnitTag(id.UnitTag)
   331  	if err != nil {
   332  		return err
   333  	}
   334  	if !canAccess(unitTag) {
   335  		return common.ErrPerm
   336  	}
   337  	storageTag, err := names.ParseStorageTag(id.StorageTag)
   338  	if err != nil {
   339  		return err
   340  	}
   341  	err = s.storage.RemoveStorageAttachment(storageTag, unitTag)
   342  	if errors.IsNotFound(err) {
   343  		err = nil
   344  	}
   345  	return err
   346  }
   347  
   348  // AddUnitStorage validates and creates additional storage instances for units.
   349  // Failures on an individual storage instance do not block remaining
   350  // instances from being processed.
   351  func (s *StorageAPI) AddUnitStorage(
   352  	args params.StoragesAddParams,
   353  ) (params.ErrorResults, error) {
   354  	canAccess, err := s.accessUnit()
   355  	if err != nil {
   356  		return params.ErrorResults{}, err
   357  	}
   358  	if len(args.Storages) == 0 {
   359  		return params.ErrorResults{}, nil
   360  	}
   361  
   362  	serverErr := func(err error) params.ErrorResult {
   363  		return params.ErrorResult{common.ServerError(err)}
   364  	}
   365  
   366  	storageErr := func(err error, s, u string) params.ErrorResult {
   367  		return serverErr(errors.Annotatef(err, "adding storage %v for %v", s, u))
   368  	}
   369  
   370  	result := make([]params.ErrorResult, len(args.Storages))
   371  	for i, one := range args.Storages {
   372  		u, err := accessUnitTag(one.UnitTag, canAccess)
   373  		if err != nil {
   374  			result[i] = serverErr(err)
   375  			continue
   376  		}
   377  
   378  		cons, err := unitStorageConstraints(s.backend, u)
   379  		if err != nil {
   380  			result[i] = serverErr(err)
   381  			continue
   382  		}
   383  
   384  		oneCons, err := validConstraints(one, cons)
   385  		if err != nil {
   386  			result[i] = storageErr(err, one.StorageName, one.UnitTag)
   387  			continue
   388  		}
   389  
   390  		_, err = s.storage.AddStorageForUnit(u, one.StorageName, oneCons)
   391  		if err != nil {
   392  			result[i] = storageErr(err, one.StorageName, one.UnitTag)
   393  		}
   394  	}
   395  	return params.ErrorResults{Results: result}, nil
   396  }
   397  
   398  func validConstraints(
   399  	p params.StorageAddParams,
   400  	cons map[string]state.StorageConstraints,
   401  ) (state.StorageConstraints, error) {
   402  	emptyCons := state.StorageConstraints{}
   403  
   404  	result, ok := cons[p.StorageName]
   405  	if !ok {
   406  		return emptyCons, errors.NotFoundf("storage %q", p.StorageName)
   407  	}
   408  
   409  	onlyCount := params.StorageConstraints{Count: p.Constraints.Count}
   410  	if p.Constraints != onlyCount {
   411  		return emptyCons, errors.New("only count can be specified")
   412  	}
   413  
   414  	if p.Constraints.Count == nil || *p.Constraints.Count == 0 {
   415  		return emptyCons, errors.New("count must be specified")
   416  	}
   417  
   418  	result.Count = *p.Constraints.Count
   419  	return result, nil
   420  }
   421  
   422  func accessUnitTag(tag string, canAccess func(names.Tag) bool) (names.UnitTag, error) {
   423  	u, err := names.ParseUnitTag(tag)
   424  	if err != nil {
   425  		return names.UnitTag{}, errors.Annotatef(err, "parsing unit tag %v", tag)
   426  	}
   427  	if !canAccess(u) {
   428  		return names.UnitTag{}, common.ErrPerm
   429  	}
   430  	return u, nil
   431  }
   432  
   433  // watchStorageAttachment returns a state.NotifyWatcher that reacts to changes
   434  // to the VolumeAttachmentInfo or FilesystemAttachmentInfo corresponding to the
   435  // tags specified.
   436  func watchStorageAttachment(
   437  	st storageInterface,
   438  	stVolume storageVolumeInterface,
   439  	stFile storageFilesystemInterface,
   440  	storageTag names.StorageTag,
   441  	hostTag names.Tag,
   442  	unitTag names.UnitTag,
   443  ) (state.NotifyWatcher, error) {
   444  	storageInstance, err := st.StorageInstance(storageTag)
   445  	if err != nil {
   446  		return nil, errors.Annotate(err, "getting storage instance")
   447  	}
   448  	var watchers []state.NotifyWatcher
   449  	switch storageInstance.Kind() {
   450  	case state.StorageKindBlock:
   451  		if stVolume == nil {
   452  			return nil, errors.NotImplementedf("BlockStorage instance")
   453  		}
   454  		volume, err := stVolume.StorageInstanceVolume(storageTag)
   455  		if err != nil {
   456  			return nil, errors.Annotate(err, "getting storage volume")
   457  		}
   458  		// We need to watch both the volume attachment, and the
   459  		// machine's block devices. A volume attachment's block
   460  		// device could change (most likely, become present).
   461  		watchers = []state.NotifyWatcher{
   462  			stVolume.WatchVolumeAttachment(hostTag, volume.VolumeTag()),
   463  		}
   464  
   465  		// TODO(caas) - we currently only support block devices on machines.
   466  		if hostTag.Kind() == names.MachineTagKind {
   467  			// TODO(axw) 2015-09-30 #1501203
   468  			// We should filter the events to only those relevant
   469  			// to the volume attachment. This means we would need
   470  			// to either start th block device watcher after we
   471  			// have provisioned the volume attachment (cleaner?),
   472  			// or have the filter ignore changes until the volume
   473  			// attachment is provisioned.
   474  			watchers = append(watchers, stVolume.WatchBlockDevices(hostTag.(names.MachineTag)))
   475  		}
   476  	case state.StorageKindFilesystem:
   477  		if stFile == nil {
   478  			return nil, errors.NotImplementedf("FilesystemStorage instance")
   479  		}
   480  		filesystem, err := stFile.StorageInstanceFilesystem(storageTag)
   481  		if err != nil {
   482  			return nil, errors.Annotate(err, "getting storage filesystem")
   483  		}
   484  		watchers = []state.NotifyWatcher{
   485  			stFile.WatchFilesystemAttachment(hostTag, filesystem.FilesystemTag()),
   486  		}
   487  	default:
   488  		return nil, errors.Errorf("invalid storage kind %v", storageInstance.Kind())
   489  	}
   490  	watchers = append(watchers, st.WatchStorageAttachment(storageTag, unitTag))
   491  	return common.NewMultiNotifyWatcher(watchers...), nil
   492  }