Skip to content

Use catalog API to resolve latest version before pulling#147

Merged
anisaoshafi merged 1 commit intomainfrom
drg-504-2
Mar 24, 2026
Merged

Use catalog API to resolve latest version before pulling#147
anisaoshafi merged 1 commit intomainfrom
drg-504-2

Conversation

@anisaoshafi
Copy link
Copy Markdown
Contributor

When the image tag is "latest", the CLI previously had to pull the image first to resolve the version for license validation.

This changes the order: the catalog API (/v1/license/catalog/{emulator_type}/version) is called first to get the latest released version, so license validation happens before any image data is downloaded.

If the catalog API is unavailable, the original behavior is preserved as a fallback (pull first, inspect image for version, then validate).

@anisaoshafi anisaoshafi force-pushed the drg-504-2 branch 6 times, most recently from da680fd to 9b57ac2 Compare March 20, 2026 17:28
@anisaoshafi anisaoshafi marked this pull request as ready for review March 20, 2026 17:44
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 20, 2026

📝 Walkthrough

Walkthrough

This pull request introduces version resolution capabilities by adding a new GetLatestCatalogVersion() API method to fetch container image versions from a catalog endpoint, and refactors the license validation flow into pre-pull and post-pull phases to leverage catalog resolution before image pulls with fallback to image inspection.

Changes

Cohort / File(s) Summary
API Client Enhancement
internal/api/client.go, internal/api/catalog_version_test.go
Added GetLatestCatalogVersion() method to PlatformAPI interface and PlatformClient to fetch catalog versions via HTTP GET to /v1/license/catalog/{emulator_type}/version. Includes unit tests validating HTTP routing, response parsing, error handling, field mismatches, timeout behavior, and unreachable endpoints.
Container Start Refactoring
internal/container/start.go
Split license validation into pre-pull phase (validates pinned versions, resolves "latest" via catalog API) and post-pull fallback phase (resolves remaining versions via image inspection); simplified validateLicense() to assume version is already resolved.
Integration Tests
test/integration/version_resolution_test.go
Added integration tests with mock HTTP server validating end-to-end version resolution: successful catalog version usage, fallback to image inspection on catalog unavailability, and graceful error handling when both catalog and license validation fail.

Sequence Diagram

sequenceDiagram
    participant CLI as CLI
    participant Catalog as Catalog API
    participant Registry as Container Registry
    participant Image as Image
    participant License as License Service

    CLI->>CLI: Pre-Pull Validation Phase
    alt Pinned Version (non-"latest")
        CLI->>License: Validate License
    else "latest" Tag
        CLI->>Catalog: GetLatestCatalogVersion()
        Catalog-->>CLI: Resolved Version (e.g., 4.14.0)
        CLI->>License: Validate License
    end
    
    Note over CLI,License: Catalog Resolution Success
    
    CLI->>Registry: Pull Image
    Registry->>Image: Fetch
    Image-->>CLI: Image Downloaded
    
    Note over CLI: Post-Pull Fallback (only if pre-pull unresolved)
    opt Unresolved Containers Remaining
        CLI->>Image: Inspect Version
        Image-->>CLI: Image Version
        CLI->>License: Validate License
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • carole-lavillonniere
  • silv-io
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: using the catalog API to resolve the latest version before pulling images, which is the core objective of this PR.
Description check ✅ Passed The description clearly explains the problem, solution, and fallback behavior, directly relating to the changes made across multiple files in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch drg-504-2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
internal/container/start.go (1)

298-311: Mutation of loop variable c.Tag is safe here but could be clearer.

On line 305, c.Tag = v mutates the loop variable. Since c is a value copy (not a pointer), this doesn't affect the original containers slice—which is correct behavior. However, this could be made more explicit for readability.

