Class: Berkshelf::CommunityREST

Inherits:
Faraday::Connection
  • 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


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/berkshelf/community_rest.rb', line 71

def initialize(uri = V1_API, options = {})
  options         = options.reverse_merge(retries: 5, retry_interval: 0.5)
  @api_uri        = uri
  @retries        = options.delete(:retries)
  @retry_interval = options.delete(:retry_interval)

  options[:builder] ||= Faraday::RackBuilder.new do |b|
    b.response :parse_json
    b.response :gzip
    b.response :follow_redirects
    b.request :retry,
      max: @retries,
      interval: @retry_interval,
      exceptions: [Faraday::Error::TimeoutError]

    b.adapter :net_http
  end

  super(api_uri, options)
end

Instance Attribute Details

#api_uriString (readonly)

Returns:


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

def api_uri
  @api_uri
end

#retriesInteger (readonly)

Returns how many retries to attempt on HTTP requests.

Returns:

  • (Integer)

    how many retries to attempt on HTTP requests


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

def retries
  @retries
end

#retry_intervalFloat (readonly)

Returns time to wait between retries.

Returns:

  • (Float)

    time to wait between retries


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

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
22
23
24
# File 'lib/berkshelf/community_rest.rb', line 13

def unpack(target, destination)
  if is_gzip_file(target)
    Archive::Tar::Minitar.unpack(Zlib::GzipReader.new(File.open(target, 'rb')), destination)
  elsif is_tar_file(target)
    Archive::Tar::Minitar.unpack(target, destination)
  else
    raise Berkshelf::UnknownCompressionType.new(target)
  end

  FileUtils.rm_rf Dir.glob("#{destination}/**/PaxHeader")
  destination
end

.uri_escape_version(version) ⇒ String

Parameters:

Returns:


29
30
31
# File 'lib/berkshelf/community_rest.rb', line 29

def uri_escape_version(version)
  version.to_s.gsub('.', '_')
end

.version_from_uri(uri) ⇒ String

Parameters:

Returns:


36
37
38
# File 'lib/berkshelf/community_rest.rb', line 36

def version_from_uri(uri)
  File.basename(uri.to_s).gsub('_', '.')
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


102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/berkshelf/community_rest.rb', line 102

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(File.join(extracted, '*')).find do |dir|
      File.cookbook?(dir)
    end
  end
ensure
  archive.unlink unless archive.nil?
end

#find(name, version) ⇒ Object


118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/berkshelf/community_rest.rb', line 118

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

  case response.status
  when (200..299)
    response.body
  when 404
    raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
  else
    raise CommunitySiteError.new(api_uri, "'#{name}' (#{version})")
  end
end

#latest_version(name) ⇒ String

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

Returns:


134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/berkshelf/community_rest.rb', line 134

def latest_version(name)
  response = get("cookbooks/#{name}")

  case response.status
  when (200..299)
    self.class.version_from_uri response.body['latest_version']
  when 404
    raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
  else
    raise CommunitySiteError.new(api_uri, "the latest version of '#{name}'")
  end
end

#satisfy(name, constraint) ⇒ String

Parameters:

Returns:


169
170
171
172
173
# File 'lib/berkshelf/community_rest.rb', line 169

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)

181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/berkshelf/community_rest.rb', line 181

def stream(target)
  local = Tempfile.new('community-rest-stream')
  local.binmode

  retryable(tries: retries, on: OpenURI::HTTPError, sleep: retry_interval) do
    open(target, 'rb', open_uri_options) do |remote|
      local.write(remote.read)
    end
  end

  local
ensure
  local.close(false) unless local.nil?
end

#versions(name) ⇒ Array

Parameters:

Returns:

  • (Array)

150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/berkshelf/community_rest.rb', line 150

def versions(name)
  response = get("cookbooks/#{name}")

  case response.status
  when (200..299)
    response.body['versions'].collect do |version_uri|
      self.class.version_from_uri(version_uri)
    end
  when 404
    raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
  else
    raise CommunitySiteError.new(api_uri, "versions of '#{name}'")
  end
end