@@ -52,6 +52,7 @@ use crate::{
5252 SubmitConfig , SubmitConfigFragment , VolumeMounts ,
5353 } ,
5454 } ,
55+ openlineage:: { OpenLineageSpec , append_conf_csv} ,
5556} ;
5657
5758pub mod affinity;
@@ -76,7 +77,7 @@ pub enum Error {
7677 #[ snafu( display( "object defines no application artifact" ) ) ]
7778 ObjectHasNoArtifact ,
7879
79- #[ snafu( display( "object has no name" ) ) ]
80+ #[ snafu( display( "object has no name" ) , visibility ( pub ( crate ) ) ) ]
8081 ObjectHasNoName ,
8182
8283 #[ snafu( display( "application has no Spark image" ) ) ]
@@ -264,36 +265,6 @@ pub mod versioned {
264265 pub open_lineage : Option < OpenLineageSpec > ,
265266 }
266267
267- /// OpenLineage lineage emission for a [`SparkApplication`].
268- ///
269- /// When enabled, the operator injects the OpenLineage Spark listener, points its HTTP transport
270- /// at the backend resolved from the discovery ConfigMap, and sets a stable job name. All injected
271- /// values are defaults: they can be overridden via `sparkConf`.
272- #[ derive( Clone , Debug , Default , Deserialize , JsonSchema , PartialEq , Serialize ) ]
273- #[ serde( rename_all = "camelCase" ) ]
274- pub struct OpenLineageSpec {
275- /// Enable OpenLineage event emission. Defaults to `false` (nothing is injected).
276- #[ serde( default ) ]
277- pub enabled : bool ,
278-
279- /// The OpenLineage namespace lineage is reported under.
280- /// Defaults to the application's Kubernetes namespace (`metadata.namespace`).
281- #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
282- pub namespace : Option < String > ,
283-
284- /// A stable OpenLineage job/application name. Setting this prevents fragmented run history
285- /// (and the intermittent `unknown` job-name bug). If unset, the operator resolves it from
286- /// `spark.app.name`, falling back to `metadata.name` (with a warning event).
287- #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
288- pub app_name : Option < String > ,
289-
290- /// Name of the OpenLineage backend [discovery ConfigMap](DOCS_BASE_URL_PLACEHOLDER/concepts/service_discovery).
291- /// It must contain the key `ADDRESS` with the base URL of the OpenLineage backend
292- /// (e.g. `http://marquez:5000`). Mirrors the `vectorAggregatorConfigMapName` field on this CRD.
293- #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
294- pub config_map_name : Option < ConfigMapName > ,
295- }
296-
297268 #[ derive( Clone , Debug , Default , Deserialize , JsonSchema , PartialEq , Serialize , Merge ) ]
298269 pub struct ConfigOverrides {
299270 #[ serde( default , rename = "spark-env.sh" ) ]
@@ -304,35 +275,6 @@ pub mod versioned {
304275 }
305276}
306277
307- /// The resolved OpenLineage job/app name and where it came from.
308- /// See [`v1alpha1::SparkApplication::resolved_openlineage_app_name`].
309- #[ derive( Clone , Debug , PartialEq ) ]
310- pub struct ResolvedOpenLineageAppName {
311- /// The resolved, non-blank name written to `spark.openlineage.appName`.
312- pub name : String ,
313- /// `true` when the name fell back to `metadata.name`; the controller then emits a warning event
314- /// because a per-run-unique name (e.g. an orchestrator-generated `-<timestamp>` suffix) fragments
315- /// backend run history.
316- pub from_metadata_name : bool ,
317- }
318-
319- /// Appends `value` to a comma-separated `--conf` value in `submit_conf`, preserving any existing
320- /// (e.g. user-supplied) entries and skipping `value` if it is already present. Used for the
321- /// OpenLineage keys that must accumulate rather than clobber (`spark.extraListeners`, `spark.jars`).
322- fn append_conf_csv ( submit_conf : & mut BTreeMap < String , String > , key : & str , value : & str ) {
323- match submit_conf. get_mut ( key) {
324- Some ( existing) if !existing. is_empty ( ) => {
325- if !existing. split ( ',' ) . any ( |entry| entry. trim ( ) == value) {
326- existing. push ( ',' ) ;
327- existing. push_str ( value) ;
328- }
329- }
330- _ => {
331- submit_conf. insert ( key. to_string ( ) , value. to_string ( ) ) ;
332- }
333- }
334- }
335-
336278impl v1alpha1:: SparkApplication {
337279 /// Returns if this [`SparkApplication`] has already created a Kubernetes Job doing the actual `spark-submit`.
338280 ///
@@ -887,39 +829,6 @@ impl v1alpha1::SparkApplication {
887829 Ok ( vec ! [ submit_cmd. join( " " ) ] )
888830 }
889831
890- /// Resolves the stable OpenLineage job/app name and its provenance (MVP §5), in priority order:
891- /// 1. `spec.openLineage.appName`, else
892- /// 2. `spark.app.name` from `sparkConf`, else
893- /// 3. `metadata.name` (a fallback the controller flags with a warning event, because a
894- /// per-run-unique name would fragment backend run history).
895- ///
896- /// Always yields a non-blank name — which is exactly what fixes the intermittent `unknown` bug.
897- pub fn resolved_openlineage_app_name ( & self ) -> Result < ResolvedOpenLineageAppName , Error > {
898- if let Some ( app_name) = self
899- . spec
900- . open_lineage
901- . as_ref ( )
902- . and_then ( |open_lineage| open_lineage. app_name . clone ( ) )
903- {
904- return Ok ( ResolvedOpenLineageAppName {
905- name : app_name,
906- from_metadata_name : false ,
907- } ) ;
908- }
909-
910- if let Some ( app_name) = self . spec . spark_conf . get ( "spark.app.name" ) {
911- return Ok ( ResolvedOpenLineageAppName {
912- name : app_name. clone ( ) ,
913- from_metadata_name : false ,
914- } ) ;
915- }
916-
917- Ok ( ResolvedOpenLineageAppName {
918- name : self . metadata . name . clone ( ) . context ( ObjectHasNoNameSnafu ) ?,
919- from_metadata_name : true ,
920- } )
921- }
922-
923832 pub fn env (
924833 & self ,
925834 s3conn : & Option < s3:: v1alpha1:: ConnectionSpec > ,
0 commit comments