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



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

def initialize(uri = V1_API, options = {})
  options         = options.reverse_merge(retries: 5, retry_interval: 0.5, ssl: { verify: Berkshelf::Config.instance.ssl.verify })
  @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 :follow_redirects
    b.request :retry,
      max: @retries,
      interval: @retry_interval,
      exceptions: [Faraday::Error::TimeoutError]

    b.adapter :httpclient
  end

  super(api_uri, options)
end

Instance Attribute Details

#api_uriString (readonly)

Returns:



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

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



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

def retries
  @retries
end

#retry_intervalFloat (readonly)

Returns time to wait between retries.

Returns:

  • (Float)

    time to wait between retries



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

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:



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

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:



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

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

.version_from_uri(uri) ⇒ String

Parameters:

Returns:



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

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



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/berkshelf/community_rest.rb', line 99

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



115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/berkshelf/community_rest.rb', line 115

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:



131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/berkshelf/community_rest.rb', line 131

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:



166
167
168
169
170
# File 'lib/berkshelf/community_rest.rb', line 166

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)


178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/berkshelf/community_rest.rb', line 178

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

  Retryable.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)


147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/berkshelf/community_rest.rb', line 147

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