Repository: osbuild/image-builder-crc
Pull request #1600: v1: send template uuid/name in compose request (HMS-6297)
Author: login: rverdile
Created: 2025-05-12T15:09:49Z | Merged: 2025-05-15T15:36:29Z

If a template is being used, the template name or UUID is sent in the compose request for system registration. If the RHEL version is >= 9.6, the name is used for registration. Any older version uses the UUID for registration.

JIRA: [HMS-6297](https://issues.redhat.com/browse/HMS-6297)


Diff:
diff --git a/internal/clients/composer/openapi.v2.gen.go b/internal/clients/composer/openapi.v2.gen.go
index d5e5701fa..31a4bafc1 100644
--- a/internal/clients/composer/openapi.v2.gen.go
+++ b/internal/clients/composer/openapi.v2.gen.go
@@ -1386,6 +1386,12 @@ type Subscription struct {
 	// Rhc Optional flag to use rhc to register the system, which also always enables Insights.
 	Rhc       *bool  `json:"rhc,omitempty"`
 	ServerUrl string `json:"server_url"`
+
+	// TemplateName Optional value to register with a template when using rhc to register the system with Insights.
+	TemplateName *string `json:"template_name,omitempty"`
+
+	// TemplateUuid Optional value to register with a template when registering the system with Insights.
+	TemplateUuid *string `json:"template_uuid,omitempty"`
 }
 
 // Timezone Timezone configuration
diff --git a/internal/clients/composer/openapi.v2.yml b/internal/clients/composer/openapi.v2.yml
index 9dd271ea3..376ba03ba 100644
--- a/internal/clients/composer/openapi.v2.yml
+++ b/internal/clients/composer/openapi.v2.yml
@@ -2229,6 +2229,14 @@ components:
           format: uri
           description: |
             Optional value to set proxy option when registering the system to Insights
+        template_uuid:
+          type: string
+          description: |
+            Optional value to register with a template when registering the system with Insights.
+        template_name:
+          type: string
+          description: |
+            Optional value to register with a template when using rhc to register the system with Insights.
     User:
       type: object
       additionalProperties: false
diff --git a/internal/v1/api.go b/internal/v1/api.go
index c067ef35d..8c76c0ccd 100644
--- a/internal/v1/api.go
+++ b/internal/v1/api.go
@@ -784,9 +784,12 @@ type ImageRequest struct {
 
 	// ContentTemplate ID of the content template. A content template and snapshot date cannot both be specified.
 	// If a content template is specified, the snapshot date used will be the one from the content template.
-	ContentTemplate *string    `json:"content_template,omitempty"`
-	ImageType       ImageTypes `json:"image_type"`
-	Ostree          *OSTree    `json:"ostree,omitempty"`
+	ContentTemplate *string `json:"content_template,omitempty"`
+
+	// ContentTemplateName Name of the content template. Used when registering the system to Insights.
+	ContentTemplateName *string    `json:"content_template_name,omitempty"`
+	ImageType           ImageTypes `json:"image_type"`
+	Ostree              *OSTree    `json:"ostree,omitempty"`
 
 	// Size Size of image, in bytes. When set to 0 the image size is a minimum
 	// defined by the image type.
diff --git a/internal/v1/api.yaml b/internal/v1/api.yaml
index 9ff63b591..5541eecc8 100644
--- a/internal/v1/api.yaml
+++ b/internal/v1/api.yaml
@@ -1254,6 +1254,10 @@ components:
           description: |
             ID of the content template. A content template and snapshot date cannot both be specified.
             If a content template is specified, the snapshot date used will be the one from the content template.
+        content_template_name:
+          type: string
+          description: |
+            Name of the content template. Used when registering the system to Insights.
         aap_registration:
           $ref: '#/components/schemas/AAPRegistration'
     ImageTypes:
diff --git a/internal/v1/handler_compose_image.go b/internal/v1/handler_compose_image.go
index 8f69eb975..92b610c19 100644
--- a/internal/v1/handler_compose_image.go
+++ b/internal/v1/handler_compose_image.go
@@ -839,6 +839,18 @@ func (h *Handlers) buildCustomizations(ctx echo.Context, cr *ComposeRequest, d *
 			ServerUrl:           cust.Subscription.ServerUrl,
 			InsightsClientProxy: &h.server.insightsClientProxy,
 		}
+
+		if d != nil && cr.ImageRequests[0].ContentTemplate != nil {
+			major, minor, err := d.RHELMajorMinor()
+			if err != nil {
+				return nil, err
+			}
+			if (major >= 9 && minor >= 6) && cr.ImageRequests[0].ContentTemplateName != nil {
+				res.Subscription.TemplateName = cr.ImageRequests[0].ContentTemplateName
+			} else {
+				res.Subscription.TemplateUuid = cr.ImageRequests[0].ContentTemplate
+			}
+		}
 	}
 
 	if cust.Packages != nil {
diff --git a/internal/v1/handler_post_compose_test.go b/internal/v1/handler_post_compose_test.go
index d2a7e1c6d..bdeec5875 100644
--- a/internal/v1/handler_post_compose_test.go
+++ b/internal/v1/handler_post_compose_test.go
@@ -2892,6 +2892,9 @@ func TestComposeCustomizations(t *testing.T) {
 							Id:       mocks.RepoPLID,
 						},
 					},
+					Subscription: &v1.Subscription{
+						Insights: true,
+					},
 				},
 				ImageRequests: []v1.ImageRequest{
 					{
@@ -2905,6 +2908,7 @@ func TestComposeCustomizations(t *testing.T) {
 					},
 				},
 			},
