Class: Dependabot::Terraform::RegistryClient
- Inherits:
-
Object
- Object
- Dependabot::Terraform::RegistryClient
- Extended by:
- T::Sig
- Defined in:
- lib/dependabot/terraform/registry_client.rb
Overview
Terraform::RegistryClient is a basic API client to interact with a terraform registry: www.terraform.io/docs/registry/api.html
Constant Summary collapse
- ARCHIVE_EXTENSIONS =
Archive extensions supported by Terraform for HTTP URLs developer.hashicorp.com/terraform/language/modules/sources#http-urls
T.let( %w(.zip .bz2 .tar.bz2 .tar.tbz2 .tbz2 .gz .tar.gz .tgz .xz .tar.xz .txz).freeze, T::Array[String] )
- PUBLIC_HOSTNAME =
"registry.terraform.io"
Class Method Summary collapse
Instance Method Summary collapse
- #all_module_versions(identifier:) ⇒ Object
- #all_provider_versions(identifier:) ⇒ Object
-
#initialize(hostname: PUBLIC_HOSTNAME, credentials: []) ⇒ RegistryClient
constructor
A new instance of RegistryClient.
- #service_url_for(service_key) ⇒ Object
- #source(dependency:) ⇒ Object
Constructor Details
#initialize(hostname: PUBLIC_HOSTNAME, credentials: []) ⇒ RegistryClient
26 27 28 29 30 31 32 33 34 |
# File 'lib/dependabot/terraform/registry_client.rb', line 26 def initialize(hostname: PUBLIC_HOSTNAME, credentials: []) @hostname = hostname @tokens = T.let( credentials.each_with_object({}) do |item, memo| memo[item["host"]] = item["token"] if item["type"] == "terraform_registry" end, T::Hash[String, String] ) end |
Class Method Details
.get_proxied_source(raw_source) ⇒ Object
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/dependabot/terraform/registry_client.rb', line 42 def self.get_proxied_source(raw_source) return raw_source unless raw_source.start_with?("http") uri = URI.parse(T.must(raw_source.split(%r{(?<!:)//}).first)) return raw_source if ARCHIVE_EXTENSIONS.any? { |ext| uri.path&.end_with?(ext) } return raw_source if URI.parse(raw_source).query&.include?("archive=") url = T.must(raw_source.split(%r{(?<!:)//}).first) + "?terraform-get=1" host = URI.parse(raw_source).host response = Dependabot::RegistryClient.get(url: url) raise PrivateSourceAuthenticationFailure, host if response.status == 401 return T.must(response.headers["X-Terraform-Get"]) if response.headers["X-Terraform-Get"] doc = Nokogiri::XML(response.body) doc.css("meta").find do |tag| tag.attributes&.fetch("name", nil)&.value == "terraform-get" end&.attributes&.fetch("content", nil)&.value rescue Excon::Error::Socket, Excon::Error::Timeout => e raise PrivateSourceAuthenticationFailure, host if e..include?("no address for") raw_source end |
Instance Method Details
#all_module_versions(identifier:) ⇒ Object
97 98 99 100 101 102 103 104 |
# File 'lib/dependabot/terraform/registry_client.rb', line 97 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:) ⇒ Object
78 79 80 81 82 83 84 85 86 87 |
# File 'lib/dependabot/terraform/registry_client.rb', line 78 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")) } rescue Excon::Error raise error("Could not fetch provider versions") end |
#service_url_for(service_key) ⇒ Object
149 150 151 152 153 |
# File 'lib/dependabot/terraform/registry_client.rb', line 149 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:) ⇒ Object
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/dependabot/terraform/registry_client.rb', line 115 def source(dependency:) type = T.must(dependency.requirements.first)[:source][:type] base_url = service_url_for(service_key_for(type)) case type # https://www.terraform.io/internals/module-registry-protocol#download-source-code-for-a-specific-module-version when "module", "modules", "registry" download_url = URI.join(base_url, "#{dependency.name}/#{dependency.version}/download") response = http_get(download_url) return nil unless response.status == 204 source_url = response.headers.fetch("X-Terraform-Get") source_url = URI.join(download_url, source_url) if source_url.start_with?("/", "./", "../") source_url = RegistryClient.get_proxied_source(source_url) if source_url when "provider", "providers" 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") end Source.from_url(source_url) if source_url rescue JSON::ParserError, Excon::Error::Timeout nil end |