@@ -3631,6 +3631,145 @@ static int64_t k8s_external_chart(k8s_dep_link_t *l, const char *name, const cha
36313631 return cbm_pipeline_external_chart (l -> ctx , name , url , file );
36323632}
36333633
3634+ /* ── A chart's own edges (PHASE 2 P2.4) ────────────────────────────── */
3635+
3636+ bool cbm_helm_values_name (const char * base ) {
3637+ size_t vl = K8S_SLEN ("values" );
3638+ if (!base || strncmp (base , "values" , vl ) != 0 ) {
3639+ return false;
3640+ }
3641+ const char * r = base + vl ;
3642+ if (* r == '-' ) {
3643+ const char * dot = strrchr (r , '.' );
3644+ if (!dot || dot == r + SKIP_ONE ) {
3645+ return false;
3646+ }
3647+ r = dot ;
3648+ }
3649+ return strcmp (r , ".yaml" ) == 0 || strcmp (r , ".yml" ) == 0 ;
3650+ }
3651+
3652+ /* Is `rel` a values file directly in chart dir `dir`? */
3653+ static bool k8s_chart_values_file (const char * rel , const char * dir ) {
3654+ size_t dl = strlen (dir );
3655+ if (dl && (strncmp (rel , dir , dl ) != 0 || rel [dl ] != '/' )) {
3656+ return false;
3657+ }
3658+ const char * base = rel + (dl ? dl + SKIP_ONE : 0 );
3659+ return !strchr (base , '/' ) && cbm_helm_values_name (base );
3660+ }
3661+
3662+ /* A chart deploys what its templates render: Chart -DEPLOYS {via: chart}->
3663+ * each Resource of <chart>/templates/ -- its nearest enclosing chart, so a
3664+ * subchart's objects are the subchart's. Its values files (values.yaml,
3665+ * values-<x>.yaml beside Chart.yaml) CONFIGURE {strategy: helm_values} it. */
3666+ static void k8s_link_chart_contents (cbm_pipeline_ctx_t * ctx , k8s_bundles_t * b ) {
3667+ if (!b -> charts || cbm_ht_count (b -> charts ) == 0 ) {
3668+ return ;
3669+ }
3670+ k8s_dep_link_t l = {.ctx = ctx , .charts = k8s_index_by_file (ctx , "Chart" )};
3671+ if (!l .charts ) {
3672+ return ;
3673+ }
3674+ int configures = 0 ;
3675+ static const char * const labels [] = {"Resource" , "File" };
3676+ for (size_t k = 0 ; k < sizeof (labels ) / sizeof (labels [0 ]); k ++ ) {
3677+ const cbm_gbuf_node_t * * nodes = NULL ;
3678+ int n = 0 ;
3679+ if (cbm_gbuf_find_by_label (ctx -> gbuf , labels [k ], & nodes , & n ) != 0 ) {
3680+ continue ;
3681+ }
3682+ for (int i = 0 ; i < n ; i ++ ) {
3683+ const char * fp = nodes [i ]-> file_path ;
3684+ char dir [CBM_SZ_1K ];
3685+ int64_t chart = 0 ;
3686+ if (fp && k8s_chart_of (b , fp , dir , sizeof (dir ))) {
3687+ chart = k8s_chart_node (& l , dir );
3688+ }
3689+ if (chart <= 0 ) {
3690+ continue ;
3691+ }
3692+ if (k == 0 ) {
3693+ char tpl [CBM_SZ_1K ];
3694+ snprintf (tpl , sizeof (tpl ), "%s%stemplates/" , dir , dir [0 ] ? "/" : "" );
3695+ if (strncmp (fp , tpl , strlen (tpl )) == 0 ) {
3696+ k8s_deploys_edge (& l , chart , nodes [i ]-> id , "{\"via\":\"chart\"}" );
3697+ }
3698+ } else if (k8s_chart_values_file (fp , dir )) {
3699+ cbm_gbuf_insert_edge (ctx -> gbuf , nodes [i ]-> id , chart , "CONFIGURES" ,
3700+ "{\"strategy\":\"helm_values\"}" );
3701+ configures ++ ;
3702+ }
3703+ }
3704+ }
3705+ k8s_free_index (l .charts );
3706+ if (l .edges + configures > 0 ) {
3707+ cbm_log_info ("pass.k8s.chart_contents" , "deploys" , itoa_k8s (l .edges ), "values" ,
3708+ itoa_k8s (configures ));
3709+ }
3710+ }
3711+
3712+ /* Each Image a chart's objects name literally (their USAGE {ref: image}, after
3713+ * pass_docker.c moved references onto component Images): Chart -DEPLOYS
3714+ * {via: chart, ref: image}-> it. A templated image names no Image, and no
3715+ * values default is read. Edges are collected first: inserting while walking
3716+ * a chart's own DEPLOYS edges would grow the list being read. */
3717+ static void k8s_link_chart_images (cbm_pipeline_ctx_t * ctx ) {
3718+ const cbm_gbuf_node_t * * charts = NULL ;
3719+ int nc = 0 ;
3720+ if (cbm_gbuf_find_by_label (ctx -> gbuf , "Chart" , & charts , & nc ) != 0 || nc == 0 ) {
3721+ return ;
3722+ }
3723+ int64_t * pairs = NULL ;
3724+ int np = 0 ;
3725+ int cap = 0 ;
3726+ for (int c = 0 ; c < nc ; c ++ ) {
3727+ const cbm_gbuf_edge_t * * dep = NULL ;
3728+ int nd = 0 ;
3729+ if (cbm_gbuf_find_edges_by_source_type (ctx -> gbuf , charts [c ]-> id , "DEPLOYS" , & dep , & nd ) !=
3730+ 0 ) {
3731+ continue ;
3732+ }
3733+ for (int d = 0 ; d < nd ; d ++ ) {
3734+ const char * dp = dep [d ]-> properties_json ;
3735+ if (!dp || !strstr (dp , "\"via\":\"chart\"" ) || strstr (dp , "\"ref\":" )) {
3736+ continue ;
3737+ }
3738+ const cbm_gbuf_edge_t * * use = NULL ;
3739+ int nu = 0 ;
3740+ if (cbm_gbuf_find_edges_by_source_type (ctx -> gbuf , dep [d ]-> target_id , "USAGE" , & use ,
3741+ & nu ) != 0 ) {
3742+ continue ;
3743+ }
3744+ for (int u = 0 ; u < nu ; u ++ ) {
3745+ const cbm_gbuf_node_t * img = cbm_gbuf_find_by_id (ctx -> gbuf , use [u ]-> target_id );
3746+ const char * up = use [u ]-> properties_json ;
3747+ if (!img || !img -> label || strcmp (img -> label , "Image" ) != 0 || !up ||
3748+ !strstr (up , "\"ref\":\"image\"" )) {
3749+ continue ;
3750+ }
3751+ if (np + PAIR_LEN > cap ) {
3752+ int ncap = cap ? cap * PAIR_LEN : CBM_SZ_64 ;
3753+ int64_t * grown =
3754+ cbm_realloc (CBM_MEM_CLASS_OTHER , pairs , (size_t )ncap * sizeof (* pairs ));
3755+ if (!grown ) {
3756+ break ;
3757+ }
3758+ pairs = grown ;
3759+ cap = ncap ;
3760+ }
3761+ pairs [np ++ ] = charts [c ]-> id ;
3762+ pairs [np ++ ] = img -> id ;
3763+ }
3764+ }
3765+ }
3766+ for (int i = 0 ; i + SKIP_ONE < np ; i += PAIR_LEN ) {
3767+ cbm_gbuf_insert_edge (ctx -> gbuf , pairs [i ], pairs [i + SKIP_ONE ], "DEPLOYS" ,
3768+ "{\"via\":\"chart\",\"ref\":\"image\"}" );
3769+ }
3770+ cbm_free (CBM_MEM_CLASS_OTHER , pairs );
3771+ }
3772+
36343773/* A HelmRelease whose chartRef names OCIRepository `oci` (its USAGE edge
36353774 * {"ref":"chartRef"}, resolved with the references) deploys the chart that
36363775 * repository fetches: the external Chart of that OCI reference (its last
@@ -4517,13 +4656,16 @@ int cbm_pipeline_pass_k8s(cbm_pipeline_ctx_t *ctx, const cbm_file_info_t *files,
45174656 * then the templates' includes, which see those dependencies' defines. */
45184657 k8s_link_local_deps (ctx , & local_deps );
45194658 k8s_link_includes (ctx , & bundles , files , file_count );
4659+ k8s_link_chart_contents (ctx , & bundles );
45204660 k8s_bundles_free (& bundles );
45214661 k8s_kusts_free (& kusts );
45224662 /* Shell and Make deploy commands, against the nodes linked above. */
45234663 cbm_pipeline_pass_shell (ctx , files , file_count );
45244664 cbm_pipeline_pass_terraform (ctx , files , file_count );
45254665 cbm_pipeline_pass_ansible (ctx , files , file_count );
45264666 cbm_pipeline_pass_docker (ctx , files , file_count );
4667+ /* The Images a chart's objects use, once the image hubs moved them. */
4668+ k8s_link_chart_images (ctx );
45274669 /* The variables a container sets, to the readers in its own image. */
45284670 (void )cbm_pipeline_env_join (ctx );
45294671
0 commit comments