+			// content template with 2 rhel repos with registration
 			composerRequest: composer.ComposeRequest{
 				Customizations: &composer.Customizations{
 					CustomRepositories: nil,
@@ -2916,6 +2920,16 @@ func TestComposeCustomizations(t *testing.T) {
 							Rhsm:     common.ToPtr(false),
 						},
 					},
+					Subscription: &composer.Subscription{
+						ActivationKey:       "",
+						BaseUrl:             "",
+						Insights:            true,
+						Rhc:                 common.ToPtr(false),
+						Organization:        "0",
+						ServerUrl:           "",
+						InsightsClientProxy: common.ToPtr(""),
+						TemplateUuid:        common.ToPtr(mocks.TemplateID),
+					},
 				},
 				Distribution: "rhel-9.5",
 				ImageRequest: &composer.ImageRequest{


Reviews:
user: login: lzap
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-13T09:17:35Z
user: login: rverdile
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T13:48:00Z
user: login: lzap
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-05-14T17:31:39Z
user: login: croissanne
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-15T13:57:14Z
user: login: croissanne
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-15T13:57:32Z

Review comments:
diff_hunk: @@ -2921,6 +2925,16 @@ func TestComposeCustomizations(t *testing.T) {
 							Rhsm:     common.ToPtr(false),
 						},
 					},
+					Subscription: &composer.Subscription{
+						ActivationKey:       "",
+						BaseUrl:             "",
+						Insights:            true,
+						Rhc:                 common.ToPtr(false),
+						Organization:        "0",
+						ServerUrl:           "",
+						InsightsClientProxy: common.ToPtr(""),
+						TemplateUuid:        common.ToPtr(mocks.TemplateID),
+					},
path: internal/v1/handler_post_compose_test.go
user: login: lzap
body: Is this hunk related?
created_at: 2025-05-13T09:17:35Z
author_association: COLLABORATOR
diff_hunk: @@ -2921,6 +2925,16 @@ func TestComposeCustomizations(t *testing.T) {
 							Rhsm:     common.ToPtr(false),
 						},
 					},
+					Subscription: &composer.Subscription{
+						ActivationKey:       "",
+						BaseUrl:             "",
+						Insights:            true,
+						Rhc:                 common.ToPtr(false),
+						Organization:        "0",
+						ServerUrl:           "",
+						InsightsClientProxy: common.ToPtr(""),
+						TemplateUuid:        common.ToPtr(mocks.TemplateID),
+					},
path: internal/v1/handler_post_compose_test.go
user: login: rverdile
body: yes, this is needed for the test to pass
created_at: 2025-05-14T13:48:00Z
author_association: CONTRIBUTOR

PR comments:
user: login: rverdile
created_at: 2025-05-12T15:09:56Z
body: Depends on these composer changes: https://github.com/osbuild/osbuild-composer/pull/4712
author_association: CONTRIBUTOR
user: login: rverdile
created_at: 2025-05-12T15:10:21Z
body: /jira-epic HMS-5321
author_association: CONTRIBUTOR
user: login: lzap
created_at: 2025-05-13T08:25:25Z
body: I think this should go into the compose request and not customization, @croissanne ?
author_association: COLLABORATOR
user: login: thozza
created_at: 2025-05-13T08:57:50Z
body: > I think this should go into the compose request and not customization, @croissanne ?

FWIW, the `Subscription` configuration is handled in the compose request as a customization. Both here and in `osbuild-composer`. I think that this boils down to a question if specifying the template UUID / name is something that will be specific to:

1. BP (across multiple image builds)
2. Or a specific image built for the BP.

If option #1 is more common, then putting it into the customization is IMO the correct place. If #2 is more common, then this could go into the compose request, but grouping is in the `Subscription` customization IMHO still makes some sense and it is acceptable.
author_association: MEMBER
user: login: lzap
created_at: 2025-05-13T09:16:31Z
body: My point is that this is a console.redhat.com related stuff that should not go into customization, definitely not the common blueprint folks were not liking the idea during our meetings.
author_association: COLLABORATOR
user: login: jlsherrill
created_at: 2025-05-13T11:47:58Z
body: This is definitely more 1), its meant to live with the blueprint and be re-used for every image build.  That said, it doesn't make sense to export and import this information with the blueprint (which i don't think is being done here).  

@lzap I'm curious why other RHC configuration options belong here but not this one?
author_association: CONTRIBUTOR
user: login: lzap
created_at: 2025-05-14T07:51:55Z
body: > @lzap I'm curious why other RHC configuration options belong here but not this one?

Insights-only related data should not be part of customization, I am working on a V2 blueprint format, schema and possibly CRC API V2 that will not have such fields and it will have to be stored separately. Take this as a more future-proofing of the change, if we choose to put it there we can refactor it later, I mean there will likely be V1 to V2 mapper anyway so no hard feelings.
author_association: COLLABORATOR
user: login: rverdile
created_at: 2025-05-15T13:18:11Z
body: rebased to resolve merge conflict
author_association: CONTRIBUTOR
user: login: croissanne
created_at: 2025-05-15T13:59:06Z
body: It makes sense here to have it in the image request as the content template chosen in ibcrc also determines the repositories being sent to composer. So it's not a "pure" customisation. 
author_association: MEMBER