right_scraper 5.0.1 → 5.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.rdoc +2 -2
- data/bin/right_scraper-retrieve +76 -0
- data/bin/right_scraper-scan +82 -0
- data/lib/right_scraper.rb +3 -0
- data/lib/right_scraper/main.rb +104 -55
- data/lib/right_scraper/processes.rb +0 -1
- data/lib/right_scraper/processes/shell.rb +2 -2
- data/lib/right_scraper/processes/ssh_agent.rb +1 -1
- data/lib/right_scraper/repositories/base.rb +1 -0
- data/lib/right_scraper/resources/cookbook.rb +18 -0
- data/lib/right_scraper/retrievers/download.rb +1 -1
- data/lib/right_scraper/scanners.rb +2 -0
- data/lib/right_scraper/scanners/cookbook_filename_scanner.rb +83 -0
- data/lib/right_scraper/scanners/cookbook_metadata.rb +134 -148
- data/lib/right_scraper/scanners/{workflow_metadata.rb → cookbook_metadata_readonly.rb} +32 -31
- data/lib/right_scraper/scrapers/base.rb +1 -1
- data/lib/right_scraper/version.rb +2 -2
- data/right_scraper.gemspec +2 -1
- data/{lib/right_scraper/resources/workflow.rb → scripts/knife_metadata.rb} +17 -29
- data/scripts/stub_ssh_askpass +3 -0
- metadata +11 -9
- data/lib/right_scraper/processes/warden.rb +0 -358
- data/lib/right_scraper/scanners/workflow_manifest.rb +0 -87
- data/lib/right_scraper/scanners/workflow_s3_upload.rb +0 -89
- data/lib/right_scraper/scrapers/workflow.rb +0 -94
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright: Copyright (c)
|
2
|
+
# Copyright: Copyright (c) 2016 RightScale, Inc.
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -24,20 +24,10 @@
|
|
24
24
|
# ancestor
|
25
25
|
require 'right_scraper/scanners'
|
26
26
|
|
27
|
-
require 'json'
|
28
|
-
|
29
27
|
module RightScraper::Scanners
|
30
28
|
|
31
|
-
#
|
32
|
-
class
|
33
|
-
# Begin a scan for the given workflow.
|
34
|
-
#
|
35
|
-
# === Parameters
|
36
|
-
# workflow(RightScraper::Resources::Workflow):: workflow to scan
|
37
|
-
def begin(workflow)
|
38
|
-
@workflow = workflow
|
39
|
-
@metadata_filename = File.basename(workflow.metadata_path)
|
40
|
-
end
|
29
|
+
# Loads existing cookbook metadata from a filesystem.
|
30
|
+
class CookbookMetadataReadOnly < CookbookMetadata
|
41
31
|
|
42
32
|
# Notice a file during scanning.
|
43
33
|
#
|
@@ -46,27 +36,38 @@ module RightScraper::Scanners
|
|
46
36
|
# not always be necessary to read the data.
|
47
37
|
#
|
48
38
|
# === Parameters
|
49
|
-
# relative_position(String):: relative pathname for the file from root of
|
50
|
-
def notice(relative_position)
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
39
|
+
# relative_position(String):: relative pathname for the file from root of cookbook
|
40
|
+
def notice(relative_position, &blk)
|
41
|
+
case relative_position
|
42
|
+
when JSON_METADATA
|
43
|
+
# preferred over RUBY_METADATA.
|
44
|
+
@read_blk = blk
|
45
|
+
when RUBY_METADATA
|
46
|
+
# defer to any JSON_METADATA, which we hope refers to the same info.
|
47
|
+
@read_blk ||= self.method(:generated_metadata_json_readonly)
|
55
48
|
end
|
49
|
+
true
|
56
50
|
end
|
57
51
|
|
58
|
-
|
59
|
-
|
60
|
-
#
|
61
|
-
# +relative_position+ being +nil+).
|
62
|
-
#
|
63
|
-
# === Parameters
|
64
|
-
# relative_position(String):: relative pathname for the directory from root of workflow
|
52
|
+
private
|
53
|
+
|
54
|
+
# Reads the existing generated 'metadata.json' or else fails.
|
65
55
|
#
|
66
56
|
# === Returns
|
67
|
-
#
|
68
|
-
def
|
69
|
-
|
57
|
+
# @return [String] metadata JSON text
|
58
|
+
def generated_metadata_json_readonly
|
59
|
+
@logger.operation(:metadata_readonly) do
|
60
|
+
# path constants
|
61
|
+
freed_metadata_dir = (@cookbook.pos == '.' && freed_dir) || ::File.join(freed_dir, @cookbook.pos)
|
62
|
+
freed_metadata_json_path = ::File.join(freed_metadata_dir, JSON_METADATA)
|
63
|
+
|
64
|
+
# in the multi-pass case we will run this scanner only on the second
|
65
|
+
# and any subsequent passed, which are outside of containment. the
|
66
|
+
# metadata must have already been generated at this point or else it
|
67
|
+
# should be considered an internal error.
|
68
|
+
return ::File.read(freed_metadata_json_path)
|
69
|
+
end
|
70
70
|
end
|
71
|
-
|
72
|
-
end
|
71
|
+
|
72
|
+
end # CookbookMetadataReadOnly
|
73
|
+
end # RightScraper::Scanners
|
@@ -26,7 +26,7 @@ require 'right_scraper/scrapers'
|
|
26
26
|
|
27
27
|
module RightScraper::Scrapers
|
28
28
|
|
29
|
-
class ScraperError <
|
29
|
+
class ScraperError < ::RightScraper::Error; end
|
30
30
|
|
31
31
|
# Base class for all scrapers. Subclasses should override
|
32
32
|
# #find_next which instantiates the resource from the file system.
|
@@ -1,5 +1,5 @@
|
|
1
1
|
#-- -*- mode: ruby; encoding: utf-8 -*-
|
2
|
-
# Copyright: Copyright (c) 2011 RightScale, Inc.
|
2
|
+
# Copyright: Copyright (c) 2011-2016 RightScale, Inc.
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -23,7 +23,7 @@
|
|
23
23
|
|
24
24
|
module RightScraper
|
25
25
|
# for gemspec, etc.
|
26
|
-
GEM_VERSION = '5.
|
26
|
+
GEM_VERSION = '5.1.1'
|
27
27
|
|
28
28
|
# (Fixnum) protocol versioning scheme; prepended to hashes to
|
29
29
|
# prevent collisions.
|
data/right_scraper.gemspec
CHANGED
@@ -37,6 +37,7 @@ require ::File.expand_path('../lib/right_scraper/version', __FILE__)
|
|
37
37
|
spec.required_ruby_version = '>= 2.1'
|
38
38
|
spec.rubyforge_project = %q{right_scraper}
|
39
39
|
spec.require_path = 'lib'
|
40
|
+
spec.executables = ::Dir.glob("bin/*").map { |p| ::File.basename(p) }
|
40
41
|
|
41
42
|
spec.add_dependency('right_aws', '>= 2.0')
|
42
43
|
spec.add_dependency('right_git')
|
@@ -58,7 +59,7 @@ will analyze the repository content and instantiate "resources" as a result. Cur
|
|
58
59
|
supported resources are Chef cookbooks and RightScale workflow definitions.
|
59
60
|
EOF
|
60
61
|
|
61
|
-
candidates = ::Dir.glob("lib
|
62
|
+
candidates = ::Dir.glob("{bin,scripts,lib/**}/*") +
|
62
63
|
%w(LICENSE README.rdoc right_scraper.gemspec)
|
63
64
|
spec.files = candidates.sort
|
64
65
|
end
|
@@ -1,5 +1,6 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
1
2
|
#--
|
2
|
-
# Copyright: Copyright (c)
|
3
|
+
# Copyright: Copyright (c) 2016 RightScale, Inc.
|
3
4
|
#
|
4
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
6
|
# a copy of this software and associated documentation files (the
|
@@ -21,34 +22,21 @@
|
|
21
22
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
23
|
#++
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
module RightScraper::Resources
|
28
|
-
|
29
|
-
class Workflow < ::RightScraper::Resources::Base
|
30
|
-
|
31
|
-
METADATA_EXT = '.meta'
|
32
|
-
DEFINITION_EXT = '.def'
|
33
|
-
|
34
|
-
# Relative path to definition file
|
35
|
-
# @pos must be set before this can be called
|
36
|
-
#
|
37
|
-
# === Return
|
38
|
-
# path(String):: Path to definition file
|
39
|
-
def definition_path
|
40
|
-
path = @pos
|
41
|
-
end
|
42
|
-
|
43
|
-
# Relative path to metadata file
|
44
|
-
# @pos must be set before this can be called
|
45
|
-
#
|
46
|
-
# === Return
|
47
|
-
# path(String):: Path to metadata file
|
48
|
-
def metadata_path
|
49
|
-
path = @pos.chomp(File.extname(@pos)) + METADATA_EXT if @pos
|
50
|
-
end
|
25
|
+
def warn(*args)
|
26
|
+
# eliminate ruby/gem warnings from output
|
27
|
+
end
|
51
28
|
|
52
|
-
|
29
|
+
require 'rubygems'
|
30
|
+
require 'chef'
|
31
|
+
require 'chef/knife/cookbook_metadata'
|
53
32
|
|
33
|
+
if ::ARGV.size != 1
|
34
|
+
$stderr.puts "Usage: #{::File.basename(__FILE__)} <cookbook_dir>"
|
35
|
+
exit 1
|
54
36
|
end
|
37
|
+
|
38
|
+
cookbook_dir = ARGV.pop
|
39
|
+
knife_metadata = ::Chef::Knife::CookbookMetadata.new
|
40
|
+
knife_metadata.name_args = [::File.basename(cookbook_dir)]
|
41
|
+
knife_metadata.config[:cookbook_path] = ::File.dirname(cookbook_dir)
|
42
|
+
knife_metadata.run
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: right_scraper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Raphael Simon
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2016-03-
|
13
|
+
date: 2016-03-15 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: right_aws
|
@@ -78,13 +78,17 @@ description: |
|
|
78
78
|
will analyze the repository content and instantiate "resources" as a result. Currently
|
79
79
|
supported resources are Chef cookbooks and RightScale workflow definitions.
|
80
80
|
email: support@rightscale.com
|
81
|
-
executables:
|
81
|
+
executables:
|
82
|
+
- right_scraper-retrieve
|
83
|
+
- right_scraper-scan
|
82
84
|
extensions: []
|
83
85
|
extra_rdoc_files:
|
84
86
|
- README.rdoc
|
85
87
|
files:
|
86
88
|
- LICENSE
|
87
89
|
- README.rdoc
|
90
|
+
- bin/right_scraper-retrieve
|
91
|
+
- bin/right_scraper-scan
|
88
92
|
- lib/right_scraper.rb
|
89
93
|
- lib/right_scraper/builders.rb
|
90
94
|
- lib/right_scraper/builders/base.rb
|
@@ -98,7 +102,6 @@ files:
|
|
98
102
|
- lib/right_scraper/processes/shell.rb
|
99
103
|
- lib/right_scraper/processes/ssh_agent.rb
|
100
104
|
- lib/right_scraper/processes/svn_client.rb
|
101
|
-
- lib/right_scraper/processes/warden.rb
|
102
105
|
- lib/right_scraper/registered_base.rb
|
103
106
|
- lib/right_scraper/repositories.rb
|
104
107
|
- lib/right_scraper/repositories/base.rb
|
@@ -108,7 +111,6 @@ files:
|
|
108
111
|
- lib/right_scraper/resources.rb
|
109
112
|
- lib/right_scraper/resources/base.rb
|
110
113
|
- lib/right_scraper/resources/cookbook.rb
|
111
|
-
- lib/right_scraper/resources/workflow.rb
|
112
114
|
- lib/right_scraper/retrievers.rb
|
113
115
|
- lib/right_scraper/retrievers/base.rb
|
114
116
|
- lib/right_scraper/retrievers/checkout_base.rb
|
@@ -117,19 +119,19 @@ files:
|
|
117
119
|
- lib/right_scraper/retrievers/svn.rb
|
118
120
|
- lib/right_scraper/scanners.rb
|
119
121
|
- lib/right_scraper/scanners/base.rb
|
122
|
+
- lib/right_scraper/scanners/cookbook_filename_scanner.rb
|
120
123
|
- lib/right_scraper/scanners/cookbook_manifest.rb
|
121
124
|
- lib/right_scraper/scanners/cookbook_metadata.rb
|
125
|
+
- lib/right_scraper/scanners/cookbook_metadata_readonly.rb
|
122
126
|
- lib/right_scraper/scanners/cookbook_s3_upload.rb
|
123
127
|
- lib/right_scraper/scanners/union.rb
|
124
|
-
- lib/right_scraper/scanners/workflow_manifest.rb
|
125
|
-
- lib/right_scraper/scanners/workflow_metadata.rb
|
126
|
-
- lib/right_scraper/scanners/workflow_s3_upload.rb
|
127
128
|
- lib/right_scraper/scrapers.rb
|
128
129
|
- lib/right_scraper/scrapers/base.rb
|
129
130
|
- lib/right_scraper/scrapers/cookbook.rb
|
130
|
-
- lib/right_scraper/scrapers/workflow.rb
|
131
131
|
- lib/right_scraper/version.rb
|
132
132
|
- right_scraper.gemspec
|
133
|
+
- scripts/knife_metadata.rb
|
134
|
+
- scripts/stub_ssh_askpass
|
133
135
|
homepage: https://github.com/rightscale/right_scraper
|
134
136
|
licenses: []
|
135
137
|
metadata: {}
|
@@ -1,358 +0,0 @@
|
|
1
|
-
#--
|
2
|
-
# Copyright: Copyright (c) 2013 RightScale, Inc.
|
3
|
-
#
|
4
|
-
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
-
# a copy of this software and associated documentation files (the
|
6
|
-
# 'Software'), to deal in the Software without restriction, including
|
7
|
-
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
-
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
-
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
-
# the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be
|
13
|
-
# included in all copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
16
|
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
18
|
-
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
19
|
-
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
20
|
-
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
21
|
-
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
-
#++
|
23
|
-
|
24
|
-
# ancestor
|
25
|
-
require 'right_scraper/processes'
|
26
|
-
|
27
|
-
require 'fileutils'
|
28
|
-
require 'right_popen'
|
29
|
-
require 'right_popen/safe_output_buffer'
|
30
|
-
require 'tmpdir'
|
31
|
-
|
32
|
-
module RightScraper
|
33
|
-
module Processes
|
34
|
-
class Warden
|
35
|
-
|
36
|
-
DEFAULT_RVM_HOME = '/usr/local/rvm'
|
37
|
-
DEFAULT_WARDEN_HOME = '/opt/warden'
|
38
|
-
|
39
|
-
RELATIVE_SCRIPTS_RVM_PATH = 'scripts/rvm'
|
40
|
-
|
41
|
-
# TEAL FIX: dynamically discover highest rvm-installed ruby 1.9 build?
|
42
|
-
DEFAULT_RVM_RUBY_VERSION = 'ruby-1.9.3-p448'
|
43
|
-
|
44
|
-
WARDEN_SERVICE_SUBDIR_NAME = 'warden'
|
45
|
-
RELATIVE_WARDEN_SCRIPT_PATH = 'bin/warden'
|
46
|
-
|
47
|
-
WARDEN_COMMAND_TIMEOUT = 60 # max seconds to spawn, link, etc.
|
48
|
-
|
49
|
-
DEFAULT_OPTIONS = {
|
50
|
-
:warden_home => DEFAULT_WARDEN_HOME,
|
51
|
-
:rvm_home => DEFAULT_RVM_HOME,
|
52
|
-
:rvm_ruby_version => DEFAULT_RVM_RUBY_VERSION
|
53
|
-
}
|
54
|
-
|
55
|
-
# marshalling
|
56
|
-
class LinkResult
|
57
|
-
attr_reader :exit_status, :stdout, :stderr
|
58
|
-
|
59
|
-
def initialize(link_result)
|
60
|
-
@exit_status = link_result['exit_status'].to_i rescue 1
|
61
|
-
@stdout = link_result['stdout'].to_s
|
62
|
-
@stderr = link_result['stderr'].to_s
|
63
|
-
end
|
64
|
-
|
65
|
-
def succeeded?
|
66
|
-
0 == exit_status
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
# exceptions
|
71
|
-
class StateError < Exception; end
|
72
|
-
class WardenError < Exception; end
|
73
|
-
|
74
|
-
class LinkError < Exception
|
75
|
-
attr_reader :link_result
|
76
|
-
|
77
|
-
def initialize(message, link_result)
|
78
|
-
super(message)
|
79
|
-
@link_result = link_result
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def initialize(options = {})
|
84
|
-
options = DEFAULT_OPTIONS.merge(options)
|
85
|
-
@warden_home = options[:warden_home]
|
86
|
-
@rvm_home = options[:rvm_home]
|
87
|
-
unless @rvm_ruby_version = options[:rvm_ruby_version]
|
88
|
-
raise ArgumentError.new('options[:rvm_ruby_version] is required')
|
89
|
-
end
|
90
|
-
|
91
|
-
# warden paths
|
92
|
-
unless @warden_home && ::File.directory?(@warden_home)
|
93
|
-
raise ArgumentError.new('options[:warden_home] is required')
|
94
|
-
end
|
95
|
-
unless @rvm_home && ::File.directory?(@rvm_home)
|
96
|
-
raise ArgumentError.new('options[:rvm_home] is required')
|
97
|
-
end
|
98
|
-
@warden_server_dir = ::File.join(@warden_home, WARDEN_SERVICE_SUBDIR_NAME)
|
99
|
-
@bin_warden_path = ::File.join(@warden_server_dir, RELATIVE_WARDEN_SCRIPT_PATH)
|
100
|
-
unless File.file?(@bin_warden_path)
|
101
|
-
raise StateError, "Warden CLI script cannot be found at #{@bin_warden_path.inspect}"
|
102
|
-
end
|
103
|
-
|
104
|
-
# rvm paths
|
105
|
-
@scripts_rvm_path = ::File.join(@rvm_home, RELATIVE_SCRIPTS_RVM_PATH)
|
106
|
-
unless File.file?(@scripts_rvm_path)
|
107
|
-
raise StateError, "RVM setup script cannot be found at #{@scripts_rvm_path.inspect}"
|
108
|
-
end
|
109
|
-
|
110
|
-
# build the jail.
|
111
|
-
@handle = send('create')['handle']
|
112
|
-
raise StateError, 'handle is invalid' unless @handle
|
113
|
-
end
|
114
|
-
|
115
|
-
# Runs the script given by container-relative path. Optionally copies
|
116
|
-
# files in/out before/after script execution.
|
117
|
-
#
|
118
|
-
# === Parameters
|
119
|
-
# @param [String|Array] cmds to execute
|
120
|
-
# @param [String|Array] copy_in file(s) to copy into jail (using same path on both sides) or nil or empty
|
121
|
-
# @param [Hash] copy_out files as map of jail source path to host destination path or empty or nil
|
122
|
-
#
|
123
|
-
# === Return
|
124
|
-
# @return [String] stdout text
|
125
|
-
#
|
126
|
-
# === Raise
|
127
|
-
# @raise [StateError] for invalid state
|
128
|
-
# @raise [LinkError] for link (to script output) failure
|
129
|
-
# @raise [WardenError] for warden failure
|
130
|
-
def run_command_in_jail(cmds, copy_in = nil, copy_out = nil)
|
131
|
-
cmds = Array(cmds)
|
132
|
-
raise ArgumentError, 'cmds is required' if cmds.empty?
|
133
|
-
raise StateError, 'handle is invalid' unless @handle
|
134
|
-
|
135
|
-
# copy any files in before running commands.
|
136
|
-
copy_in = Array(copy_in)
|
137
|
-
send_copy_in_cmds(copy_in) if !copy_in.empty?
|
138
|
-
|
139
|
-
# note that appending --privileged will run script as root, but we have
|
140
|
-
# no use case for running scripts as root at this time.
|
141
|
-
output = []
|
142
|
-
cmds.each do |cmd|
|
143
|
-
job_id = send("spawn --handle #{@handle} --script #{cmd.inspect}")['job_id']
|
144
|
-
link_result = LinkResult.new(send("link --handle #{@handle} --job_id #{job_id}"))
|
145
|
-
if link_result.succeeded?
|
146
|
-
output << link_result.stdout
|
147
|
-
else
|
148
|
-
raise LinkError.new('Script failed running in isolation.', link_result)
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
# copy any files out after command(s) succeeded.
|
153
|
-
if copy_out && !copy_out.empty?
|
154
|
-
copy_out_cmds = copy_out.inject([]) do |result, (src_path, dst_path)|
|
155
|
-
# create output directories because warden will only copy files.
|
156
|
-
parent_dir = ::File.dirname(dst_path)
|
157
|
-
::FileUtils.mkdir_p(parent_dir)
|
158
|
-
result << "copy_out --handle #{@handle} --src_path #{src_path.inspect} --dst_path #{dst_path.inspect}"
|
159
|
-
result
|
160
|
-
end
|
161
|
-
send(copy_out_cmds)
|
162
|
-
end
|
163
|
-
|
164
|
-
return output.join("\n")
|
165
|
-
end
|
166
|
-
|
167
|
-
def cleanup
|
168
|
-
raise StateError, 'handle is invalid' unless @handle
|
169
|
-
lay_to_rest
|
170
|
-
send("destroy --handle #{@handle}")
|
171
|
-
ensure
|
172
|
-
@handle = nil
|
173
|
-
end
|
174
|
-
|
175
|
-
private
|
176
|
-
|
177
|
-
def create_uuid
|
178
|
-
(0..15).to_a.map{|a| rand(16).to_s(16)}.join
|
179
|
-
end
|
180
|
-
|
181
|
-
# warden doesn't create directories on copy_in (or _out) so we need to
|
182
|
-
# generate a script and execute it before invoking copy_in.
|
183
|
-
#
|
184
|
-
# @param [Array] copy_in as array of files to copy into jail
|
185
|
-
def send_copy_in_cmds(copy_in)
|
186
|
-
mkdir_cmds = copy_in.
|
187
|
-
map { |dst_path| ::File.dirname(dst_path) }.uniq.sort.
|
188
|
-
map { |parent_dir| "mkdir -p #{parent_dir}" }
|
189
|
-
shell_script = <<EOS
|
190
|
-
#!/bin/bash
|
191
|
-
rm $0 # this script will self-destruct
|
192
|
-
#{mkdir_cmds.join(" &&\n")}
|
193
|
-
EOS
|
194
|
-
mkdir_script_name = "mkdir_script_#{create_uuid}.sh"
|
195
|
-
|
196
|
-
job_id = nil
|
197
|
-
::Dir.mktmpdir do |tmpdir|
|
198
|
-
mkdir_script_path = ::File.join(tmpdir, mkdir_script_name)
|
199
|
-
::File.open(mkdir_script_path, 'w') { |f| f.puts shell_script }
|
200
|
-
create_parent_dir_cmds = [
|
201
|
-
"copy_in --handle #{@handle} --src_path #{mkdir_script_path} --dst_path /tmp/mkdirs.sh",
|
202
|
-
"spawn --handle #{@handle} --script '/bin/bash /tmp/mkdirs.sh'",
|
203
|
-
]
|
204
|
-
job_id = send(create_parent_dir_cmds)['job_id']
|
205
|
-
end
|
206
|
-
|
207
|
-
link_result = LinkResult.new(send("link --handle #{@handle} --job_id #{job_id}"))
|
208
|
-
if link_result.succeeded?
|
209
|
-
copy_in_cmds = copy_in.inject([]) do |result, src_path|
|
210
|
-
result << "copy_in --handle #{@handle} --src_path #{src_path.inspect} --dst_path #{src_path.inspect}"
|
211
|
-
result
|
212
|
-
end
|
213
|
-
send(copy_in_cmds)
|
214
|
-
else
|
215
|
-
raise LinkError.new('Failed to create parent directories for files to be copied.', link_result)
|
216
|
-
end
|
217
|
-
true
|
218
|
-
end
|
219
|
-
|
220
|
-
# Sends one or more commands to warden and accumulates the stdout and
|
221
|
-
# stderr from those commands.
|
222
|
-
def send(warden_cmd)
|
223
|
-
# warden runs in a ruby 1.9.3 environment, for which we need rvm and a
|
224
|
-
# slew of fancy setup on the assumption that the current environemnt is
|
225
|
-
# not that. ideally this code would run in a standalone service where
|
226
|
-
# the warden-client gem could be used to simplify some of this.
|
227
|
-
warden_cmds = Array(warden_cmd).map do |line|
|
228
|
-
# execute bin/warden (Geronimo)
|
229
|
-
"bundle exec #{RELATIVE_WARDEN_SCRIPT_PATH} -- #{line}"
|
230
|
-
end
|
231
|
-
|
232
|
-
shell_script = <<EOS
|
233
|
-
#!/bin/bash
|
234
|
-
source #{@scripts_rvm_path} 1>/dev/null &&
|
235
|
-
rvm use #{@rvm_ruby_version}@global 1>/dev/null &&
|
236
|
-
cd #{@warden_server_dir} 1>/dev/null &&
|
237
|
-
#{warden_cmds.join(" &&\n")}
|
238
|
-
EOS
|
239
|
-
|
240
|
-
# ensure bundler env vars for current process don't interfere.
|
241
|
-
::Bundler.with_clean_env do
|
242
|
-
::Dir.mktmpdir do |tmpdir|
|
243
|
-
@process = nil
|
244
|
-
@interupted_to_close = false
|
245
|
-
@stdout_buffer = []
|
246
|
-
@stderr_buffer = ::RightScale::RightPopen::SafeOutputBuffer.new
|
247
|
-
warden_script_path = ::File.join(tmpdir, "run_warden_#{create_uuid}.sh")
|
248
|
-
::File.open(warden_script_path, 'w') { |f| f.puts shell_script }
|
249
|
-
cmd = "/bin/bash #{warden_script_path}"
|
250
|
-
::RightScale::RightPopen.popen3_sync(
|
251
|
-
cmd,
|
252
|
-
:target => self,
|
253
|
-
:inherit_io => true, # avoid killing any rails connection
|
254
|
-
:watch_handler => :watch_warden,
|
255
|
-
:stderr_handler => :stderr_warden,
|
256
|
-
:stdout_handler => :stdout_warden,
|
257
|
-
:timeout_handler => :timeout_warden,
|
258
|
-
:exit_handler => :exit_warden,
|
259
|
-
:timeout_seconds => WARDEN_COMMAND_TIMEOUT)
|
260
|
-
if @process
|
261
|
-
@process = nil
|
262
|
-
warden_output = @stdout_buffer.join
|
263
|
-
if warden_output.empty?
|
264
|
-
result = {}
|
265
|
-
else
|
266
|
-
result = parse_warden_output(warden_output)
|
267
|
-
end
|
268
|
-
return result
|
269
|
-
else
|
270
|
-
raise WardenError, 'Unable to execute warden.'
|
271
|
-
end
|
272
|
-
end
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
# Warden outputs something that looks like YAML but also somewhat like a
|
277
|
-
# Java configuration file. in any case, the output is ambiguous because it
|
278
|
-
# does not escape characters and it is possible to spawn a process that
|
279
|
-
# prints output text that appears to be the start of a new key. *sigh*
|
280
|
-
#
|
281
|
-
# all we can do here is attempt to parse the output by some simple rules
|
282
|
-
# and hope for the best.
|
283
|
-
#
|
284
|
-
# example:
|
285
|
-
# exit_status : 0
|
286
|
-
# stdout : a
|
287
|
-
# b
|
288
|
-
# c
|
289
|
-
#
|
290
|
-
# stderr :
|
291
|
-
# info.state : active
|
292
|
-
# ...
|
293
|
-
def parse_warden_output(warden_output)
|
294
|
-
parsed_lines = {}
|
295
|
-
current_key = nil
|
296
|
-
regex = /^([a-z._]+) \: (.*)$/
|
297
|
-
warden_output.lines.each do |line|
|
298
|
-
if parts = regex.match(line)
|
299
|
-
current_key = parts[1]
|
300
|
-
parsed_lines[current_key] = [parts[2]]
|
301
|
-
elsif current_key
|
302
|
-
parsed_lines[current_key] << line.chomp
|
303
|
-
else
|
304
|
-
raise WardenError, "Unable to parse warden output:\n#{warden_output.inspect}"
|
305
|
-
end
|
306
|
-
end
|
307
|
-
parsed_lines.inject({}) do |result, (key, value)|
|
308
|
-
result[key] = value.join("\n")
|
309
|
-
result
|
310
|
-
end
|
311
|
-
end
|
312
|
-
|
313
|
-
def lay_to_rest
|
314
|
-
if @process
|
315
|
-
if @process.interrupt
|
316
|
-
@interupted_to_close = true
|
317
|
-
@process.sync_exit_with_target
|
318
|
-
else
|
319
|
-
@process.safe_close_io
|
320
|
-
end
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
|
-
def stdout_warden(data)
|
325
|
-
@stdout_buffer << data
|
326
|
-
end
|
327
|
-
|
328
|
-
def stderr_warden(data)
|
329
|
-
@stderr_buffer.safe_buffer_data(data)
|
330
|
-
end
|
331
|
-
|
332
|
-
def watch_warden(process)
|
333
|
-
if @interupted_to_close
|
334
|
-
true
|
335
|
-
else
|
336
|
-
@process = process
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
def timeout_warden
|
341
|
-
unless @interupted_to_close
|
342
|
-
raise WardenError, 'Timed out waiting for warden to respond'
|
343
|
-
end
|
344
|
-
end
|
345
|
-
|
346
|
-
def exit_warden(status)
|
347
|
-
unless @interupted_to_close || status.success?
|
348
|
-
raise WardenError,
|
349
|
-
"Warden failed exit_status = #{status.exitstatus}:\n" +
|
350
|
-
"stdout = #{@stdout_buffer.join}\n" +
|
351
|
-
"stderr = #{@stderr_buffer.display_text}"
|
352
|
-
end
|
353
|
-
true
|
354
|
-
end
|
355
|
-
|
356
|
-
end # Warden
|
357
|
-
end # Processes
|
358
|
-
end # RightScraper
|