@@ -2,13 +2,22 @@ use dockerfile_parser::ImageRef;
22use k8s_openapi:: api:: core:: v1:: LocalObjectReference ;
33use schemars:: JsonSchema ;
44use serde:: { Deserialize , Serialize } ;
5+ use snafu:: { ResultExt , Snafu } ;
56use strum:: AsRefStr ;
67
7- #[ cfg( doc) ]
8- use crate :: kvp:: Labels ;
8+ use crate :: kvp:: { LABEL_VALUE_MAX_LEN , LabelValue , LabelValueError } ;
99
1010pub const STACKABLE_DOCKER_REPO : & str = "oci.stackable.tech/sdp" ;
1111
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+
1221/// Specify which image to use, the easiest way is to only configure the `productVersion`.
1322/// You can also configure a custom image registry to pull from, as well as completely custom
1423/// images.
@@ -67,8 +76,8 @@ pub struct ResolvedProductImage {
6776 /// Version of the product, e.g. `1.4.1`.
6877 pub product_version : String ,
6978
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 ,
7281
7382 /// Image to be used for the product image e.g. `oci.stackable.tech/sdp/superset:1.4.1-stackable2.1.0`
7483 pub image : String ,
@@ -101,7 +110,11 @@ impl ProductImage {
101110 /// `image_base_name` should be base of the image name in the container image registry, e.g. `trino`.
102111 /// `operator_version` needs to be the full operator version and a valid semver string.
103112 /// 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 > {
105118 let image_pull_policy = self . pull_policy . as_ref ( ) . to_string ( ) ;
106119 let pull_secrets = self . pull_secrets . clone ( ) ;
107120
@@ -111,17 +124,17 @@ impl ProductImage {
111124 ProductImageSelection :: Custom ( image_selection) => {
112125 let image = ImageRef :: parse ( & image_selection. custom ) ;
113126 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 ) ;
117127
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 {
119132 product_version,
120- app_version_label ,
133+ app_version_label_value ,
121134 image : image_selection. custom . clone ( ) ,
122135 image_pull_policy,
123136 pull_secrets,
124- }
137+ } )
125138 }
126139 ProductImageSelection :: StackableVersion ( image_selection) => {
127140 let repo = image_selection
@@ -147,14 +160,15 @@ impl ProductImage {
147160 let image = format ! (
148161 "{repo}/{image_base_name}:{product_version}-stackable{stackable_version}" ,
149162 ) ;
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 {
152166 product_version,
153- app_version_label ,
167+ app_version_label_value ,
154168 image,
155169 image_pull_policy,
156170 pull_secrets,
157- }
171+ } )
158172 }
159173 }
160174 }
@@ -174,6 +188,21 @@ impl ProductImage {
174188 } ) => pv,
175189 }
176190 }
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+ }
177206}
178207
179208#[ cfg( test) ]
@@ -191,7 +220,7 @@ mod tests {
191220 "# ,
192221 ResolvedProductImage {
193222 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" ) ,
195224 product_version: "1.4.1" . to_string( ) ,
196225 image_pull_policy: "Always" . to_string( ) ,
197226 pull_secrets: None ,
@@ -205,7 +234,7 @@ mod tests {
205234 "# ,
206235 ResolvedProductImage {
207236 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" ) ,
209238 product_version: "1.4.1" . to_string( ) ,
210239 image_pull_policy: "Always" . to_string( ) ,
211240 pull_secrets: None ,
@@ -219,7 +248,7 @@ mod tests {
219248 "# ,
220249 ResolvedProductImage {
221250 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" ) ,
223252 product_version: "1.4.1" . to_string( ) ,
224253 image_pull_policy: "Always" . to_string( ) ,
225254 pull_secrets: None ,
@@ -234,7 +263,7 @@ mod tests {
234263 "# ,
235264 ResolvedProductImage {
236265 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" ) ,
238267 product_version: "1.4.1" . to_string( ) ,
239268 image_pull_policy: "Always" . to_string( ) ,
240269 pull_secrets: None ,
@@ -250,7 +279,7 @@ mod tests {
250279 "# ,
251280 ResolvedProductImage {
252281 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" ) ,
254283 product_version: "1.4.1" . to_string( ) ,
255284 image_pull_policy: "Always" . to_string( ) ,
256285 pull_secrets: None ,
@@ -265,7 +294,7 @@ mod tests {
265294 "# ,
266295 ResolvedProductImage {
267296 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" ) ,
269298 product_version: "1.4.1" . to_string( ) ,
270299 image_pull_policy: "Always" . to_string( ) ,
271300 pull_secrets: None ,
@@ -280,7 +309,7 @@ mod tests {
280309 "# ,
281310 ResolvedProductImage {
282311 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" ) ,
284313 product_version: "1.4.1" . to_string( ) ,
285314 image_pull_policy: "Always" . to_string( ) ,
286315 pull_secrets: None ,
@@ -295,7 +324,7 @@ mod tests {
295324 "# ,
296325 ResolvedProductImage {
297326 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" ) ,
299328 product_version: "1.4.1" . to_string( ) ,
300329 image_pull_policy: "Always" . to_string( ) ,
301330 pull_secrets: None ,
@@ -310,7 +339,7 @@ mod tests {
310339 "# ,
311340 ResolvedProductImage {
312341 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" ) ,
314343 product_version: "1.4.1" . to_string( ) ,
315344 image_pull_policy: "Always" . to_string( ) ,
316345 pull_secrets: None ,
@@ -325,7 +354,7 @@ mod tests {
325354 "# ,
326355 ResolvedProductImage {
327356 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" ) ,
329358 product_version: "1.4.1" . to_string( ) ,
330359 image_pull_policy: "Always" . to_string( ) ,
331360 pull_secrets: None ,
@@ -341,7 +370,7 @@ mod tests {
341370 "# ,
342371 ResolvedProductImage {
343372 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" ) ,
345374 product_version: "1.4.1" . to_string( ) ,
346375 image_pull_policy: "Always" . to_string( ) ,
347376 pull_secrets: None ,
@@ -357,7 +386,7 @@ mod tests {
357386 "# ,
358387 ResolvedProductImage {
359388 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" ) ,
361390 product_version: "1.4.1" . to_string( ) ,
362391 image_pull_policy: "IfNotPresent" . to_string( ) ,
363392 pull_secrets: None ,
@@ -373,7 +402,7 @@ mod tests {
373402 "# ,
374403 ResolvedProductImage {
375404 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" ) ,
377406 product_version: "1.4.1" . to_string( ) ,
378407 image_pull_policy: "Always" . to_string( ) ,
379408 pull_secrets: None ,
@@ -389,7 +418,7 @@ mod tests {
389418 "# ,
390419 ResolvedProductImage {
391420 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" ) ,
393422 product_version: "1.4.1" . to_string( ) ,
394423 image_pull_policy: "Never" . to_string( ) ,
395424 pull_secrets: None ,
@@ -408,7 +437,7 @@ mod tests {
408437 "# ,
409438 ResolvedProductImage {
410439 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" ) ,
412441 product_version: "1.4.1" . to_string( ) ,
413442 image_pull_policy: "Always" . to_string( ) ,
414443 pull_secrets: Some ( vec![ LocalObjectReference { name: "myPullSecrets1" . to_string( ) } , LocalObjectReference { name: "myPullSecrets2" . to_string( ) } ] ) ,
@@ -421,7 +450,9 @@ mod tests {
421450 #[ case] expected : ResolvedProductImage ,
422451 ) {
423452 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" ) ;
425456
426457 assert_eq ! ( resolved_product_image, expected) ;
427458 }
0 commit comments