vscripts 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +24 -0
- data/Gemfile +2 -0
- data/LICENSE +14 -0
- data/README.md +65 -0
- data/Rakefile +19 -0
- data/VERSION +1 -0
- data/bin/vscripts +4 -0
- data/lib/vscripts.rb +12 -0
- data/lib/vscripts/aws.rb +10 -0
- data/lib/vscripts/aws/ec2.rb +69 -0
- data/lib/vscripts/aws/metadata.rb +50 -0
- data/lib/vscripts/command.rb +14 -0
- data/lib/vscripts/command_line.rb +72 -0
- data/lib/vscripts/commands/tags2facts.rb +84 -0
- data/lib/vscripts/util/local_system.rb +91 -0
- data/lib/vscripts/version.rb +6 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/vscripts/aws/ec2_spec.rb +81 -0
- data/spec/vscripts/aws/metadata_spec.rb +48 -0
- data/spec/vscripts/aws_spec.rb +7 -0
- data/spec/vscripts/command_line_spec.rb +34 -0
- data/spec/vscripts/command_spec.rb +9 -0
- data/spec/vscripts/commands/tags2facts_spec.rb +34 -0
- data/spec/vscripts/util/local_system_spec.rb +80 -0
- data/spec/vscripts/version_spec.rb +7 -0
- data/spec/vscripts_spec.rb +4 -0
- data/tasks/deploy.rake +95 -0
- data/tasks/lint.rake +22 -0
- data/vscripts.gemspec +35 -0
- metadata +270 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'yaml'
|
3
|
+
require 'English'
|
4
|
+
|
5
|
+
module VScripts
|
6
|
+
module Util
|
7
|
+
# Local system functions library
|
8
|
+
module LocalSystem
|
9
|
+
# Hosts file path
|
10
|
+
def hosts_path
|
11
|
+
'/etc/hosts'
|
12
|
+
end
|
13
|
+
|
14
|
+
# Hostname file path
|
15
|
+
def hostname_path
|
16
|
+
'/etc/hostname'
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the current fully qualified domain
|
20
|
+
def local_fqdn
|
21
|
+
`hostname -f`.strip
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the local host name
|
25
|
+
def local_host_name
|
26
|
+
`hostname`.strip
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns the local domain name
|
30
|
+
def local_domain_name
|
31
|
+
`dnsdomainname`.strip
|
32
|
+
end
|
33
|
+
|
34
|
+
# Tries to get the reverse dns
|
35
|
+
def external_dns
|
36
|
+
ext_ip = `wget -q -O - checkip.dyndns.org \
|
37
|
+
| sed -e 's/[^[:digit:]|.]//g'`
|
38
|
+
`dig +short -x #{ext_ip}`.strip
|
39
|
+
rescue
|
40
|
+
false
|
41
|
+
end
|
42
|
+
|
43
|
+
# Creates the directory for the specified file
|
44
|
+
def ensure_file_dir(file)
|
45
|
+
path = File.dirname(file)
|
46
|
+
`mkdir -p #{path}`
|
47
|
+
end
|
48
|
+
|
49
|
+
# Writes to file
|
50
|
+
def write_file(file, body)
|
51
|
+
ensure_file_dir(file)
|
52
|
+
File.open(file, 'w') do |newfile|
|
53
|
+
newfile.write(body)
|
54
|
+
end
|
55
|
+
rescue Errno::EACCES
|
56
|
+
puts "FATAL: You need to be root in order to write to #{file}"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Ensures the specified file has the specified content
|
60
|
+
def ensure_file_content(file, body)
|
61
|
+
write = write_file(file, body)
|
62
|
+
read = IO.read(file)
|
63
|
+
read == body || write
|
64
|
+
rescue
|
65
|
+
write
|
66
|
+
end
|
67
|
+
|
68
|
+
# Gets system checks from environment variables
|
69
|
+
def checks
|
70
|
+
config['SystemChecks']
|
71
|
+
rescue
|
72
|
+
{}
|
73
|
+
end
|
74
|
+
|
75
|
+
# Runs each command specified and returns the name and exit status
|
76
|
+
def process_checks
|
77
|
+
codes = {}
|
78
|
+
checks.each do |name, command|
|
79
|
+
system("#{command} > /dev/null 2>&1")
|
80
|
+
codes[name] = $CHILD_STATUS.exitstatus
|
81
|
+
end
|
82
|
+
codes
|
83
|
+
end
|
84
|
+
|
85
|
+
# Extract the codes for each check
|
86
|
+
def status_codes
|
87
|
+
process_checks.values.compact
|
88
|
+
end
|
89
|
+
end # module LocalSystem
|
90
|
+
end # module Util
|
91
|
+
end # module VScripts
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'vscripts/aws/ec2'
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
describe VScripts::AWS::EC2 do
|
6
|
+
before :each do
|
7
|
+
::AWS.config({
|
8
|
+
:access_key_id => '1234',
|
9
|
+
:secret_access_key => '5678',
|
10
|
+
:region => 'us-east-1',
|
11
|
+
:logger => nil,
|
12
|
+
:stub_requests => true
|
13
|
+
})
|
14
|
+
|
15
|
+
@tags = {'Name' => 'test'}
|
16
|
+
|
17
|
+
@inst1 = OpenStruct.new(id: '1111', status: :running)
|
18
|
+
@inst2 = OpenStruct.new(id: '2222', status: :running, tags: @tags)
|
19
|
+
@inst3 = OpenStruct.new(id: '3333', status: :terminated)
|
20
|
+
|
21
|
+
@ec2 = VScripts::AWS::EC2
|
22
|
+
@ec2.any_instance.stub(:check_instance) { true }
|
23
|
+
@ec2.any_instance.stub(:instance_id) { @inst1.id }
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#instance' do
|
27
|
+
it 'returns AWS::EC2' do
|
28
|
+
expect(subject.ec2).to be_an_instance_of ::AWS::EC2
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#new' do
|
33
|
+
it 'returns AWS::EC2::Instance' do
|
34
|
+
expect(subject.instance).to be_an_instance_of ::AWS::EC2::Instance
|
35
|
+
expect(subject.instance.id).to eq(@inst1.id)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#all_tags' do
|
40
|
+
it 'returns AWS::EC2::ResourceTagCollection' do
|
41
|
+
expect(subject.all_tags)
|
42
|
+
.to be_an_instance_of ::AWS::EC2::ResourceTagCollection
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#tags_without' do
|
47
|
+
it 'returns a Hash' do
|
48
|
+
expect(subject.tags_without)
|
49
|
+
.to be_an_instance_of ::AWS::EC2::ResourceTagCollection
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#name' do
|
54
|
+
it 'returns Hash value' do
|
55
|
+
subject.stub(:all_tags_hash) {@tags}
|
56
|
+
expect(subject.name).to eq(@tags['Name'])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#named_instances' do
|
61
|
+
it 'returns AWS::EC2::InstanceCollection' do
|
62
|
+
expect(subject.named_instances)
|
63
|
+
.to be_an_instance_of ::AWS::EC2::InstanceCollection
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#functional_instances' do
|
68
|
+
it 'returns an Array' do
|
69
|
+
subject.stub(:named_instances) { [@inst1, @inst2, @inst3] }
|
70
|
+
expect(subject.functional_instances).to be_an Array
|
71
|
+
expect(subject.functional_instances).to include(@inst1, @inst2)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#similar_instances' do
|
76
|
+
it 'returns an Array' do
|
77
|
+
subject.stub(:functional_instances) { [@inst1, @inst2] }
|
78
|
+
expect(subject.similar_instances).to include(@tags['Name'])
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'vscripts/aws/metadata'
|
3
|
+
|
4
|
+
describe VScripts::AWS::Metadata do
|
5
|
+
before(:each) do
|
6
|
+
@dummy = DummyClass.new
|
7
|
+
@dummy.extend VScripts::AWS::Metadata
|
8
|
+
@dummy.stub_chain(:open, :read).and_return('Remote server response')
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#metadata_url' do
|
12
|
+
it 'returns metadata url string' do
|
13
|
+
expect(@dummy.metadata_url).to be_a String
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#zone' do
|
18
|
+
it 'returns zone string' do
|
19
|
+
expect(@dummy.zone).to eq('Remote server response')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#region' do
|
24
|
+
it 'returns region string' do
|
25
|
+
@dummy.stub(:zone).and_return('us-test-1a')
|
26
|
+
expect(@dummy.region).to eq('us-test-1')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#instance_id' do
|
31
|
+
it 'returns instance id string' do
|
32
|
+
expect(@dummy.instance_id).to eq('Remote server response')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#public_hostname' do
|
37
|
+
it 'returns public hostname string' do
|
38
|
+
expect(@dummy.public_hostname).to eq('Remote server response')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#ec2_instance?' do
|
43
|
+
it 'returns true' do
|
44
|
+
Net::HTTP.stub(:get_response).and_return(true)
|
45
|
+
expect(@dummy.ec2_instance?).to be_true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'vscripts/command_line'
|
2
|
+
|
3
|
+
describe VScripts::CommandLine do
|
4
|
+
before :each do
|
5
|
+
@cmd = VScripts::Command.list.first.to_s
|
6
|
+
@cli = VScripts::CommandLine.new([@cmd, 'extra_args'])
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#new' do
|
10
|
+
it 'returns the command line arguments' do
|
11
|
+
expect(@cli).to be_an_instance_of VScripts::CommandLine
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#global' do
|
16
|
+
it 'returns the global options as a Hash' do
|
17
|
+
expect(@cli.global).to be_an_instance_of Hash
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#command' do
|
22
|
+
it 'returns the command name' do
|
23
|
+
expect(@cli.command).to be_a Symbol
|
24
|
+
expect(@cli.command).to eql @cmd.capitalize.to_sym
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#extra' do
|
29
|
+
it 'returns the rest of the arguments as an Array' do
|
30
|
+
expect(@cli.extra).to be_an_instance_of Array
|
31
|
+
expect(@cli.extra).to eql ['extra_args']
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'vscripts/commands/tags2facts'
|
2
|
+
|
3
|
+
describe VScripts::Commands::Tags2facts do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@tags2facts = VScripts::Commands::Tags2facts.new(['extra_args'])
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#new' do
|
10
|
+
it 'returns cli arguments and loads EC2 SDK' do
|
11
|
+
expect(@tags2facts.cli).to be_a Hash
|
12
|
+
expect(@tags2facts.ec2).to be_an_instance_of VScripts::AWS::EC2
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#parser' do
|
17
|
+
it 'returns parser' do
|
18
|
+
expect(@tags2facts.parser).to be_an_instance_of Trollop::Parser
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#exclude_list' do
|
23
|
+
it 'returns exclude list' do
|
24
|
+
expect(@tags2facts.exclude_list).to eq(['Name', 'Domain'])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#tags_json' do
|
29
|
+
it 'returns JSON formatted string' do
|
30
|
+
@tags2facts.stub_chain(:tags_hash, :to_h) {{ key: 'value' }}
|
31
|
+
expect(@tags2facts.tags_json).to eq("{\n \"key\": \"value\"\n}")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'vscripts/util/local_system'
|
4
|
+
|
5
|
+
describe VScripts::Util::LocalSystem do
|
6
|
+
before(:all) do
|
7
|
+
@dummy = DummyClass.new
|
8
|
+
@dummy.extend VScripts::Util::LocalSystem
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#hosts_path' do
|
12
|
+
it 'returns the path to hosts file' do
|
13
|
+
expect(@dummy.hosts_path).to be_a String
|
14
|
+
end
|
15
|
+
end
|
16
|
+
describe '#hostname_path' do
|
17
|
+
it 'returns the path to the hostname file' do
|
18
|
+
expect(@dummy.hostname_path).to be_a String
|
19
|
+
end
|
20
|
+
end
|
21
|
+
describe '#local_fqdn' do
|
22
|
+
it 'returns the local FQDN' do
|
23
|
+
expect(@dummy.local_fqdn).to be_a String
|
24
|
+
end
|
25
|
+
end
|
26
|
+
describe '#local_host_name' do
|
27
|
+
it 'returns the local host name' do
|
28
|
+
expect(@dummy.local_host_name).to be_a String
|
29
|
+
end
|
30
|
+
end
|
31
|
+
describe '#local_domain_name' do
|
32
|
+
it 'returns the local domain name' do
|
33
|
+
expect(@dummy.local_domain_name).to be_a String
|
34
|
+
end
|
35
|
+
end
|
36
|
+
describe '#ensure_file_dir' do
|
37
|
+
it 'create a directory for the specified files' do
|
38
|
+
test_dir = Dir::Tmpname.make_tmpname '/tmp/vscripts', nil
|
39
|
+
test_file = 'test_file'
|
40
|
+
@dummy.ensure_file_dir("#{test_dir}/#{test_file}")
|
41
|
+
expect(Dir.exists?(test_dir)).to be_true
|
42
|
+
`rm -r #{test_dir}`
|
43
|
+
end
|
44
|
+
end
|
45
|
+
describe '#write_file' do
|
46
|
+
it 'should write to a file' do
|
47
|
+
test_file = Dir::Tmpname.make_tmpname '/tmp/vscripts', nil
|
48
|
+
@dummy.write_file(test_file, 'test')
|
49
|
+
expect(IO.read(test_file)).to eq('test')
|
50
|
+
`rm #{test_file}`
|
51
|
+
end
|
52
|
+
end
|
53
|
+
describe '#ensure_file_content' do
|
54
|
+
it 'should ensure content of file' do
|
55
|
+
test_dir = Dir::Tmpname.make_tmpname '/tmp/vscripts', nil
|
56
|
+
test_file = 'test_file'
|
57
|
+
test = "#{test_dir}/#{test_file}"
|
58
|
+
@dummy.ensure_file_content(test, 'test')
|
59
|
+
expect(IO.read(test)).to eq('test')
|
60
|
+
`rm -r #{test_dir}`
|
61
|
+
end
|
62
|
+
end
|
63
|
+
describe '#checks' do
|
64
|
+
it 'returns an array of checks' do
|
65
|
+
expect(@dummy.checks).to be_a Hash
|
66
|
+
end
|
67
|
+
end
|
68
|
+
describe '#process_checks' do
|
69
|
+
it 'should execute system command and return exit code' do
|
70
|
+
@dummy.stub(:checks) {{'test command' => 'exit 5'}}
|
71
|
+
expect(@dummy.process_checks).to eq({"test command"=>5})
|
72
|
+
end
|
73
|
+
end
|
74
|
+
describe '#status_codes' do
|
75
|
+
it 'should return an Array of exit codes' do
|
76
|
+
@dummy.stub(:checks) {{'test command' => 'exit 5'}}
|
77
|
+
expect(@dummy.status_codes).to eq([5])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/tasks/deploy.rake
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
DEV_BRANCH = 'develop'
|
2
|
+
LOG_RANGE = "master...#{DEV_BRANCH}"
|
3
|
+
VERSION_FILE = 'VERSION'
|
4
|
+
CHANGELOG_FILE = 'CHANGELOG.md'
|
5
|
+
|
6
|
+
desc 'Automated deployment'
|
7
|
+
task :deploy do
|
8
|
+
check_branch
|
9
|
+
commit_changelog
|
10
|
+
merge_changes
|
11
|
+
push_release
|
12
|
+
package_and_push_gem
|
13
|
+
end
|
14
|
+
|
15
|
+
def check_branch
|
16
|
+
if ! `git ls-files --others --exclude-standard`.empty?
|
17
|
+
abort 'ERROR: There are untracked files!'
|
18
|
+
elsif ! system('git diff --quiet && git diff --cached --quiet')
|
19
|
+
abort 'ERROR: There are staged but uncommitted files!'
|
20
|
+
end
|
21
|
+
`git fetch`
|
22
|
+
end
|
23
|
+
|
24
|
+
def bump_version
|
25
|
+
@version = File.read(VERSION_FILE).strip
|
26
|
+
@version.gsub(/(\d+)\.(\d+)\.(\d+)/) {
|
27
|
+
@major, @minor, @patch = $1.to_i, $2.to_i, $3.to_i
|
28
|
+
}
|
29
|
+
@new_version = "#{@major}.#{@minor}.#{@patch + 1}"
|
30
|
+
File.write('VERSION', @new_version)
|
31
|
+
end
|
32
|
+
|
33
|
+
def changelog
|
34
|
+
File.read(CHANGELOG_FILE)
|
35
|
+
end
|
36
|
+
|
37
|
+
def changes
|
38
|
+
@changes ||= `git log #{LOG_RANGE} --no-merges`
|
39
|
+
end
|
40
|
+
|
41
|
+
def pretty_changes
|
42
|
+
`git log #{LOG_RANGE} --reverse --no-merges --pretty=format:' * %s ([%cn - %h](https://github.com/vghn/vscripts/commit/%H))'`
|
43
|
+
end
|
44
|
+
|
45
|
+
def update_changelog
|
46
|
+
bump_version
|
47
|
+
puts 'Writing new changelog'
|
48
|
+
File.write(
|
49
|
+
CHANGELOG_FILE,
|
50
|
+
"Version #{@new_version}\n---\n" + pretty_changes + "\n\n" + changelog
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
def commit_changelog
|
55
|
+
if changes.lines.count > 0
|
56
|
+
update_changelog
|
57
|
+
puts 'Committing version and changelog'
|
58
|
+
`git commit -am \"Bump version.\"`
|
59
|
+
else
|
60
|
+
puts 'No changes committed. Exiting.'
|
61
|
+
exit 0
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def branch
|
66
|
+
@branch ||= `git branch`.match(/^\* (\S+)/)[1]
|
67
|
+
end
|
68
|
+
|
69
|
+
def switch_to_branch(name)
|
70
|
+
`git checkout #{name}` unless branch == name
|
71
|
+
end
|
72
|
+
|
73
|
+
def merge
|
74
|
+
@merge ||= `git merge --no-ff #{DEV_BRANCH} -m \"v#{@new_version}\"`
|
75
|
+
end
|
76
|
+
|
77
|
+
def merge_changes
|
78
|
+
switch_to_branch('master')
|
79
|
+
puts "Merging \"#{DEV_BRANCH}\" branch"
|
80
|
+
abort 'ERROR: Conflicts found; Stopping!' if merge =~ /conflict/i
|
81
|
+
end
|
82
|
+
|
83
|
+
def push_release
|
84
|
+
puts "Tagging v#{@new_version} release."
|
85
|
+
`git tag -a v#{@new_version} -m 'Version #{@new_version}'`
|
86
|
+
puts 'Pushing all branches and tags'
|
87
|
+
`git push --all --follow-tags`
|
88
|
+
end
|
89
|
+
|
90
|
+
def package_and_push_gem
|
91
|
+
puts 'Building gem'
|
92
|
+
Rake::Task['build'].invoke
|
93
|
+
puts 'Pushing gem to Rubygems'
|
94
|
+
`gem push pkg/vscripts-#{@new_version}.gem`
|
95
|
+
end
|