forked from puppetlabs-toy-chest/wash
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathentryBase.go
216 lines (183 loc) · 6.01 KB
/
entryBase.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
package plugin
import (
"context"
"flag"
"time"
"github.com/puppetlabs/wash/activity"
)
type defaultOpCode int8
const (
// ListOp represents Parent#List
ListOp defaultOpCode = iota
// ReadOp represents Readable/BlockReadable#Read
ReadOp
// MetadataOp represents Entry#Metadata
MetadataOp
)
var defaultOpCodeToNameMap = [3]string{"List", "Read", "Metadata"}
/*
EntryBase implements Entry, making it easy to create new entries.
You should use plugin.NewEntry to create new EntryBase objects.
Each of the setters supports the builder pattern, which enables you
to do something like
e := plugin.NewEntry("foo")
e.
DisableCachingFor(plugin.ListOp).
Attributes().
SetCrtime(crtime).
SetMtime(mtime).
SetMeta(meta)
*/
type EntryBase struct {
name string
attributes EntryAttributes
specifiedPartialMetadata JSONObject
slashReplacer rune
id string
ttl [3]time.Duration
wrappedTypes SchemaMap
isPrefetched bool
isInaccessible bool
}
// NewEntry creates a new entry
func NewEntry(name string) EntryBase {
if name == "" {
panic("plugin.NewEntry: received an empty name")
}
e := EntryBase{
name: name,
slashReplacer: '#',
}
for op := range e.ttl {
e.SetTTLOf(defaultOpCode(op), 15*time.Second)
}
return e
}
func (e *EntryBase) partialMetadata() JSONObject {
if e.specifiedPartialMetadata != nil {
return e.specifiedPartialMetadata
}
// This case strictly enforces the "attributes are a subset of the
// (partial) metadata" invariant. Here, if no partial metadata was
// set, then we assume that attributes == partialMetadata.
return e.attributes.ToMap()
}
// ENTRY INTERFACE
// Metadata returns the entry's partial metadata. Override this if e has
// additional metadata properties that couldn't be included in the partial
// metadata because doing so would have slowed down parent#List.
func (e *EntryBase) Metadata(ctx context.Context) (JSONObject, error) {
// Disable Metadata's caching in case the plugin author forgot to do this
e.DisableCachingFor(MetadataOp)
return e.partialMetadata(), nil
}
func (e *EntryBase) eb() *EntryBase {
return e
}
// OTHER METHODS USED TO FACILITATE PLUGIN DEVELOPMENT
// AND TESTING
// Name returns the entry's name as it was passed into
// plugin.NewEntry. You should use e.Name() when making
// the appropriate API calls within your plugin.
func (e *EntryBase) Name() string {
return e.name
}
// ID returns the entry's ID. It won't panic on an empty string. See ID() for more detail.
// This exists primarily to support the `external` package.
func (e *EntryBase) ID() string {
return e.id
}
// String returns a unique identifier for the entry suitable for logging and error messages.
func (e *EntryBase) String() string {
return e.id
}
// Attributes returns a pointer to the entry's attributes. Use it
// to individually set the entry's attributes
func (e *EntryBase) Attributes() *EntryAttributes {
return &e.attributes
}
// SetAttributes sets the entry's attributes. Use it to set
// the entry's attributes in a single operation, which is useful
// when you've already pre-computed them.
func (e *EntryBase) SetAttributes(attr EntryAttributes) *EntryBase {
e.attributes = attr
return e
}
// SetPartialMetadata sets the entry's partial metadata. This is typically the
// raw object that's returned by the plugin API's List endpoint, or a wrapper
// that includes the raw object + some additional information. For example, if
// the entry represents a Docker container, then obj would be a Container struct.
// If the entry represents a Docker volume, then obj would be a Volume struct.
//
// In general, the partial metadata is the subset of metadata that can be fetched
// "quickly" s.t. parent#List isn't slowed down.
//
// SetPartialMetadata will panic if obj does not serialize to a JSON object.
func (e *EntryBase) SetPartialMetadata(obj interface{}) *EntryBase {
e.specifiedPartialMetadata = ToJSONObject(obj)
return e
}
// MarkInaccessible sets the inaccessible attribute and logs a message about why the entry is
// inaccessible.
func (e *EntryBase) MarkInaccessible(ctx context.Context, err error) {
activity.Warnf(ctx, "Omitting %v: %v", e.id, err)
e.isInaccessible = true
}
// IsInaccessible returns whether the entry is inaccessible.
func (e *EntryBase) IsInaccessible() bool {
return e.isInaccessible
}
// Prefetched marks the entry as a prefetched entry. A prefetched entry
// is an entry that was fetched as part of a batch operation that
// fetched multiple levels of hierarchy at once. Volume directories and
// files are good examples of prefetched entries (see the volume
// package for more details).
func (e *EntryBase) Prefetched() *EntryBase {
e.isPrefetched = true
return e
}
/*
SetSlashReplacer overrides the default '/' replacer '#' to char.
The '/' replacer is used when determining the entry's cname. See
plugin.CName for more details.
*/
func (e *EntryBase) SetSlashReplacer(char rune) *EntryBase {
if char == '/' {
panic("e.SetSlashReplacer called with '/'")
}
e.slashReplacer = char
return e
}
// SetTTLOf sets the specified op's TTL
func (e *EntryBase) SetTTLOf(op defaultOpCode, ttl time.Duration) *EntryBase {
e.ttl[op] = ttl
return e
}
// TTLOf returns the TTL set for the specified op
func (e *EntryBase) TTLOf(op defaultOpCode) time.Duration {
return e.ttl[op]
}
// DisableCachingFor disables caching for the specified op
func (e *EntryBase) DisableCachingFor(op defaultOpCode) *EntryBase {
e.SetTTLOf(op, -1)
return e
}
// DisableDefaultCaching disables the default caching
// for List, Open and Metadata.
func (e *EntryBase) DisableDefaultCaching() *EntryBase {
for op := range e.ttl {
e.DisableCachingFor(defaultOpCode(op))
}
return e
}
// SetTestID sets the entry's cache ID for testing.
// It can only be called by the tests.
func (e *EntryBase) SetTestID(id string) {
if notRunningTests() {
panic("SetTestID can be only be called by the tests")
}
e.id = id
}
func notRunningTests() bool {
return flag.Lookup("test.v") == nil
}