scout-camp 0.1.13 → 0.1.14
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 +52 -11
- data/Rakefile +5 -0
- data/VERSION +1 -1
- data/bin/scout-camp +46 -0
- data/doc/terraform.md +188 -0
- data/lib/scout/aws/s3.rb +6 -4
- data/lib/scout/offsite/resource.rb +110 -5
- data/lib/scout/offsite/step.rb +21 -14
- data/lib/scout/offsite/sync.rb +38 -10
- data/lib/scout/offsite.rb +1 -0
- data/lib/scout/render/engine.rb +119 -0
- data/lib/scout/render/helpers.rb +92 -0
- data/lib/scout/render/resource.rb +54 -0
- data/lib/scout/render.rb +3 -0
- data/lib/scout/sinatra/auth.rb +158 -0
- data/lib/scout/sinatra/base/assets.rb +245 -0
- data/lib/scout/sinatra/base/favicon.rb +43 -0
- data/lib/scout/sinatra/base/headers.rb +77 -0
- data/lib/scout/sinatra/base/helpers.rb +14 -0
- data/lib/scout/sinatra/base/parameters.rb +147 -0
- data/lib/scout/sinatra/base/post_processing.rb +18 -0
- data/lib/scout/sinatra/base/session.rb +72 -0
- data/lib/scout/sinatra/base.rb +253 -0
- data/lib/scout/sinatra/entity.rb +259 -0
- data/lib/scout/sinatra/finder.rb +9 -0
- data/lib/scout/sinatra/fragment.rb +275 -0
- data/lib/scout/sinatra/htmx.rb +68 -0
- data/lib/scout/sinatra/knowledge_base.rb +14 -0
- data/lib/scout/sinatra/tool.rb +11 -0
- data/lib/scout/sinatra/workflow.rb +129 -0
- data/lib/scout-camp.rb +1 -1
- data/scout-camp.gemspec +39 -3
- data/scout_commands/find +83 -0
- data/scout_commands/glob +90 -0
- data/share/aws/lambda_function.rb +53 -30
- data/share/terraform/aws/efs_host/data.tf +1 -2
- data/share/terraform/aws/efs_host/main.tf +1 -1
- data/share/terraform/aws/efs_host/variables.tf +5 -1
- data/test/scout/render/test_engine.rb +88 -0
- data/test/scout/render/test_resource.rb +29 -0
- data/test/scout/sinatra/base/test_headers.rb +125 -0
- data/test/scout/sinatra/base/test_parameters.rb +88 -0
- data/test/scout/sinatra/test_base.rb +27 -0
- data/test/scout/sinatra/test_entity.rb +44 -0
- data/test/scout/sinatra/test_render.rb +44 -0
- data/test/scout/sinatra/test_workflow.rb +157 -0
- data/test/test_helper.rb +26 -0
- metadata +103 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5535e519a380d0c2d9f9ce786c35d8ec780f58c2376901385ceb7e4bc2a9f8ef
|
|
4
|
+
data.tar.gz: 2c8ba6faccd6c9d3ebbb7951cfa6aa2b67f8ea2c374cc39a05875a1c77d2cb0e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb1a6148578827919e2a4c4216db0c9ba69cd8666882a08bce5cf3c2e3c5e3ab56ae99bb40de806ba5b0baef93c3d2a6c76bb972a73bd3e6e82258f481760661
|
|
7
|
+
data.tar.gz: e29a4bca9bd0d91574769c57e5f99beb2317926c2ed5863448883d5a9e3ec253a108dd849984c9ed9225575988870f05ddc20a8784e03109fb2e8b533cbd4875
|
data/.vimproject
CHANGED
|
@@ -2,6 +2,14 @@ scout-camp=/$PWD filter="*" {
|
|
|
2
2
|
bin=bin {
|
|
3
3
|
scout-camp
|
|
4
4
|
}
|
|
5
|
+
chats=chats filter="*"{
|
|
6
|
+
auth
|
|
7
|
+
|
|
8
|
+
intro
|
|
9
|
+
javascript
|
|
10
|
+
sinatra
|
|
11
|
+
unit
|
|
12
|
+
}
|
|
5
13
|
lib=lib {
|
|
6
14
|
scout-camp.rb
|
|
7
15
|
scout=scout {
|
|
@@ -21,27 +29,58 @@ scout-camp=/$PWD filter="*" {
|
|
|
21
29
|
sync.rb
|
|
22
30
|
resource.rb
|
|
23
31
|
}
|
|
32
|
+
|
|
33
|
+
render=render{
|
|
34
|
+
resource.rb
|
|
35
|
+
helpers.rb
|
|
36
|
+
engine.rb
|
|
37
|
+
}
|
|
38
|
+
render.rb
|
|
39
|
+
sinatra=sinatra{
|
|
40
|
+
entity.rb
|
|
41
|
+
workflow.rb
|
|
42
|
+
|
|
43
|
+
base=base{
|
|
44
|
+
post_processing.rb
|
|
45
|
+
parameters.rb
|
|
46
|
+
|
|
47
|
+
helpers.rb
|
|
48
|
+
assets.rb
|
|
49
|
+
headers.rb
|
|
50
|
+
session.rb
|
|
51
|
+
favicon.rb
|
|
52
|
+
}
|
|
53
|
+
base.rb
|
|
54
|
+
|
|
55
|
+
tool.rb
|
|
56
|
+
|
|
57
|
+
fragment.rb
|
|
58
|
+
auth.rb
|
|
59
|
+
htmx.rb
|
|
60
|
+
finder.rb
|
|
61
|
+
knowledge_base.rb
|
|
62
|
+
}
|
|
24
63
|
}
|
|
25
64
|
}
|
|
26
65
|
scout_commands=scout_commands {
|
|
66
|
+
find
|
|
67
|
+
glob
|
|
27
68
|
offsite
|
|
28
69
|
sync
|
|
29
|
-
|
|
30
70
|
terraform=terraform{
|
|
71
|
+
apply
|
|
72
|
+
destroy
|
|
73
|
+
lambda_task
|
|
31
74
|
list
|
|
32
|
-
add=add{
|
|
33
|
-
relay
|
|
34
|
-
lambda
|
|
35
|
-
host
|
|
36
|
-
fargate
|
|
37
|
-
}
|
|
38
|
-
status
|
|
39
75
|
outputs
|
|
40
|
-
apply
|
|
41
76
|
plan
|
|
42
|
-
destroy
|
|
43
77
|
remove
|
|
44
|
-
|
|
78
|
+
status
|
|
79
|
+
task
|
|
80
|
+
add=add{
|
|
81
|
+
lambda
|
|
82
|
+
relay
|
|
83
|
+
}
|
|
45
84
|
}
|
|
46
85
|
}
|
|
47
86
|
share=share {
|
|
@@ -166,6 +205,8 @@ scout-camp=/$PWD filter="*" {
|
|
|
166
205
|
aws=aws{
|
|
167
206
|
lambda_function.rb
|
|
168
207
|
}
|
|
208
|
+
views=views{
|
|
209
|
+
}
|
|
169
210
|
}
|
|
170
211
|
test=test {
|
|
171
212
|
test_helper.rb
|
data/Rakefile
CHANGED
|
@@ -42,6 +42,11 @@ Juwelier::Tasks.new do |gem|
|
|
|
42
42
|
#gem.add_development_dependency "simplecov", ">= 0"
|
|
43
43
|
|
|
44
44
|
gem.add_runtime_dependency 'scout-essentials', '>= 0'
|
|
45
|
+
gem.add_runtime_dependency 'aws-sdk-s3', '>= 0'
|
|
46
|
+
gem.add_runtime_dependency 'sinatra', '>= 0'
|
|
47
|
+
gem.add_runtime_dependency 'omniauth', '>= 0'
|
|
48
|
+
gem.add_runtime_dependency 'mimemagic', '>= 0'
|
|
49
|
+
gem.add_runtime_dependency 'omniauth-google-oauth2', '>= 0'
|
|
45
50
|
end
|
|
46
51
|
Juwelier::RubygemsDotOrgTasks.new
|
|
47
52
|
require 'rake/testtask'
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.14
|
data/bin/scout-camp
CHANGED
|
@@ -2,6 +2,52 @@
|
|
|
2
2
|
|
|
3
3
|
$LOAD_PATH.unshift File.join(__dir__, '../lib')
|
|
4
4
|
|
|
5
|
+
if _i = ARGV.index("--log")
|
|
6
|
+
require 'scout/log'
|
|
7
|
+
log = ARGV[_i+1]
|
|
8
|
+
Log.severity = log.to_i
|
|
9
|
+
ARGV.delete "--log"
|
|
10
|
+
ARGV.delete log
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
dev_dir = nil
|
|
14
|
+
if _i = ARGV.index("--dev")
|
|
15
|
+
dev_dir = ARGV[_i+1]
|
|
16
|
+
ARGV.delete "--dev"
|
|
17
|
+
ARGV.delete dev_dir
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
if dev_dir.nil?
|
|
21
|
+
_s = nil
|
|
22
|
+
ARGV.each_with_index do |s,i|
|
|
23
|
+
if s.match(/^--dev(?:=(.*))?/)
|
|
24
|
+
dev_dir = $1
|
|
25
|
+
_s = s
|
|
26
|
+
next
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
ARGV.delete _s if _s
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
if dev_dir.nil? && ENV["SCOUT_DEV"]
|
|
33
|
+
dev_dir = ENV["SCOUT_DEV"]
|
|
34
|
+
ARGV.delete "--dev"
|
|
35
|
+
ARGV.delete dev_dir
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
if dev_dir
|
|
39
|
+
['scout-*/lib'].each do |pattern|
|
|
40
|
+
Dir.glob(File.join(File.expand_path(dev_dir), pattern)).each do |f|
|
|
41
|
+
$LOAD_PATH.unshift f
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
['rbbt-*/lib'].each do |pattern|
|
|
45
|
+
Dir.glob(File.join(File.expand_path(dev_dir), pattern)).each do |f|
|
|
46
|
+
$LOAD_PATH.unshift f
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
5
51
|
require 'scout-camp'
|
|
6
52
|
|
|
7
53
|
load Scout.bin.scout.find
|
data/doc/terraform.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Using the Terraform DLS (Domain Specific Language)
|
|
2
|
+
|
|
3
|
+
This document describes how to use the Terraform DLS implemented in lib/scout/terraform_dsl to programmatically compose Terraform configurations and manage deployments from Ruby. It is written for AI agents (or developers) that will generate Ruby code to create, plan, apply and inspect Terraform deployments using the provided DSL.
|
|
4
|
+
|
|
5
|
+
Where things live
|
|
6
|
+
|
|
7
|
+
- Library implementation: lib/scout/terraform_dsl.rb and lib/scout/terraform_dsl/*.rb
|
|
8
|
+
- Utilities and runtime: lib/scout/terraform_dsl/util.rb and lib/scout/terraform_dsl/deployment.rb
|
|
9
|
+
- Example module templates: share/terraform (e.g. share/terraform/aws/*)
|
|
10
|
+
|
|
11
|
+
Key classes and concepts
|
|
12
|
+
|
|
13
|
+
- TerraformDSL: main class that models a planned Terraform deployment. You create a TerraformDSL instance, add modules, providers, backends, custom files, then call config to write template files into a directory.
|
|
14
|
+
|
|
15
|
+
- TerraformDSL::Module: returned by add(...) and acts as a reference to a module instance inside the generated Terraform configuration. You can call arbitrary methods on it to get references to its outputs (e.g. my_module.some_output), which serialize to Terraform references like module.<instance>.<output>.
|
|
16
|
+
|
|
17
|
+
- TerraformDSL::Module::Output: representation of an output reference that serializes to a module output reference in Terraform JSON form.
|
|
18
|
+
|
|
19
|
+
- TerraformDSL::Deployment: helper for interacting with Terraform in a directory created by TerraformDSL#config. It wraps terraform init/plan/apply/destroy/refresh and provides helpers for reading outputs and state.
|
|
20
|
+
|
|
21
|
+
- Misc helpers in TerraformDSL (util.rb):
|
|
22
|
+
- TerraformDSL.module_variables(module_dir) -> inspect variables.tf for a module
|
|
23
|
+
- TerraformDSL.module_outputs(module_dir) -> inspect output.tf for a module
|
|
24
|
+
|
|
25
|
+
Common workflow (example)
|
|
26
|
+
|
|
27
|
+
1) Create a TerraformDSL and add elements/modules
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
require 'scout/terraform_dsl'
|
|
31
|
+
|
|
32
|
+
# Create DSL (by default it uses share/terraform as modules dir)
|
|
33
|
+
# You can pass a custom modules dir if needed
|
|
34
|
+
dsl = TerraformDSL.new
|
|
35
|
+
|
|
36
|
+
# Add a provider block (creates a provider config file)
|
|
37
|
+
# Returns a Module reference if a provider module exists at modules_dir/<name>/provider
|
|
38
|
+
# Example: add AWS provider configuration (set credentials/region via variables)
|
|
39
|
+
dsl.provider('aws', region: 'eu-west-1')
|
|
40
|
+
|
|
41
|
+
# Add a module instance using the shared templates under share/terraform
|
|
42
|
+
# add(provider, module_name, variables)
|
|
43
|
+
# variables[:name] defaults to "<provider>_<module_name>" but you can override
|
|
44
|
+
web = dsl.add('aws', 'host', name: 'web1', ami: 'ami-01234567', instance_type: 't3.micro')
|
|
45
|
+
db = dsl.add('aws', 'host', name: 'db1', ami: 'ami-01234567', instance_type: 't3.small')
|
|
46
|
+
|
|
47
|
+
# You can connect modules using module output references returned by Module instances
|
|
48
|
+
# E.g. use an output from db as input to web
|
|
49
|
+
# (Assuming the module exposes the output name you want)
|
|
50
|
+
# web2 = dsl.add('aws', 'some_module', name: 'web2', subnet_id: db.subnet_id)
|
|
51
|
+
|
|
52
|
+
# Request that specific outputs from a module become deployment outputs
|
|
53
|
+
# Provide outputs in variables[:outputs] or use :outputs => 'all'
|
|
54
|
+
app = dsl.add('aws','host', name: 'app1', ami: 'ami-01234567', outputs: ['aws_instance_id', 'aws_instance_ip'])
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
2) Add custom provider/backend/remote files if needed
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
# Add a backend configuration (example for s3 backend)
|
|
61
|
+
dsl.backend('s3', bucket: 'my-bucket', key: 'path/to/state', region: 'eu-west-1')
|
|
62
|
+
|
|
63
|
+
# Add a remote data block pointing to another state
|
|
64
|
+
# Returns a direct reference object you can use as a value in variables
|
|
65
|
+
other_state = dsl.remote('s3', 'shared_state', bucket: 'my-bucket', region: 'eu-west-1')
|
|
66
|
+
# other_state is a DirectReference object that serializes to a terraform reference
|
|
67
|
+
|
|
68
|
+
# Add arbitrary terraform content (for uncommon provider configs)
|
|
69
|
+
dsl.custom('provider.aws.extra', <<~TF)
|
|
70
|
+
provider "aws" {
|
|
71
|
+
# extra provider settings
|
|
72
|
+
}
|
|
73
|
+
TF
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
3) Generate config files into a directory
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
# Write files into a working dir (if you pass no arg, a directory based on elements digest is used)
|
|
80
|
+
config_dir = dsl.config # returns the directory path (Path-like) containing .tf files
|
|
81
|
+
# You can pass a path: dsl.config('/tmp/my-deploy')
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
4) Manage the terraform deployment
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
# Create a Deployment helper for that directory
|
|
88
|
+
deployment = TerraformDSL::Deployment.new(config_dir)
|
|
89
|
+
|
|
90
|
+
# Initialize provider plugins and backend
|
|
91
|
+
deployment.init
|
|
92
|
+
|
|
93
|
+
# Validate if wanted
|
|
94
|
+
deployment.validate
|
|
95
|
+
|
|
96
|
+
# Create a plan (saves plan to main.plan)
|
|
97
|
+
deployment.plan
|
|
98
|
+
|
|
99
|
+
# Apply (will run plan if you didn't)
|
|
100
|
+
deployment.apply
|
|
101
|
+
|
|
102
|
+
# Read outputs (hash with output_name => value)
|
|
103
|
+
outs = deployment.outputs
|
|
104
|
+
puts outs['app1_aws_instance_ip']
|
|
105
|
+
|
|
106
|
+
# Destroy the whole deployment
|
|
107
|
+
deployment.destroy
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Notes about Module references and variables
|
|
111
|
+
|
|
112
|
+
- When you call add(...) you get back a TerraformDSL::Module object representing that instance.
|
|
113
|
+
- Calling arbitrary methods on that object returns a Module::Output that serializes into the Terraform reference module.<instance>.<output>. Example: web.ami_id or web.aws_instance_id depending on module output name.
|
|
114
|
+
- You can directly use these Module output references when setting variables for other modules (they will be serialized correctly into the .tf generated files).
|
|
115
|
+
|
|
116
|
+
- The DSL also accepts values that are already Terraform-style strings such as "module.some_mod.some_output" — the DSL converts those into Output references when generating variable assignments.
|
|
117
|
+
|
|
118
|
+
Inspection utilities
|
|
119
|
+
|
|
120
|
+
- Inspect module variables (what variables a module expects):
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
module_dir = Scout.share.terraform['aws']['host']
|
|
124
|
+
vars = TerraformDSL.module_variables(module_dir)
|
|
125
|
+
# vars is a hash: { 'ami' => {description: '...', type: 'string', default: ...}, ... }
|
|
126
|
+
|
|
127
|
+
outs = TerraformDSL.module_outputs(module_dir)
|
|
128
|
+
# outs is a hash of available outputs and their descriptions
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Deployment helper details
|
|
132
|
+
|
|
133
|
+
- TerraformDSL::Deployment.run(cmd)
|
|
134
|
+
- Executes terraform <cmd> and returns stdout. Raises TerraformException on non-zero exit.
|
|
135
|
+
|
|
136
|
+
- TerraformDSL::Deployment.run_log(cmd, log_file=nil)
|
|
137
|
+
- Runs terraform <cmd> streaming STDERR and STDOUT into logs and raising TerraformException on error. Accepts a log_file to capture full logs.
|
|
138
|
+
|
|
139
|
+
- Useful methods on Deployment objects:
|
|
140
|
+
- init, plan, apply, refresh, validate, update (init+plan+apply), destroy
|
|
141
|
+
- plan_file -> path to stored plan (main.plan)
|
|
142
|
+
- log_file -> path to log file for that work directory
|
|
143
|
+
- outputs -> reads terraform output -json and returns a Ruby hash with values
|
|
144
|
+
- element_state(element) -> shows terraform state show <element>
|
|
145
|
+
- provisioned_elements -> terraform state list
|
|
146
|
+
|
|
147
|
+
Advanced: bundling and loading
|
|
148
|
+
|
|
149
|
+
- You can bundle a deployment directory into a tar.gz with Deployment#bundle(file) and load it back with Deployment.load(file). The loader will extract the bundle into a working dir and refresh the state.
|
|
150
|
+
|
|
151
|
+
Where to find module templates
|
|
152
|
+
|
|
153
|
+
- The default module directory is Scout.share.terraform which points to share/terraform in this repository. Look under share/terraform/aws, share/terraform/ssh etc. Each module is a directory containing Terraform templates (main.tf, variables.tf, output.tf, ...).
|
|
154
|
+
|
|
155
|
+
Example full script (end-to-end)
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
require 'scout/terraform_dsl'
|
|
159
|
+
|
|
160
|
+
# Build DSL
|
|
161
|
+
dsl = TerraformDSL.new
|
|
162
|
+
dsl.provider('aws', region: 'eu-west-1')
|
|
163
|
+
app = dsl.add('aws', 'host', name: 'app1', ami: 'ami-01234567', instance_type: 't3.micro', outputs: ['aws_instance_id','aws_instance_ip'])
|
|
164
|
+
|
|
165
|
+
# Generate files
|
|
166
|
+
config_dir = dsl.config
|
|
167
|
+
|
|
168
|
+
# Manage terraform
|
|
169
|
+
deployment = TerraformDSL::Deployment.new(config_dir)
|
|
170
|
+
deployment.init
|
|
171
|
+
deployment.plan
|
|
172
|
+
deployment.apply
|
|
173
|
+
puts deployment.outputs # => hash of outputs
|
|
174
|
+
|
|
175
|
+
# Cleanup
|
|
176
|
+
deployment.destroy
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Tips for agents generating code
|
|
180
|
+
|
|
181
|
+
- Always call dsl.config before creating a Deployment. config writes the .tf files and returns the directory path used by Deployment.
|
|
182
|
+
- Prefer using Module objects to pass references between modules instead of hard-coding strings. Example: db.private_ip (where db is the Module returned by add).
|
|
183
|
+
- Use TerraformDSL.module_variables and module_outputs to discover what a module expects/returns when generating variables and wiring between modules.
|
|
184
|
+
- Use Deployment.run_log when you want log files and streamed logs for troubleshooting.
|
|
185
|
+
|
|
186
|
+
If you need more examples, look at the modules under share/terraform/aws and share/terraform/ssh for real-world templates.
|
|
187
|
+
|
|
188
|
+
End of document
|
data/lib/scout/aws/s3.rb
CHANGED
|
@@ -24,8 +24,10 @@ module Open
|
|
|
24
24
|
def self.claim_uri(uri)
|
|
25
25
|
if Path === uri and not uri.located?
|
|
26
26
|
is_s3?(uri.find)
|
|
27
|
-
|
|
27
|
+
elsif String === uri
|
|
28
28
|
is_s3? uri
|
|
29
|
+
else
|
|
30
|
+
false
|
|
29
31
|
end
|
|
30
32
|
end
|
|
31
33
|
|
|
@@ -286,9 +288,9 @@ module Open
|
|
|
286
288
|
end
|
|
287
289
|
|
|
288
290
|
if Open.directory?(source)
|
|
289
|
-
cmd = "aws s3 sync #{sync_args * " "} #{source} #{target}"
|
|
291
|
+
cmd = "aws s3 sync #{sync_args * " "} '#{source}' '#{target}'"
|
|
290
292
|
else
|
|
291
|
-
cmd = "aws s3 cp #{source} #{target}"
|
|
293
|
+
cmd = "aws s3 cp '#{source}' '#{target}'"
|
|
292
294
|
end
|
|
293
295
|
case other
|
|
294
296
|
when String
|
|
@@ -296,7 +298,7 @@ module Open
|
|
|
296
298
|
when Array
|
|
297
299
|
cmd << " " << other * " "
|
|
298
300
|
end
|
|
299
|
-
cmd << " && rm -Rf #{source}" if delete && ! files
|
|
301
|
+
cmd << " && rm -Rf '#{source}'" if delete && ! files
|
|
300
302
|
|
|
301
303
|
if print
|
|
302
304
|
cmd
|
|
@@ -1,12 +1,45 @@
|
|
|
1
1
|
require 'scout/resource'
|
|
2
2
|
require_relative 'sync'
|
|
3
3
|
module Resource
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
class << self
|
|
5
|
+
attr_accessor :sync_servers, :file_servers
|
|
6
|
+
|
|
7
|
+
def sync_servers
|
|
8
|
+
@sync_servers ||= begin
|
|
9
|
+
config_file = Scout.etc.sync_servers.find_with_extension('yaml', produce: false)
|
|
10
|
+
if config_file.exists?(produce: false)
|
|
11
|
+
config_file.yaml
|
|
12
|
+
else
|
|
13
|
+
{}
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def sync_server(resource)
|
|
19
|
+
return sync_servers[resource.to_s]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def file_servers
|
|
23
|
+
@file_servers ||= begin
|
|
24
|
+
config_file = Scout.etc.file_servers.find_with_extension('yaml', produce: false)
|
|
25
|
+
if config_file.exists?(produce: false)
|
|
26
|
+
config_file.yaml
|
|
27
|
+
else
|
|
28
|
+
{}
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def file_server(resource)
|
|
34
|
+
return file_servers[resource.to_s]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
6
38
|
|
|
39
|
+
def self.sync(path, map = nil, source: nil, target: nil, resource: nil, **kwargs)
|
|
7
40
|
if source
|
|
8
41
|
paths = [path]
|
|
9
|
-
real_paths, identified_paths = SSHLine.locate(source, paths)
|
|
42
|
+
real_paths, identified_paths = SSHLine.locate(source, paths, map: nil)
|
|
10
43
|
else
|
|
11
44
|
resource = path.pkgdir if resource.nil? and path.is_a?(Path) and path.pkgdir.is_a?(Resource)
|
|
12
45
|
resource = Resource.default_resource if resource.nil?
|
|
@@ -17,18 +50,90 @@ module Resource
|
|
|
17
50
|
path = Path.setup(path, pkgdir: resource) unless path.is_a?(Path)
|
|
18
51
|
real_paths = path.directory? ? path.find_all : path.glob_all
|
|
19
52
|
end
|
|
20
|
-
|
|
53
|
+
|
|
21
54
|
identified_paths = real_paths.collect{|path| resource.identify(path) }
|
|
22
55
|
end
|
|
23
56
|
|
|
24
57
|
if target
|
|
25
|
-
|
|
58
|
+
map = 'user' if map.nil?
|
|
59
|
+
target_paths, identified_paths = SSHLine.locate(target, identified_paths, map: map)
|
|
26
60
|
else
|
|
27
61
|
target_paths = identified_paths.collect{|p| p.find(map) }
|
|
28
62
|
end
|
|
29
63
|
|
|
30
64
|
real_paths.zip(target_paths).each do |source_path,target_path|
|
|
65
|
+
next if source_path.nil?
|
|
31
66
|
Open.sync(source_path, target_path, kwargs.merge(source: source, target: target))
|
|
32
67
|
end
|
|
33
68
|
end
|
|
69
|
+
|
|
70
|
+
def self.get_from_server(path, final_path, remote_server)
|
|
71
|
+
url = File.join(remote_server, '/resource/', self.to_s, 'get_file')
|
|
72
|
+
url << "?" << Misc.hash2GET_params(:file => path, :create => false)
|
|
73
|
+
|
|
74
|
+
begin
|
|
75
|
+
@server_missing_resource_cache ||= Set.new
|
|
76
|
+
raise "Resource Not Found" if @server_missing_resource_cache.include? url
|
|
77
|
+
Net::HTTP.get_response URI(url) do |response|
|
|
78
|
+
case response
|
|
79
|
+
when Net::HTTPSuccess, Net::HTTPOK
|
|
80
|
+
Misc.sensiblewrite(final_path) do |file|
|
|
81
|
+
response.read_body do |chunk|
|
|
82
|
+
file.write chunk
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
when Net::HTTPRedirection, Net::HTTPFound
|
|
86
|
+
location = response['location']
|
|
87
|
+
Log.debug("Feching directory from: #{location}. Into: #{final_path}")
|
|
88
|
+
FileUtils.mkdir_p final_path unless File.exist? final_path
|
|
89
|
+
Misc.in_dir final_path do
|
|
90
|
+
CMD.cmd('tar xvfz -', :in => Open.open(location, :nocache => true))
|
|
91
|
+
end
|
|
92
|
+
when Net::HTTPInternalServerError
|
|
93
|
+
@server_missing_resource_cache << url
|
|
94
|
+
raise "Resource Not Found"
|
|
95
|
+
else
|
|
96
|
+
raise "Response not understood: #{response.inspect}"
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
rescue
|
|
100
|
+
Log.warn "Could not retrieve (#{self.to_s}) #{ path } from #{ remote_server }"
|
|
101
|
+
Log.error $!.message
|
|
102
|
+
Open.rm_rf final_path if Open.exist? final_path
|
|
103
|
+
raise $!
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def file_server
|
|
108
|
+
Resource.file_server(self)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def sync_server
|
|
112
|
+
Resource.sync_server(self)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
alias local_produce produce
|
|
116
|
+
def produce(path, *args, **kwargs)
|
|
117
|
+
if sync_server = self.sync_server
|
|
118
|
+
begin
|
|
119
|
+
Resource.sync(Resource.default_resource.identify(path), source: sync_server)
|
|
120
|
+
return path if Open.exists?(path)
|
|
121
|
+
rescue
|
|
122
|
+
Log.exception $!
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
if file_server = self.file_server
|
|
127
|
+
begin
|
|
128
|
+
Resource.get_from_server(Resource.default_resource.identify(path), path.find, file_server)
|
|
129
|
+
return path if Open.exists?(path)
|
|
130
|
+
rescue
|
|
131
|
+
Log.exception $!
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
local_produce(path, *args, **kwargs)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
|
|
34
139
|
end
|
data/lib/scout/offsite/step.rb
CHANGED
|
@@ -5,7 +5,7 @@ require 'scout/workflow/step'
|
|
|
5
5
|
module OffsiteStep
|
|
6
6
|
|
|
7
7
|
extend Annotation
|
|
8
|
-
annotation :server, :workflow_name, :clean_id, :
|
|
8
|
+
annotation :server, :workflow_name, :clean_id, :batch
|
|
9
9
|
|
|
10
10
|
def inputs_directory
|
|
11
11
|
@inputs_directory ||= begin
|
|
@@ -13,7 +13,7 @@ module OffsiteStep
|
|
|
13
13
|
file = ".scout/tmp/step_inputs/#{workflow}/#{task_name}/#{name}"
|
|
14
14
|
TmpFile.with_path do |inputs_dir|
|
|
15
15
|
save_inputs(inputs_dir)
|
|
16
|
-
SSHLine.rsync(inputs_dir, file, target: server, directory:
|
|
16
|
+
SSHLine.rsync(inputs_dir, file, target: server, directory: inputs_dir.directory?)
|
|
17
17
|
end
|
|
18
18
|
file
|
|
19
19
|
end
|
|
@@ -30,6 +30,13 @@ module OffsiteStep
|
|
|
30
30
|
wf = Workflow.require_workflow "#{workflow_name}";
|
|
31
31
|
EOF
|
|
32
32
|
|
|
33
|
+
rec_dependencies.collect{|d| d.workflow }.compact.uniq.each do |other_workflow|
|
|
34
|
+
next if workflow_name == other_workflow.to_s
|
|
35
|
+
parts << <<~EOF.strip
|
|
36
|
+
Workflow.require_workflow "#{other_workflow}" rescue nil;
|
|
37
|
+
EOF
|
|
38
|
+
end
|
|
39
|
+
|
|
33
40
|
if inputs_directory
|
|
34
41
|
parts << <<~EOF.strip
|
|
35
42
|
job = wf.job(:#{task_name}, "#{clean_name}", :load_inputs => "#{inputs_directory}");
|
|
@@ -68,33 +75,33 @@ job = wf.job(:#{task_name}, "#{clean_name}");
|
|
|
68
75
|
status == :done
|
|
69
76
|
end
|
|
70
77
|
|
|
71
|
-
def
|
|
78
|
+
def orchestrate_batch
|
|
72
79
|
bundle_files = offsite_job_ssh <<~EOF
|
|
73
80
|
require 'rbbt/hpc'
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
rules = Workflow::Orchestrator.load_rules_for_job(job)
|
|
82
|
+
Workflow::Scheduler.produce(job, rules)
|
|
76
83
|
job.join
|
|
77
84
|
job.bundle_files
|
|
78
85
|
EOF
|
|
79
86
|
SSHLine.sync(bundle_files, source: server)
|
|
80
|
-
self.load
|
|
81
87
|
end
|
|
82
88
|
|
|
83
|
-
|
|
84
|
-
def exec
|
|
89
|
+
def exec(noload)
|
|
85
90
|
bundle_files = offsite_job_ssh <<~EOF
|
|
86
|
-
job
|
|
91
|
+
Workflow.produce(job)
|
|
92
|
+
job.join
|
|
87
93
|
job.bundle_files
|
|
88
94
|
EOF
|
|
89
95
|
SSHLine.sync(bundle_files, source: server)
|
|
90
|
-
self.load
|
|
96
|
+
noload ? self.path : self.load
|
|
91
97
|
end
|
|
92
98
|
|
|
93
|
-
def run
|
|
94
|
-
if
|
|
95
|
-
|
|
99
|
+
def run(noload=false)
|
|
100
|
+
if batch
|
|
101
|
+
orchestrate_batch
|
|
102
|
+
noload ? self.path : self.load
|
|
96
103
|
else
|
|
97
|
-
exec
|
|
104
|
+
exec(noload)
|
|
98
105
|
end
|
|
99
106
|
end
|
|
100
107
|
end
|