♻️ Optional: Use explicit copy for clarity
 func validateLicensesFromImages(ctx context.Context, rt runtime.Runtime, sink output.Sink, opts StartOptions, tel *telemetry.Client, containers []runtime.ContainerConfig, token string) error {
-	for _, c := range containers {
+	for i := range containers {
+		c := containers[i]
 		v, err := rt.GetImageVersion(ctx, c.Image)
 		if err != nil {
 			return fmt.Errorf("could not resolve version from image %s: %w", c.Image, err)
 		}
 		c.Tag = v
 		if err := validateLicense(ctx, sink, opts, tel, c, token); err != nil {
 			return err
 		}
 	}
 	return nil
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/container/start.go` around lines 298 - 311, In
validateLicensesFromImages, avoid mutating the loop variable directly for
clarity: make an explicit copy (e.g., cfg := c), set cfg.Tag = v, and pass cfg
to validateLicense instead of c; this keeps behavior identical (since c is a
value copy) but makes the intent explicit and improves readability when working
with containers, c.Tag, and the validateLicense call.
test/integration/version_resolution_test.go (1)

19-53: Consider improving robustness of path parsing in mock server.

The path parsing on lines 29-30 extracts emulatorType by splitting the path and taking parts[len(parts)-2]. This assumes the path always ends with /version and the emulator type is the second-to-last segment.

This works for the current endpoint format (/v1/license/catalog/{emulator_type}/version), but could silently misbehave if the path format changes or if requests arrive malformed during test development.

♻️ Optional: Use more explicit path matching
 		case r.Method == http.MethodGet && strings.HasPrefix(r.URL.Path, "/v1/license/catalog/"):
 			if catalogVersion == "" {
 				w.WriteHeader(http.StatusServiceUnavailable)
 				return
 			}
-			parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
-			emulatorType := parts[len(parts)-2]
+			// Expected: /v1/license/catalog/{emulator_type}/version
+			trimmed := strings.TrimPrefix(r.URL.Path, "/v1/license/catalog/")
+			trimmed = strings.TrimSuffix(trimmed, "/version")
+			emulatorType := trimmed
 			w.Header().Set("Content-Type", "application/json")
 			_, _ = fmt.Fprintf(w, `{"emulator_type":%q,"version":%q}`, emulatorType, catalogVersion)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/integration/version_resolution_test.go` around lines 19 - 53, In
createVersionResolutionMockServer, avoid fragile index-based extraction of
emulatorType: when handling the GET for catalog, explicitly verify the path
starts with "/v1/license/catalog/" and ends with "/version", then derive
emulatorType by trimming that prefix and suffix (or by splitting and validating
the expected segment names) and return a 404/400 if the extracted emulatorType
is empty or the path shape is unexpected; keep setting Content-Type and writing
the JSON response as before but only after validating the path so malformed
requests won't silently produce wrong emulatorType values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/container/start.go`:
- Around line 298-311: In validateLicensesFromImages, avoid mutating the loop
variable directly for clarity: make an explicit copy (e.g., cfg := c), set
cfg.Tag = v, and pass cfg to validateLicense instead of c; this keeps behavior
identical (since c is a value copy) but makes the intent explicit and improves
readability when working with containers, c.Tag, and the validateLicense call.

In `@test/integration/version_resolution_test.go`:
- Around line 19-53: In createVersionResolutionMockServer, avoid fragile
index-based extraction of emulatorType: when handling the GET for catalog,
explicitly verify the path starts with "/v1/license/catalog/" and ends with
"/version", then derive emulatorType by trimming that prefix and suffix (or by
splitting and validating the expected segment names) and return a 404/400 if the
extracted emulatorType is empty or the path shape is unexpected; keep setting
Content-Type and writing the JSON response as before but only after validating
the path so malformed requests won't silently produce wrong emulatorType values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: c32ff3e3-faa2-4615-bfbb-09522b257e36

📥 Commits

Reviewing files that changed from the base of the PR and between 1aa2f2e and 8968154.

📒 Files selected for processing (4)
  • internal/api/catalog_version_test.go
  • internal/api/client.go
  • internal/container/start.go
  • test/integration/version_resolution_test.go

Copy link
Copy Markdown
Collaborator

@carole-lavillonniere carole-lavillonniere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work 👏

Copy link
Copy Markdown
Member

@silv-io silv-io left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@anisaoshafi anisaoshafi merged commit 59d5815 into main Mar 24, 2026
14 of 15 checks passed
@anisaoshafi anisaoshafi deleted the drg-504-2 branch March 24, 2026 10:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants