quest 1.0.7
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/LICENSE +15 -0
- data/README.md +99 -0
- data/bin/ballad +10 -0
- data/bin/quest +107 -0
- data/bin/questctl +38 -0
- data/bin/test_all_quests +21 -0
- data/lib/quest.rb +6 -0
- data/lib/quest/api.rb +83 -0
- data/lib/quest/colorization.rb +26 -0
- data/lib/quest/logger.rb +5 -0
- data/lib/quest/messenger.rb +77 -0
- data/lib/quest/quest_watcher.rb +31 -0
- data/lib/quest/rspec_runner.rb +37 -0
- metadata +202 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f44a8be44d1dabdcf3496a45686269255c4af45c
|
4
|
+
data.tar.gz: e3c77ca330b15ba15d9b20d84f82325c6115f901
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 293f9cd835a9d13c1efc8b162ea15bdbe20a1e9bcb6a914f3a1aecb4934b79cdab8275ef5a3ce4d7bf1920a2f718c9097d6332aee9fa4a6f81f4e200df1d587a
|
7
|
+
data.tar.gz: b353bfcbb12b6ccfc0071f13d2289c21f4aac540e6ddf06baff0d78130c8b6e803cfa7f16d44d6e3dad62385f28c04ed8b187affdf2446587cf0c146e093a234
|
data/LICENSE
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Copyright (C) 2015 Puppet Labs Inc
|
2
|
+
|
3
|
+
Puppet Labs can be contacted at: info@puppetlabs.com
|
4
|
+
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
you may not use this file except in compliance with the License.
|
7
|
+
You may obtain a copy of the License at
|
8
|
+
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
See the License for the specific language governing permissions and
|
15
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
## Quest
|
2
|
+
|
3
|
+
Quest-driven learning with RSpec and Serverspec.
|
4
|
+
|
5
|
+
### What it is
|
6
|
+
|
7
|
+
The quest gem provides a learner with live feedback as a learner progresses through
|
8
|
+
a series of configuration management tasks defined by RSpec tests.
|
9
|
+
It was designed to support the [Puppet Quest Guide](https://github.com/puppetlabs/puppet-quest-guide)
|
10
|
+
content on the [Puppet Learning VM](https://puppetlabs.com/download-learning-vm), but can be
|
11
|
+
used for any project with a compatible format.
|
12
|
+
|
13
|
+
### Installation
|
14
|
+
|
15
|
+
From source:
|
16
|
+
|
17
|
+
git clone https://github.com/puppetlabs/quest.git
|
18
|
+
cd quest
|
19
|
+
gem build quest.gemspec
|
20
|
+
gem install quest<VERSION>.gem
|
21
|
+
|
22
|
+
### Setup
|
23
|
+
|
24
|
+
This gem was designed to support the [Puppet Quest Guide](https://github.com/puppetlabs/puppet-quest-guide),
|
25
|
+
and works with any set of tests and metadata following the same pattern.
|
26
|
+
|
27
|
+
Any project you wish to use with this tool must contain a directory with the following contents:
|
28
|
+
|
29
|
+
An `index.json` file consisting with available quest names, the list of files to watch for each quest
|
30
|
+
and an optional setup command for that quest. These setup commands will be run with the directory
|
31
|
+
of your tests as the current working directory when a user begins the quest.
|
32
|
+
|
33
|
+
```
|
34
|
+
{
|
35
|
+
"welcome": {
|
36
|
+
"setup_command": "puppet apply ./manifests/welcome.pp"
|
37
|
+
}
|
38
|
+
}
|
39
|
+
```
|
40
|
+
|
41
|
+
A series of `*_spec.rb` files corresponding the the quest names specified in the `index.json`.
|
42
|
+
These files must contain valid Rspec tests that correspond to the tasks in that quest. For example,
|
43
|
+
`my_first_quest_spec.rb` might contain the following. (Note the callous abuse of the RSpec format to
|
44
|
+
coerce its output into something appropriate to the quest tool interface.)
|
45
|
+
|
46
|
+
```
|
47
|
+
describe "Task 1: do
|
48
|
+
it 'create a user named test' do
|
49
|
+
file('/etc/passwd').should contain "test"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
describe "Task 2: do
|
53
|
+
...
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
A `spec_helper` file with any required libraries, helper functions, or variables
|
58
|
+
needed by the tests.
|
59
|
+
|
60
|
+
```
|
61
|
+
require 'serverspec'
|
62
|
+
require 'pathname'
|
63
|
+
|
64
|
+
PROD_PATH = '/etc/puppetlabs/code/environments/production'
|
65
|
+
MODULE_PATH = PROD_PATH + "modules/"
|
66
|
+
```
|
67
|
+
|
68
|
+
### Use
|
69
|
+
|
70
|
+
The `questctl` command is used to start the quest service. Run it from the
|
71
|
+
task directory, or use the `--task_dir` flag.
|
72
|
+
|
73
|
+
```
|
74
|
+
/usr/local/bin/questctl start --task_dir /usr/src/puppet-quest-guide/tests
|
75
|
+
```
|
76
|
+
|
77
|
+
This works best when the service is managed by an init system, for example:
|
78
|
+
|
79
|
+
```
|
80
|
+
# /etc/systemd/system/quest.service
|
81
|
+
[Unit]
|
82
|
+
Description=Quest tool service
|
83
|
+
|
84
|
+
[Service]
|
85
|
+
ExecStart=/usr/local/bin/questctl start --task_dir /usr/src/puppet-quest-guide/tests
|
86
|
+
|
87
|
+
[Install]
|
88
|
+
WantedBy=multi-user.target
|
89
|
+
```
|
90
|
+
|
91
|
+
### How it works
|
92
|
+
|
93
|
+
The quest service runs a set of RSpec tests for the current test in a loop,
|
94
|
+
updating the quest status each time the test completes. The service provides
|
95
|
+
api endpoints on port 4567 that allow access to the RSpec output and a POST
|
96
|
+
endpoint to change the current quest.
|
97
|
+
|
98
|
+
The `quest` command is a wrapper for these API endpoints.
|
99
|
+
|
data/bin/ballad
ADDED
data/bin/quest
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'quest'
|
5
|
+
require 'gli'
|
6
|
+
require 'net/http'
|
7
|
+
require 'json'
|
8
|
+
rescue
|
9
|
+
require 'rubygems'
|
10
|
+
require 'quest'
|
11
|
+
require 'gli'
|
12
|
+
require 'json'
|
13
|
+
end
|
14
|
+
|
15
|
+
module GLIWrapper
|
16
|
+
include GLI::App
|
17
|
+
extend self
|
18
|
+
|
19
|
+
BASE_URI = URI('http://localhost:4567/')
|
20
|
+
OFFER_BAILOUT = false
|
21
|
+
|
22
|
+
def get_path(*path)
|
23
|
+
begin
|
24
|
+
Net::HTTP.get(URI.join(BASE_URI, *path))
|
25
|
+
rescue Errno::ECONNREFUSED => e
|
26
|
+
puts "You may need to restart the quest service: systemctl restart quest"
|
27
|
+
raise e
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def post_path(form_data, *path)
|
32
|
+
begin
|
33
|
+
Net::HTTP.post_form(URI.join(BASE_URI, *path), form_data)
|
34
|
+
rescue Errno::ECONNREFUSED => e
|
35
|
+
puts "You may need to restart the quest service: systemctl restart quest"
|
36
|
+
raise e
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def offer_bailout(message)
|
41
|
+
print "#{message} Continue? [Y/n]:"
|
42
|
+
raise "Cancelled" unless [ 'y', 'yes', ''].include? STDIN.gets.strip.downcase
|
43
|
+
end
|
44
|
+
|
45
|
+
def fancy_status
|
46
|
+
active_quest = get_path('active_quest')
|
47
|
+
output = "Quest: #{active_quest}\n"
|
48
|
+
examples = JSON.parse(get_path('status/', 'examples'))
|
49
|
+
examples.each do |example|
|
50
|
+
if example["status"] == "passed"
|
51
|
+
output << '√ '.green
|
52
|
+
else
|
53
|
+
output << 'X '.yellow
|
54
|
+
end
|
55
|
+
output << example["full_description"] + "\n"
|
56
|
+
end
|
57
|
+
output
|
58
|
+
end
|
59
|
+
|
60
|
+
def summary_status
|
61
|
+
active_quest = get_path('active_quest')
|
62
|
+
output = "Quest: #{active_quest} - Progress: "
|
63
|
+
summary = JSON.parse(get_path('status/', 'summary'))
|
64
|
+
complete_count = summary["example_count"].to_i - summary["failure_count"].to_i
|
65
|
+
output << "#{complete_count} of #{summary['example_count']} tasks."
|
66
|
+
end
|
67
|
+
|
68
|
+
program_desc 'Track the status of quests and tasks.'
|
69
|
+
|
70
|
+
desc 'Begin a quest'
|
71
|
+
arg :quest_name
|
72
|
+
arg_name 'quest_name'
|
73
|
+
command :begin do |c|
|
74
|
+
c.action do |global_options, options, args|
|
75
|
+
if args.length < 1
|
76
|
+
raise 'You must specify a quest name. Refer to the Quest Guide or use the "quest list" command.'
|
77
|
+
elsif not JSON.parse(get_path('quests')).include? args[0]
|
78
|
+
raise "#{args[0]} is not a valid quest name. Refer to the Quest Guide or use the \"quest list\" command."
|
79
|
+
elsif OFFER_BAILOUT and not get_path('status/', 'summary/', 'failure_count') == '0'
|
80
|
+
offer_bailout("The current quest is not complete. If you begin a new quest, your agent nodes will be reset. Your master node and Puppet code will not be affected.\n")
|
81
|
+
end
|
82
|
+
post_path({}, 'begin/', args[0])
|
83
|
+
puts "You have started the #{args[0]} quest."
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
desc 'List available quests'
|
88
|
+
command :list do |c|
|
89
|
+
c.action do |global_options, options, args|
|
90
|
+
puts JSON.parse(get_path('quests'))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
desc 'Show status of the current quest'
|
95
|
+
command :status do |c|
|
96
|
+
c.switch [:s], :desc => 'Show status in summary form.'
|
97
|
+
c.action do |global_options, options, args|
|
98
|
+
if options[:s]
|
99
|
+
puts summary_status
|
100
|
+
else
|
101
|
+
puts fancy_status
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
exit run(ARGV)
|
107
|
+
end
|
data/bin/questctl
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'quest'
|
4
|
+
require 'gli'
|
5
|
+
|
6
|
+
module GLIWrapper
|
7
|
+
include GLI::App
|
8
|
+
extend self
|
9
|
+
|
10
|
+
# Controllers for the quest service
|
11
|
+
# Move to questctl
|
12
|
+
|
13
|
+
program_desc 'Controller for quest tool services.'
|
14
|
+
|
15
|
+
desc 'Start the quest service'
|
16
|
+
command :start do |c|
|
17
|
+
c.flag [:q, :task_dir, 'task_dir'], :desc => 'Specify the task directory.'
|
18
|
+
c.action do |global_options, options, args|
|
19
|
+
messenger = Quest::Messenger.new( {'task_dir' => options[:task_dir]} )
|
20
|
+
api = Thread.new do
|
21
|
+
Quest::API.set :messenger, messenger
|
22
|
+
Quest::API.run!
|
23
|
+
end
|
24
|
+
watcher = Thread.new do
|
25
|
+
Quest::QuestWatcher.new(messenger).run!
|
26
|
+
end
|
27
|
+
Signal.trap("INT") do
|
28
|
+
puts "Exiting"
|
29
|
+
Thread.kill watcher
|
30
|
+
Thread.kill api
|
31
|
+
exit 130
|
32
|
+
end
|
33
|
+
watcher.join
|
34
|
+
api.join
|
35
|
+
end
|
36
|
+
end
|
37
|
+
exit run(ARGV)
|
38
|
+
end
|
data/bin/test_all_quests
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'quest'
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
BASE_URI = URI('http://localhost:4567/')
|
7
|
+
|
8
|
+
def get_path(*path)
|
9
|
+
Net::HTTP.get(URI.join(BASE_URI, *path))
|
10
|
+
end
|
11
|
+
|
12
|
+
def post_path(form_data, *path)
|
13
|
+
Net::HTTP.post_form(URI.join(BASE_URI, *path), form_data)
|
14
|
+
end
|
15
|
+
|
16
|
+
quests = JSON.parse(get_path('quests'))
|
17
|
+
|
18
|
+
quests.each do |q|
|
19
|
+
completion_script = File.join('/usr/src/puppet-quest-guide/tests', 'test_tests', "#{q}.sh")
|
20
|
+
`#{completion_script}`
|
21
|
+
end
|
data/lib/quest.rb
ADDED
data/lib/quest/api.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
|
3
|
+
module Quest
|
4
|
+
|
5
|
+
class API < Sinatra::Base
|
6
|
+
|
7
|
+
# Set defaults
|
8
|
+
before { content_type 'application/json' }
|
9
|
+
not_found { JSON.dump("error" => "Not Found") }
|
10
|
+
|
11
|
+
set :traps, false
|
12
|
+
|
13
|
+
helpers do
|
14
|
+
def messenger
|
15
|
+
settings.messenger
|
16
|
+
end
|
17
|
+
def quest_status
|
18
|
+
settings.messenger.quest_status
|
19
|
+
end
|
20
|
+
def active_quest
|
21
|
+
settings.messenger.active_quest
|
22
|
+
end
|
23
|
+
def active_quest_status
|
24
|
+
settings.messenger.quest_status[settings.messenger.active_quest]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
get '/status' do
|
29
|
+
active_quest_status.to_json
|
30
|
+
end
|
31
|
+
|
32
|
+
get '/status/examples' do
|
33
|
+
active_quest_status['examples'].to_json
|
34
|
+
end
|
35
|
+
|
36
|
+
get '/status/examples/:number' do
|
37
|
+
active_quest_status['examples'][params[:number].to_i - 1].to_json
|
38
|
+
end
|
39
|
+
|
40
|
+
get '/status/examples/count' do
|
41
|
+
content_type 'text/html'
|
42
|
+
active_quest_status['examples'].size
|
43
|
+
end
|
44
|
+
|
45
|
+
get '/status/examples/:number/description' do
|
46
|
+
active_quest_status['examples'][params[:number].to_i - 1]['description'].to_json
|
47
|
+
end
|
48
|
+
|
49
|
+
get '/status/examples/:number/file_path' do
|
50
|
+
active_quest_status['examples'][params[:number].to_i - 1]['file_path'].to_json
|
51
|
+
end
|
52
|
+
|
53
|
+
get '/status/examples/:number/status' do
|
54
|
+
active_quest_status['examples'][params[:number].to_i - 1]['status'].to_json
|
55
|
+
end
|
56
|
+
|
57
|
+
get '/status/examples/:number/run_time' do
|
58
|
+
active_quest_status['examples'][params[:number].to_i - 1]['run_time'].to_json
|
59
|
+
end
|
60
|
+
|
61
|
+
get '/status/summary' do
|
62
|
+
active_quest_status['summary'].to_json
|
63
|
+
end
|
64
|
+
|
65
|
+
get '/status/summary/failure_count' do
|
66
|
+
active_quest_status['summary']['failure_count'].to_json
|
67
|
+
end
|
68
|
+
|
69
|
+
get '/active_quest' do
|
70
|
+
content_type 'text/html'
|
71
|
+
active_quest
|
72
|
+
end
|
73
|
+
|
74
|
+
get '/quests' do
|
75
|
+
messenger.quests.to_json
|
76
|
+
end
|
77
|
+
|
78
|
+
post '/begin/:quest' do
|
79
|
+
messenger.begin_quest(params[:quest])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class String
|
2
|
+
def black; "\e[30m#{self}\e[0m" end
|
3
|
+
def red; "\e[31m#{self}\e[0m" end
|
4
|
+
def green; "\e[32m#{self}\e[0m" end
|
5
|
+
def brown; "\e[33m#{self}\e[0m" end
|
6
|
+
def yellow; "\e[1;33m#{self}\e[0m" end
|
7
|
+
def blue; "\e[34m#{self}\e[0m" end
|
8
|
+
def magenta; "\e[35m#{self}\e[0m" end
|
9
|
+
def cyan; "\e[36m#{self}\e[0m" end
|
10
|
+
def gray; "\e[37m#{self}\e[0m" end
|
11
|
+
|
12
|
+
def bg_black; "\e[40m#{self}\e[0m" end
|
13
|
+
def bg_red; "\e[41m#{self}\e[0m" end
|
14
|
+
def bg_green; "\e[42m#{self}\e[0m" end
|
15
|
+
def bg_brown; "\e[43m#{self}\e[0m" end
|
16
|
+
def bg_blue; "\e[44m#{self}\e[0m" end
|
17
|
+
def bg_magenta; "\e[45m#{self}\e[0m" end
|
18
|
+
def bg_cyan; "\e[46m#{self}\e[0m" end
|
19
|
+
def bg_gray; "\e[47m#{self}\e[0m" end
|
20
|
+
|
21
|
+
def bold; "\e[1m#{self}\e[21m" end
|
22
|
+
def italic; "\e[3m#{self}\e[23m" end
|
23
|
+
def underline; "\e[4m#{self}\e[24m" end
|
24
|
+
def blink; "\e[5m#{self}\e[25m" end
|
25
|
+
def reverse_color; "\e[7m#{self}\e[27m" end
|
26
|
+
end
|
data/lib/quest/logger.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Quest
|
6
|
+
|
7
|
+
# Shared state and methods for reading from the content directory
|
8
|
+
class Messenger
|
9
|
+
|
10
|
+
require 'fileutils'
|
11
|
+
|
12
|
+
attr_reader :quest_index_file
|
13
|
+
attr_reader :spec_helper
|
14
|
+
attr_accessor :active_quest
|
15
|
+
attr_accessor :quest_status
|
16
|
+
|
17
|
+
def initialize(config = {})
|
18
|
+
@task_dir = config['task_dir'] || Dir.pwd
|
19
|
+
@quest_index_file = File.join(@task_dir, 'index.json')
|
20
|
+
validate_task_dir
|
21
|
+
@spec_helper = File.join(@task_dir, 'spec_helper.rb')
|
22
|
+
@quest_status = {}
|
23
|
+
@active_quest = quests.first
|
24
|
+
run_setup_command(@active_quest)
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_raw_status(quest, raw_status_hash)
|
28
|
+
@quest_status[quest] = raw_status_hash
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate_task_dir
|
32
|
+
begin
|
33
|
+
JSON.parse(File.read(@quest_index_file))
|
34
|
+
rescue Errno::ENOENT
|
35
|
+
puts "No valid quest index.json file found at #{@quest_index_file}"
|
36
|
+
exit 1
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def run_setup_command(quest)
|
41
|
+
if setup_command
|
42
|
+
begin
|
43
|
+
puts "Setting up the #{active_quest} quest..."
|
44
|
+
Dir.chdir(@task_dir){
|
45
|
+
setup_io = IO.popen(setup_command) do |io|
|
46
|
+
io.each do |line|
|
47
|
+
puts line
|
48
|
+
end
|
49
|
+
end
|
50
|
+
}
|
51
|
+
rescue
|
52
|
+
puts "Setup for #{active_quest} failed"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def spec_path(quest)
|
58
|
+
File.join(@task_dir, "#{quest}_spec.rb")
|
59
|
+
end
|
60
|
+
|
61
|
+
def quests
|
62
|
+
JSON.parse(File.read(@quest_index_file)).keys
|
63
|
+
end
|
64
|
+
|
65
|
+
def setup_command
|
66
|
+
JSON.parse(File.read(@quest_index_file))[@active_quest]["setup_command"]
|
67
|
+
end
|
68
|
+
|
69
|
+
def begin_quest(quest)
|
70
|
+
if quests.include?(quest)
|
71
|
+
@active_quest = quest
|
72
|
+
run_setup_command(quest)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'timers'
|
2
|
+
|
3
|
+
module Quest
|
4
|
+
class QuestWatcher
|
5
|
+
|
6
|
+
def initialize(messenger)
|
7
|
+
@messenger = messenger
|
8
|
+
@timers = Timers::Group.new
|
9
|
+
@lock = Mutex.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def start_timer
|
13
|
+
task_timer = @timers.now_and_every(5) do
|
14
|
+
unless @lock.locked?
|
15
|
+
@lock.lock
|
16
|
+
active_quest = @messenger.active_quest
|
17
|
+
runner = Quest::RSpecRunner.new(@messenger.spec_path(active_quest), @messenger.spec_helper)
|
18
|
+
@messenger.set_raw_status(active_quest, runner.result)
|
19
|
+
@lock.unlock
|
20
|
+
end
|
21
|
+
end
|
22
|
+
loop {@timers.wait}
|
23
|
+
end
|
24
|
+
|
25
|
+
# This is the main function to set up and run the watcher process
|
26
|
+
def run!
|
27
|
+
start_timer
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Quest
|
2
|
+
class RSpecRunner
|
3
|
+
# Based loosely on https://github.com/guard/guard-rspec/blob/master/lib/guard/rspec/rspec_process.rb
|
4
|
+
|
5
|
+
attr_reader :result
|
6
|
+
|
7
|
+
def initialize(spec_file, spec_helper)
|
8
|
+
@spec_file = spec_file
|
9
|
+
@spec_helper = spec_helper
|
10
|
+
Tempfile.open('quest-rspec-runner') do |tmp_file|
|
11
|
+
@exit_code = run_spec(@spec_file, @spec_helper, tmp_file.path)
|
12
|
+
@result = read_result(tmp_file.path)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def run_spec(spec_file, spec_helper, tmp_file)
|
17
|
+
home = ENV["HOME"] || '/tmp'
|
18
|
+
command = "HOME=#{home} rspec #{spec_file} -r #{spec_helper} -f json -o #{tmp_file}"
|
19
|
+
begin
|
20
|
+
pid = Kernel.spawn(command)
|
21
|
+
result = ::Process.wait2(pid)
|
22
|
+
result.last.exitstatus
|
23
|
+
rescue Errno::ENOENT => ex
|
24
|
+
raise Failure, "Failed: #{command} (#{ex})"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def read_result(tmp_file)
|
29
|
+
begin
|
30
|
+
JSON.parse(File.read(tmp_file))
|
31
|
+
rescue Errno::ENOENT => ex
|
32
|
+
raise Failure, "Cannot open status file #{tmp_file}, (#{ex})"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: quest
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.7
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kevin Henner
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-11-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: serverspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.36'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.36'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: json
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.7'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.7'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rack
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.6'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.6'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: gli
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.12'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.12'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: mono_logger
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.1'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.1'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: sinatra
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: highline
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: net-ssh
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: timers
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
description: quest uses serverspec to track completion of configuration management
|
154
|
+
related learning tasks.
|
155
|
+
email:
|
156
|
+
- kevin@puppetlabs.com
|
157
|
+
executables:
|
158
|
+
- quest
|
159
|
+
- questctl
|
160
|
+
- test_all_quests
|
161
|
+
extensions: []
|
162
|
+
extra_rdoc_files: []
|
163
|
+
files:
|
164
|
+
- LICENSE
|
165
|
+
- README.md
|
166
|
+
- bin/ballad
|
167
|
+
- bin/quest
|
168
|
+
- bin/questctl
|
169
|
+
- bin/test_all_quests
|
170
|
+
- lib/quest.rb
|
171
|
+
- lib/quest/api.rb
|
172
|
+
- lib/quest/colorization.rb
|
173
|
+
- lib/quest/logger.rb
|
174
|
+
- lib/quest/messenger.rb
|
175
|
+
- lib/quest/quest_watcher.rb
|
176
|
+
- lib/quest/rspec_runner.rb
|
177
|
+
homepage: http://github.com/puppetlabs/quest
|
178
|
+
licenses:
|
179
|
+
- Apache 2.0
|
180
|
+
metadata: {}
|
181
|
+
post_install_message:
|
182
|
+
rdoc_options: []
|
183
|
+
require_paths:
|
184
|
+
- lib
|
185
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
186
|
+
requirements:
|
187
|
+
- - '>='
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '0'
|
190
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - '>='
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
195
|
+
requirements: []
|
196
|
+
rubyforge_project:
|
197
|
+
rubygems_version: 2.4.8
|
198
|
+
signing_key:
|
199
|
+
specification_version: 4
|
200
|
+
summary: Track completion of configuration management tasks.
|
201
|
+
test_files: []
|
202
|
+
has_rdoc:
|