Class: BoltServer::TransportApp
- Inherits:
-
Sinatra::Base
- Object
- Sinatra::Base
- BoltServer::TransportApp
- Defined in:
- lib/bolt_server/transport_app.rb
Constant Summary collapse
- PARTIAL_SCHEMAS =
These partial schemas are reused to build multiple request schemas
%w[target-any target-ssh target-winrm task].freeze
- REQUEST_SCHEMAS =
These schemas combine shared schemas to describe client requests
%w[ action-check_node_connections action-run_command action-run_task action-run_script action-upload_file transport-ssh transport-winrm ].freeze
- ACTIONS =
%w[ check_node_connections run_command run_task run_script upload_file ].freeze
Instance Method Summary collapse
- #check_node_connections(targets, body) ⇒ Object
-
#initialize(config) ⇒ TransportApp
constructor
A new instance of TransportApp.
- #make_ssh_target(target_hash) ⇒ Object
- #make_winrm_target(target_hash) ⇒ Object
-
#result_set_to_status_hash(result_set, aggregate: false) ⇒ Object
Turns a Bolt::ResultSet object into a status hash that is fit to return to the client in a response.
- #run_command(target, body) ⇒ Object
- #run_script(target, body) ⇒ Object
- #run_task(target, body) ⇒ Object
- #scrub_stack_trace(result) ⇒ Object
- #upload_file(target, body) ⇒ Object
- #validate_schema(schema, body) ⇒ Object
Constructor Details
#initialize(config) ⇒ TransportApp
Returns a new instance of TransportApp.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/bolt_server/transport_app.rb', line 32 def initialize(config) @config = config @schemas = Hash[REQUEST_SCHEMAS.map do |basename| [basename, JSON.parse(File.read(File.join(__dir__, ['schemas', "#{basename}.json"])))] end] PARTIAL_SCHEMAS.each do |basename| schema_content = JSON.parse(File.read(File.join(__dir__, ['schemas', 'partials', "#{basename}.json"]))) shared_schema = JSON::Schema.new(schema_content, Addressable::URI.parse("partial:#{basename}")) JSON::Validator.add_schema(shared_schema) end @executor = Bolt::Executor.new(0) @file_cache = BoltServer::FileCache.new(@config).setup super(nil) end |
Instance Method Details
#check_node_connections(targets, body) ⇒ Object
110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/bolt_server/transport_app.rb', line 110 def check_node_connections(targets, body) error = validate_schema(@schemas["action-check_node_connections"], body) return [], error unless error.nil? # Puppet Enterprise's orchestrator service uses the # check_node_connections endpoint to check whether nodes that should be # contacted over SSH or WinRM are responsive. The wait time here is 0 # because the endpoint is meant to be used for a single check of all # nodes; External implementations of wait_until_available (like # orchestrator's) should contact the endpoint in their own loop. [@executor.wait_until_available(targets, wait_time: 0), nil] end |
#make_ssh_target(target_hash) ⇒ Object
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/bolt_server/transport_app.rb', line 203 def make_ssh_target(target_hash) defaults = { 'host-key-check' => false } overrides = { 'load-config' => false } opts = defaults.merge(target_hash.clone).merge(overrides) if opts['private-key-content'] private_key_content = opts.delete('private-key-content') opts['private-key'] = { 'key-data' => private_key_content } end Bolt::Target.new(target_hash['hostname'], opts) end |
#make_winrm_target(target_hash) ⇒ Object
242 243 244 245 246 247 248 249 |
# File 'lib/bolt_server/transport_app.rb', line 242 def make_winrm_target(target_hash) overrides = { 'protocol' => 'winrm' } opts = target_hash.clone.merge(overrides) Bolt::Target.new(target_hash['hostname'], opts) end |
#result_set_to_status_hash(result_set, aggregate: false) ⇒ Object
Turns a Bolt::ResultSet object into a status hash that is fit to return to the client in a response.
If the ‘result_set` has more than one result, the status hash will have a `status` value and a list of target `results`. If the `result_set` contains only one item, it will be returned as a single result object. Set `aggregate` to treat it as a set of results with length 1 instead.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/bolt_server/transport_app.rb', line 75 def result_set_to_status_hash(result_set, aggregate: false) scrubbed_results = result_set.map do |result| scrub_stack_trace(result.status_hash) end if aggregate || scrubbed_results.length > 1 # For actions that act on multiple targets, construct a status hash for the aggregate result all_succeeded = scrubbed_results.all? { |r| r[:status] == 'success' } { status: all_succeeded ? 'success' : 'failure', result: scrubbed_results } else # If there was only one target, return the first result on its own scrubbed_results.first end end |
#run_command(target, body) ⇒ Object
102 103 104 105 106 107 108 |
# File 'lib/bolt_server/transport_app.rb', line 102 def run_command(target, body) error = validate_schema(@schemas["action-run_command"], body) return [], error unless error.nil? command = body['command'] [@executor.run_command(target, command), nil] end |
#run_script(target, body) ⇒ Object
166 167 168 169 170 171 172 173 174 |
# File 'lib/bolt_server/transport_app.rb', line 166 def run_script(target, body) error = validate_schema(@schemas["action-run_script"], body) return [], error unless error.nil? # Download the file onto the machine. file_location = @file_cache.update_file(body['script']) [@executor.run_script(target, file_location, body['arguments'])] end |
#run_task(target, body) ⇒ Object
93 94 95 96 97 98 99 100 |
# File 'lib/bolt_server/transport_app.rb', line 93 def run_task(target, body) error = validate_schema(@schemas["action-run_task"], body) return [], error unless error.nil? task = Bolt::Task::PuppetServer.new(body['task'], @file_cache) parameters = body['parameters'] || {} [@executor.run_task(target, task, parameters), nil] end |
#scrub_stack_trace(result) ⇒ Object
51 52 53 54 55 56 |
# File 'lib/bolt_server/transport_app.rb', line 51 def scrub_stack_trace(result) if result.dig(:result, '_error', 'details', 'stack_trace') result[:result]['_error']['details'].reject! { |k| k == 'stack_trace' } end result end |
#upload_file(target, body) ⇒ Object
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 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 164 |
# File 'lib/bolt_server/transport_app.rb', line 123 def upload_file(target, body) error = validate_schema(@schemas["action-upload_file"], body) return [], error unless error.nil? files = body['files'] destination = body['destination'] job_id = body['job_id'] cache_dir = @file_cache.create_cache_dir(job_id.to_s) FileUtils.mkdir_p(cache_dir) files.each do |file| relative_path = file['relative_path'] uri = file['uri'] sha256 = file['sha256'] kind = file['kind'] path = File.join(cache_dir, relative_path) if kind == 'file' # The parent should already be created by `directory` entries, # but this is to be on the safe side. parent = File.dirname(path) FileUtils.mkdir_p(parent) @file_cache.serial_execute { @file_cache.download_file(path, sha256, uri) } elsif kind == 'directory' # Create directory in cache so we can move files in. FileUtils.mkdir_p(path) else return [400, Bolt::Error.new("Invalid `kind` of '#{kind}' supplied. Must be `file` or `directory`.", 'boltserver/schema-error').to_json] end end # We need to special case the scenario where only one file was # included in the request to download. Otherwise, the call to upload_file # will attempt to upload with a directory as a source and potentially a # filename as a destination on the host. In that case the end result will # be the file downloaded to a directory with the same name as the source # filename, rather than directly to the filename set in the destination. upload_source = if files.size == 1 && files[0]['kind'] == 'file' File.join(cache_dir, files[0]['relative_path']) else cache_dir end [@executor.upload_file(target, upload_source, destination), nil] end |
#validate_schema(schema, body) ⇒ Object
58 59 60 61 62 63 64 65 |
# File 'lib/bolt_server/transport_app.rb', line 58 def validate_schema(schema, body) schema_error = JSON::Validator.fully_validate(schema, body) if schema_error.any? Bolt::Error.new("There was an error validating the request body.", 'boltserver/schema-error', schema_error) end end |