@@ -2,13 +2,22 @@ use dockerfile_parser::ImageRef;
2
2
use k8s_openapi:: api:: core:: v1:: LocalObjectReference ;
3
3
use schemars:: JsonSchema ;
4
4
use serde:: { Deserialize , Serialize } ;
5
+ use snafu:: { ResultExt , Snafu } ;
5
6
use strum:: AsRefStr ;
6
7
7
- #[ cfg( doc) ]
8
- use crate :: kvp:: Labels ;
8
+ use crate :: kvp:: { LABEL_VALUE_MAX_LEN , LabelValue , LabelValueError } ;
9
9
10
10
pub const STACKABLE_DOCKER_REPO : & str = "oci.stackable.tech/sdp" ;
11
11
12
+ #[ derive( Debug , Snafu ) ]
13
+ pub enum Error {
14
+ #[ snafu( display( "could not parse or create label from app version {app_version:?}" ) ) ]
15
+ ParseAppVersionLabel {
16
+ source : LabelValueError ,
17
+ app_version : String ,
18
+ } ,
19
+ }
20
+
12
21
/// Specify which image to use, the easiest way is to only configure the `productVersion`.
13
22
/// You can also configure a custom image registry to pull from, as well as completely custom
14
23
/// images.
@@ -67,8 +76,8 @@ pub struct ResolvedProductImage {
67
76
/// Version of the product, e.g. `1.4.1`.
68
77
pub product_version : String ,
69
78
70
- /// App version as formatted for [` Labels::recommended`]
71
- pub app_version_label : String ,
79
+ /// App version formatted for Labels
80
+ pub app_version_label_value : LabelValue ,
72
81
73
82
/// Image to be used for the product image e.g. `oci.stackable.tech/sdp/superset:1.4.1-stackable2.1.0`
74
83
pub image : String ,
@@ -101,7 +110,11 @@ impl ProductImage {
101
110
/// `image_base_name` should be base of the image name in the container image registry, e.g. `trino`.
102
111
/// `operator_version` needs to be the full operator version and a valid semver string.
103
112
/// Accepted values are `23.7.0`, `0.0.0-dev` or `0.0.0-pr123`. Other variants are not supported.
104
- pub fn resolve ( & self , image_base_name : & str , operator_version : & str ) -> ResolvedProductImage {
113
+ pub fn resolve (
114
+ & self ,
115
+ image_base_name : & str ,
116
+ operator_version : & str ,
117
+ ) -> Result < ResolvedProductImage , Error > {
105
118
let image_pull_policy = self . pull_policy . as_ref ( ) . to_string ( ) ;
106
119
let pull_secrets = self . pull_secrets . clone ( ) ;
107
120
@@ -111,17 +124,17 @@ impl ProductImage {
111
124
ProductImageSelection :: Custom ( image_selection) => {
112
125
let image = ImageRef :: parse ( & image_selection. custom ) ;
113
126
let image_tag_or_hash = image. tag . or ( image. hash ) . unwrap_or ( "latest" . to_string ( ) ) ;
114
- let mut app_version_label = format ! ( "{}-{}" , product_version, image_tag_or_hash) ;
115
- // TODO Use new label mechanism once added
116
- app_version_label. truncate ( 63 ) ;
117
127
118
- ResolvedProductImage {
128
+ let app_version = format ! ( "{}-{}" , product_version, image_tag_or_hash) ;
129
+ let app_version_label_value = Self :: prepare_app_version_label_value ( & app_version) ?;
130
+
131
+ Ok ( ResolvedProductImage {
119
132
product_version,
120
- app_version_label ,
133
+ app_version_label_value ,
121
134
image : image_selection. custom . clone ( ) ,
122
135
image_pull_policy,
123
136
pull_secrets,
124
- }
137
+ } )
125
138
}
126
139
ProductImageSelection :: StackableVersion ( image_selection) => {
127
140
let repo = image_selection
@@ -147,14 +160,15 @@ impl ProductImage {
147
160
let image = format ! (
148
161
"{repo}/{image_base_name}:{product_version}-stackable{stackable_version}" ,
149
162
) ;
150
- let app_version_label = format ! ( "{product_version}-stackable{stackable_version}" , ) ;
151
- ResolvedProductImage {
163
+ let app_version = format ! ( "{product_version}-stackable{stackable_version}" ) ;
164
+ let app_version_label_value = Self :: prepare_app_version_label_value ( & app_version) ?;
165
+ Ok ( ResolvedProductImage {
152
166
product_version,
153
- app_version_label ,
167
+ app_version_label_value ,
154
168
image,
155
169
image_pull_policy,
156
170
pull_secrets,
157
- }
171
+ } )
158
172
}
159
173
}
160
174
}
@@ -174,6 +188,21 @@ impl ProductImage {
174
188
} ) => pv,
175
189
}
176
190
}
191
+
192
+ fn prepare_app_version_label_value ( app_version : & str ) -> Result < LabelValue , Error > {
193
+ let mut formatted_app_version = app_version. to_string ( ) ;
194
+ // Labels cannot have more than `LABEL_VALUE_MAX_LEN` characters.
195
+ formatted_app_version. truncate ( LABEL_VALUE_MAX_LEN ) ;
196
+ // The hash has the format `sha256:85fa483aa99b9997ce476b86893ad5ed81fb7fd2db602977eb`
197
+ // As the colon (`:`) is not a valid label value character, we replace it with a valid "-" character.
198
+ let formatted_app_version = formatted_app_version. replace ( ":" , "-" ) ;
199
+
200
+ formatted_app_version
201
+ . parse ( )
202
+ . with_context ( |_| ParseAppVersionLabelSnafu {
203
+ app_version : formatted_app_version. to_string ( ) ,
204
+ } )
205
+ }
177
206
}
178
207
179
208
#[ cfg( test) ]
@@ -191,7 +220,7 @@ mod tests {
191
220
"# ,
192
221
ResolvedProductImage {
193
222
image: "oci.stackable.tech/sdp/superset:1.4.1-stackable23.7.42" . to_string( ) ,
194
- app_version_label : "1.4.1-stackable23.7.42" . to_string ( ) ,
223
+ app_version_label_value : "1.4.1-stackable23.7.42" . parse ( ) . expect ( "static app version label is always valid" ) ,
195
224
product_version: "1.4.1" . to_string( ) ,
196
225
image_pull_policy: "Always" . to_string( ) ,
197
226
pull_secrets: None ,
@@ -205,7 +234,7 @@ mod tests {
205
234
"# ,
206
235
ResolvedProductImage {
207
236
image: "oci.stackable.tech/sdp/superset:1.4.1-stackable0.0.0-dev" . to_string( ) ,
208
- app_version_label : "1.4.1-stackable0.0.0-dev" . to_string ( ) ,
237
+ app_version_label_value : "1.4.1-stackable0.0.0-dev" . parse ( ) . expect ( "static app version label is always valid" ) ,
209
238
product_version: "1.4.1" . to_string( ) ,
210
239
image_pull_policy: "Always" . to_string( ) ,
211
240
pull_secrets: None ,
@@ -219,7 +248,7 @@ mod tests {
219
248
"# ,
220
249
ResolvedProductImage {
221
250
image: "oci.stackable.tech/sdp/superset:1.4.1-stackable0.0.0-dev" . to_string( ) ,
222
- app_version_label : "1.4.1-stackable0.0.0-dev" . to_string ( ) ,
251
+ app_version_label_value : "1.4.1-stackable0.0.0-dev" . parse ( ) . expect ( "static app version label is always valid" ) ,
223
252
product_version: "1.4.1" . to_string( ) ,
224
253
image_pull_policy: "Always" . to_string( ) ,
225
254
pull_secrets: None ,
@@ -234,7 +263,7 @@ mod tests {
234
263
"# ,
235
264
ResolvedProductImage {
236
265
image: "oci.stackable.tech/sdp/superset:1.4.1-stackable2.1.0" . to_string( ) ,
237
- app_version_label : "1.4.1-stackable2.1.0" . to_string ( ) ,
266
+ app_version_label_value : "1.4.1-stackable2.1.0" . parse ( ) . expect ( "static app version label is always valid" ) ,
238
267
product_version: "1.4.1" . to_string( ) ,
239
268
image_pull_policy: "Always" . to_string( ) ,
240
269
pull_secrets: None ,
@@ -250,7 +279,7 @@ mod tests {
250
279
"# ,
251
280
ResolvedProductImage {
252
281
image: "my.corp/myteam/stackable/trino:1.4.1-stackable2.1.0" . to_string( ) ,
253
- app_version_label : "1.4.1-stackable2.1.0" . to_string ( ) ,
282
+ app_version_label_value : "1.4.1-stackable2.1.0" . parse ( ) . expect ( "static app version label is always valid" ) ,
254
283
product_version: "1.4.1" . to_string( ) ,
255
284
image_pull_policy: "Always" . to_string( ) ,
256
285
pull_secrets: None ,
@@ -265,7 +294,7 @@ mod tests {
265
294
"# ,
266
295
ResolvedProductImage {
267
296
image: "my.corp/myteam/stackable/superset" . to_string( ) ,
268
- app_version_label : "1.4.1-latest" . to_string ( ) ,
297
+ app_version_label_value : "1.4.1-latest" . parse ( ) . expect ( "static app version label is always valid" ) ,
269
298
product_version: "1.4.1" . to_string( ) ,
270
299
image_pull_policy: "Always" . to_string( ) ,
271
300
pull_secrets: None ,
@@ -280,7 +309,7 @@ mod tests {
280
309
"# ,
281
310
ResolvedProductImage {
282
311
image: "my.corp/myteam/stackable/superset:latest-and-greatest" . to_string( ) ,
283
- app_version_label : "1.4.1-latest-and-greatest" . to_string ( ) ,
312
+ app_version_label_value : "1.4.1-latest-and-greatest" . parse ( ) . expect ( "static app version label is always valid" ) ,
284
313
product_version: "1.4.1" . to_string( ) ,
285
314
image_pull_policy: "Always" . to_string( ) ,
286
315
pull_secrets: None ,
@@ -295,7 +324,7 @@ mod tests {
295
324
"# ,
296
325
ResolvedProductImage {
297
326
image: "127.0.0.1:8080/myteam/stackable/superset" . to_string( ) ,
298
- app_version_label : "1.4.1-latest" . to_string ( ) ,
327
+ app_version_label_value : "1.4.1-latest" . parse ( ) . expect ( "static app version label is always valid" ) ,
299
328
product_version: "1.4.1" . to_string( ) ,
300
329
image_pull_policy: "Always" . to_string( ) ,
301
330
pull_secrets: None ,
@@ -310,7 +339,7 @@ mod tests {
310
339
"# ,
311
340
ResolvedProductImage {
312
341
image: "127.0.0.1:8080/myteam/stackable/superset:latest-and-greatest" . to_string( ) ,
313
- app_version_label : "1.4.1-latest-and-greatest" . to_string ( ) ,
342
+ app_version_label_value : "1.4.1-latest-and-greatest" . parse ( ) . expect ( "static app version label is always valid" ) ,
314
343
product_version: "1.4.1" . to_string( ) ,
315
344
image_pull_policy: "Always" . to_string( ) ,
316
345
pull_secrets: None ,
@@ -325,7 +354,7 @@ mod tests {
325
354
"# ,
326
355
ResolvedProductImage {
327
356
image: "oci.stackable.tech/sdp/superset@sha256:85fa483aa99b9997ce476b86893ad5ed81fb7fd2db602977eb8c42f76efc1098" . to_string( ) ,
328
- app_version_label : "1.4.1-sha256: 85fa483aa99b9997ce476b86893ad5ed81fb7fd2db602977eb" . to_string ( ) ,
357
+ app_version_label_value : "1.4.1-sha256- 85fa483aa99b9997ce476b86893ad5ed81fb7fd2db602977eb" . parse ( ) . expect ( "static app version label is always valid" ) ,
329
358
product_version: "1.4.1" . to_string( ) ,
330
359
image_pull_policy: "Always" . to_string( ) ,
331
360
pull_secrets: None ,
@@ -341,7 +370,7 @@ mod tests {
341
370
"# ,
342
371
ResolvedProductImage {
343
372
image: "my.corp/myteam/stackable/superset:latest-and-greatest" . to_string( ) ,
344
- app_version_label : "1.4.1-latest-and-greatest" . to_string ( ) ,
373
+ app_version_label_value : "1.4.1-latest-and-greatest" . parse ( ) . expect ( "static app version label is always valid" ) ,
345
374
product_version: "1.4.1" . to_string( ) ,
346
375
image_pull_policy: "Always" . to_string( ) ,
347
376
pull_secrets: None ,
@@ -357,7 +386,7 @@ mod tests {
357
386
"# ,
358
387
ResolvedProductImage {
359
388
image: "my.corp/myteam/stackable/superset:latest-and-greatest" . to_string( ) ,
360
- app_version_label : "1.4.1-latest-and-greatest" . to_string ( ) ,
389
+ app_version_label_value : "1.4.1-latest-and-greatest" . parse ( ) . expect ( "static app version label is always valid" ) ,
361
390
product_version: "1.4.1" . to_string( ) ,
362
391
image_pull_policy: "IfNotPresent" . to_string( ) ,
363
392
pull_secrets: None ,
@@ -373,7 +402,7 @@ mod tests {
373
402
"# ,
374
403
ResolvedProductImage {
375
404
image: "my.corp/myteam/stackable/superset:latest-and-greatest" . to_string( ) ,
376
- app_version_label : "1.4.1-latest-and-greatest" . to_string ( ) ,
405
+ app_version_label_value : "1.4.1-latest-and-greatest" . parse ( ) . expect ( "static app version label is always valid" ) ,
377
406
product_version: "1.4.1" . to_string( ) ,
378
407
image_pull_policy: "Always" . to_string( ) ,
379
408
pull_secrets: None ,
@@ -389,7 +418,7 @@ mod tests {
389
418
"# ,
390
419
ResolvedProductImage {
391
420
image: "my.corp/myteam/stackable/superset:latest-and-greatest" . to_string( ) ,
392
- app_version_label : "1.4.1-latest-and-greatest" . to_string ( ) ,
421
+ app_version_label_value : "1.4.1-latest-and-greatest" . parse ( ) . expect ( "static app version label is always valid" ) ,
393
422
product_version: "1.4.1" . to_string( ) ,
394
423
image_pull_policy: "Never" . to_string( ) ,
395
424
pull_secrets: None ,
@@ -408,7 +437,7 @@ mod tests {
408
437
"# ,
409
438
ResolvedProductImage {
410
439
image: "my.corp/myteam/stackable/superset:latest-and-greatest" . to_string( ) ,
411
- app_version_label : "1.4.1-latest-and-greatest" . to_string ( ) ,
440
+ app_version_label_value : "1.4.1-latest-and-greatest" . parse ( ) . expect ( "static app version label is always valid" ) ,
412
441
product_version: "1.4.1" . to_string( ) ,
413
442
image_pull_policy: "Always" . to_string( ) ,
414
443
pull_secrets: Some ( vec![ LocalObjectReference { name: "myPullSecrets1" . to_string( ) } , LocalObjectReference { name: "myPullSecrets2" . to_string( ) } ] ) ,
@@ -421,7 +450,9 @@ mod tests {
421
450
#[ case] expected : ResolvedProductImage ,
422
451
) {
423
452
let product_image: ProductImage = serde_yaml:: from_str ( & input) . expect ( "Illegal test input" ) ;
424
- let resolved_product_image = product_image. resolve ( & image_base_name, & operator_version) ;
453
+ let resolved_product_image = product_image
454
+ . resolve ( & image_base_name, & operator_version)
455
+ . expect ( "Illegal test input" ) ;
425
456
426
457
assert_eq ! ( resolved_product_image, expected) ;
427
458
}
0 commit comments