lib/mixlib/install/backend/artifactory.rb in mixlib-install-0.8.0.alpha.2 vs lib/mixlib/install/backend/artifactory.rb in mixlib-install-0.8.0.alpha.3
- old
+ new
@@ -23,67 +23,141 @@
module Mixlib
class Install
class Backend
class Artifactory
- ARTIFACTORY_ENDPOINT = "http://artifactory.chef.co".freeze
+ class ConnectionError < StandardError; end
+ class AuthenticationError < StandardError; end
- attr_reader :options
- attr_reader :client
+ ENDPOINT = "http://artifactory.chef.co".freeze
+ attr_accessor :options
+
def initialize(options)
@options = options
- @client = ::Artifactory::Client.new(endpoint: ARTIFACTORY_ENDPOINT)
end
+ # Create filtered list of artifacts
+ #
+ # @return [Array<ArtifactInfo>] list of artifacts for the configured
+ # channel, product name, and product version.
+ # @return [ArtifactInfo] arifact info for the configured
+ # channel, product name, product version and platform info
+ #
def info
- begin
- results = client.get("/api/search/prop", params, headers)["results"]
- rescue Errno::ETIMEDOUT => e
- raise e, "unstable channel uses endpoint #{ARTIFACTORY_ENDPOINT} \
-which is currently only accessible through Chef's internal network."
+ artifacts = artifactory_info.collect { |a| create_artifact(a) }
+
+ artifacts_for_version = artifacts.find_all do |a|
+ a.version == options.resolved_version(artifacts)
end
if options.platform
- artifact(results.first)
- else
- results.collect do |result|
- artifact(result)
+ artifacts_for_version.find do |a|
+ a.platform == options.platform &&
+ a.platform_version == options.platform_version &&
+ a.architecture == options.architecture
end
+ else
+ artifacts_for_version
end
end
- private
+ # Fetches all artifacts from the configured Artifactory repository using
+ # channel and product name as search criteria
+ #
+ # @return [Array<Hash>] list of artifactory hash data
+ #
+ # Hash data:
+ # download_uri: The full url download path
+ # <property_name>: The names of the properties associcated to the artifact
+ #
+ def artifactory_info
+ query = <<-QUERY
+items.find(
+ {"repo": "omnibus-#{options.channel}-local"},
+ {"@omnibus.project": "#{options.product_name}"}
+).include("repo", "path", "name", "property")
+ QUERY
- def artifact(result)
+ results = artifactory_request do
+ client.post("/api/search/aql", query, "Content-Type" => "text/plain")
+ end
+
+ # Merge artifactory properties and downloadUri to a flat Hash
+ results["results"].collect do |result|
+ { "downloadUri" => generate_download_uri(result) }.merge(
+ map_properties(result["properties"])
+ )
+ end
+ end
+
+ def create_artifact(artifact_map)
ArtifactInfo.new(
- md5: result["properties"]["omnibus.md5"].first,
- sha256: result["properties"]["omnibus.sha256"].first,
- version: result["properties"]["omnibus.version"].first,
- platform: result["properties"]["omnibus.platform"].first,
- platform_version: result["properties"]["omnibus.platform_version"].first,
- architecture: result["properties"]["omnibus.architecture"].first,
- url: result["uri"]
+ md5: artifact_map["omnibus.md5"],
+ sha256: artifact_map["omnibus.sha256"],
+ version: artifact_map["omnibus.version"],
+ platform: artifact_map["omnibus.platform"],
+ platform_version: artifact_map["omnibus.platform_version"],
+ architecture: artifact_map["omnibus.architecture"],
+ url: artifact_map["downloadUri"]
)
end
- def params
- params = {
- "repos" => "omnibus-current-local",
- "omnibus.version" => options.product_version
- }
+ private
- if options.platform
- params["omnibus.platform"] = options.platform
- params["omnibus.platform_version"] = options.platform_version
- params["omnibus.architecture"] = options.architecture
+ # Converts Array<Hash> where the Hash is a key pair and
+ # value pair to a simplifed key/pair Hash
+ #
+ def map_properties(properties)
+ return {} if properties.nil?
+ properties.each_with_object({}) do |prop, h|
+ h[prop["key"]] = prop["value"]
end
+ end
- params
+ # Construct the downloadUri from raw artifactory data
+ #
+ def generate_download_uri(result)
+ uri = []
+ uri << endpoint.sub(/\/$/, "")
+ uri << result["repo"]
+ uri << result["path"]
+ uri << result["name"]
+ uri.join("/")
end
- def headers
- { "X-Result-Detail" => "properties" }
+ def client
+ @client ||= ::Artifactory::Client.new(
+ endpoint: endpoint,
+ username: ENV["ARTIFACTORY_USERNAME"],
+ password: ENV["ARTIFACTORY_PASSWORD"]
+ )
+ end
+
+ def endpoint
+ @endpoint ||= ENV.fetch("ARTIFACTORY_ENDPOINT", ENDPOINT)
+ end
+
+ def artifactory_request
+ begin
+ results = yield
+ rescue Errno::ETIMEDOUT, ::Artifactory::Error::ConnectionError
+ raise ConnectionError, <<-EOS
+Artifactory endpoint '#{::Artifactory.endpoint}' is unreachable. Check that
+the endpoint is correct and there is an open connection to Chef's private network.
+ EOS
+ rescue ::Artifactory::Error::HTTPError => e
+ if e.code == 401 && e.message =~ /Bad credentials/
+ raise AuthenticationError, <<-EOS
+Artifactory server denied credentials. Verify ARTIFACTORY_USERNAME and
+ARTIFACTORY_PASSWORD environment variables are configured properly.
+ EOS
+ else
+ raise e
+ end
+ end
+
+ results
end
end
end
end
end