scout-camp 0.1.4 → 0.1.6
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/.vimproject +30 -0
- data/VERSION +1 -1
- data/lib/scout/aws/s3.rb +42 -6
- data/scout-camp.gemspec +10 -4
- data/scout_commands/terraform/{add → add/lambda} +11 -28
- data/scout_commands/terraform/add/relay +40 -0
- data/scout_commands/terraform/lambda_task +124 -0
- data/scout_commands/terraform/list +2 -1
- data/scout_commands/terraform/task +92 -0
- data/share/aws/lambda_function.rb +27 -0
- data/share/terraform/aws/host/variables.tf +0 -1
- data/share/terraform/ssh/cmd/main.tf +34 -0
- data/share/terraform/ssh/cmd/variables.tf +19 -0
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd5f35a6409cd8556f8fb6cb8641c28c36fb3b0dcc9110eeef8c8d9a5ea42594
|
4
|
+
data.tar.gz: 7bb5e7f5b07ac7d9ca601909bc3c5632d6f9d1630020e308d901d8b3a771d0f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e460301e94edf73163f57309498db7d04b38b202c0437938dc0bf10cb0a31e1683f8d908309b0876fe106e781f2b69e24563656562eb57e0a759116bfb38f904
|
7
|
+
data.tar.gz: 6a78670a91b259399cad19a95411c214f3382173c37bb2611f4b13deb8a32a1626d0f2dfb793e8462253e6322156b104816a0bc5f3f37ce9d0cf3017bf735adf
|
data/.vimproject
CHANGED
@@ -26,12 +26,17 @@ scout-camp=/$PWD filter="*" {
|
|
26
26
|
offsite
|
27
27
|
terraform=terraform{
|
28
28
|
list
|
29
|
+
add=add{
|
30
|
+
lambda
|
31
|
+
relay
|
32
|
+
}
|
29
33
|
add
|
30
34
|
status
|
31
35
|
apply
|
32
36
|
plan
|
33
37
|
destroy
|
34
38
|
remove
|
39
|
+
lambda_task
|
35
40
|
}
|
36
41
|
}
|
37
42
|
share=share {
|
@@ -66,11 +71,36 @@ scout-camp=/$PWD filter="*" {
|
|
66
71
|
output.tf
|
67
72
|
variables.tf
|
68
73
|
}
|
74
|
+
fargate=fargate{
|
75
|
+
locals.tf
|
76
|
+
main.tf
|
77
|
+
variables.tf
|
78
|
+
}
|
69
79
|
provider=provider {
|
70
80
|
data.tf
|
71
81
|
output.tf
|
72
82
|
}
|
73
83
|
}
|
84
|
+
ssh=ssh{
|
85
|
+
cmd=cmd{
|
86
|
+
main.tf
|
87
|
+
variables.tf
|
88
|
+
}
|
89
|
+
ollama=ollama{
|
90
|
+
main.tf
|
91
|
+
variables.tf
|
92
|
+
}
|
93
|
+
vllm=vllm{
|
94
|
+
main.tf
|
95
|
+
variables.tf
|
96
|
+
}
|
97
|
+
relay=relay{
|
98
|
+
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}
|
102
|
+
aws=aws{
|
103
|
+
lambda_function.rb
|
74
104
|
}
|
75
105
|
}
|
76
106
|
test=test {
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.6
|
data/lib/scout/aws/s3.rb
CHANGED
@@ -10,7 +10,11 @@ module Open
|
|
10
10
|
extend Hook
|
11
11
|
|
12
12
|
def self.lock(*args, &block)
|
13
|
-
|
13
|
+
begin
|
14
|
+
yield nil
|
15
|
+
rescue KeepLocked
|
16
|
+
$!.payload
|
17
|
+
end
|
14
18
|
end
|
15
19
|
|
16
20
|
def self.is_s3?(uri)
|
@@ -51,6 +55,7 @@ module Open
|
|
51
55
|
bucket, key = parse_s3_uri(uri)
|
52
56
|
s3 = Aws::S3::Client.new
|
53
57
|
content = Open.open_pipe(&block).read if block_given?
|
58
|
+
content = content.read if IO === content
|
54
59
|
s3.put_object(bucket: bucket, key: key, body: content)
|
55
60
|
end
|
56
61
|
|
@@ -69,8 +74,13 @@ module Open
|
|
69
74
|
|
70
75
|
resp.contents.each do |object|
|
71
76
|
key = object.key
|
72
|
-
|
73
|
-
|
77
|
+
|
78
|
+
if prefix.empty?
|
79
|
+
remaining = key.sub(%r{^/}, '')
|
80
|
+
else
|
81
|
+
remaining = key[prefix.length..-1] || ''
|
82
|
+
remaining = remaining.sub(%r{^/}, '')
|
83
|
+
end
|
74
84
|
|
75
85
|
if File.fnmatch?(pattern, remaining, File::FNM_PATHNAME)
|
76
86
|
matches << "s3://#{bucket}/#{key}"
|
@@ -125,6 +135,18 @@ module Open
|
|
125
135
|
false
|
126
136
|
end
|
127
137
|
|
138
|
+
def self.cp(source, target)
|
139
|
+
source_bucket, source_key = parse_s3_uri(source)
|
140
|
+
target_bucket, target_key = parse_s3_uri(target)
|
141
|
+
|
142
|
+
s3 = Aws::S3::Client.new
|
143
|
+
s3.copy_object({
|
144
|
+
copy_source: "#{source_bucket}/#{source_key}",
|
145
|
+
bucket: target_bucket,
|
146
|
+
key: target_key
|
147
|
+
})
|
148
|
+
end
|
149
|
+
|
128
150
|
def self.exists?(uri)
|
129
151
|
bucket, key = parse_s3_uri(uri)
|
130
152
|
return false if key.empty? # Can't check existence of bucket this way
|
@@ -137,12 +159,26 @@ module Open
|
|
137
159
|
end
|
138
160
|
|
139
161
|
self.singleton_class.alias_method :exist?, :exists?
|
140
|
-
|
141
162
|
|
142
163
|
def self.sensible_write(path, content = nil, options = {}, &block)
|
164
|
+
content = content.to_s if content.respond_to?(:write_file)
|
143
165
|
Open::S3.write(path, content)
|
144
166
|
end
|
145
167
|
|
168
|
+
def self.mkdir(path)
|
169
|
+
end
|
170
|
+
|
171
|
+
def self.link(source, target, options = {})
|
172
|
+
cp(source, target)
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.ln(source, target, options = {})
|
176
|
+
cp(source, target)
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.ln_s(source, target, options = {})
|
180
|
+
cp(source, target)
|
181
|
+
end
|
146
182
|
end
|
147
183
|
end
|
148
184
|
|
@@ -155,8 +191,8 @@ module Path
|
|
155
191
|
end
|
156
192
|
|
157
193
|
def glob(*args)
|
158
|
-
if Open::S3.is_s3?(self)
|
159
|
-
Open::S3.glob(self, *args)
|
194
|
+
if Open::S3.is_s3?(self.find)
|
195
|
+
Open::S3.glob(self.find, *args)
|
160
196
|
else
|
161
197
|
orig_glob(*args)
|
162
198
|
end
|
data/scout-camp.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: scout-camp 0.1.
|
5
|
+
# stub: scout-camp 0.1.6 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "scout-camp".freeze
|
9
|
-
s.version = "0.1.
|
9
|
+
s.version = "0.1.6".freeze
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["Miguel Vazquez".freeze]
|
14
|
-
s.date = "2025-
|
14
|
+
s.date = "2025-04-08"
|
15
15
|
s.description = "Functionalities to deploy and use scouts in remote servers like AWS".freeze
|
16
16
|
s.email = "mikisvaz@gmail.com".freeze
|
17
17
|
s.executables = ["scout-camp".freeze]
|
@@ -38,13 +38,17 @@ Gem::Specification.new do |s|
|
|
38
38
|
"lib/scout/terraform_dsl/util.rb",
|
39
39
|
"scout-camp.gemspec",
|
40
40
|
"scout_commands/offsite",
|
41
|
-
"scout_commands/terraform/add",
|
41
|
+
"scout_commands/terraform/add/lambda",
|
42
|
+
"scout_commands/terraform/add/relay",
|
42
43
|
"scout_commands/terraform/apply",
|
43
44
|
"scout_commands/terraform/destroy",
|
45
|
+
"scout_commands/terraform/lambda_task",
|
44
46
|
"scout_commands/terraform/list",
|
45
47
|
"scout_commands/terraform/plan",
|
46
48
|
"scout_commands/terraform/remove",
|
47
49
|
"scout_commands/terraform/status",
|
50
|
+
"scout_commands/terraform/task",
|
51
|
+
"share/aws/lambda_function.rb",
|
48
52
|
"share/terraform/aws/bucket/main.tf",
|
49
53
|
"share/terraform/aws/bucket/output.tf",
|
50
54
|
"share/terraform/aws/bucket/variables.tf",
|
@@ -64,6 +68,8 @@ Gem::Specification.new do |s|
|
|
64
68
|
"share/terraform/aws/role/main.tf",
|
65
69
|
"share/terraform/aws/role/output.tf",
|
66
70
|
"share/terraform/aws/role/variables.tf",
|
71
|
+
"share/terraform/ssh/cmd/main.tf",
|
72
|
+
"share/terraform/ssh/cmd/variables.tf",
|
67
73
|
"test/scout/aws/test_s3.rb",
|
68
74
|
"test/scout/offsite/test_ssh.rb",
|
69
75
|
"test/scout/offsite/test_step.rb",
|
@@ -14,6 +14,7 @@ $ #{$0} [<options>] <name>
|
|
14
14
|
-w--workflows* Workflows to provision
|
15
15
|
-w--dependencies* Workflows to provision
|
16
16
|
-b--bucket* Bucket to connect
|
17
|
+
--pkg* Pkgdir, scout or rbbt (defaults to scout)
|
17
18
|
EOF
|
18
19
|
if options[:help]
|
19
20
|
if defined? scout_usage
|
@@ -31,37 +32,20 @@ deployments = Scout.var.deployments.glob_all("*").collect(&:basename)
|
|
31
32
|
raise "Deployment clash" if deployments.include? name
|
32
33
|
dir = Scout.var.deployments[name]
|
33
34
|
|
34
|
-
IndiferentHash.add_defaults options, workflows: '', dependencies: 'scout-gear,scout-camp', bucket: 'scout.var'
|
35
|
+
IndiferentHash.add_defaults options, workflows: '', dependencies: 'scout-gear,scout-camp', bucket: 'scout.var', pkg: 'scout'
|
35
36
|
|
36
37
|
workflows = options[:workflows].split(",")
|
37
38
|
dependencies = options[:dependencies].split(",")
|
39
|
+
dependencies.push 'scout-camp' unless dependencies.include?('scout-camp')
|
40
|
+
pkg = options[:pkg]
|
38
41
|
|
39
|
-
|
40
|
-
def lambda_handler(event:, context:)
|
41
|
-
require 'scout'
|
42
|
-
Path.path_maps[:bucket] = "s3://#{ENV["AWS_BUCKET"]}/{TOPLEVEL}/{SUBPATH}"
|
43
|
-
Path.path_maps[:default] = :bucket
|
44
|
-
|
45
|
-
require 'scout/workflow'
|
46
|
-
require 'scout/aws/s3'
|
47
|
-
|
48
|
-
workflow, task_name, jobname, inputs = IndiferentHash.process_options event,
|
49
|
-
:workflow, :task_name, :jobname, :inputs
|
50
|
-
|
51
|
-
wf = Workflow.require_workflow workflow
|
52
|
-
|
53
|
-
job = wf.job(task_name, jobname, inputs)
|
54
|
-
job.run
|
55
|
-
end
|
56
|
-
EOF
|
57
|
-
|
58
|
-
def lambda_package(dependencies, workflows, code)
|
42
|
+
def lambda_package(dependencies, workflows, function_file, pkg)
|
59
43
|
TmpFile.with_path do |dir|
|
60
|
-
dir["lambda_function.rb"]
|
44
|
+
Open.cp function_file, dir["lambda_function.rb"]
|
61
45
|
dir["Gemfile"].write "source 'https://rubygems.org'\n" + dependencies.collect{|d| "gem '#{d}'"}*"\n"
|
62
46
|
workflows.each do |workflow|
|
63
|
-
Open.mkdir dir["
|
64
|
-
Open.
|
47
|
+
Open.mkdir dir[".#{pkg}"].workflows
|
48
|
+
Open.ln_s Scout.workflows[workflow], dir[".#{pkg}"].workflows[workflow]
|
65
49
|
end
|
66
50
|
Misc.in_dir dir do
|
67
51
|
`bundle config set path 'vendor/bundle'`
|
@@ -69,19 +53,18 @@ def lambda_package(dependencies, workflows, code)
|
|
69
53
|
`rm -Rf vendor/bundle/ruby/*/cache`
|
70
54
|
`rm -Rf vendor/bundle/ruby/*/gems/RubyInline*`
|
71
55
|
TmpFile.with_file nil, extension: :zip do |zip|
|
72
|
-
`zip -r "#{zip}" vendor/bundle lambda_function.rb
|
56
|
+
`zip -r "#{zip}" vendor/bundle lambda_function.rb .#{pkg}`
|
73
57
|
yield zip
|
74
58
|
end
|
75
59
|
end
|
76
60
|
end
|
77
61
|
end
|
78
62
|
|
79
|
-
|
80
|
-
lambda_package dependencies, workflows, code do |file|
|
81
|
-
terraform = TerraformDSL.new
|
63
|
+
lambda_package dependencies, workflows, Scout.share.aws.lambda_function.set_extension('rb'), options[:pkg] do |file|
|
82
64
|
filename = 'lambda_package.zip'
|
83
65
|
Open.cp file, dir[filename]
|
84
66
|
|
67
|
+
terraform = TerraformDSL.new
|
85
68
|
role = terraform.add :aws, :role, role_name: "lambda_execution_role", principal: {Service: ["lambda.amazonaws.com"]}
|
86
69
|
|
87
70
|
terraform.add :aws, :policy_attachment, name: "exec_policy", policy_name: "lamda_basic_execution",
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'scout'
|
4
|
+
|
5
|
+
$0 = "scout #{$previous_commands.any? ? $previous_commands*" " + " " : "" }#{ File.basename(__FILE__) }" if $previous_commands
|
6
|
+
|
7
|
+
options = SOPT.setup <<EOF
|
8
|
+
|
9
|
+
Add a new deployment
|
10
|
+
|
11
|
+
$ #{$0} [<options>] <name>
|
12
|
+
|
13
|
+
-h--help Print this help
|
14
|
+
-s--server* Server name
|
15
|
+
-u--user* User in server
|
16
|
+
-m--model* Model name
|
17
|
+
EOF
|
18
|
+
if options[:help]
|
19
|
+
if defined? scout_usage
|
20
|
+
scout_usage
|
21
|
+
else
|
22
|
+
puts SOPT.doc
|
23
|
+
end
|
24
|
+
exit 0
|
25
|
+
end
|
26
|
+
|
27
|
+
name = ARGV.shift
|
28
|
+
raise MissingParameterException, :name if name.nil?
|
29
|
+
|
30
|
+
server, user, model = IndiferentHash.process_options options, :server, :user, :model
|
31
|
+
|
32
|
+
deployments = Scout.var.deployments.glob_all("*").collect(&:basename)
|
33
|
+
raise "Deployment clash" if deployments.include? name
|
34
|
+
dir = Scout.var.deployments[name]
|
35
|
+
|
36
|
+
terraform = TerraformDSL.new
|
37
|
+
|
38
|
+
ollama = terraform.add :ssh, :cmd, service_id: "#{name}_ollama", name: "ollama", host: server, user: user, command: 'module load ollama; ollama serve'
|
39
|
+
terraform.add :ssh, :cmd, service_id: "#{name}_ollama", name: "process", host: server, user: user, command: %Q(bash -l -c "scout-ai llm process -ck 'backend ollama ask,model #{model} ask'"), depends_on: [ollama]
|
40
|
+
terraform.config dir
|
@@ -0,0 +1,124 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'scout'
|
4
|
+
|
5
|
+
$0 = "scout #{$previous_commands.any? ? $previous_commands*" " + " " : "" }#{ File.basename(__FILE__) }" if $previous_commands
|
6
|
+
|
7
|
+
options = SOPT.setup <<EOF
|
8
|
+
|
9
|
+
Call a task on AWS
|
10
|
+
|
11
|
+
$ #{$0} [<options>] <workflow> <task> [<other|->]*
|
12
|
+
|
13
|
+
-h--help Print this help
|
14
|
+
--prefix* Prefix, defaults to Scout
|
15
|
+
--clean Clean job
|
16
|
+
--recursive_clean Clean job recursively
|
17
|
+
EOF
|
18
|
+
workflow, task_name = ARGV
|
19
|
+
|
20
|
+
if workflow.nil? && options[:help]
|
21
|
+
if defined? scout_usage
|
22
|
+
scout_usage
|
23
|
+
else
|
24
|
+
puts SOPT.doc
|
25
|
+
end
|
26
|
+
exit 0
|
27
|
+
end
|
28
|
+
|
29
|
+
raise ParamterException, "No workflow specified" if workflow.nil?
|
30
|
+
|
31
|
+
prefix, clean, recursive_clean = IndiferentHash.process_options options, :prefix, :clean, :recursive_clean,
|
32
|
+
prefix: "Scout"
|
33
|
+
|
34
|
+
require 'aws-sdk-lambda'
|
35
|
+
|
36
|
+
payload = {}
|
37
|
+
payload["workflow"] = workflow
|
38
|
+
payload["task_name"] = task_name
|
39
|
+
|
40
|
+
if clean
|
41
|
+
payload["clean"] = true
|
42
|
+
elsif recursive_clean
|
43
|
+
payload["clean"] = 'recursive'
|
44
|
+
end
|
45
|
+
|
46
|
+
lambda_handler = "#{prefix}Job"
|
47
|
+
|
48
|
+
def aws_lambda(name, payload)
|
49
|
+
client = Aws::Lambda::Client.new
|
50
|
+
resp = client.invoke({
|
51
|
+
function_name: name,
|
52
|
+
payload: payload.to_json,
|
53
|
+
})
|
54
|
+
|
55
|
+
JSON.load(resp.payload)
|
56
|
+
end
|
57
|
+
|
58
|
+
def SOPT_str(task_info)
|
59
|
+
sopt_options = []
|
60
|
+
task_info[:inputs].each do |name|
|
61
|
+
options = task_info[:input_options][name]
|
62
|
+
type = task_info[:input_types][name]
|
63
|
+
shortcut = (options && options[:shortcut]) || name.to_s.slice(0,1)
|
64
|
+
boolean = type.to_sym == :boolean
|
65
|
+
|
66
|
+
sopt_options << "-#{shortcut}--#{name}#{boolean ? "" : "*"}"
|
67
|
+
end
|
68
|
+
|
69
|
+
sopt_options * ":"
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_SOPT(task_info)
|
73
|
+
IndiferentHash.setup task_info
|
74
|
+
sopt_option_string = self.SOPT_str(task_info)
|
75
|
+
job_options = SOPT.get sopt_option_string
|
76
|
+
|
77
|
+
task_info[:inputs].each do |name|
|
78
|
+
options = task_info[:input_options][name]
|
79
|
+
type = task_info[:input_types][name]
|
80
|
+
next unless type.to_s.include?('array')
|
81
|
+
if job_options.include?(name) && (! Open.exist?(job_options[name]) || type.to_s.include?('file') || type.to_s.include?('path'))
|
82
|
+
job_options[name] = job_options[name].split(",")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
job_options
|
86
|
+
end
|
87
|
+
|
88
|
+
res = case task_name
|
89
|
+
when nil
|
90
|
+
|
91
|
+
if options[:help]
|
92
|
+
if defined? scout_usage
|
93
|
+
scout_usage
|
94
|
+
else
|
95
|
+
puts SOPT.doc
|
96
|
+
end
|
97
|
+
exit 0
|
98
|
+
end
|
99
|
+
|
100
|
+
aws_lambda(lambda_handler, payload)
|
101
|
+
else
|
102
|
+
task_info = aws_lambda(lambda_handler, payload.merge(task_name: "info", inputs: {task_name: task_name}))
|
103
|
+
|
104
|
+
payload["inputs"] = get_SOPT(task_info)
|
105
|
+
|
106
|
+
if options[:help]
|
107
|
+
if defined? scout_usage
|
108
|
+
scout_usage
|
109
|
+
else
|
110
|
+
puts SOPT.doc
|
111
|
+
end
|
112
|
+
exit 0
|
113
|
+
end
|
114
|
+
|
115
|
+
aws_lambda(lambda_handler, payload)
|
116
|
+
end
|
117
|
+
|
118
|
+
if Hash === res && res["errorMessage"]
|
119
|
+
Log.error res["errorMessage"]
|
120
|
+
Log.stack res["stackTrace"] if res["stackTrace"]
|
121
|
+
exit -1
|
122
|
+
else
|
123
|
+
iii res
|
124
|
+
end
|
@@ -11,6 +11,7 @@ List all deployments
|
|
11
11
|
$ #{$0} [<options>] <filename>
|
12
12
|
|
13
13
|
-h--help Print this help
|
14
|
+
-a--active Show if they are active
|
14
15
|
EOF
|
15
16
|
if options[:help]
|
16
17
|
if defined? scout_usage
|
@@ -26,7 +27,7 @@ deployments = Scout.var.deployments.glob_all("*")
|
|
26
27
|
deployments.each do |dir|
|
27
28
|
name = File.basename dir
|
28
29
|
deployment = TerraformDSL::Deployment.new dir
|
29
|
-
active = deployment.provisioned_elements.any?
|
30
|
+
active = options[:active] && deployment.provisioned_elements.any?
|
30
31
|
|
31
32
|
if active
|
32
33
|
puts Log.color(:title, name ) + "\t" + dir + "\t" + "Active"
|
@@ -0,0 +1,92 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'scout'
|
4
|
+
|
5
|
+
$0 = "scout #{$previous_commands.any? ? $previous_commands*" " + " " : "" }#{ File.basename(__FILE__) }" if $previous_commands
|
6
|
+
|
7
|
+
options = SOPT.setup <<EOF
|
8
|
+
|
9
|
+
Call a task on AWS
|
10
|
+
|
11
|
+
$ #{$0} [<options>] <workflow> <task> [<other|->]*
|
12
|
+
|
13
|
+
-h--help Print this help
|
14
|
+
--prefix* Prefix, defaults to Scout
|
15
|
+
EOF
|
16
|
+
if options[:help]
|
17
|
+
if defined? scout_usage
|
18
|
+
scout_usage
|
19
|
+
else
|
20
|
+
puts SOPT.doc
|
21
|
+
end
|
22
|
+
exit 0
|
23
|
+
end
|
24
|
+
|
25
|
+
workflow, task_name = ARGV
|
26
|
+
|
27
|
+
raise ParamterException, "No workflow specified" if workflow.nil?
|
28
|
+
|
29
|
+
prefix = IndiferentHash.process_options options, :prefix, prefix: "Scout"
|
30
|
+
require 'aws-sdk-lambda'
|
31
|
+
payload = {}
|
32
|
+
payload["workflow"] = workflow
|
33
|
+
payload["task_name"] = task_name
|
34
|
+
lambda_handler = "#{prefix}Job"
|
35
|
+
|
36
|
+
def aws_lambda(name, payload)
|
37
|
+
client = Aws::Lambda::Client.new
|
38
|
+
resp = client.invoke({
|
39
|
+
function_name: name,
|
40
|
+
payload: payload.to_json,
|
41
|
+
})
|
42
|
+
|
43
|
+
JSON.load(resp.payload)
|
44
|
+
end
|
45
|
+
|
46
|
+
def SOPT_str(task_info)
|
47
|
+
sopt_options = []
|
48
|
+
task_info[:inputs].each do |name|
|
49
|
+
options = task_info[:input_options][name]
|
50
|
+
type = task_info[:input_types][name]
|
51
|
+
shortcut = (options && options[:shortcut]) || name.to_s.slice(0,1)
|
52
|
+
boolean = type.to_sym == :boolean
|
53
|
+
|
54
|
+
sopt_options << "-#{shortcut}--#{name}#{boolean ? "" : "*"}"
|
55
|
+
end
|
56
|
+
|
57
|
+
sopt_options * ":"
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_SOPT(task_info)
|
61
|
+
IndiferentHash.setup task_info
|
62
|
+
sopt_option_string = self.SOPT_str(task_info)
|
63
|
+
job_options = SOPT.get sopt_option_string
|
64
|
+
|
65
|
+
task_info[:inputs].each do |name|
|
66
|
+
options = task_info[:input_options][name]
|
67
|
+
type = task_info[:input_types][name]
|
68
|
+
next unless type.to_s.include?('array')
|
69
|
+
if job_options.include?(name) && (! Open.exist?(job_options[name]) || type.to_s.include?('file') || type.to_s.include?('path'))
|
70
|
+
job_options[name] = job_options[name].split(",")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
job_options
|
74
|
+
end
|
75
|
+
|
76
|
+
res = case task_name
|
77
|
+
when nil
|
78
|
+
aws_lambda(lambda_handler, payload)
|
79
|
+
else
|
80
|
+
task_info = aws_lambda(lambda_handler, payload.merge(task_name: "info", inputs: {task_name: task_name}))
|
81
|
+
|
82
|
+
payload["inputs"] = get_SOPT(task_info)
|
83
|
+
|
84
|
+
aws_lambda(lambda_handler, payload)
|
85
|
+
end
|
86
|
+
|
87
|
+
if res["errorMessage"]
|
88
|
+
Log.error res["errorMessage"]
|
89
|
+
Log.stack res["stackTrace"]
|
90
|
+
else
|
91
|
+
iii res
|
92
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
def lambda_handler(event:, context:)
|
2
|
+
require 'scout'
|
3
|
+
|
4
|
+
Path.path_maps[:bucket] = "s3://#{ENV["AWS_BUCKET"]}/{TOPLEVEL}/{SUBPATH}"
|
5
|
+
Path.path_maps[:default] = :bucket
|
6
|
+
|
7
|
+
require 'scout/workflow'
|
8
|
+
require 'scout/aws/s3'
|
9
|
+
|
10
|
+
workflow, task_name, jobname, inputs = IndiferentHash.process_options event,
|
11
|
+
:workflow, :task_name, :jobname, :inputs
|
12
|
+
|
13
|
+
raise ParamterException, "No workflow specified" if workflow.nil?
|
14
|
+
|
15
|
+
workflow = Workflow.require_workflow workflow
|
16
|
+
|
17
|
+
case task_name
|
18
|
+
when nil
|
19
|
+
return {tasks: workflow.tasks.keys, documentation: workflow.documentation}
|
20
|
+
when "info"
|
21
|
+
raise ParamterException, "No task_name specified" if task_name.nil?
|
22
|
+
return workflow.task_info(inputs["task_name"])
|
23
|
+
else
|
24
|
+
job = workflow.job(task_name, jobname, inputs)
|
25
|
+
job.run
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
terraform {
|
2
|
+
required_providers {
|
3
|
+
ssh = {
|
4
|
+
source = "loafoe/ssh"
|
5
|
+
version = ">= 1.0.0"
|
6
|
+
}
|
7
|
+
}
|
8
|
+
}
|
9
|
+
|
10
|
+
resource "ssh_resource" "up" {
|
11
|
+
host = var.host
|
12
|
+
user = var.user
|
13
|
+
agent = true
|
14
|
+
when = "create"
|
15
|
+
|
16
|
+
commands = [
|
17
|
+
"${var.command} > ${var.service_id}.log 2>&1 & echo $! > /tmp/${var.service_id}.pid"
|
18
|
+
]
|
19
|
+
|
20
|
+
}
|
21
|
+
|
22
|
+
resource "ssh_resource" "down" {
|
23
|
+
host = var.host
|
24
|
+
user = var.user
|
25
|
+
agent = true
|
26
|
+
when = "destroy"
|
27
|
+
|
28
|
+
commands = [
|
29
|
+
"kill $(cat /tmp/${var.service_id}.pid) || true",
|
30
|
+
"rm -f /tmp/${var.service_id}.pid"
|
31
|
+
]
|
32
|
+
}
|
33
|
+
|
34
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
variable "host" {
|
2
|
+
description = "Target host for SSH"
|
3
|
+
type = string
|
4
|
+
}
|
5
|
+
|
6
|
+
variable "user" {
|
7
|
+
description = "Username for SSH"
|
8
|
+
type = string
|
9
|
+
}
|
10
|
+
|
11
|
+
variable "service_id" {
|
12
|
+
description = "Identifier of the service, should be unique to avoid collisions"
|
13
|
+
type = string
|
14
|
+
}
|
15
|
+
|
16
|
+
variable "command" {
|
17
|
+
description = "Command to execute"
|
18
|
+
type = string
|
19
|
+
}
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scout-camp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miguel Vazquez
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-04-08 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: scout-essentials
|
@@ -50,13 +50,17 @@ files:
|
|
50
50
|
- lib/scout/terraform_dsl/util.rb
|
51
51
|
- scout-camp.gemspec
|
52
52
|
- scout_commands/offsite
|
53
|
-
- scout_commands/terraform/add
|
53
|
+
- scout_commands/terraform/add/lambda
|
54
|
+
- scout_commands/terraform/add/relay
|
54
55
|
- scout_commands/terraform/apply
|
55
56
|
- scout_commands/terraform/destroy
|
57
|
+
- scout_commands/terraform/lambda_task
|
56
58
|
- scout_commands/terraform/list
|
57
59
|
- scout_commands/terraform/plan
|
58
60
|
- scout_commands/terraform/remove
|
59
61
|
- scout_commands/terraform/status
|
62
|
+
- scout_commands/terraform/task
|
63
|
+
- share/aws/lambda_function.rb
|
60
64
|
- share/terraform/aws/bucket/main.tf
|
61
65
|
- share/terraform/aws/bucket/output.tf
|
62
66
|
- share/terraform/aws/bucket/variables.tf
|
@@ -76,6 +80,8 @@ files:
|
|
76
80
|
- share/terraform/aws/role/main.tf
|
77
81
|
- share/terraform/aws/role/output.tf
|
78
82
|
- share/terraform/aws/role/variables.tf
|
83
|
+
- share/terraform/ssh/cmd/main.tf
|
84
|
+
- share/terraform/ssh/cmd/variables.tf
|
79
85
|
- test/scout/aws/test_s3.rb
|
80
86
|
- test/scout/offsite/test_ssh.rb
|
81
87
|
- test/scout/offsite/test_step.rb
|