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 }