gitlab-swat 0.1.0
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 +7 -0
- data/.gitlab-ci.yml +20 -0
- data/.rubocop.yml +30 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +52 -0
- data/README.md +101 -0
- data/cog-command +10 -0
- data/config.yaml +50 -0
- data/gitlab-swat.gemspec +27 -0
- data/lib/cog_cmd/swat/dryrun.rb +18 -0
- data/lib/cog_cmd/swat/reload.rb +25 -0
- data/lib/cog_cmd/swat/strike.rb +18 -0
- data/lib/rails_loader.rb +44 -0
- data/lib/swat.rb +131 -0
- data/lib/swat_git.rb +62 -0
- data/lib/swat_run.rb +20 -0
- data/scripts/invalid.rb +0 -0
- data/scripts/test.rb +23 -0
- data/spec/helpers/fail_stub.sh +6 -0
- data/spec/helpers/rails_stub.rb +8 -0
- data/spec/rails_loader_spec.rb +21 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/swat_command_execution_spec.rb +64 -0
- data/spec/swat_command_spec.rb +13 -0
- data/spec/swat_config_spec.rb +8 -0
- data/spec/swat_dryrun_spec.rb +21 -0
- data/spec/swat_git_spec.rb +66 -0
- data/spec/swat_parameters_spec.rb +17 -0
- data/spec/swat_reload_spec.rb +61 -0
- data/spec/swat_script_spec.rb +24 -0
- data/spec/swat_strike_spec.rb +22 -0
- metadata +151 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d7764e07cd7efc9efec68895fe07f7c8a2c3db9d
|
4
|
+
data.tar.gz: 679cc6ac1ae287113cb9fc3f70c6282c2eaaedb6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f323d1a8b248bad4c21b11a734a97697bb96961df59dee5b724dbe64bf4ecda27773a8120b40438349097c6f5511a30c60996412671fb40a49f8fe5e6b00deef
|
7
|
+
data.tar.gz: d5165ea4b0ebfb42e9ddc10d63fae6b8e2a5b1ea80e30f324ed0514b2ec99fc812b674684aebe3cb21f2e16bef0c0f40698f9bbe88dde91f0a57bc031d508a0a
|
data/.gitlab-ci.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
image: ruby:2.3
|
2
|
+
before_script:
|
3
|
+
- ruby -v
|
4
|
+
- which ruby
|
5
|
+
- gem install bundler --no-ri --no-rdoc
|
6
|
+
- bundle install --jobs $(nproc) "${FLAGS[@]}" --path vendor
|
7
|
+
- git config --global user.email "you@example.com"
|
8
|
+
- git config --global user.name "Your Name"
|
9
|
+
|
10
|
+
cache:
|
11
|
+
paths:
|
12
|
+
- vendor
|
13
|
+
|
14
|
+
rspec:
|
15
|
+
script:
|
16
|
+
- bundle exec rspec -f d -c -b
|
17
|
+
|
18
|
+
rubocop:
|
19
|
+
script:
|
20
|
+
- bundle exec rubocop
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# Commonly used screens these days easily fit more than 80 characters.
|
2
|
+
Metrics/LineLength:
|
3
|
+
Max: 120
|
4
|
+
# Just use double quotes please
|
5
|
+
#
|
6
|
+
Style/StringLiterals:
|
7
|
+
EnforcedStyle: double_quotes
|
8
|
+
|
9
|
+
Style/FrozenStringLiteralComment:
|
10
|
+
Enabled: false
|
11
|
+
|
12
|
+
# Jim Weirich block style
|
13
|
+
Style/BlockDelimiters:
|
14
|
+
EnforcedStyle: semantic
|
15
|
+
|
16
|
+
Style/SignalException:
|
17
|
+
EnforcedStyle: semantic
|
18
|
+
|
19
|
+
Style/RaiseArgs:
|
20
|
+
EnforcedStyle: compact
|
21
|
+
|
22
|
+
Metrics/MethodLength:
|
23
|
+
Max: 15
|
24
|
+
|
25
|
+
Metrics/AbcSize:
|
26
|
+
Enabled: false
|
27
|
+
|
28
|
+
Metrics/BlockLength:
|
29
|
+
Exclude:
|
30
|
+
- 'spec/**/*.rb'
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
ast (2.3.0)
|
5
|
+
cog-rb (0.4.4)
|
6
|
+
rake (~> 11.2)
|
7
|
+
diff-lcs (1.3)
|
8
|
+
docile (1.1.5)
|
9
|
+
json (2.0.3)
|
10
|
+
parser (2.4.0.0)
|
11
|
+
ast (~> 2.2)
|
12
|
+
powerpack (0.1.1)
|
13
|
+
rainbow (2.2.1)
|
14
|
+
rake (11.3.0)
|
15
|
+
rspec (3.5.0)
|
16
|
+
rspec-core (~> 3.5.0)
|
17
|
+
rspec-expectations (~> 3.5.0)
|
18
|
+
rspec-mocks (~> 3.5.0)
|
19
|
+
rspec-core (3.5.4)
|
20
|
+
rspec-support (~> 3.5.0)
|
21
|
+
rspec-expectations (3.5.0)
|
22
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
23
|
+
rspec-support (~> 3.5.0)
|
24
|
+
rspec-mocks (3.5.0)
|
25
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
26
|
+
rspec-support (~> 3.5.0)
|
27
|
+
rspec-support (3.5.0)
|
28
|
+
rubocop (0.48.0)
|
29
|
+
parser (>= 2.3.3.1, < 3.0)
|
30
|
+
powerpack (~> 0.1)
|
31
|
+
rainbow (>= 1.99.1, < 3.0)
|
32
|
+
ruby-progressbar (~> 1.7)
|
33
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
34
|
+
ruby-progressbar (1.8.1)
|
35
|
+
simplecov (0.14.1)
|
36
|
+
docile (~> 1.1.0)
|
37
|
+
json (>= 1.8, < 3)
|
38
|
+
simplecov-html (~> 0.10.0)
|
39
|
+
simplecov-html (0.10.0)
|
40
|
+
unicode-display_width (1.1.3)
|
41
|
+
|
42
|
+
PLATFORMS
|
43
|
+
ruby
|
44
|
+
|
45
|
+
DEPENDENCIES
|
46
|
+
cog-rb (~> 0.4)
|
47
|
+
rspec (~> 3.5)
|
48
|
+
rubocop (~> 0.42)
|
49
|
+
simplecov (~> 0.13)
|
50
|
+
|
51
|
+
BUNDLED WITH
|
52
|
+
1.13.7
|
data/README.md
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
[](https://gitlab.com/gitlab-cog/swat/commits/master)
|
2
|
+
|
3
|
+
# GitLab SWAT
|
4
|
+
|
5
|
+
A Successful Deployment Ends Peacefully With No Bullets Fired.
|
6
|
+
If That’s Simply Not Possible, SWAT Uses Special Weapons and Tactics to Keep the Public Safe
|
7
|
+
|
8
|
+
## Defining ~~Weapons~~ Scripts
|
9
|
+
|
10
|
+
A script should only contain one class with the following structure
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
module Swat
|
14
|
+
#
|
15
|
+
# Command to use when calling the script file, it has to respect the module and class name
|
16
|
+
# because it will be imported from rails and called by name
|
17
|
+
#
|
18
|
+
class Command < BaseCommand
|
19
|
+
def prepare(context)
|
20
|
+
fail "I need at least 1 argument" if @args.empty?
|
21
|
+
context[:some_key] = "something"
|
22
|
+
"text to add to the prepare stage result"
|
23
|
+
end
|
24
|
+
|
25
|
+
def pre_check(context)
|
26
|
+
fail "something is not right" unless context[:some_key] == "something"
|
27
|
+
"text to add to the pre_check stage result"
|
28
|
+
end
|
29
|
+
|
30
|
+
def execute(context)
|
31
|
+
fail "execution failed!" if context.empty?
|
32
|
+
"Context so far is #{context}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
### Execution Stages
|
39
|
+
|
40
|
+
* prepare: initial stage, used to parse arguments, or whatever is necessary before getting into the pre_check stage.
|
41
|
+
* pre_check: stage used to validate that the command should continue to the execute stage if it is running in execute mode. Dryrun would only reach this stage.
|
42
|
+
* execute: the actual operation.
|
43
|
+
|
44
|
+
Any stage that raises an exception will stop the execution, and will force and early return with a failure state and the different messages from the executed phases.
|
45
|
+
|
46
|
+
### Available tools
|
47
|
+
|
48
|
+
* `@args` the arguments tha are sent from the execution and reach the command, simply a string array.
|
49
|
+
* `context` a hashmap that is created before the prepare stage and is sent to all methods, use this to accumulate state across stages.
|
50
|
+
|
51
|
+
# Configuring in cog
|
52
|
+
|
53
|
+
## Environment variables
|
54
|
+
|
55
|
+
* **SCRIPTS_REMOTE_URL** url pointing to the remote repository
|
56
|
+
* **SCRIPTS_LOCAL_PATH** folder where the remote repository will be downloaded to
|
57
|
+
* **RAILS_RUNNER_COMMAND** command used to run rails, for example: bundle exec rails runner ./scripts/lib/swat_run.rb
|
58
|
+
* **RAILS_WORKING_DIR** working dir in which to execute the rails runner command
|
59
|
+
|
60
|
+
## Cog Commands
|
61
|
+
|
62
|
+
* `dryrun <script> [args]` executes the given script with arguments in dryrun mode
|
63
|
+
* `strike <script> [args]` executes the given script with arguments in execute mode
|
64
|
+
* `reload [-f]` clones or pulls the scripts repo, use _-f_ to wipe the repo and clone it from scratch
|
65
|
+
|
66
|
+
# Development
|
67
|
+
|
68
|
+
## How to run integration tests
|
69
|
+
|
70
|
+
### Dry Run Mode
|
71
|
+
|
72
|
+
```sh
|
73
|
+
$ SCRIPTS_LOCAL_PATH=/home/user/src/gitlab.com/gitlab-cog/swat/scripts RAILS_RUNNER_COMMAND="rails r /home/user/src/gitlab.com/gitlab-cog/swat/lib/swat_run.rb" RAILS_WORKING_DIR=/home/user/src/gitlab.com/gitlab-cog/rails-project COG_COMMAND="dryrun" COG_ARGV_0="test" COG_ARGV_1="success" COG_ARGV_2="success" COG_ARGC=3 ./cog-command
|
74
|
+
COG_TEMPLATE: execution_result
|
75
|
+
{"execution_mode":"dryrun","prepare":{"successful":true,"output":"preparation is fine so far"},"pre_check":{"successful":true,"output":"all is gut"}}
|
76
|
+
```
|
77
|
+
|
78
|
+
### Strike Mode
|
79
|
+
|
80
|
+
```sh
|
81
|
+
$ SCRIPTS_LOCAL_PATH=/home/user/src/gitlab.com/gitlab-cog/swat/scripts RAILS_RUNNER_COMMAND="rails r /home/user/src/gitlab.com/gitlab-cog/swat/lib/swat_run.rb" RAILS_WORKING_DIR=/home/user/src/gitlab.com/gitlab-cog/rails-project COG_COMMAND="strike" COG_ARGV_0="test" COG_ARGV_1="success" COG_ARGV_2="success" COG_ARGC=3 ./cog-command
|
82
|
+
COG_TEMPLATE: execution_result
|
83
|
+
{"execution_mode":"execute","prepare":{"successful":true,"output":"preparation is fine so far"},"pre_check":{"successful":true,"output":"all is gut"},"execute":{"successful":true,"output":"Context so far is {:prepared=\u003e\"done\", :checks=\u003e\"done\"}"}}
|
84
|
+
```
|
85
|
+
|
86
|
+
### Reload command
|
87
|
+
|
88
|
+
```sh
|
89
|
+
$ SCRIPTS_LOCAL_PATH=/tmp/testing-cog/second SCRIPTS_REMOTE_URL=$(pwd) COG_COMMAND="reload" ./cog-command
|
90
|
+
{"source":"/home/user/src/gitlab.com/gitlab-cog/swat","target":"/tmp/testing-cog/scripts","action":"clone","wiped":false, "head":"1234 current commit"}
|
91
|
+
```
|
92
|
+
|
93
|
+
```sh
|
94
|
+
$ SCRIPTS_LOCAL_PATH=/tmp/testing-cog/second SCRIPTS_REMOTE_URL=$(pwd) COG_COMMAND="reload" ./cog-command
|
95
|
+
{"source":"/home/user/src/gitlab.com/gitlab-cog/swat","target":"/tmp/testing-cog/scripts","action":"pull","wiped":false, "head":"1234 current commit"}
|
96
|
+
```
|
97
|
+
|
98
|
+
```sh
|
99
|
+
$ SCRIPTS_LOCAL_PATH=/tmp/testing-cog/second SCRIPTS_REMOTE_URL=$(pwd) COG_COMMAND="reload" COG_OPTS=wipe COG_OPT_WIPE=true ./cog-command
|
100
|
+
{"source":"/home/user/src/gitlab.com/gitlab-cog/swat","target":"/tmp/testing-cog/scripts","action":"clone","wiped":true, "head":"1234 current commit"}
|
101
|
+
```
|
data/cog-command
ADDED
data/config.yaml
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
---
|
2
|
+
cog_bundle_version: 4
|
3
|
+
name: swat
|
4
|
+
version: 0.0.1
|
5
|
+
docker:
|
6
|
+
image: gitlab/swat
|
7
|
+
tag: 0.0.1
|
8
|
+
description: >
|
9
|
+
A Successful Deployment Ends Peacefully With No Bullets Fired.
|
10
|
+
If That’s Simply Not Possible, SWAT Uses Special Weapons and Tactics to Keep the Public Safe
|
11
|
+
config:
|
12
|
+
env:
|
13
|
+
- var: SCRIPT_REMOTE_URL
|
14
|
+
description: Required Url for a git repo where the scripts are stored
|
15
|
+
- var: SCRIPT_LOCAL_PATH
|
16
|
+
description: Required path to a local semi-persistent folder where to clone and update the scripts repo
|
17
|
+
- var: RAILS_EXECUTABLE
|
18
|
+
description: Required path in which to find the rails executable script with which load the script
|
19
|
+
homepage: https://gitlab.com/gitlab-cog/swat
|
20
|
+
author: Pablo Carranza <pablo@gitlab.com>
|
21
|
+
permissions:
|
22
|
+
- swat:reload
|
23
|
+
- swat:dryrun
|
24
|
+
- swat:strike
|
25
|
+
commands:
|
26
|
+
reload:
|
27
|
+
description: Clones or pulls the scripts repo
|
28
|
+
executable: /home/bundle/cog-command
|
29
|
+
options:
|
30
|
+
wipe:
|
31
|
+
type: bool
|
32
|
+
required: false
|
33
|
+
rules:
|
34
|
+
- must have swat:reload
|
35
|
+
dryrun:
|
36
|
+
description: Runs a script up to the precheck phase, used for training, showing or simply checking
|
37
|
+
executable: /home/bundle/cog-command
|
38
|
+
arguments: "<script> [args...]"
|
39
|
+
rules:
|
40
|
+
- must have swat:dryrun
|
41
|
+
strike:
|
42
|
+
description: Runs a script to the end, performing changes to the system.
|
43
|
+
executable: /home/bundle/cog-command
|
44
|
+
arguments: "<script> [args...]"
|
45
|
+
rules:
|
46
|
+
- must have swat:execute
|
47
|
+
templates:
|
48
|
+
execution_result:
|
49
|
+
body: |
|
50
|
+
~json var=$results~
|
data/gitlab-swat.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "gitlab-swat"
|
3
|
+
s.version = "0.1.0"
|
4
|
+
s.date = "2017-04-01"
|
5
|
+
s.summary = "ChatOps Cog Bundle that enables admins to remotely run predefined scripts in a rails console"
|
6
|
+
s.description = <<~eos
|
7
|
+
A Successful Deployment Ends Peacefully With No Bullets Fired.
|
8
|
+
If That’s Simply Not Possible, SWAT Uses Special Weapons and Tactics to Keep the Public Safe
|
9
|
+
|
10
|
+
GitLab-Swat allows admins to quickly deploy scripts that can be remotely executed through a rails console
|
11
|
+
|
12
|
+
Allowing fast action by using an external git repository as the scripts source, but keeping safety high by
|
13
|
+
enforcing a prepare-pre check-execute model that allows execution break at any stage if things are not going
|
14
|
+
as expected
|
15
|
+
eos
|
16
|
+
s.authors = ["Pablo Carranza"]
|
17
|
+
s.email = "pablo@gitlab.com"
|
18
|
+
s.files = `git ls-files -z`.split("\x0")
|
19
|
+
s.test_files = s.files.grep(%r{^(spec)/})
|
20
|
+
s.license = "MIT"
|
21
|
+
|
22
|
+
s.add_dependency "cog-rb", "~> 0.4"
|
23
|
+
|
24
|
+
s.add_development_dependency "rspec", "~> 3.5"
|
25
|
+
s.add_development_dependency "rubocop", "~> 0.42"
|
26
|
+
s.add_development_dependency "simplecov", "~> 0.13"
|
27
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "cog"
|
2
|
+
require "swat"
|
3
|
+
require "rails_loader"
|
4
|
+
|
5
|
+
module CogCmd
|
6
|
+
module Swat
|
7
|
+
#
|
8
|
+
# Cog Command that loads and dryruns the given script
|
9
|
+
#
|
10
|
+
class Dryrun < Cog::Command
|
11
|
+
def run_command
|
12
|
+
rails = ::Swat::RailsLoader.new
|
13
|
+
response.template = "execution_result"
|
14
|
+
response.content = rails.run("dryrun #{request.args.join(' ')}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "cog"
|
2
|
+
require "json"
|
3
|
+
require "swat_git"
|
4
|
+
|
5
|
+
module CogCmd
|
6
|
+
module Swat
|
7
|
+
#
|
8
|
+
# Cog Command that [re]loads a local git repo for scripts
|
9
|
+
#
|
10
|
+
class Reload < Cog::Command
|
11
|
+
def run_command
|
12
|
+
git = ::Swat::Git.new
|
13
|
+
git.wipe if wipe?
|
14
|
+
result = { source: git.source,
|
15
|
+
target: git.target,
|
16
|
+
wiped: wipe? }.merge(git.update)
|
17
|
+
response.content = result.to_json
|
18
|
+
end
|
19
|
+
|
20
|
+
def wipe?
|
21
|
+
request.options["wipe"] == true || request.options["wipe"] == "true"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "cog"
|
2
|
+
require "swat"
|
3
|
+
require "rails_loader"
|
4
|
+
|
5
|
+
module CogCmd
|
6
|
+
module Swat
|
7
|
+
#
|
8
|
+
# Cog Command that loads and executes the given script
|
9
|
+
#
|
10
|
+
class Strike < Cog::Command
|
11
|
+
def run_command
|
12
|
+
rails = ::Swat::RailsLoader.new
|
13
|
+
response.template = "execution_result"
|
14
|
+
response.content = rails.run("execute #{request.args.join(' ')}").to_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/rails_loader.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require "open3"
|
2
|
+
|
3
|
+
module Swat
|
4
|
+
#
|
5
|
+
# Loads Rails command runner
|
6
|
+
#
|
7
|
+
# Loads RAILS_RUNNER_COMMAND from the environment to define
|
8
|
+
#
|
9
|
+
# Sample script:
|
10
|
+
#
|
11
|
+
# SCRIPTS_LOCAL_PATH=path_to/scripts bundle exec rails runner
|
12
|
+
# path_to/scripts/lib/swat_run.rb execute test success success 2&>/dev/null
|
13
|
+
#
|
14
|
+
# Can read the runner command and the working dir from environment variables
|
15
|
+
# such as:
|
16
|
+
# RAILS_RUNNER_COMMAND
|
17
|
+
# RAILS_WORKING_DIR
|
18
|
+
#
|
19
|
+
class RailsLoader
|
20
|
+
def initialize(command = ENV["RAILS_RUNNER_COMMAND"] || "",
|
21
|
+
working_dir = ENV["RAILS_WORKING_DIR"] || Dir.getwd)
|
22
|
+
fail "Invalid RAILS_RUNNER_COMMAND, please provide a rails command" if command.nil? || command.empty?
|
23
|
+
fail "Invalid RAILS_WORKING_DIR, '#{working_dir}' does not exists" unless Dir.exist?(working_dir)
|
24
|
+
@rails_command = command
|
25
|
+
@rails_working_dir = working_dir
|
26
|
+
end
|
27
|
+
|
28
|
+
def run(args)
|
29
|
+
command = "#{@rails_command} #{args}"
|
30
|
+
env = {
|
31
|
+
"PATH" => ENV["PATH"],
|
32
|
+
"SCRIPTS_REMOTE_URL" => ENV["SCRIPTS_REMOTE_URL"],
|
33
|
+
"SCRIPTS_LOCAL_PATH" => ENV["SCRIPTS_LOCAL_PATH"]
|
34
|
+
}
|
35
|
+
Open3.popen3(env, command, unsetenv_others: true, chdir: @rails_working_dir) do |_, stdout, stderr, wait_thr|
|
36
|
+
if wait_thr.value != 0
|
37
|
+
fail "Command #{args} failed with err: '#{stderr.read.strip}' " \
|
38
|
+
"out: '#{stdout.read.strip}'"
|
39
|
+
end
|
40
|
+
stdout.read.strip
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/swat.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
require "pathname"
|
2
|
+
#
|
3
|
+
# Swat module, where all the stuff lives
|
4
|
+
#
|
5
|
+
module Swat
|
6
|
+
#
|
7
|
+
# Defines the basic behavior of a command
|
8
|
+
#
|
9
|
+
# Inherith to reuse all this logic, only pre-check and execute are mandatory methods
|
10
|
+
# the rest can be as is
|
11
|
+
#
|
12
|
+
# Use the context object to send state from one stage to the next
|
13
|
+
class BaseCommand
|
14
|
+
def initialize(args)
|
15
|
+
@args = args
|
16
|
+
end
|
17
|
+
|
18
|
+
def prepare(_context)
|
19
|
+
# validate arguments here
|
20
|
+
end
|
21
|
+
|
22
|
+
def pre_check(_context)
|
23
|
+
fail "PreChecks are not defined"
|
24
|
+
end
|
25
|
+
|
26
|
+
def execute(_context)
|
27
|
+
fail "Execution is not defined"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Represents the whole execution pipeline of a command
|
33
|
+
#
|
34
|
+
# Returns a hashmap that contains the following keys
|
35
|
+
# execution_mode: self descriptive
|
36
|
+
# prepare, precheck, execute: the different stages of execution
|
37
|
+
# each stage will include a hash with:
|
38
|
+
# succesful: boolean, indicating if it was successful or not
|
39
|
+
# output: the stdout in case the stage was successfull, stderr otherwise
|
40
|
+
class CommandExecution
|
41
|
+
def initialize(command, execution_mode = "dryrun")
|
42
|
+
@command = command
|
43
|
+
@stages = stages(execution_mode)
|
44
|
+
@result = { execution_mode: execution_mode }
|
45
|
+
@context = {}
|
46
|
+
end
|
47
|
+
|
48
|
+
def run
|
49
|
+
@stages.each do |stage|
|
50
|
+
execute_stage(stage)
|
51
|
+
break unless @result[stage][:successful]
|
52
|
+
end
|
53
|
+
@result
|
54
|
+
end
|
55
|
+
|
56
|
+
def execute_stage(stage)
|
57
|
+
@result[stage] = { successful: true, output: @command.public_send(stage, @context) }
|
58
|
+
rescue => e
|
59
|
+
@result[stage] = { successful: false, output: e.to_s }
|
60
|
+
end
|
61
|
+
|
62
|
+
def stages(execution_mode)
|
63
|
+
case execution_mode
|
64
|
+
when "dryrun"
|
65
|
+
%i(prepare pre_check)
|
66
|
+
when "execute"
|
67
|
+
%i(prepare pre_check execute)
|
68
|
+
else
|
69
|
+
fail "Invalid execution mode '#{execution_mode}'"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Parameters parsing helper class
|
76
|
+
#
|
77
|
+
# Assumes the first parameter to be the execution mode (dryrun or execute)
|
78
|
+
# Assumes the second parameter to be the script name
|
79
|
+
# Captures the rest as arguments that are piped in to the script
|
80
|
+
class Parameters
|
81
|
+
attr_accessor :execution_mode, :command, :args
|
82
|
+
|
83
|
+
def initialize(args = ARGV.clone)
|
84
|
+
@args = args
|
85
|
+
@execution_mode = @args.shift
|
86
|
+
@command = @args.shift
|
87
|
+
fail "Execution mode is mandatory" if @execution_mode.nil?
|
88
|
+
fail "No command was specified" if @command.nil?
|
89
|
+
end
|
90
|
+
|
91
|
+
def to_s
|
92
|
+
"#{@execution_mode} #{@command} #{@args.inspect}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# An executable script
|
98
|
+
#
|
99
|
+
# Loads the script from file system using ruby's `require` command, then creates a
|
100
|
+
# ::Swat::Command and then calls `run` on it
|
101
|
+
#
|
102
|
+
# If a Swat::Command object cannot be found (NameError) the command is considered bogus.
|
103
|
+
class Script
|
104
|
+
def initialize(parameters, scripts_path = ENV["SCRIPTS_LOCAL_PATH"])
|
105
|
+
@parameters = parameters
|
106
|
+
@scripts_path = Pathname.new(scripts_path || "scripts")
|
107
|
+
end
|
108
|
+
|
109
|
+
def run
|
110
|
+
CommandExecution.new(create_command, @parameters.execution_mode).run
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def command_file
|
116
|
+
@command_file ||= @scripts_path.join("#{@parameters.command}.rb")
|
117
|
+
end
|
118
|
+
|
119
|
+
def create_command
|
120
|
+
fail "Could not find command #{@parameters.command} in #{command_file.expand_path}" unless command_file.file?
|
121
|
+
require command_file.expand_path
|
122
|
+
::Swat::Command.new(@parameters.args)
|
123
|
+
rescue NameError
|
124
|
+
raise "#{@parameters.command} does not define a Swat::Command object"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.run
|
129
|
+
Script.new(Parameters.new).run
|
130
|
+
end
|
131
|
+
end
|
data/lib/swat_git.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
module Swat
|
4
|
+
#
|
5
|
+
# Git handler
|
6
|
+
#
|
7
|
+
class Git
|
8
|
+
attr_accessor :source, :target
|
9
|
+
|
10
|
+
def initialize(source = ENV["SCRIPTS_REMOTE_URL"], target = ENV["SCRIPTS_LOCAL_PATH"])
|
11
|
+
@source = source
|
12
|
+
@target = target
|
13
|
+
end
|
14
|
+
|
15
|
+
def wipe
|
16
|
+
FileUtils.rm_rf(@target) if File.writable?(@target)
|
17
|
+
end
|
18
|
+
|
19
|
+
def update
|
20
|
+
if Dir.exist?(@target)
|
21
|
+
pull
|
22
|
+
else
|
23
|
+
clone
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def valid?
|
28
|
+
return false unless File.exist?(@target)
|
29
|
+
Dir.chdir(@target) {
|
30
|
+
`git config --get remote.origin.url`.strip == @source
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def clone
|
37
|
+
repo_path = Pathname.new(@target)
|
38
|
+
parent_folder = repo_path.parent
|
39
|
+
repo_name = repo_path.basename
|
40
|
+
fail "Invalid target path #{parent_folder}" unless File.writable?(parent_folder)
|
41
|
+
Dir.chdir(parent_folder) do
|
42
|
+
`git clone #{@source} #{repo_name} 2> /dev/null`
|
43
|
+
end
|
44
|
+
{ action: "clone", head: current_commit }
|
45
|
+
end
|
46
|
+
|
47
|
+
def pull
|
48
|
+
fail "Invalid target repo #{@target}" unless valid?
|
49
|
+
Dir.chdir(@target) do
|
50
|
+
`git pull origin 2> /dev/null`
|
51
|
+
end
|
52
|
+
{ action: "pull", head: current_commit }
|
53
|
+
end
|
54
|
+
|
55
|
+
def current_commit
|
56
|
+
fail "Invalid target repo #{@target}" unless valid?
|
57
|
+
Dir.chdir(@target) do
|
58
|
+
`git log --oneline -1 HEAD`.strip
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/swat_run.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# Usage: through rails execute the following command:
|
2
|
+
# bundle exec rails runner <path to>/marvin_run.rb <command> [arguments]
|
3
|
+
#
|
4
|
+
# Such as:
|
5
|
+
# - <path to>/marvin.rb points to the place where this file is located
|
6
|
+
# - command is the name of a file existing inside the scripts folder of this repo
|
7
|
+
# - arguments are the required arguments for the given command
|
8
|
+
#
|
9
|
+
# Implementing new commands:
|
10
|
+
# - Create a file in the scripts folder such as scripts/<command>.rb
|
11
|
+
# - In this file create a Swat::Command class that inheriths Swat::BaseCommand
|
12
|
+
# - It is mandatory to override pre_check and execute, these methods should either execute or fail with an exception
|
13
|
+
# - Optionally the method `validate` can be implemented to validate the received arguments before execution
|
14
|
+
|
15
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
16
|
+
|
17
|
+
require "json"
|
18
|
+
require_relative "swat"
|
19
|
+
|
20
|
+
puts Swat.run.to_json
|
data/scripts/invalid.rb
ADDED
File without changes
|
data/scripts/test.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Swat
|
2
|
+
#
|
3
|
+
# Test command
|
4
|
+
#
|
5
|
+
class Command < BaseCommand
|
6
|
+
def prepare(context)
|
7
|
+
fail "I need at least 1 argument" if @args.empty?
|
8
|
+
context[:prepared] = "done"
|
9
|
+
"preparation is fine so far"
|
10
|
+
end
|
11
|
+
|
12
|
+
def pre_check(context)
|
13
|
+
fail "something is not right" unless @args[0].to_sym == :success
|
14
|
+
context[:checks] = "done"
|
15
|
+
"all is gut"
|
16
|
+
end
|
17
|
+
|
18
|
+
def execute(context)
|
19
|
+
fail "execution failed!" unless @args[1].to_sym == :success
|
20
|
+
"Context so far is #{context}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
require "rails_loader"
|
3
|
+
|
4
|
+
describe Swat::RailsLoader do
|
5
|
+
it "can load and execute a command" do
|
6
|
+
expect(::Swat::RailsLoader
|
7
|
+
.new("./spec/helpers/rails_stub.rb lib/swat_run.rb")
|
8
|
+
.run("dryrun test success"))
|
9
|
+
.to eq("{\"execution_mode\":\"dryrun\",\"prepare\":{\"successful\":true," \
|
10
|
+
"\"output\":\"preparation is fine so far\"}," \
|
11
|
+
"\"pre_check\":{\"successful\":true,\"output\":\"all is gut\"}}")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "can err out when the return code is not 0" do
|
15
|
+
expect {
|
16
|
+
::Swat::RailsLoader
|
17
|
+
.new("./spec/helpers/fail_stub.sh")
|
18
|
+
.run("dryrun")
|
19
|
+
}.to raise_error(/Command dryrun failed with err/)
|
20
|
+
end
|
21
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require "simplecov"
|
2
|
+
|
3
|
+
SimpleCov.start do
|
4
|
+
add_filter "vendor"
|
5
|
+
end
|
6
|
+
|
7
|
+
require "rspec"
|
8
|
+
require "swat"
|
9
|
+
require "tmpdir"
|
10
|
+
require "fileutils"
|
11
|
+
|
12
|
+
class GitSpecHelper
|
13
|
+
attr_accessor :source_repo, :target_dir
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@source_repo = Dir.mktmpdir
|
17
|
+
@target_dir = Dir.mktmpdir
|
18
|
+
Dir.chdir(@source_repo) do
|
19
|
+
`git init`
|
20
|
+
`echo "hi" > readme.md`
|
21
|
+
`git add readme.md`
|
22
|
+
`git commit -m "initial commit"`
|
23
|
+
end
|
24
|
+
clean_target
|
25
|
+
end
|
26
|
+
|
27
|
+
def destroy
|
28
|
+
FileUtils.rm_rf(@source_repo)
|
29
|
+
FileUtils.rm_rf(@target_dir) if File.exist?(@target_dir)
|
30
|
+
end
|
31
|
+
|
32
|
+
def clean_target
|
33
|
+
FileUtils.rm_rf(@target_dir)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def with_environment(args: [])
|
38
|
+
ENV["COG_ARGC"] = args.length.times { |n| ENV["COG_ARGV_#{n}"] = args[n] }.to_s
|
39
|
+
yield
|
40
|
+
ensure
|
41
|
+
ENV.delete("COG_ARGC")
|
42
|
+
args.length.times { |n| ENV.delete("COG_ARGV_#{n}") }
|
43
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
require_relative "../scripts/test"
|
3
|
+
|
4
|
+
describe Swat::CommandExecution do
|
5
|
+
it "fails with an invalid execution mode" do
|
6
|
+
expect { Swat::CommandExecution.new(Swat::Command.new([]), "invalid") }
|
7
|
+
.to raise_error(/Invalid execution mode 'invalid'/)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "fails prepare stage without arguments" do
|
11
|
+
executor = Swat::CommandExecution.new(Swat::Command.new([]))
|
12
|
+
expect(executor.run).to eq(
|
13
|
+
execution_mode: "dryrun",
|
14
|
+
prepare: { successful: false, output: "I need at least 1 argument" }
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "stops execution on pre_checks when constraints are not met" do
|
19
|
+
executor = Swat::CommandExecution.new(Swat::Command.new([:failure]))
|
20
|
+
expect(executor.run).to eq(
|
21
|
+
execution_mode: "dryrun",
|
22
|
+
prepare: { successful: true, output: "preparation is fine so far" },
|
23
|
+
pre_check: { successful: false, output: "something is not right" }
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "it only goes as far as pre checks in dry run mode" do
|
28
|
+
executor = Swat::CommandExecution.new(Swat::Command.new([:success]))
|
29
|
+
expect(executor.run).to eq(
|
30
|
+
execution_mode: "dryrun",
|
31
|
+
prepare: { successful: true, output: "preparation is fine so far" },
|
32
|
+
pre_check: { successful: true, output: "all is gut" }
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "stops execution on pre_checks when prechecks fail" do
|
37
|
+
executor = Swat::CommandExecution.new(Swat::Command.new([:failure]), "execute")
|
38
|
+
expect(executor.run).to eq(
|
39
|
+
execution_mode: "execute",
|
40
|
+
prepare: { successful: true, output: "preparation is fine so far" },
|
41
|
+
pre_check: { successful: false, output: "something is not right" }
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "execution can fail and report it" do
|
46
|
+
executor = Swat::CommandExecution.new(Swat::Command.new(%i(success failure)), "execute")
|
47
|
+
expect(executor.run).to eq(
|
48
|
+
execution_mode: "execute",
|
49
|
+
prepare: { successful: true, output: "preparation is fine so far" },
|
50
|
+
pre_check: { successful: true, output: "all is gut" },
|
51
|
+
execute: { successful: false, output: "execution failed!" }
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "execution can succeed" do
|
56
|
+
executor = Swat::CommandExecution.new(Swat::Command.new(%i(success success)), "execute")
|
57
|
+
expect(executor.run).to eq(
|
58
|
+
execution_mode: "execute",
|
59
|
+
prepare: { successful: true, output: "preparation is fine so far" },
|
60
|
+
pre_check: { successful: true, output: "all is gut" },
|
61
|
+
execute: { successful: true, output: "Context so far is {:prepared=>\"done\", :checks=>\"done\"}" }
|
62
|
+
)
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe Swat::BaseCommand do
|
4
|
+
it "errs when calling prechecks" do
|
5
|
+
cmd = Swat::BaseCommand.new(:sentinel)
|
6
|
+
expect { cmd.pre_check({}) }.to raise_error(/PreChecks are not defined/)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "errs when calling execute" do
|
10
|
+
cmd = Swat::BaseCommand.new(:sentinel)
|
11
|
+
expect { cmd.execute({}) }.to raise_error(/Execution is not defined/)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "cog_cmd/swat/dryrun"
|
3
|
+
|
4
|
+
describe CogCmd::Swat::Dryrun do
|
5
|
+
before do allow(STDIN).to receive(:tty?) { true } end
|
6
|
+
before do
|
7
|
+
ENV["RAILS_RUNNER_COMMAND"] = "./spec/helpers/rails_stub.rb lib/swat_run.rb"
|
8
|
+
end
|
9
|
+
after do ENV.delete("RAILS_RUNNER_COMMAND") end
|
10
|
+
|
11
|
+
it "can be called" do
|
12
|
+
command = CogCmd::Swat::Dryrun.new
|
13
|
+
with_environment(args: ["test success"]) do
|
14
|
+
command.run_command
|
15
|
+
end
|
16
|
+
expect(command.response.content)
|
17
|
+
.to eq("{\"execution_mode\":\"dryrun\",\"prepare\":{\"successful\":true," \
|
18
|
+
"\"output\":\"preparation is fine so far\"}," \
|
19
|
+
"\"pre_check\":{\"successful\":true,\"output\":\"all is gut\"}}")
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "swat_git"
|
3
|
+
|
4
|
+
describe Swat::Git do
|
5
|
+
context "Given a valid URL" do
|
6
|
+
before(:all) do
|
7
|
+
@git = GitSpecHelper.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_commit
|
11
|
+
Dir.chdir(@git.source_repo) {
|
12
|
+
`echo "how you doing?" >> readme.md`
|
13
|
+
`git commit -a -m "Another line in"`
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
before(:each) do @git.clean_target end
|
18
|
+
|
19
|
+
it "can clone a repo" do
|
20
|
+
repo = Swat::Git.new(@git.source_repo, @git.target_dir)
|
21
|
+
repo.update
|
22
|
+
expect(repo.valid?).to be_truthy
|
23
|
+
end
|
24
|
+
|
25
|
+
it "can determine that a repo is invalid when it doesn't exists" do
|
26
|
+
expect(Swat::Git.new(@git.source_repo, @git.target_dir).valid?).to be_falsey
|
27
|
+
end
|
28
|
+
|
29
|
+
it "can clone and then update a repo" do
|
30
|
+
repo = Swat::Git.new(@git.source_repo, @git.target_dir)
|
31
|
+
repo.update
|
32
|
+
add_commit
|
33
|
+
repo.update
|
34
|
+
expect(repo.valid?).to be_truthy
|
35
|
+
end
|
36
|
+
|
37
|
+
it "can wipe a non existing repo" do
|
38
|
+
repo = Swat::Git.new(@git.source_repo, @git.target_dir)
|
39
|
+
repo.wipe
|
40
|
+
expect(File.exist?(@git.target_dir)).to be_falsey
|
41
|
+
end
|
42
|
+
|
43
|
+
it "can clone and then wipe a repo" do
|
44
|
+
repo = Swat::Git.new(@git.source_repo, @git.target_dir)
|
45
|
+
repo.update
|
46
|
+
repo.wipe
|
47
|
+
expect(File.exist?(@git.target_dir)).to be_falsey
|
48
|
+
end
|
49
|
+
|
50
|
+
it "can clone and then update a repo multiple times" do
|
51
|
+
repo = Swat::Git.new(@git.source_repo, @git.target_dir)
|
52
|
+
repo.update
|
53
|
+
3.times do
|
54
|
+
add_commit
|
55
|
+
repo.update
|
56
|
+
end
|
57
|
+
expect(Dir.chdir(@git.target_dir) {
|
58
|
+
`git log --oneline | wc -l`.strip
|
59
|
+
}).to eq("5")
|
60
|
+
end
|
61
|
+
|
62
|
+
after(:all) do
|
63
|
+
@git.destroy
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe Swat::Parameters do
|
4
|
+
it "loads the command with arguments" do
|
5
|
+
allow(ARGV).to receive(:clone) { %w(dryrun command arg) }
|
6
|
+
params = Swat::Parameters.new
|
7
|
+
expect(params.execution_mode).to eq("dryrun")
|
8
|
+
expect(params.command).to eq("command")
|
9
|
+
expect(params.args).to eq(["arg"])
|
10
|
+
expect(params.to_s).to eq("dryrun command [\"arg\"]")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "fails to load if there is no command" do
|
14
|
+
allow(ARGV).to receive(:clone) { [] }
|
15
|
+
expect { Swat::Parameters.new }.to raise_error(/Execution mode is mandatory/)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "cog_cmd/swat/reload"
|
3
|
+
|
4
|
+
describe CogCmd::Swat::Reload do
|
5
|
+
before do allow(STDIN).to receive(:tty?) { true } end
|
6
|
+
|
7
|
+
let(:git) { GitSpecHelper.new }
|
8
|
+
|
9
|
+
before do
|
10
|
+
ENV["SCRIPTS_REMOTE_URL"] = git.source_repo
|
11
|
+
ENV["SCRIPTS_LOCAL_PATH"] = git.target_dir
|
12
|
+
end
|
13
|
+
|
14
|
+
it "reloads the repo" do
|
15
|
+
command = CogCmd::Swat::Reload.new
|
16
|
+
command.run_command
|
17
|
+
expect(JSON.parse(command.response.content)).to include(
|
18
|
+
"source" => git.source_repo,
|
19
|
+
"target" => git.target_dir,
|
20
|
+
"action" => "clone",
|
21
|
+
"wiped" => false
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "reloads the repo" do
|
26
|
+
begin
|
27
|
+
command = CogCmd::Swat::Reload.new
|
28
|
+
ENV["COG_OPTS"] = "wipe"
|
29
|
+
ENV["COG_OPT_WIPE"] = "true"
|
30
|
+
command.run_command
|
31
|
+
expect(JSON.parse(command.response.content)).to include(
|
32
|
+
"source" => git.source_repo,
|
33
|
+
"target" => git.target_dir,
|
34
|
+
"action" => "clone",
|
35
|
+
"wiped" => true
|
36
|
+
)
|
37
|
+
ensure
|
38
|
+
ENV.delete("COG_OPTS")
|
39
|
+
ENV.delete("COG_OPT_WIPE")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it "reloads the repo 2 times" do
|
44
|
+
command = CogCmd::Swat::Reload.new
|
45
|
+
command.run_command
|
46
|
+
command.run_command
|
47
|
+
command.run_command
|
48
|
+
expect(JSON.parse(command.response.content)).to include(
|
49
|
+
"source" => git.source_repo,
|
50
|
+
"target" => git.target_dir,
|
51
|
+
"action" => "pull",
|
52
|
+
"wiped" => false
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
after do
|
57
|
+
ENV.delete("SCRIPTS_REMOTE_URL")
|
58
|
+
ENV.delete("SCRIPTS_LOCAL_PATH")
|
59
|
+
git.destroy
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe Swat::Script do
|
4
|
+
context "with valid parameters" do
|
5
|
+
let(:params) {
|
6
|
+
Swat::Parameters.new(%w(dryrun test arg1 arg2))
|
7
|
+
}
|
8
|
+
|
9
|
+
it "can be created" do
|
10
|
+
script = Swat::Script.new(params)
|
11
|
+
expect(script).not_to be_nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it "finds the test script" do
|
15
|
+
script = Swat::Script.new(params)
|
16
|
+
script.run
|
17
|
+
end
|
18
|
+
|
19
|
+
it "fils to find a valid script with a descriptive message" do
|
20
|
+
script = Swat::Script.new(params, "scr")
|
21
|
+
expect { script.run }.to raise_error(/Could not find command test in /)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "cog_cmd/swat/strike"
|
3
|
+
|
4
|
+
describe CogCmd::Swat::Strike do
|
5
|
+
before do allow(STDIN).to receive(:tty?) { true } end
|
6
|
+
before do
|
7
|
+
ENV["RAILS_RUNNER_COMMAND"] = "./spec/helpers/rails_stub.rb lib/swat_run.rb"
|
8
|
+
end
|
9
|
+
after do ENV.delete("RAILS_RUNNER_COMMAND") end
|
10
|
+
|
11
|
+
it "can be called" do
|
12
|
+
command = CogCmd::Swat::Strike.new
|
13
|
+
with_environment(args: ["test success success"]) do
|
14
|
+
command.run_command
|
15
|
+
end
|
16
|
+
expect(command.response.content)
|
17
|
+
.to eq("{\"execution_mode\":\"execute\",\"prepare\":{\"successful\":true," \
|
18
|
+
"\"output\":\"preparation is fine so far\"},\"pre_check\":{\"successful\":true," \
|
19
|
+
"\"output\":\"all is gut\"},\"execute\":{\"successful\":true," \
|
20
|
+
"\"output\":\"Context so far is {:prepared=>\\\"done\\\", :checks=>\\\"done\\\"}\"}}")
|
21
|
+
end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gitlab-swat
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pablo Carranza
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-04-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: cog-rb
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.5'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.42'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.42'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.13'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.13'
|
69
|
+
description: |
|
70
|
+
A Successful Deployment Ends Peacefully With No Bullets Fired.
|
71
|
+
If That’s Simply Not Possible, SWAT Uses Special Weapons and Tactics to Keep the Public Safe
|
72
|
+
|
73
|
+
GitLab-Swat allows admins to quickly deploy scripts that can be remotely executed through a rails console
|
74
|
+
|
75
|
+
Allowing fast action by using an external git repository as the scripts source, but keeping safety high by
|
76
|
+
enforcing a prepare-pre check-execute model that allows execution break at any stage if things are not going
|
77
|
+
as expected
|
78
|
+
email: pablo@gitlab.com
|
79
|
+
executables: []
|
80
|
+
extensions: []
|
81
|
+
extra_rdoc_files: []
|
82
|
+
files:
|
83
|
+
- ".gitlab-ci.yml"
|
84
|
+
- ".rubocop.yml"
|
85
|
+
- Gemfile
|
86
|
+
- Gemfile.lock
|
87
|
+
- README.md
|
88
|
+
- cog-command
|
89
|
+
- config.yaml
|
90
|
+
- gitlab-swat.gemspec
|
91
|
+
- lib/cog_cmd/swat/dryrun.rb
|
92
|
+
- lib/cog_cmd/swat/reload.rb
|
93
|
+
- lib/cog_cmd/swat/strike.rb
|
94
|
+
- lib/rails_loader.rb
|
95
|
+
- lib/swat.rb
|
96
|
+
- lib/swat_git.rb
|
97
|
+
- lib/swat_run.rb
|
98
|
+
- scripts/invalid.rb
|
99
|
+
- scripts/test.rb
|
100
|
+
- spec/helpers/fail_stub.sh
|
101
|
+
- spec/helpers/rails_stub.rb
|
102
|
+
- spec/rails_loader_spec.rb
|
103
|
+
- spec/spec_helper.rb
|
104
|
+
- spec/swat_command_execution_spec.rb
|
105
|
+
- spec/swat_command_spec.rb
|
106
|
+
- spec/swat_config_spec.rb
|
107
|
+
- spec/swat_dryrun_spec.rb
|
108
|
+
- spec/swat_git_spec.rb
|
109
|
+
- spec/swat_parameters_spec.rb
|
110
|
+
- spec/swat_reload_spec.rb
|
111
|
+
- spec/swat_script_spec.rb
|
112
|
+
- spec/swat_strike_spec.rb
|
113
|
+
homepage:
|
114
|
+
licenses:
|
115
|
+
- MIT
|
116
|
+
metadata: {}
|
117
|
+
post_install_message:
|
118
|
+
rdoc_options: []
|
119
|
+
require_paths:
|
120
|
+
- lib
|
121
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
requirements: []
|
132
|
+
rubyforge_project:
|
133
|
+
rubygems_version: 2.5.2
|
134
|
+
signing_key:
|
135
|
+
specification_version: 4
|
136
|
+
summary: ChatOps Cog Bundle that enables admins to remotely run predefined scripts
|
137
|
+
in a rails console
|
138
|
+
test_files:
|
139
|
+
- spec/helpers/fail_stub.sh
|
140
|
+
- spec/helpers/rails_stub.rb
|
141
|
+
- spec/rails_loader_spec.rb
|
142
|
+
- spec/spec_helper.rb
|
143
|
+
- spec/swat_command_execution_spec.rb
|
144
|
+
- spec/swat_command_spec.rb
|
145
|
+
- spec/swat_config_spec.rb
|
146
|
+
- spec/swat_dryrun_spec.rb
|
147
|
+
- spec/swat_git_spec.rb
|
148
|
+
- spec/swat_parameters_spec.rb
|
149
|
+
- spec/swat_reload_spec.rb
|
150
|
+
- spec/swat_script_spec.rb
|
151
|
+
- spec/swat_strike_spec.rb
|