Class: Berkshelf::CommunityREST

Inherits:
Object
  • Object
show all
Defined in:
lib/berkshelf/community_rest.rb

Constant Summary collapse

V1_API =
"https://supermarket.chef.io".freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(uri = V1_API, options = {}) ⇒ CommunityREST

Returns a new instance of CommunityREST.

Parameters:

  • uri (String) (defaults to: V1_API)

    (CommunityREST::V1_API) location of community site to connect to

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :retries (Integer) — default: 5

    retry requests on 5XX failures

  • :retry_interval (Float) — default: 0.5

    how often we should pause between retries



70
71
72
73
74
75
76
77
78
79
# File 'lib/berkshelf/community_rest.rb', line 70

def initialize(uri = V1_API, options = {})
  options = options.dup
  options = { retries: 5, retry_interval: 0.5, ssl: Berkshelf::Config.instance.ssl }.merge(options)
  @api_uri = uri
  options[:server_url] = uri
  @retries = options.delete(:retries)
  @retry_interval = options.delete(:retry_interval)

  @connection = Berkshelf::RidleyCompatJSON.new(**options)
end

Instance Attribute Details

#api_uriString (readonly)

Returns:



53
54
55
# File 'lib/berkshelf/community_rest.rb', line 53

def api_uri
  @api_uri
end

#connectionBerkshelf::RidleyCompat (readonly)



61
62
63
# File 'lib/berkshelf/community_rest.rb', line 61

def connection
  @connection
end

#retriesInteger (readonly)

Returns how many retries to attempt on HTTP requests.

Returns:

  • (Integer)

    how many retries to attempt on HTTP requests



56
57
58
# File 'lib/berkshelf/community_rest.rb', line 56

def retries
  @retries
end

#retry_intervalFloat (readonly)

Returns time to wait between retries.

Returns:

  • (Float)

    time to wait between retries



59
60
61
# File 'lib/berkshelf/community_rest.rb', line 59

def retry_interval
  @retry_interval
end

Class Method Details

.unpack(target, destination) ⇒ String

Parameters:

  • target (String)

    file path to the tar.gz archive on disk

  • destination (String)

    file path to extract the contents of the target to

Returns:



13
14
15
16
17
18
19
20
21
# File 'lib/berkshelf/community_rest.rb', line 13

def unpack(target, destination)
  if is_gzip_file(target) || is_tar_file(target)
    Mixlib::Archive.new(target).extract(destination)
  else
    raise Berkshelf::UnknownCompressionType.new(target, destination)
  end

  destination
end

.uri_escape_version(version) ⇒ String

Parameters:

Returns:



26
27
28
# File 'lib/berkshelf/community_rest.rb', line 26

def uri_escape_version(version)
  version.to_s.tr(".", "_")
end

.version_from_uri(uri) ⇒ String

Parameters:

Returns:



33
34
35
# File 'lib/berkshelf/community_rest.rb', line 33

def version_from_uri(uri)
  File.basename(uri.to_s).tr("_", ".")
end

Instance Method Details

#download(name, version) ⇒ String?

Download and extract target cookbook archive to the local file system, returning its filepath.

Parameters:

  • name (String)

    the name of the cookbook

  • version (String)

    the targeted version of the cookbook

Returns:

  • (String, nil)

    cookbook filepath, or nil if archive does not contain a cookbook



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/berkshelf/community_rest.rb', line 91

def download(name, version)
  archive = stream(find(name, version)["file"])
  scratch = Dir.mktmpdir
  extracted = self.class.unpack(archive.path, scratch)

  if File.cookbook?(extracted)
    extracted
  else
    Dir.glob("#{extracted}/*").find do |dir|
      File.cookbook?(dir)
    end
  end
ensure
  archive.unlink unless archive.nil?
end

#find(name, version) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/berkshelf/community_rest.rb', line 107

def find(name, version)
  body = connection.get("cookbooks/#{name}/versions/#{self.class.uri_escape_version(version)}")

  # Artifactory responds with a 200 and blank body for unknown cookbooks.
  raise CookbookNotFound.new(name, nil, "at `#{api_uri}'") if body.nil?

  body
rescue CookbookNotFound
  raise
rescue Berkshelf::APIClient::ServiceNotFound
  raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
rescue
  raise CommunitySiteError.new(api_uri, "'#{name}' (#{version})")
end

#latest_version(name) ⇒ String

Returns the latest version of the cookbook and its download link.

Returns:



125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/berkshelf/community_rest.rb', line 125

def latest_version(name)
  body = connection.get("cookbooks/#{name}")

  # Artifactory responds with a 200 and blank body for unknown cookbooks.
  raise CookbookNotFound.new(name, nil, "at `#{api_uri}'") if body.nil?

  self.class.version_from_uri body["latest_version"]
rescue Berkshelf::APIClient::ServiceNotFound
  raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
rescue
  raise CommunitySiteError.new(api_uri, "the latest version of '#{name}'")
end

#satisfy(name, constraint) ⇒ String

Parameters:

Returns:



161
162
163
164
165
# File 'lib/berkshelf/community_rest.rb', line 161

def satisfy(name, constraint)
  Semverse::Constraint.satisfy_best(constraint, versions(name)).to_s
rescue Semverse::NoSolutionError
  nil
end

#stream(target) ⇒ Tempfile

Stream the response body of a remote URL to a file on the local file system

Parameters:

  • target (String)

    a URL to stream the response body from

Returns:

  • (Tempfile)


173
174
175
176
177
178
179
180
181
# File 'lib/berkshelf/community_rest.rb', line 173

def stream(target)
  local = Tempfile.new("community-rest-stream")
  local.binmode
  Retryable.retryable(tries: retries, on: Berkshelf::APIClientError, sleep: retry_interval) do
    connection.streaming_request(target, {}, local)
  end
ensure
  local.close(false) unless local.nil?
end

#versions(name) ⇒ Array

Parameters:

Returns:

  • (Array)


141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/berkshelf/community_rest.rb', line 141

def versions(name)
  body = connection.get("cookbooks/#{name}")

  # Artifactory responds with a 200 and blank body for unknown cookbooks.
  raise CookbookNotFound.new(name, nil, "at `#{api_uri}'") if body.nil?

  body["versions"].collect do |version_uri|
    self.class.version_from_uri(version_uri)
  end

rescue Berkshelf::APIClient::ServiceNotFound
  raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
rescue
  raise CommunitySiteError.new(api_uri, "versions of '#{name}'")
end