vscripts 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|