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

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // The resource package provides the functionality of the "resources"
     5  // feature in Juju.
     6  package resource
     7  
     8  import (
     9  	"fmt"
    10  	"time"
    11  
    12  	"github.com/juju/errors"
    13  	"gopkg.in/juju/charm.v6-unstable/resource"
    14  )
    15  
    16  // Resource defines a single resource within a Juju model.
    17  //
    18  // Each application will have have exactly the same resources associated
    19  // with it as are defined in the charm's metadata, no more, no less.
    20  // When associated with the application the resource may have additional
    21  // information associated with it.
    22  //
    23  // A resource may be a "placeholder", meaning it is only partially
    24  // populated before an upload (whether local or from the charm store).
    25  // In that case the following fields are not set:
    26  //
    27  //   Timestamp
    28  //   Username
    29  //
    30  // For "upload" placeholders, the following additional fields are
    31  // not set:
    32  //
    33  //   Fingerprint
    34  //   Size
    35  //
    36  // A resource may also be added to the model as "pending", meaning it
    37  // is queued up to be used as a resource for the application. Until it is
    38  // "activated", a pending resources is virtually invisible. There may
    39  // be more that one pending resource for a given resource ID.
    40  type Resource struct {
    41  	resource.Resource
    42  
    43  	// ID uniquely identifies a resource-application pair within the model.
    44  	// Note that the model ignores pending resources (those with a
    45  	// pending ID) except for in a few clearly pending-related places.
    46  	// ID may be empty if the ID (assigned by the model) is not known.
    47  	ID string
    48  
    49  	// PendingID identifies that this resource is pending and
    50  	// distinguishes it from other pending resources with the same model
    51  	// ID (and from the active resource). The active resource for the
    52  	// applications will not have PendingID set.
    53  	PendingID string
    54  
    55  	// TODO(ericsnow) Use names.ApplicationTag for applicationID?
    56  
    57  	// ApplicationID identifies the application for the resource.
    58  	ApplicationID string
    59  
    60  	// TODO(ericsnow) Use names.UserTag for Username?
    61  
    62  	// Username is the ID of the user that added the revision
    63  	// to the model (whether implicitly or explicitly).
    64  	Username string
    65  
    66  	// Timestamp indicates when the resource was added to the model.
    67  	Timestamp time.Time
    68  }
    69  
    70  // Validate ensures that the spec is valid.
    71  func (res Resource) Validate() error {
    72  	// TODO(ericsnow) Ensure that the "placeholder" fields are not set
    73  	// if IsLocalPlaceholder() returns true (and that they *are* set
    74  	// otherwise)? Also ensure an "upload" origin in the "placeholder"
    75  	// case?
    76  
    77  	if err := res.Resource.Validate(); err != nil {
    78  		return errors.Annotate(err, "bad info")
    79  	}
    80  
    81  	if res.ApplicationID == "" {
    82  		return errors.NewNotValid(nil, "missing application ID")
    83  	}
    84  
    85  	// TODO(ericsnow) Require that Username be set if timestamp is?
    86  
    87  	if res.Timestamp.IsZero() && res.Username != "" {
    88  		return errors.NewNotValid(nil, "missing timestamp")
    89  	}
    90  
    91  	return nil
    92  }
    93  
    94  // IsPlaceholder indicates whether or not the resource is a
    95  // "placeholder" (partially populated pending an upload).
    96  func (res Resource) IsPlaceholder() bool {
    97  	return res.Timestamp.IsZero()
    98  }
    99  
   100  // TimestampGranular returns the timestamp at a resolution of 1 second.
   101  func (res Resource) TimestampGranular() time.Time {
   102  	return time.Unix(res.Timestamp.Unix(), 0)
   103  }
   104  
   105  // RevisionString returns the human-readable revision for the resource.
   106  func (res Resource) RevisionString() string {
   107  	switch res.Origin {
   108  	case resource.OriginUpload:
   109  		if res.IsPlaceholder() {
   110  			return "-"
   111  		}
   112  		return res.TimestampGranular().UTC().String()
   113  	case resource.OriginStore:
   114  		return fmt.Sprintf("%d", res.Revision)
   115  	default:
   116  		// note: this should probably never happen.
   117  		return "-"
   118  	}
   119  }
   120  
   121  // AsMap returns the mapping of resource name to info for each of the
   122  // given resources.
   123  func AsMap(resources []Resource) map[string]Resource {
   124  	results := make(map[string]Resource, len(resources))
   125  	for _, res := range resources {
   126  		results[res.Name] = res
   127  	}
   128  	return results
   129  }