Class: Puppet::Provider::ElasticREST

Inherits:
Puppet::Provider
  • Object
show all
Defined in:
lib/puppet/provider/elastic_rest.rb

Overview

Parent class encapsulating general-use functions for children REST-based providers.

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(value = {}) ⇒ ElasticREST

Returns a new instance of ElasticREST.



165
166
167
168
# File 'lib/puppet/provider/elastic_rest.rb', line 165

def initialize(value = {})
  super(value)
  @property_flush = {}
end

Class Attribute Details

.api_discovery_uriObject

Returns the value of attribute api_discovery_uri.



9
10
11
# File 'lib/puppet/provider/elastic_rest.rb', line 9

def api_discovery_uri
  @api_discovery_uri
end

.api_resource_styleObject

Returns the value of attribute api_resource_style.



10
11
12
# File 'lib/puppet/provider/elastic_rest.rb', line 10

def api_resource_style
  @api_resource_style
end

.api_uriObject

Returns the value of attribute api_uri.



11
12
13
# File 'lib/puppet/provider/elastic_rest.rb', line 11

def api_uri
  @api_uri
end

.discrete_resource_creationObject

Returns the value of attribute discrete_resource_creation.



12
13
14
# File 'lib/puppet/provider/elastic_rest.rb', line 12

def discrete_resource_creation
  @discrete_resource_creation
end

.metadataObject

Returns the value of attribute metadata.



13
14
15
# File 'lib/puppet/provider/elastic_rest.rb', line 13

def 
  @metadata
end

.metadata_pipelineObject

Returns the value of attribute metadata_pipeline.



14
15
16
# File 'lib/puppet/provider/elastic_rest.rb', line 14

def 
  @metadata_pipeline
end

Class Method Details

.api_objects(protocol = 'http', validate_tls = true, host = 'localhost', port = 9200, timeout = 10, username = nil, password = nil, ca_file = nil, ca_path = nil) ⇒ Object

Fetch Elasticsearch API objects. Accepts a variety of argument functions dictating how to connect to the Elasticsearch API.

Returns:

  • Array an array of Hashes representing the found API objects, whether they be templates, pipelines, et cetera.



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/puppet/provider/elastic_rest.rb', line 82

def self.api_objects protocol = 'http', \
                     validate_tls = true, \
                     host = 'localhost', \
                     port = 9200, \
                     timeout = 10, \
                     username = nil, \
                     password = nil, \
                     ca_file = nil, \
                     ca_path = nil

  uri = URI("#{protocol}://#{host}:#{port}/#{format_uri(api_discovery_uri)}")
  http = Net::HTTP.new uri.host, uri.port
  req = Net::HTTP::Get.new uri.request_uri

  http.use_ssl = uri.scheme == 'https'
  [[ca_file, :ca_file=], [ca_path, :ca_path=]].each do |arg, method|
    http.send method, arg if arg and http.respond_to? method
  end

  response = rest http, req, validate_tls, timeout, username, password

  if response.respond_to? :code and response.code.to_i == 200
    JSON.parse(response.body).map do |object_name, api_object|
      {
        :name => object_name,
        :ensure => :present,
         => (api_object),
        :provider => name
      }
    end
  else
    []
  end
end

.format_uri(resource_path, property_flush = {}) ⇒ Object

Helper to format a remote URL request for Elasticsearch which takes into account path ordering, et cetera.



62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/puppet/provider/elastic_rest.rb', line 62

def self.format_uri(resource_path, property_flush = {})
  return api_uri if resource_path.nil?
  if discrete_resource_creation and not property_flush[:ensure].nil?
    resource_path
  else
    case api_resource_style
    when :prefix
      resource_path + '/' + api_uri
    else
      api_uri + '/' + resource_path
    end
  end
end

.instancesObject

Fetch an array of provider objects from the Elasticsearch API.



130
131
132
# File 'lib/puppet/provider/elastic_rest.rb', line 130

def self.instances
  api_objects.map { |resource| new resource }
end

.prefetch(resources) ⇒ Object

Unlike a typical #prefetch, which just ties discovered #instances to the correct resources, we need to quantify all the ways the resources in the catalog know about Elasticsearch API access and use those settings to fetch any templates we can before associating resources and providers.



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/puppet/provider/elastic_rest.rb', line 138

def self.prefetch(resources)
  # Get all relevant API access methods from the resources we know about
  resources.map do |_, resource|
    p = resource.parameters
    [
      p[:protocol].value,
      p[:validate_tls].value,
      p[:host].value,
      p[:port].value,
      p[:timeout].value,
      (p.key?(:username) ? p[:username].value : nil),
      (p.key?(:password) ? p[:password].value : nil),
      (p.key?(:ca_file) ? p[:ca_file].value : nil),
      (p.key?(:ca_path) ? p[:ca_path].value : nil)
    ]
    # Deduplicate identical settings, and fetch templates
  end.uniq.map do |api|
    api_objects(*api)
    # Flatten and deduplicate the array, instantiate providers, and do the
    # typical association dance
  end.flatten.uniq.map { |resource| new resource }.each do |prov|
    if resource = resources[prov.name]
      resource.provider = prov
    end
  end
