Class: ACE::TransportApp

Inherits:
Sinatra::Base
  • Object
show all
Defined in:
lib/ace/transport_app.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config = nil) ⇒ TransportApp

Returns a new instance of TransportApp.



21
22
23
24
25
26
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/ace/transport_app.rb', line 21

def initialize(config = nil)
  @logger = Logging.logger[self]
  @config = config
  @executor = Bolt::Executor.new(0)
  tasks_cache_dir = File.join(@config['cache-dir'], 'tasks')
  @mutex = ACE::FileMutex.new(Tempfile.new('ace.lock'))
  @file_cache = BoltServer::FileCache.new(@config.data.merge('cache-dir' => tasks_cache_dir),
                                          cache_dir_mutex: @mutex, do_purge: false).setup
  environments_cache_dir = File.join(@config['cache-dir'], 'environment_cache')
  @plugins_mutex = ACE::FileMutex.new(Tempfile.new('ace.plugins.lock'))
  @plugins = ACE::PluginCache.new(environments_cache_dir,
                                  cache_dir_mutex: @plugins_mutex, do_purge: false).setup

  @schemas = {
    "run_task" => JSON.parse(File.read(File.join(__dir__, 'schemas', 'ace-run_task.json'))),
    "execute_catalog" => JSON.parse(File.read(File.join(__dir__, 'schemas', 'ace-execute_catalog.json')))
  }
  shared_schema = JSON::Schema.new(JSON.parse(File.read(File.join(__dir__, 'schemas', 'task.json'))),
                                   Addressable::URI.parse("file:task"))
  JSON::Validator.add_schema(shared_schema)

  ACE::PuppetUtil.init_global_settings(config['ssl-ca-cert'],
                                       config['ssl-ca-crls'],
                                       config['ssl-key'],
                                       config['ssl-cert'],
                                       config['cache-dir'],
                                       URI.parse(config['puppet-server-uri']))

  ace_pid = Process.pid
  @logger.info "ACE started: #{ace_pid}"
  fork do
    # :nocov:
    # FileCache and PluginCache cleanup timer started in a seperate fork
    # so that there is only a single timer responsible for purging old files
    @logger.info "FileCache process started: #{Process.pid}"
    @fc_purge = BoltServer::FileCache.new(@config.data.merge('cache-dir' => tasks_cache_dir),
                                          cache_dir_mutex: @mutex,
                                          do_purge: true)

    @pc_purge = ACE::PluginCache.new(environments_cache_dir,
                                     cache_dir_mutex: @plugins_mutex, do_purge: true)
    loop do
      begin
        # is the parent process alibve
        Process.getpgid(ace_pid)
        sleep 10 # how often to check if parent process is alive
      rescue Interrupt
        # handle ctrl-c event
        break
      rescue StandardError
        # parent is no longer alive
        break
      end
    end
    @logger.info "FileCache process ended"
    # :nocov:
  end

  super(nil)
end

Class Method Details

.init_puppet_target(certname, transport, target) ⇒ Puppet device instance

Initialises the puppet target.

Examples:

Connect to device.

init_puppet_target('test_device.domain.com', 'panos',  JSON.parse("target":{
                                                                    "remote-transport":"panos",
                                                                    "host":"fw.example.net",
                                                                    "user":"foo",
                                                                    "password":"wibble"
                                                                  }) ) => panos.device

Parameters:

  • certname

    The certificate name of the target.

  • transport

    The transport provider of the target.

  • target

    Target connection hash or legacy connection URI

Returns:

  • (Puppet device instance)

    Returns Puppet device instance

Raises:

  • (puppetlabs/ace/invalid_param)

    If nil parameter or no connection detail found



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/ace/transport_app.rb', line 95

def self.init_puppet_target(certname, transport, target)
  unless target
    raise ACE::Error.new("There was an error parsing the Puppet target. 'target' not found",
                         'puppetlabs/ace/invalid_param')
  end
  unless certname
    raise ACE::Error.new("There was an error parsing the Puppet compiler details. 'certname' not found",
                         'puppetlabs/ace/invalid_param')
  end
  unless transport
    raise ACE::Error.new("There was an error parsing the Puppet target. 'transport' not found",
                         'puppetlabs/ace/invalid_param')
  end

  if target['uri']
    if target['uri'] =~ URI::DEFAULT_PARSER.make_regexp
      # Correct URL
      url = target['uri']
    else
      raise ACE::Error.new("There was an error parsing the URI of the Puppet target",
                           'puppetlabs/ace/invalid_param')
    end
  else
    url = Hash[target.map { |(k, v)| [k.to_sym, v] }]
    url.delete(:"remote-transport")
  end

  device_struct = Struct.new(:provider, :url, :name, :options)
  type = target['remote-transport']
  # Return device
  begin
    require 'puppet/resource_api/transport'
    transport = Puppet::ResourceApi::Transport.connect(type, url)
    Puppet::ResourceApi::Transport.inject_device(type, transport)
  rescue Puppet::DevError, LoadError => e
    raise e unless e.message.include?("Transport for `#{type}` not registered with") || e.class == LoadError
    # fallback to puppet device if there's no transport
    Puppet::Util::NetworkDevice.init(device_struct.new(transport,
                                                       url,
                                                       certname,
                                                       {}))
  end
end

.trusted_facts(certname) ⇒ Object

returns a hash of trusted facts that will be used to request a catalog for the target



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/ace/transport_app.rb', line 166

def self.trusted_facts(certname)
  # if the certname is a valid FQDN, it will split
  # it in to the correct hostname.domain format
  # otherwise hostname will be the certname and domain
  # will be empty
  hostname, domain = certname.split('.', 2)
  trusted_facts = {
    "authenticated": "remote",
    "extensions": {},
    "certname": certname,
    "hostname": hostname
  }
  trusted_facts[:domain] = domain if domain
  trusted_facts
end

Instance Method Details

#nest_metrics(metrics) ⇒ Object



158
159
160
161
162
# File 'lib/ace/transport_app.rb', line 158

def nest_metrics(metrics)
  Hash[metrics.fetch('resources', {}).values.map do |name, _human_name, value|
    [name, value]
  end]
end

#scrub_stack_trace(result) ⇒ Object



139
140
141
142
143
144
145
146
147
# File 'lib/ace/transport_app.rb', line 139

def scrub_stack_trace(result)
  if result.dig(:result, '_error', 'details', 'stack_trace')
    result[:result]['_error']['details'].reject! { |k| k == 'stack_trace' }
  end
  if result.dig(:result, '_error', 'details', 'backtrace')
    result[:result]['_error']['details'].reject! { |k| k == 'backtrace' }
  end
  result
end

#validate_schema(schema, body) ⇒ Object



149
150
151
152
153
154
155
156
# File 'lib/ace/transport_app.rb', line 149

def validate_schema(schema, body)
  schema_error = JSON::Validator.fully_validate(schema, body)
  if schema_error.any?
    raise ACE::Error.new("There was an error validating the request body.",
                         'puppetlabs/ace/schema-error',
                         schema_error: schema_error.first)
  end
end