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