diff --git a/internal/buildapi/server.go b/internal/buildapi/server.go index b5f1be7e..0741ffda 100644 --- a/internal/buildapi/server.go +++ b/internal/buildapi/server.go @@ -1462,6 +1462,9 @@ func listBuilds(c *gin.Context) { return } + // Resolve external route once for translating internal registry URLs + externalRoute, _ := getExternalRegistryRoute(ctx, k8sClient, namespace) + resp := make([]BuildListItem, 0, len(list.Items)) for _, b := range list.Items { var startStr, compStr string @@ -1471,6 +1474,18 @@ func listBuilds(c *gin.Context) { if b.Status.CompletionTime != nil { compStr = b.Status.CompletionTime.Format(time.RFC3339) } + + containerImage := b.Spec.GetContainerPush() + diskImage := b.Spec.GetExportOCI() + if b.Spec.GetUseServiceAccountAuth() && externalRoute != "" { + if containerImage != "" { + containerImage = translateToExternalURL(containerImage, externalRoute) + } + if diskImage != "" { + diskImage = translateToExternalURL(diskImage, externalRoute) + } + } + resp = append(resp, BuildListItem{ Name: b.Name, Phase: b.Status.Phase, @@ -1479,8 +1494,8 @@ func listBuilds(c *gin.Context) { CreatedAt: b.CreationTimestamp.Format(time.RFC3339), StartTime: startStr, CompletionTime: compStr, - ContainerImage: b.Spec.GetContainerPush(), - DiskImage: b.Spec.GetExportOCI(), + ContainerImage: containerImage, + DiskImage: diskImage, }) } writeJSON(c, http.StatusOK, resp) diff --git a/internal/common/tasks/scripts/push_artifact.sh b/internal/common/tasks/scripts/push_artifact.sh index 653d0f4f..55dac9ba 100644 --- a/internal/common/tasks/scripts/push_artifact.sh +++ b/internal/common/tasks/scripts/push_artifact.sh @@ -175,6 +175,7 @@ target="$(params.target)" arch="$(params.arch)" config_file="/etc/partition-config/partition-rules.yaml" +default_partitions="" if [ -f "$config_file" ]; then # Use yq to extract included partitions for target (using bracket notation for safety) default_partitions=$(yq eval ".targets[\"${target}\"].include[]" "$config_file" 2>/dev/null | tr '\n' ',' | sed 's/,$//') @@ -182,15 +183,18 @@ if [ -f "$config_file" ]; then if [ -n "$default_partitions" ]; then echo "Default partitions for target '$target': $default_partitions" else - default_partitions="boot_a,system_a,system_b" - echo "No default partitions configured for target '$target', using fallback: $default_partitions" + echo "No default partitions configured for target '$target', skipping default-partitions annotation" fi else - default_partitions="boot_a,system_a,system_b" - echo "No partition configuration found, using fallback: $default_partitions" + echo "No partition configuration found, skipping default-partitions annotation" fi -default_partitions_escaped=$(json_escape "$default_partitions") +default_partitions_annotation="" +if [ -n "$default_partitions" ]; then + default_partitions_escaped=$(json_escape "$default_partitions") + default_partitions_annotation=", + \"automotive.sdv.cloud.redhat.com/default-partitions\": \"${default_partitions_escaped}\"" +fi cd /workspace/shared @@ -279,8 +283,7 @@ if [ -d "${parts_dir}" ] && [ -n "$(ls -A "${parts_dir}" 2>/dev/null)" ]; then "automotive.sdv.cloud.redhat.com/parts": "${file_list}", "automotive.sdv.cloud.redhat.com/distro": "${distro}", "automotive.sdv.cloud.redhat.com/target": "${target}", - "automotive.sdv.cloud.redhat.com/arch": "${arch}", - "automotive.sdv.cloud.redhat.com/default-partitions": "${default_partitions_escaped}" + "automotive.sdv.cloud.redhat.com/arch": "${arch}"${default_partitions_annotation} }, ${layer_annotations_json} } @@ -308,14 +311,6 @@ EOF echo "" echo "=== Multi-layer artifact pushed successfully ===" - echo "" - echo "After pull, you get:" - echo "$file_list" | sed 's/,/\n/g' | while read -r f; do echo " ./$f"; done - echo "" - echo "Pull commands:" - echo " All files: oras pull ${repo_url}" - echo " Single file: oras pull ${repo_url} --include \"boot_a.simg.gz\"" - echo " Inspect: oras manifest fetch ${repo_url} | jq ." else # Fallback to single-file push (original behavior) @@ -355,5 +350,4 @@ else echo "" echo "=== Artifact pushed successfully ===" - echo "Pull: oras pull ${repo_url}" fi diff --git a/internal/controller/imagebuild/controller.go b/internal/controller/imagebuild/controller.go index 696292ae..17050145 100644 --- a/internal/controller/imagebuild/controller.go +++ b/internal/controller/imagebuild/controller.go @@ -532,9 +532,58 @@ func (r *ImageBuildReconciler) createBuildTaskRun( "password": []byte(tokenResp.Status.Token), }, } - if _, err := clientset.CoreV1().Secrets(imageBuild.Namespace).Create(ctx, ociSecret, metav1.CreateOptions{}); err != nil { + if _, err := clientset.CoreV1().Secrets(imageBuild.Namespace).Create(ctx, ociSecret, metav1.CreateOptions{}); err != nil && !errors.IsAlreadyExists(err) { return fmt.Errorf("failed to create flash OCI auth secret: %w", err) } + } else if imageBuild.Spec.SecretRef != "" && flashImageRef != "" { + // External registry: read credentials from the registry-auth secret and + // create a flash-oci-auth secret with username/password keys that the + // flash script expects. + registrySecret := &corev1.Secret{} + if err := r.Get(ctx, client.ObjectKey{ + Namespace: imageBuild.Namespace, + Name: imageBuild.Spec.SecretRef, + }, registrySecret); err != nil { + return fmt.Errorf("failed to read registry secret %q for flash OCI credentials: %w", imageBuild.Spec.SecretRef, err) + } + regUser := registrySecret.Data["REGISTRY_USERNAME"] + regPass := registrySecret.Data["REGISTRY_PASSWORD"] + hasUser := len(regUser) > 0 + hasPass := len(regPass) > 0 + if hasUser && hasPass { + flashOCIAuthSecretName = imageBuild.Name + "-flash-oci-auth" + ociSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: flashOCIAuthSecretName, + Namespace: imageBuild.Namespace, + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "automotive-dev-operator", + "app.kubernetes.io/part-of": "automotive-dev", + "automotive.sdv.cloud.redhat.com/build-name": imageBuild.Name, + "automotive.sdv.cloud.redhat.com/transient": "true", + "automotive.sdv.cloud.redhat.com/resource-type": "flash-oci-auth", + }, + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(imageBuild, automotivev1alpha1.GroupVersion.WithKind("ImageBuild")), + }, + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "username": regUser, + "password": regPass, + }, + } + if err := r.Create(ctx, ociSecret); err != nil && !errors.IsAlreadyExists(err) { + return fmt.Errorf("failed to create flash OCI auth secret from registry credentials: %w", err) + } + } else if hasUser || hasPass { + missing := "REGISTRY_PASSWORD" + if !hasUser { + missing = "REGISTRY_USERNAME" + } + log.Info("Partial registry credentials in secret, skipping flash OCI auth", + "secret", imageBuild.Spec.SecretRef, "missing", missing) + } } params = append(params,