/
catalogsource_types.go
326 lines (278 loc) · 12.8 KB
/
catalogsource_types.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
package v1alpha1
import (
"encoding/json"
"fmt"
"time"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
const (
CatalogSourceCRDAPIVersion = GroupName + "/" + GroupVersion
CatalogSourceKind = "CatalogSource"
DefaultRegistryPollDuration = 15 * time.Minute
)
// SourceType indicates the type of backing store for a CatalogSource
type SourceType string
const (
// SourceTypeInternal (deprecated) specifies a CatalogSource of type SourceTypeConfigmap
SourceTypeInternal SourceType = "internal"
// SourceTypeConfigmap specifies a CatalogSource that generates a configmap-server registry
SourceTypeConfigmap SourceType = "configmap"
// SourceTypeGrpc specifies a CatalogSource that can use an operator registry image to generate a
// registry-server or connect to a pre-existing registry at an address.
SourceTypeGrpc SourceType = "grpc"
)
const (
// CatalogSourceSpecInvalidError denotes when fields on the spec of the CatalogSource are not valid.
CatalogSourceSpecInvalidError ConditionReason = "SpecInvalidError"
// CatalogSourceConfigMapError denotes when there is an issue extracting manifests from the specified ConfigMap.
CatalogSourceConfigMapError ConditionReason = "ConfigMapError"
// CatalogSourceRegistryServerError denotes when there is an issue querying the specified registry server.
CatalogSourceRegistryServerError ConditionReason = "RegistryServerError"
// CatalogSourceIntervalInvalidError denotes if the registry polling interval is invalid.
CatalogSourceIntervalInvalidError ConditionReason = "InvalidIntervalError"
)
type CatalogSourceSpec struct {
// SourceType is the type of source
SourceType SourceType `json:"sourceType"`
// Priority field assigns a weight to the catalog source to prioritize them so that it can be consumed by the dependency resolver.
// Usage:
// Higher weight indicates that this catalog source is preferred over lower weighted catalog sources during dependency resolution.
// The range of the priority value can go from positive to negative in the range of int32.
// The default value to a catalog source with unassigned priority would be 0.
// The catalog source with the same priority values will be ranked lexicographically based on its name.
// +optional
Priority int `json:"priority,omitempty"`
// ConfigMap is the name of the ConfigMap to be used to back a configmap-server registry.
// Only used when SourceType = SourceTypeConfigmap or SourceTypeInternal.
// +optional
ConfigMap string `json:"configMap,omitempty"`
// Address is a host that OLM can use to connect to a pre-existing registry.
// Format: <registry-host or ip>:<port>
// Only used when SourceType = SourceTypeGrpc.
// Ignored when the Image field is set.
// +optional
Address string `json:"address,omitempty"`
// Image is an operator-registry container image to instantiate a registry-server with.
// Only used when SourceType = SourceTypeGrpc.
// If present, the address field is ignored.
// +optional
Image string `json:"image,omitempty"`
// GrpcPodConfig exposes different overrides for the pod spec of the CatalogSource Pod.
// Only used when SourceType = SourceTypeGrpc and Image is set.
// +optional
GrpcPodConfig *GrpcPodConfig `json:"grpcPodConfig,omitempty"`
// UpdateStrategy defines how updated catalog source images can be discovered
// Consists of an interval that defines polling duration and an embedded strategy type
// +optional
UpdateStrategy *UpdateStrategy `json:"updateStrategy,omitempty"`
// Secrets represent set of secrets that can be used to access the contents of the catalog.
// It is best to keep this list small, since each will need to be tried for every catalog entry.
// +optional
Secrets []string `json:"secrets,omitempty"`
// Metadata
DisplayName string `json:"displayName,omitempty"`
Description string `json:"description,omitempty"`
Publisher string `json:"publisher,omitempty"`
Icon Icon `json:"icon,omitempty"`
}
type SecurityConfig string
const (
Legacy SecurityConfig = "legacy"
Restricted SecurityConfig = "restricted"
)
// GrpcPodConfig contains configuration specified for a catalog source
type GrpcPodConfig struct {
// NodeSelector is a selector which must be true for the pod to fit on a node.
// Selector which must match a node's labels for the pod to be scheduled on that node.
// +optional
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
// Tolerations are the catalog source's pod's tolerations.
// +optional
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
// If specified, indicates the pod's priority.
// If not specified, the pod priority will be default or zero if there is no
// default.
// +optional
PriorityClassName *string `json:"priorityClassName,omitempty"`
// SecurityContextConfig can be one of `legacy` or `restricted`. The CatalogSource's pod is either injected with
// the right pod.spec.securityContext and pod.spec.container[*].securityContext values to allow the pod to run in
// Pod Security Admission(PSA) controller's `restricted` mode, or doesn't set these values at all, in which case the pod
// can only be run in PSA `baseline` or `privileged` namespaces. By default, SecurityContextConfig is set to `restricted`.
// If the value is unspecified, the default value of `restricted` is used. Specifying any other value will result in a
// validation error. When using older catalog images, which could not be run in `restricted` mode, the SecurityContextConfig
// should be set to `legacy`.
// More information about PSA can be found here: https://kubernetes.io/docs/concepts/security/pod-security-admission/'
// +optional
// +kubebuilder:validation:Enum=legacy;restricted
// +kubebuilder:default:=restricted
SecurityContextConfig SecurityConfig `json:"securityContextConfig,omitempty"`
}
// UpdateStrategy holds all the different types of catalog source update strategies
// Currently only registry polling strategy is implemented
type UpdateStrategy struct {
*RegistryPoll `json:"registryPoll,omitempty"`
}
type RegistryPoll struct {
// Interval is used to determine the time interval between checks of the latest catalog source version.
// The catalog operator polls to see if a new version of the catalog source is available.
// If available, the latest image is pulled and gRPC traffic is directed to the latest catalog source.
RawInterval string `json:"interval,omitempty"`
Interval *metav1.Duration `json:"-"`
ParsingError string `json:"-"`
}
// UnmarshalJSON implements the encoding/json.Unmarshaler interface.
func (u *UpdateStrategy) UnmarshalJSON(data []byte) (err error) {
type alias struct {
*RegistryPoll `json:"registryPoll,omitempty"`
}
us := alias{}
if err = json.Unmarshal(data, &us); err != nil {
return err
}
registryPoll := &RegistryPoll{
RawInterval: us.RegistryPoll.RawInterval,
}
duration, err := time.ParseDuration(registryPoll.RawInterval)
if err != nil {
registryPoll.ParsingError = fmt.Sprintf("error parsing spec.updateStrategy.registryPoll.interval. Using the default value of %s instead. Error: %s", DefaultRegistryPollDuration, err)
registryPoll.Interval = &metav1.Duration{Duration: DefaultRegistryPollDuration}
} else {
registryPoll.Interval = &metav1.Duration{Duration: duration}
}
u.RegistryPoll = registryPoll
return nil
}
type RegistryServiceStatus struct {
Protocol string `json:"protocol,omitempty"`
ServiceName string `json:"serviceName,omitempty"`
ServiceNamespace string `json:"serviceNamespace,omitempty"`
Port string `json:"port,omitempty"`
CreatedAt metav1.Time `json:"createdAt,omitempty"`
}
func (s *RegistryServiceStatus) Address() string {
return fmt.Sprintf("%s.%s.svc:%s", s.ServiceName, s.ServiceNamespace, s.Port)
}
type GRPCConnectionState struct {
Address string `json:"address,omitempty"`
LastObservedState string `json:"lastObservedState"`
LastConnectTime metav1.Time `json:"lastConnect,omitempty"`
}
type CatalogSourceStatus struct {
// A human readable message indicating details about why the CatalogSource is in this condition.
// +optional
Message string `json:"message,omitempty"`
// Reason is the reason the CatalogSource was transitioned to its current state.
// +optional
Reason ConditionReason `json:"reason,omitempty"`
// The last time the CatalogSource image registry has been polled to ensure the image is up-to-date
LatestImageRegistryPoll *metav1.Time `json:"latestImageRegistryPoll,omitempty"`
ConfigMapResource *ConfigMapResourceReference `json:"configMapReference,omitempty"`
RegistryServiceStatus *RegistryServiceStatus `json:"registryService,omitempty"`
GRPCConnectionState *GRPCConnectionState `json:"connectionState,omitempty"`
// Represents the state of a CatalogSource. Note that Message and Reason represent the original
// status information, which may be migrated to be conditions based in the future. Any new features
// introduced will use conditions.
// +optional
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"`
}
type ConfigMapResourceReference struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
UID types.UID `json:"uid,omitempty"`
ResourceVersion string `json:"resourceVersion,omitempty"`
LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"`
}
func (r *ConfigMapResourceReference) IsAMatch(object *metav1.ObjectMeta) bool {
return r.UID == object.GetUID() && r.ResourceVersion == object.GetResourceVersion()
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient
// +kubebuilder:resource:shortName=catsrc,categories=olm
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Display",type=string,JSONPath=`.spec.displayName`,description="The pretty name of the catalog"
// +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.sourceType`,description="The type of the catalog"
// +kubebuilder:printcolumn:name="Publisher",type=string,JSONPath=`.spec.publisher`,description="The publisher of the catalog"
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// CatalogSource is a repository of CSVs, CRDs, and operator packages.
type CatalogSource struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
Spec CatalogSourceSpec `json:"spec"`
// +optional
Status CatalogSourceStatus `json:"status"`
}
func (c *CatalogSource) Address() string {
if c.Spec.Address != "" {
return c.Spec.Address
}
return c.Status.RegistryServiceStatus.Address()
}
func (c *CatalogSource) SetError(reason ConditionReason, err error) {
c.Status.Reason = reason
c.Status.Message = ""
if err != nil {
c.Status.Message = err.Error()
}
}
func (c *CatalogSource) SetLastUpdateTime() {
now := metav1.Now()
c.Status.LatestImageRegistryPoll = &now
}
// Check if it is time to update based on polling setting
func (c *CatalogSource) Update() bool {
if !c.Poll() {
return false
}
interval := c.Spec.UpdateStrategy.Interval.Duration
latest := c.Status.LatestImageRegistryPoll
if latest == nil {
logrus.WithField("CatalogSource", c.Name).Debugf("latest poll %v", latest)
} else {
logrus.WithField("CatalogSource", c.Name).Debugf("latest poll %v", *c.Status.LatestImageRegistryPoll)
}
if c.Status.LatestImageRegistryPoll.IsZero() {
logrus.WithField("CatalogSource", c.Name).Debugf("creation timestamp plus interval before now %t", c.CreationTimestamp.Add(interval).Before(time.Now()))
if c.CreationTimestamp.Add(interval).Before(time.Now()) {
return true
}
} else {
logrus.WithField("CatalogSource", c.Name).Debugf("latest poll plus interval before now %t", c.Status.LatestImageRegistryPoll.Add(interval).Before(time.Now()))
if c.Status.LatestImageRegistryPoll.Add(interval).Before(time.Now()) {
return true
}
}
return false
}
// Poll determines whether the polling feature is enabled on the particular catalog source
func (c *CatalogSource) Poll() bool {
if c.Spec.UpdateStrategy == nil {
return false
}
// if polling interval is zero polling will not be done
if c.Spec.UpdateStrategy.RegistryPoll == nil {
return false
}
// if catalog source is not backed by an image polling will not be done
if c.Spec.Image == "" {
return false
}
// if image is not type gRPC polling will not be done
if c.Spec.SourceType != SourceTypeGrpc {
return false
}
return true
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// CatalogSourceList is a repository of CSVs, CRDs, and operator packages.
type CatalogSourceList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []CatalogSource `json:"items"`
}