class Dependabot::Terraform::RegistryClient

Terraform::RegistryClient is a basic API client to interact with a terraform registry: www.terraform.io/docs/registry/api.html

Constants

PUBLIC_HOSTNAME

Attributes

hostname[R]
tokens[R]

Public Class Methods

new(hostname: PUBLIC_HOSTNAME, credentials: []) click to toggle source
# File lib/dependabot/terraform/registry_client.rb, line 15
def initialize(hostname: PUBLIC_HOSTNAME, credentials: [])
  @hostname = hostname
  @tokens = credentials.each_with_object({}) do |item, memo|
    memo[item["host"]] = item["token"] if item["type"] == "terraform_registry"
  end
end

Public Instance Methods

all_module_versions(identifier:) click to toggle source

Fetch all the versions of a module, and return a Version representation of them.

@param identifier [String] the identifier for the dependency, i.e: “hashicorp/consul/aws” @return [Array<Dependabot::Terraform::Version>] @raise [Dependabot::DependabotError] when the versions cannot be retrieved

# File lib/dependabot/terraform/registry_client.rb, line 45
def all_module_versions(identifier:)
  base_url = service_url_for("modules.v1")
  response = http_get!(URI.join(base_url, "#{identifier}/versions"))

  JSON.parse(response.body).
    fetch("modules").first.fetch("versions").
    map { |release| version_class.new(release.fetch("version")) }
end
all_provider_versions(identifier:) click to toggle source

Fetch all the versions of a provider, and return a Version representation of them.

@param identifier [String] the identifier for the dependency, i.e: “hashicorp/aws” @return [Array<Dependabot::Terraform::Version>] @raise [Dependabot::DependabotError] when the versions cannot be retrieved

# File lib/dependabot/terraform/registry_client.rb, line 29
def all_provider_versions(identifier:)
  base_url = service_url_for("providers.v1")
  response = http_get!(URI.join(base_url, "#{identifier}/versions"))

  JSON.parse(response.body).
    fetch("versions").
    map { |release| version_class.new(release.fetch("version")) }
end
service_url_for(service_key) click to toggle source

Perform service discovery and return the absolute URL for the requested service. www.terraform.io/docs/internals/remote-service-discovery.html

@param service_key [String] the service type described in www.terraform.io/docs/internals/remote-service-discovery.html#supported-services @param return String @raise [Dependabot::PrivateSourceAuthenticationFailure] when the service is not available

# File lib/dependabot/terraform/registry_client.rb, line 81
def service_url_for(service_key)
  url_for(services.fetch(service_key))
rescue KeyError
  raise Dependabot::PrivateSourceAuthenticationFailure, "Host does not support required Terraform-native service"
end
source(dependency:) click to toggle source

Fetch the “source” for a module or provider. We use the API to fetch the source for a dependency, this typically points to a source code repository, and then instantiate a Dependabot::Source object that we can use to fetch Metadata about a specific version of the dependency.

@param dependency [Dependabot::Dependency] the dependency who's source we're attempting to find @return [nil, Dependabot::Source]

# File lib/dependabot/terraform/registry_client.rb, line 62
def source(dependency:)
  type = dependency.requirements.first[:source][:type]
  base_url = service_url_for(service_key_for(type))
  response = http_get(URI.join(base_url, "#{dependency.name}/#{dependency.version}"))
  return nil unless response.status == 200

  source_url = JSON.parse(response.body).fetch("source")
  Source.from_url(source_url) if source_url
rescue JSON::ParserError, Excon::Error::Timeout
  nil
end

Private Instance Methods

error(message) click to toggle source
# File lib/dependabot/terraform/registry_client.rb, line 142
def error(message)
  Dependabot::DependabotError.new(message)
end
headers_for(hostname) click to toggle source
# File lib/dependabot/terraform/registry_client.rb, line 95
def headers_for(hostname)
  token = tokens[hostname]
  token ? { "Authorization" => "Bearer #{token}" } : {}
end
http_get(url) click to toggle source
# File lib/dependabot/terraform/registry_client.rb, line 119
def http_get(url)
  Excon.get(url.to_s, idempotent: true, **SharedHelpers.excon_defaults(headers: headers_for(hostname)))
end
http_get!(url) click to toggle source
# File lib/dependabot/terraform/registry_client.rb, line 123
def http_get!(url)
  response = http_get(url)

  raise Dependabot::PrivateSourceAuthenticationFailure, hostname if response.status == 401
  raise error("Response from registry was #{response.status}") unless response.status == 200

  response
end
service_key_for(type) click to toggle source
# File lib/dependabot/terraform/registry_client.rb, line 108
def service_key_for(type)
  case type
  when "module", "modules", "registry"
    "modules.v1"
  when "provider", "providers"
    "providers.v1"
  else
    raise error("Invalid source type")
  end
end
services() click to toggle source
# File lib/dependabot/terraform/registry_client.rb, line 100
def services
  @services ||=
    begin
      response = http_get(url_for("/.well-known/terraform.json"))
      response.status == 200 ? JSON.parse(response.body) : {}
    end
end
url_for(path) click to toggle source
# File lib/dependabot/terraform/registry_client.rb, line 132
def url_for(path)
  uri = URI.parse(path)
  return uri.to_s if uri.scheme == "https"
  raise error("Unsupported scheme provided") if uri.host && uri.scheme

  uri.host = hostname
  uri.scheme = "https"
  uri.to_s
end
version_class() click to toggle source
# File lib/dependabot/terraform/registry_client.rb, line 91
def version_class
  Version
end