forked from criteo-forks/consul
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconfig_entry_status.go
358 lines (314 loc) · 11.9 KB
/
config_entry_status.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
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package api
import (
"fmt"
"time"
"golang.org/x/exp/slices"
)
// ResourceReference is a reference to a ConfigEntry
// with an optional reference to a subsection of that ConfigEntry
// that can be specified as SectionName
type ResourceReference struct {
// Kind is the kind of ConfigEntry that this resource refers to.
Kind string
// Name is the identifier for the ConfigEntry this resource refers to.
Name string
// SectionName is a generic subresource identifier that specifies
// a subset of the ConfigEntry to which this reference applies. Usage
// of this field should be up to the controller that leverages it. If
// unused, this should be blank.
SectionName string
// Partition is the partition the config entry is associated with.
// Partitioning is a Consul Enterprise feature.
Partition string `json:",omitempty"`
// Namespace is the namespace the config entry is associated with.
// Namespacing is a Consul Enterprise feature.
Namespace string `json:",omitempty"`
}
// ConfigEntryStatus is used for propagating back asynchronously calculated
// messages from control loops to a user
type ConfigEntryStatus struct {
// Conditions is the set of condition objects associated with
// a ConfigEntry status.
Conditions []Condition
}
// Condition is used for a single message and state associated
// with an object. For example, a ConfigEntry that references
// multiple other resources may have different statuses with
// respect to each of those resources.
type Condition struct {
// Type is a value from a bounded set of types that an object might have
Type string
// Status is a value from a bounded set of statuses that an object might have
Status ConditionStatus
// Reason is a value from a bounded set of reasons for a given status
Reason string
// Message is a message that gives more detailed information about
// why a Condition has a given status and reason
Message string
// Resource is an optional reference to a resource for which this
// condition applies
Resource *ResourceReference
// LastTransitionTime is the time at which this Condition was created
LastTransitionTime *time.Time
}
type (
ConditionStatus string
)
const (
ConditionStatusTrue ConditionStatus = "True"
ConditionStatusFalse ConditionStatus = "False"
ConditionStatusUnknown ConditionStatus = "Unknown"
)
// GatewayConditionType is a type of condition associated with a
// Gateway. This type should be used with the GatewayStatus.Conditions
// field.
type GatewayConditionType string
// GatewayConditionReason defines the set of reasons that explain why a
// particular Gateway condition type has been raised.
type GatewayConditionReason string
// the following are directly from the k8s spec
const (
// This condition is true when the controller managing the Gateway is
// syntactically and semantically valid enough to produce some configuration
// in the underlying data plane. This does not indicate whether or not the
// configuration has been propagated to the data plane.
//
// Possible reasons for this condition to be True are:
//
// * "Accepted"
//
// Possible reasons for this condition to be False are:
//
// * InvalidCertificates
//
GatewayConditionAccepted GatewayConditionType = "Accepted"
// This reason is used with the "Accepted" condition when the condition is
// True.
GatewayReasonAccepted GatewayConditionReason = "Accepted"
// This reason is used with the "Accepted" condition when the gateway has multiple invalid
// certificates and cannot bind to any routes
GatewayReasonInvalidCertificates GatewayConditionReason = "InvalidCertificates"
// This reason is used with the "Accepted" condition when the gateway has multiple invalid
// JWT providers and cannot bind to any routes
GatewayReasonInvalidJWTProviders GatewayConditionReason = "InvalidJWTProviders"
// This condition indicates that the gateway was unable to resolve
// conflicting specification requirements for this Listener. If a
// Listener is conflicted, its network port should not be configured
// on any network elements.
//
// Possible reasons for this condition to be true are:
//
// * "RouteConflict"
//
// Possible reasons for this condition to be False are:
//
// * "NoConflict"
//
// Controllers may raise this condition with other reasons,
// but should prefer to use the reasons listed above to improve
// interoperability.
GatewayConditionConflicted GatewayConditionType = "Conflicted"
// This reason is used with the "Conflicted" condition when the condition
// is False.
GatewayReasonNoConflict GatewayConditionReason = "NoConflict"
// This reason is used with the "Conflicted" condition when the route is
// in a conflicted state, such as when a TCPListener attempts to bind to two routes
GatewayReasonRouteConflict GatewayConditionReason = "RouteConflict"
// This condition indicates whether the controller was able to
// resolve all the object references for the Gateway. When setting this
// condition to False, a ResourceReference to the misconfigured Listener should
// be provided.
//
// Possible reasons for this condition to be true are:
//
// * "ResolvedRefs"
//
// Possible reasons for this condition to be False are:
//
// * "InvalidCertificateRef"
// * "InvalidRouteKinds"
// * "RefNotPermitted"
//
GatewayConditionResolvedRefs GatewayConditionType = "ResolvedRefs"
// This reason is used with the "ResolvedRefs" condition when the condition
// is true.
GatewayReasonResolvedRefs GatewayConditionReason = "ResolvedRefs"
// This reason is used with the "ResolvedRefs" condition when a
// Listener has a TLS configuration with at least one TLS CertificateRef
// that is invalid or does not exist.
// A CertificateRef is considered invalid when it refers to a nonexistent
// or unsupported resource or kind, or when the data within that resource
// is malformed.
// This reason must be used only when the reference is allowed, either by
// referencing an object in the same namespace as the Gateway, or when
// a cross-namespace reference has been explicitly allowed by a ReferenceGrant.
// If the reference is not allowed, the reason RefNotPermitted must be used
// instead.
GatewayListenerReasonInvalidCertificateRef GatewayConditionReason = "InvalidCertificateRef"
// This reason is used with the "ResolvedRefs" condition when a
// Listener has a JWT configuration with at least one JWTProvider
// that is invalid or does not exist.
// A JWTProvider is considered invalid when it refers to a nonexistent
// or unsupported resource or kind, or when the data within that resource
// is malformed.
GatewayListenerReasonInvalidJWTProviderRef GatewayConditionReason = "InvalidJWTProviderRef"
)
var validGatewayConditionReasonsMapping = map[GatewayConditionType]map[ConditionStatus][]GatewayConditionReason{
GatewayConditionAccepted: {
ConditionStatusTrue: {
GatewayReasonAccepted,
},
ConditionStatusFalse: {
GatewayReasonInvalidCertificates,
GatewayReasonInvalidJWTProviders,
},
ConditionStatusUnknown: {},
},
GatewayConditionConflicted: {
ConditionStatusTrue: {
GatewayReasonRouteConflict,
},
ConditionStatusFalse: {
GatewayReasonNoConflict,
},
ConditionStatusUnknown: {},
},
GatewayConditionResolvedRefs: {
ConditionStatusTrue: {
GatewayReasonResolvedRefs,
},
ConditionStatusFalse: {
GatewayListenerReasonInvalidCertificateRef,
GatewayListenerReasonInvalidJWTProviderRef,
},
ConditionStatusUnknown: {},
},
}
func ValidateGatewayConditionReason(name GatewayConditionType, status ConditionStatus, reason GatewayConditionReason) error {
if err := checkConditionStatus(status); err != nil {
return err
}
reasons, ok := validGatewayConditionReasonsMapping[name]
if !ok {
return fmt.Errorf("unrecognized GatewayConditionType %q", name)
}
reasonsForStatus, ok := reasons[status]
if !ok {
return fmt.Errorf("unrecognized ConditionStatus %q", status)
}
if !slices.Contains(reasonsForStatus, reason) {
return fmt.Errorf("gateway condition reason %q not allowed for gateway condition type %q with status %q", reason, name, status)
}
return nil
}
// RouteConditionType is a type of condition for a route.
type RouteConditionType string
// RouteConditionReason is a reason for a route condition.
type RouteConditionReason string
// The following statuses are taken from the K8's Spec
// With the exception of: "RouteReasonInvalidDiscoveryChain" and "NoUpstreamServicesTargeted"
const (
// This condition indicates whether the route has been accepted or rejected
// by a Gateway, and why.
//
// Possible reasons for this condition to be true are:
//
// * "Accepted"
//
// Possible reasons for this condition to be False are:
//
// * "InvalidDiscoveryChain"
// * "NoUpstreamServicesTargeted"
//
//
// Controllers may raise this condition with other reasons,
// but should prefer to use the reasons listed above to improve
// interoperability.
RouteConditionAccepted RouteConditionType = "Accepted"
// This reason is used with the "Accepted" condition when the Route has been
// accepted by the Gateway.
RouteReasonAccepted RouteConditionReason = "Accepted"
// This reason is used with the "Accepted" condition when the route has an
// invalid discovery chain, this includes conditions like the protocol being invalid
// or the discovery chain failing to compile
RouteReasonInvalidDiscoveryChain RouteConditionReason = "InvalidDiscoveryChain"
// This reason is used with the "Accepted" condition when the route
RouteReasonNoUpstreamServicesTargeted RouteConditionReason = "NoUpstreamServicesTargeted"
)
// the following statuses are custom to Consul
const (
// This condition indicates whether the route was able to successfully bind the
// Listener on the gateway
// Possible reasons for this condition to be true are:
//
// * "Bound"
//
// Possible reasons for this condition to be false are:
//
// * "FailedToBind"
// * "GatewayNotFound"
//
RouteConditionBound RouteConditionType = "Bound"
// This reason is used with the "Bound" condition when the condition
// is true
RouteReasonBound RouteConditionReason = "Bound"
// This reason is used with the "Bound" condition when the route failed
// to bind to the gateway
RouteReasonFailedToBind RouteConditionReason = "FailedToBind"
// This reason is used with the "Bound" condition when the route fails
// to find the gateway
RouteReasonGatewayNotFound RouteConditionReason = "GatewayNotFound"
// This reason is used with the "Accepted" condition when the route references non-existent
// JWTProviders
RouteReasonJWTProvidersNotFound RouteConditionReason = "JWTProvidersNotFound"
)
var validRouteConditionReasonsMapping = map[RouteConditionType]map[ConditionStatus][]RouteConditionReason{
RouteConditionAccepted: {
ConditionStatusTrue: {
RouteReasonAccepted,
},
ConditionStatusFalse: {
RouteReasonInvalidDiscoveryChain,
RouteReasonNoUpstreamServicesTargeted,
},
ConditionStatusUnknown: {},
},
RouteConditionBound: {
ConditionStatusTrue: {
RouteReasonBound,
},
ConditionStatusFalse: {
RouteReasonGatewayNotFound,
RouteReasonFailedToBind,
RouteReasonJWTProvidersNotFound,
},
ConditionStatusUnknown: {},
},
}
func ValidateRouteConditionReason(name RouteConditionType, status ConditionStatus, reason RouteConditionReason) error {
if err := checkConditionStatus(status); err != nil {
return err
}
reasons, ok := validRouteConditionReasonsMapping[name]
if !ok {
return fmt.Errorf("unrecognized RouteConditionType %s", name)
}
reasonsForStatus, ok := reasons[status]
if !ok {
return fmt.Errorf("unrecognized ConditionStatus %s", name)
}
if !slices.Contains(reasonsForStatus, reason) {
return fmt.Errorf("route condition reason %s not allowed for route condition type %s with status %s", reason, name, status)
}
return nil
}
func checkConditionStatus(status ConditionStatus) error {
switch status {
case ConditionStatusTrue, ConditionStatusFalse, ConditionStatusUnknown:
return nil
default:
return fmt.Errorf("unrecognized condition status: %q", status)
}
}