Class: Equilibrium::RegistryClient

Inherits:
Object
  • Object
show all
Defined in:
lib/equilibrium/registry_client.rb

Overview

Registry client for Docker Registry v2 API

Supports fetching container image tags from various registry providers. Currently designed for registries that allow anonymous access (like public GCR).

Google Container Registry (GCR) Pros and Cons: ✅ Pros: Simple anonymous access for public repos, provides digest info via non-standard 'manifest' field ❌ Cons: Being deprecated in favor of Artifact Registry, non-standard API extensions may not be portable

Other registries require complex authentication and don't provide manifest field, making it difficult to get digest information without separate API calls per tag:

  • GitHub Container Registry (GHCR): Requires Bearer token auth
  • Docker Hub: Requires token exchange auth flow
  • AWS ECR Public: Requires AWS credentials even for public repos

Note: The 'manifest' field in responses is a non-standard extension only provided by some registries (like GCR). Most registries follow the Docker Registry v2 spec which only returns 'name' and 'tags' fields. To get digest information, separate calls to /v2//manifests/ are required per the official specification.

PAGINATION ANALYSIS (August 2025): Current implementation fetches ALL tags in single API call. Analysis of production registries:

  • apm-inject: 29 tags
  • dd-lib-dotnet-init: 31 tags
  • dd-lib-java-init: 18 tags
  • dd-lib-js-init: 56 tags (largest)
  • dd-lib-php-init: 10 tags
  • dd-lib-python-init: 27 tags
  • dd-lib-ruby-init: 19 tags Total: 190 tags across all registries

GCR PAGINATION RESEARCH FINDINGS:

RECOMMENDATION: Current single-request approach is sufficient. Implementation complexity for pagination: 3-4 hours with minimal benefit. Consider only if individual repositories approach thousands of tags.

Defined Under Namespace

Classes: Error

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(registry) ⇒ RegistryClient



57
58
59
60
# File 'lib/equilibrium/registry_client.rb', line 57

def initialize(registry)
  @registry = registry
  @uri = URI(build_api_url(registry))
end

Instance Attribute Details

#uriObject (readonly)

Returns the value of attribute uri.



55
56
57
# File 'lib/equilibrium/registry_client.rb', line 55

def uri
  @uri
end

Instance Method Details

#tagged_digestsObject

Raises:



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/equilibrium/registry_client.rb', line 62

def tagged_digests
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  http.read_timeout = 30
  http.open_timeout = 10

  response = http.request(Net::HTTP::Get.new(uri))

  raise Error, "API request failed: #{response.code} #{response.message}" unless response.is_a?(Net::HTTPSuccess)

  data = JSON.parse(response.body).tap do |json|
    SchemaValidator.validate!(json, Equilibrium::Schemas::REGISTRY_API_RESPONSE, error_prefix: "Registry API response validation failed")
  end

  # Data is already validated by schema, safe to process
  all_tags = data["tags"]

  # The 'manifest' field is a non-standard extension provided by some registries (like GCR)
  # Most registries (GHCR, Docker Hub, ECR) only return 'name' and 'tags' per Docker Registry v2 spec
  # When manifest data is missing, digest information is not available from the tags endpoint
  manifests = data.fetch("manifest")

  # Build mapping from tag names to their SHA256 digests
  # Only works when registry provides non-standard 'manifest' field in tags response
  tag_to_digest = manifests.each_with_object({}) do |(digest, manifest_info), mapping|
    tags_for_digest = manifest_info["tag"]
    tags_for_digest.each { |tag| mapping[tag] = digest }
  end

  # Validate that all_tags from API response matches tag_to_digest keys
  all_tags_set = all_tags.to_set
  manifest_tags_set = tag_to_digest.keys.to_set

  unless all_tags_set == manifest_tags_set
    raise Error, "Tag mismatch: API tags #{all_tags_set.to_a.sort} != manifest tags #{manifest_tags_set.to_a.sort}"
  end

  tag_to_digest
end