end

.process_metadata(raw_metadata) ⇒ Object

Passes API objects through arbitrary Procs/lambdas in order to postprocess API responses.



119
120
121
122
123
124
125
126
127
# File 'lib/puppet/provider/elastic_rest.rb', line 119

def self.()
  if .is_a? Array and !.empty?
    .reduce() do |md, processor|
      processor.call md
    end
  else
    
  end
end

.rest(http, req, validate_tls = true, timeout = 10, username = nil, password = nil) ⇒ Object

Perform a REST API request against the indicated endpoint.

Returns:

  • Net::HTTPResponse



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/puppet/provider/elastic_rest.rb', line 27

def self.rest http, \
              req, \
              validate_tls = true, \
              timeout = 10, \
              username = nil, \
              password = nil

  if username and password
    req.basic_auth username, password
  elsif username or password
    Puppet.warning(
      'username and password must both be defined, skipping basic auth'
    )
  end

  req['Accept'] = 'application/json'

  http.read_timeout = timeout
  http.open_timeout = timeout
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE unless validate_tls

  begin
    http.request req
  rescue EOFError => e
    # Because the provider attempts a best guess at API access, we
    # only fail when HTTP operations fail for mutating methods.
    unless %w(GET OPTIONS HEAD).include? req.method
      raise Puppet::Error,
        "Received '#{e}' from the Elasticsearch API. Are your API settings correct?"
    end
  end
end

Instance Method Details

#createObject

Set this provider’s ‘:ensure` property to `:present`.



254
255
256
# File 'lib/puppet/provider/elastic_rest.rb', line 254

def create
  @property_flush[:ensure] = :present
end

#destroyObject

Set this provider’s ‘:ensure` property to `:absent`.



263
264
265
# File 'lib/puppet/provider/elastic_rest.rb', line 263

def destroy
  @property_flush[:ensure] = :absent
end

#exists?Boolean

Returns:

  • (Boolean)


258
259
260
# File 'lib/puppet/provider/elastic_rest.rb', line 258

def exists?
  @property_hash[:ensure] == :present
end

#flushObject

Call Elasticsearch’s REST API to appropriately PUT/DELETE/or otherwise update any managed API objects.



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/puppet/provider/elastic_rest.rb', line 172

def flush
  uri = URI(
    format(
      '%s://%s:%d/%s',
      resource[:protocol],
      resource[:host],
      resource[:port],
      self.class.format_uri(resource[:name], @property_flush)
    )
  )

  case @property_flush[:ensure]
  when :absent
    req = Net::HTTP::Delete.new uri.request_uri
  else
    req = Net::HTTP::Put.new uri.request_uri
    req.body = JSON.generate(
      if  != :content and @property_flush[:ensure] == :present
        { .to_s => resource[] }
      else
        resource[]
      end
    )
    # As of Elasticsearch 6.x, required when requesting with a payload (so we
    # set it always to be safe)
    req['Content-Type'] = 'application/json' if req['Content-Type'].nil?
  end

  http = Net::HTTP.new uri.host, uri.port
  http.use_ssl = uri.scheme == 'https'
  [:ca_file, :ca_path].each do |arg|
    if !resource[arg].nil? and http.respond_to? arg
      http.send "#{arg}=".to_sym, resource[arg]
    end
  end

  response = self.class.rest(
    http,
    req,
    resource[:validate_tls],
    resource[:timeout],
    resource[:username],
    resource[:password]
  )

  # Attempt to return useful error output
  unless response.code.to_i == 200
    json = JSON.parse(response.body)

    err_msg = if json.key? 'error'
                if json['error'].is_a? Hash \
                    and json['error'].key? 'root_cause'
                  # Newer versions have useful output
                  json['error']['root_cause'].first['reason']
                else
                  # Otherwise fallback to old-style error messages
                  json['error']
                end
              else
                # As a last resort, return the response error code
                "HTTP #{response.code}"
              end

    raise Puppet::Error, "Elasticsearch API responded with: #{err_msg}"
  end

  @property_hash = self.class.api_objects(
    resource[:protocol],
    resource[:validate_tls],
    resource[:host],
    resource[:port],
    resource[:timeout],
    resource[:username],
    resource[:password],
    resource[:ca_file],
    resource[:ca_path]
  ).detect do |t|
    t[:name] == resource[:name]
  end
end

#metadataObject

Fetch arbitrary metadata for the class from an instance object.

Returns:

  • String



20
21
22
# File 'lib/puppet/provider/elastic_rest.rb', line 20

def 
  self.class.
end