-
Notifications
You must be signed in to change notification settings - Fork 2k
/
node.go
274 lines (230 loc) · 7.9 KB
/
node.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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
package structs
import (
"reflect"
"time"
"github.com/hashicorp/nomad/helper"
)
// CSITopology is a map of topological domains to topological segments.
// A topological domain is a sub-division of a cluster, like "region",
// "zone", "rack", etc.
//
// According to CSI, there are a few requirements for the keys within this map:
// - Valid keys have two segments: an OPTIONAL prefix and name, separated
// by a slash (/), for example: "com.company.example/zone".
// - The key name segment is REQUIRED. The prefix is OPTIONAL.
// - The key name MUST be 63 characters or less, begin and end with an
// alphanumeric character ([a-z0-9A-Z]), and contain only dashes (-),
// underscores (_), dots (.), or alphanumerics in between, for example
// "zone".
// - The key prefix MUST be 63 characters or less, begin and end with a
// lower-case alphanumeric character ([a-z0-9]), contain only
// dashes (-), dots (.), or lower-case alphanumerics in between, and
// follow domain name notation format
// (https://tools.ietf.org/html/rfc1035#section-2.3.1).
// - The key prefix SHOULD include the plugin's host company name and/or
// the plugin name, to minimize the possibility of collisions with keys
// from other plugins.
// - If a key prefix is specified, it MUST be identical across all
// topology keys returned by the SP (across all RPCs).
// - Keys MUST be case-insensitive. Meaning the keys "Zone" and "zone"
// MUST not both exist.
// - Each value (topological segment) MUST contain 1 or more strings.
// - Each string MUST be 63 characters or less and begin and end with an
// alphanumeric character with '-', '_', '.', or alphanumerics in
// between.
//
// However, Nomad applies lighter restrictions to these, as they are already
// only referenced by plugin within the scheduler and as such collisions and
// related concerns are less of an issue. We may implement these restrictions
// in the future.
type CSITopology struct {
Segments map[string]string
}
func (t *CSITopology) Copy() *CSITopology {
if t == nil {
return nil
}
return &CSITopology{
Segments: helper.CopyMapStringString(t.Segments),
}
}
func (t *CSITopology) Equal(o *CSITopology) bool {
if t == nil || o == nil {
return t == o
}
return helper.CompareMapStringString(t.Segments, o.Segments)
}
// CSINodeInfo is the fingerprinted data from a CSI Plugin that is specific to
// the Node API.
type CSINodeInfo struct {
// ID is the identity of a given nomad client as observed by the storage
// provider.
ID string
// MaxVolumes is the maximum number of volumes that can be attached to the
// current host via this provider.
// If 0 then unlimited volumes may be attached.
MaxVolumes int64
// AccessibleTopology specifies where (regions, zones, racks, etc.) the node is
// accessible from within the storage provider.
//
// A plugin that returns this field MUST also set the `RequiresTopologies`
// property.
//
// This field is OPTIONAL. If it is not specified, then we assume that the
// the node is not subject to any topological constraint, and MAY
// schedule workloads that reference any volume V, such that there are
// no topological constraints declared for V.
//
// Example 1:
// accessible_topology =
// {"region": "R1", "zone": "Z2"}
// Indicates the node exists within the "region" "R1" and the "zone"
// "Z2" within the storage provider.
AccessibleTopology *CSITopology
// RequiresNodeStageVolume indicates whether the client should Stage/Unstage
// volumes on this node.
RequiresNodeStageVolume bool
}
func (n *CSINodeInfo) Copy() *CSINodeInfo {
if n == nil {
return nil
}
nc := new(CSINodeInfo)
*nc = *n
nc.AccessibleTopology = n.AccessibleTopology.Copy()
return nc
}
// CSIControllerInfo is the fingerprinted data from a CSI Plugin that is specific to
// the Controller API.
type CSIControllerInfo struct {
// SupportsReadOnlyAttach is set to true when the controller returns the
// ATTACH_READONLY capability.
SupportsReadOnlyAttach bool
// SupportsPublishVolume is true when the controller implements the methods
// required to attach and detach volumes. If this is false Nomad should skip
// the controller attachment flow.
SupportsAttachDetach bool
// SupportsListVolumes is true when the controller implements the ListVolumes
// RPC. NOTE: This does not guaruntee that attached nodes will be returned
// unless SupportsListVolumesAttachedNodes is also true.
SupportsListVolumes bool
// SupportsListVolumesAttachedNodes indicates whether the plugin will return
// attached nodes data when making ListVolume RPCs
SupportsListVolumesAttachedNodes bool
}
func (c *CSIControllerInfo) Copy() *CSIControllerInfo {
if c == nil {
return nil
}
nc := new(CSIControllerInfo)
*nc = *c
return nc
}
// CSIInfo is the current state of a single CSI Plugin. This is updated regularly
// as plugin health changes on the node.
type CSIInfo struct {
PluginID string
AllocID string
Healthy bool
HealthDescription string
UpdateTime time.Time
Provider string // vendor name from CSI GetPluginInfoResponse
ProviderVersion string // vendor version from CSI GetPluginInfoResponse
// RequiresControllerPlugin is set when the CSI Plugin returns the
// CONTROLLER_SERVICE capability. When this is true, the volumes should not be
// scheduled on this client until a matching controller plugin is available.
RequiresControllerPlugin bool
// RequiresTopologies is set when the CSI Plugin returns the
// VOLUME_ACCESSIBLE_CONSTRAINTS capability. When this is true, we must
// respect the Volume and Node Topology information.
RequiresTopologies bool
// CSI Specific metadata
ControllerInfo *CSIControllerInfo `json:",omitempty"`
NodeInfo *CSINodeInfo `json:",omitempty"`
}
func (c *CSIInfo) Copy() *CSIInfo {
if c == nil {
return nil
}
nc := new(CSIInfo)
*nc = *c
nc.ControllerInfo = c.ControllerInfo.Copy()
nc.NodeInfo = c.NodeInfo.Copy()
return nc
}
func (c *CSIInfo) SetHealthy(hs bool) {
c.Healthy = hs
if hs {
c.HealthDescription = "healthy"
} else {
c.HealthDescription = "unhealthy"
}
}
func (c *CSIInfo) Equal(o *CSIInfo) bool {
if c == nil && o == nil {
return c == o
}
nc := *c
nc.UpdateTime = time.Time{}
no := *o
no.UpdateTime = time.Time{}
return reflect.DeepEqual(nc, no)
}
func (c *CSIInfo) IsController() bool {
if c == nil || c.ControllerInfo == nil {
return false
}
return true
}
func (c *CSIInfo) IsNode() bool {
if c == nil || c.NodeInfo == nil {
return false
}
return true
}
// DriverInfo is the current state of a single driver. This is updated
// regularly as driver health changes on the node.
type DriverInfo struct {
Attributes map[string]string
Detected bool
Healthy bool
HealthDescription string
UpdateTime time.Time
}
func (di *DriverInfo) Copy() *DriverInfo {
if di == nil {
return nil
}
cdi := new(DriverInfo)
*cdi = *di
cdi.Attributes = helper.CopyMapStringString(di.Attributes)
return cdi
}
// MergeHealthCheck merges information from a health check for a drier into a
// node's driver info
func (di *DriverInfo) MergeHealthCheck(other *DriverInfo) {
di.Healthy = other.Healthy
di.HealthDescription = other.HealthDescription
di.UpdateTime = other.UpdateTime
}
// MergeFingerprint merges information from fingerprinting a node for a driver
// into a node's driver info for that driver.
func (di *DriverInfo) MergeFingerprintInfo(other *DriverInfo) {
di.Detected = other.Detected
di.Attributes = other.Attributes
}
// DriverInfo determines if two driver info objects are equal..As this is used
// in the process of health checking, we only check the fields that are
// computed by the health checker. In the future, this will be merged.
func (di *DriverInfo) HealthCheckEquals(other *DriverInfo) bool {
if di == nil && other == nil {
return true
}
if di.Healthy != other.Healthy {
return false
}
if di.HealthDescription != other.HealthDescription {
return false
}
return true
}