22use crate :: errors:: FlokiError ;
33use crate :: image;
44use serde:: { Deserialize , Serialize } ;
5+ use serde_yaml:: { Mapping as YamlMapping , Value as YamlValue } ;
56use tera:: from_value;
67use tera:: Context ;
78use tera:: Tera ;
@@ -128,6 +129,30 @@ enum LoaderType {
128129 Toml ,
129130}
130131
132+ fn strip_yaml_tags ( value : & YamlValue ) -> YamlValue {
133+ match value {
134+ // For tagged values, we just return the inner value.
135+ YamlValue :: Tagged ( tagged) => strip_yaml_tags ( & tagged. value ) ,
136+
137+ // For sequences and mappings, we need to recursively strip tags.
138+ YamlValue :: Sequence ( items) => {
139+ let mut stripped = Vec :: with_capacity ( items. len ( ) ) ;
140+ for item in items {
141+ stripped. push ( strip_yaml_tags ( item) ) ;
142+ }
143+ YamlValue :: Sequence ( stripped)
144+ }
145+ YamlValue :: Mapping ( map) => {
146+ let mut stripped = YamlMapping :: with_capacity ( map. len ( ) ) ;
147+ for ( key, value) in map {
148+ stripped. insert ( strip_yaml_tags ( key) , strip_yaml_tags ( value) ) ;
149+ }
150+ YamlValue :: Mapping ( stripped)
151+ }
152+ _ => value. clone ( ) ,
153+ }
154+ }
155+
131156fn makeloader ( path : & Path , loader : LoaderType ) -> impl tera:: Function {
132157 // Get the dirname of the Path given (if a file), or just the directory.
133158 let directory = if path. is_file ( ) {
@@ -145,8 +170,15 @@ fn makeloader(path: &Path, loader: LoaderType) -> impl tera::Function {
145170 . and_then ( |full_path| std:: fs:: read_to_string ( full_path) . map_err ( Into :: into) )
146171 // Parse the file using the relevant parser
147172 . and_then ( |contents| match loader {
148- LoaderType :: Yaml => serde_yaml:: from_str ( & contents)
149- . map_err ( |err| format ! ( "Failed to parse file as YAML: {err}" ) . into ( ) ) ,
173+ LoaderType :: Yaml => {
174+ let raw: YamlValue = serde_yaml:: from_str ( & contents) . map_err ( |err| {
175+ tera:: Error :: msg ( format ! ( "Failed to parse file as YAML: {err}" ) )
176+ } ) ?;
177+ let stripped = strip_yaml_tags ( & raw ) ;
178+ serde_yaml:: from_value :: < tera:: Value > ( stripped) . map_err ( |err| {
179+ tera:: Error :: msg ( format ! ( "Failed to convert YAML value: {err}" ) )
180+ } )
181+ }
150182 LoaderType :: Json => serde_json:: from_str ( & contents)
151183 . map_err ( |err| format ! ( "Failed to parse file as JSON: {err}" ) . into ( ) ) ,
152184 LoaderType :: Toml => toml:: from_str ( & contents)
@@ -372,4 +404,36 @@ mod test {
372404 assert_eq ! ( config, "floki: floki" ) ;
373405 Ok ( ( ) )
374406 }
407+
408+ #[ test]
409+ fn test_strip_yaml_tags_drops_reference_tag ( ) {
410+ let yaml = "value: !reference [template, script]" ;
411+ let raw: YamlValue = serde_yaml:: from_str ( yaml) . unwrap ( ) ;
412+ let stripped = strip_yaml_tags ( & raw ) ;
413+
414+ let map = match stripped {
415+ YamlValue :: Mapping ( map) => map,
416+ other => panic ! ( "expected mapping, got {:?}" , other) ,
417+ } ;
418+
419+ let key = YamlValue :: String ( "value" . into ( ) ) ;
420+ let field = map. get ( & key) . expect ( "missing value key" ) ;
421+
422+ match field {
423+ YamlValue :: Sequence ( items) => {
424+ assert_eq ! ( items. len( ) , 2 ) ;
425+ assert_eq ! ( items[ 0 ] , YamlValue :: String ( "template" . into( ) ) ) ;
426+ assert_eq ! ( items[ 1 ] , YamlValue :: String ( "script" . into( ) ) ) ;
427+ }
428+ other => panic ! ( "expected sequence, got {:?}" , other) ,
429+ }
430+ }
431+
432+ #[ test]
433+ fn test_tera_yamlload_with_gitlab_reference ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
434+ let template = r#"{% set values = yaml(file="test_resources/gitlab_reference.yaml") %}script0: {{ values.job.script[0] }} script1: {{ values.job.script[1] }}"# ;
435+ let rendered = render_template ( template, Path :: new ( "floki.yaml" ) ) ?;
436+ assert_eq ! ( rendered, "script0: .shared_template script1: script" ) ;
437+ Ok ( ( ) )
438+ }
375439}
0 commit comments