slugforge 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +316 -0
- data/bin/slugforge +9 -0
- data/lib/slugforge.rb +19 -0
- data/lib/slugforge/build.rb +4 -0
- data/lib/slugforge/build/build_project.rb +31 -0
- data/lib/slugforge/build/export_upstart.rb +85 -0
- data/lib/slugforge/build/package.rb +63 -0
- data/lib/slugforge/cli.rb +125 -0
- data/lib/slugforge/commands.rb +130 -0
- data/lib/slugforge/commands/build.rb +20 -0
- data/lib/slugforge/commands/config.rb +24 -0
- data/lib/slugforge/commands/deploy.rb +383 -0
- data/lib/slugforge/commands/project.rb +21 -0
- data/lib/slugforge/commands/tag.rb +148 -0
- data/lib/slugforge/commands/wrangler.rb +142 -0
- data/lib/slugforge/configuration.rb +125 -0
- data/lib/slugforge/helper.rb +186 -0
- data/lib/slugforge/helper/build.rb +46 -0
- data/lib/slugforge/helper/config.rb +37 -0
- data/lib/slugforge/helper/enumerable.rb +46 -0
- data/lib/slugforge/helper/fog.rb +90 -0
- data/lib/slugforge/helper/git.rb +89 -0
- data/lib/slugforge/helper/path.rb +76 -0
- data/lib/slugforge/helper/project.rb +86 -0
- data/lib/slugforge/models/host.rb +233 -0
- data/lib/slugforge/models/host/fog_host.rb +33 -0
- data/lib/slugforge/models/host/hostname_host.rb +9 -0
- data/lib/slugforge/models/host/ip_address_host.rb +9 -0
- data/lib/slugforge/models/host_group.rb +65 -0
- data/lib/slugforge/models/host_group/aws_tag_group.rb +22 -0
- data/lib/slugforge/models/host_group/ec2_instance_group.rb +21 -0
- data/lib/slugforge/models/host_group/hostname_group.rb +16 -0
- data/lib/slugforge/models/host_group/ip_address_group.rb +16 -0
- data/lib/slugforge/models/host_group/security_group_group.rb +20 -0
- data/lib/slugforge/models/logger.rb +36 -0
- data/lib/slugforge/models/tag_manager.rb +125 -0
- data/lib/slugforge/slugins.rb +125 -0
- data/lib/slugforge/version.rb +9 -0
- data/scripts/post-install.sh +143 -0
- data/scripts/unicorn-shepherd.sh +305 -0
- data/spec/fixtures/array.yaml +3 -0
- data/spec/fixtures/fog_credentials.yaml +4 -0
- data/spec/fixtures/invalid_syntax.yaml +1 -0
- data/spec/fixtures/one.yaml +3 -0
- data/spec/fixtures/two.yaml +3 -0
- data/spec/fixtures/valid.yaml +4 -0
- data/spec/slugforge/commands/deploy_spec.rb +72 -0
- data/spec/slugforge/commands_spec.rb +33 -0
- data/spec/slugforge/configuration_spec.rb +200 -0
- data/spec/slugforge/helper/fog_spec.rb +81 -0
- data/spec/slugforge/helper/git_spec.rb +152 -0
- data/spec/slugforge/models/host_group/aws_tag_group_spec.rb +54 -0
- data/spec/slugforge/models/host_group/ec2_instance_group_spec.rb +51 -0
- data/spec/slugforge/models/host_group/hostname_group_spec.rb +20 -0
- data/spec/slugforge/models/host_group/ip_address_group_spec.rb +54 -0
- data/spec/slugforge/models/host_group/security_group_group_spec.rb +52 -0
- data/spec/slugforge/models/tag_manager_spec.rb +75 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/support/env.rb +3 -0
- data/spec/support/example_groups/configuration_writer.rb +24 -0
- data/spec/support/example_groups/helper_provider.rb +10 -0
- data/spec/support/factories.rb +18 -0
- data/spec/support/fog.rb +15 -0
- data/spec/support/helpers.rb +18 -0
- data/spec/support/mock_logger.rb +6 -0
- data/spec/support/ssh.rb +8 -0
- data/spec/support/streams.rb +13 -0
- data/templates/foreman/master.conf.erb +21 -0
- data/templates/foreman/process-master.conf.erb +2 -0
- data/templates/foreman/process.conf.erb +19 -0
- metadata +344 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'slugforge/models/host_group/aws_tag_group'
|
3
|
+
|
4
|
+
describe Slugforge::AwsTagGroup do
|
5
|
+
let(:compute) { ::Fog::Compute.new(:provider => 'AWS', :aws_access_key_id => '', :aws_secret_access_key => '') }
|
6
|
+
|
7
|
+
let(:server_objects) do
|
8
|
+
3.times.map { compute.servers.create }.each { |s| s.wait_for { ready? } }
|
9
|
+
end
|
10
|
+
|
11
|
+
after(:each) do
|
12
|
+
server_objects.each { |server| server.destroy }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '.detect' do
|
16
|
+
context 'valid instance' do
|
17
|
+
let(:results) { described_class.detect(pattern, compute) }
|
18
|
+
let(:tag_key) { 'name' }
|
19
|
+
let(:tag_value) { 'valid' }
|
20
|
+
let(:pattern) { "#{tag_key}=#{tag_value}" }
|
21
|
+
|
22
|
+
context 'one host is found' do
|
23
|
+
before(:each) { server_objects.first.tags[tag_key] = tag_value }
|
24
|
+
|
25
|
+
it 'returns the array with one element' do
|
26
|
+
results.hosts.count.should == 1
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns an array of FogHosts' do
|
30
|
+
results.hosts.select { |host| host.class == Slugforge::FogHost }.count.should == results.hosts.count
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'multiple hosts are found' do
|
35
|
+
before(:each) { server_objects.each { |s| s.tags[tag_key] = tag_value } }
|
36
|
+
|
37
|
+
it 'returns the array with all hosts' do
|
38
|
+
results.hosts.count.should == server_objects.count
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'returns an array of FogHosts' do
|
42
|
+
results.hosts.select { |host| host.class == Slugforge::FogHost }.count.should == results.hosts.count
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'no hosts are found' do
|
48
|
+
let(:results) { described_class.detect(pattern, compute) }
|
49
|
+
let(:pattern) { 'name=value' }
|
50
|
+
it { results.should be_nil }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'slugforge/models/host_group/ec2_instance_group'
|
3
|
+
|
4
|
+
describe Slugforge::Ec2InstanceGroup do
|
5
|
+
let(:compute) { ::Fog::Compute.new(:provider => 'AWS', :aws_access_key_id => '', :aws_secret_access_key => '') }
|
6
|
+
|
7
|
+
let(:server_objects) do
|
8
|
+
3.times.map { compute.servers.create }.each { |s| s.wait_for { ready? } }
|
9
|
+
end
|
10
|
+
|
11
|
+
after(:each) do
|
12
|
+
server_objects.each { |server| server.destroy }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '.detect' do
|
16
|
+
context 'valid instance' do
|
17
|
+
let(:results) { described_class.detect(pattern, compute) }
|
18
|
+
let(:pattern) { server_objects.first.id }
|
19
|
+
|
20
|
+
it 'returns the array with one element' do
|
21
|
+
results.hosts.count.should == 1
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'returns an array of FogHosts' do
|
25
|
+
results.hosts.select { |host| host.class == Slugforge::FogHost }.count.should == results.hosts.count
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'invalid instance' do
|
30
|
+
context 'pattern is too short' do
|
31
|
+
let(:results) { described_class.detect(pattern, compute) }
|
32
|
+
let(:pattern) { 'i-1234567' }
|
33
|
+
it { results.should be_nil }
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'pattern is too long' do
|
37
|
+
let(:results) { described_class.detect(pattern, compute) }
|
38
|
+
let(:pattern) { 'i-123456789' }
|
39
|
+
it { results.should be_nil }
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'instance does not exist' do
|
43
|
+
let(:results) { described_class.detect(pattern, compute) }
|
44
|
+
# This host *could* exist, but it's a long shot
|
45
|
+
let(:pattern) { 'i-00000000' }
|
46
|
+
it { results.should be_nil }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'slugforge/models/host_group/hostname_group'
|
3
|
+
|
4
|
+
describe Slugforge::HostnameGroup do
|
5
|
+
describe '.detect' do
|
6
|
+
context 'the pattern matches' do
|
7
|
+
let(:results) { described_class.detect(pattern, nil) }
|
8
|
+
let(:pattern) { 'ec2-something.compute-1.amazon.aws.com' }
|
9
|
+
|
10
|
+
it 'returns the array with one element' do
|
11
|
+
results.hosts.count.should == 1
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'returns an array of HostnameHosts' do
|
15
|
+
results.hosts.select { |host| host.class == Slugforge::HostnameHost }.count.should == results.hosts.count
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'slugforge/models/host_group/ip_address_group'
|
3
|
+
|
4
|
+
describe Slugforge::IpAddressGroup do
|
5
|
+
describe '.detect' do
|
6
|
+
context 'valid address pattern' do
|
7
|
+
let(:results) { described_class.detect(pattern, nil) }
|
8
|
+
|
9
|
+
context 'short octets' do
|
10
|
+
let(:pattern) { '1.2.3.4' }
|
11
|
+
|
12
|
+
it 'returns an array with one element' do
|
13
|
+
results.hosts.count.should == 1
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns an array of IpAddressHosts' do
|
17
|
+
results.hosts.select { |host| host.class == Slugforge::IpAddressHost }.count.should == results.hosts.count
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'long octets' do
|
22
|
+
let(:pattern) { '123.234.111.222' }
|
23
|
+
|
24
|
+
it 'returns an array with one element' do
|
25
|
+
results.hosts.count.should == 1
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'returns an array of IpAddressHosts' do
|
29
|
+
results.hosts.select { |host| host.class == Slugforge::IpAddressHost }.count.should == results.hosts.count
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'invalid address pattern' do
|
35
|
+
let(:results) { described_class.detect(pattern, nil) }
|
36
|
+
|
37
|
+
context 'the pattern contains letters' do
|
38
|
+
let(:pattern) { '1.2.a.4' }
|
39
|
+
it { results.should be_nil }
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'the pattern contains less than 4 octets' do
|
43
|
+
let(:pattern) { '1.2.3' }
|
44
|
+
it { results.should be_nil }
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'the pattern contains more than 4 octets' do
|
48
|
+
let(:pattern) { '1.2.3.4.5' }
|
49
|
+
it { results.should be_nil }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'slugforge/models/host_group/security_group_group'
|
3
|
+
|
4
|
+
describe Slugforge::SecurityGroupGroup do
|
5
|
+
let(:compute) { ::Fog::Compute.new(:provider => 'AWS', :aws_access_key_id => '', :aws_secret_access_key => '') }
|
6
|
+
|
7
|
+
let(:server_objects) do
|
8
|
+
3.times.map { compute.servers.create }.each { |s| s.wait_for { ready? } }
|
9
|
+
end
|
10
|
+
|
11
|
+
after(:each) do
|
12
|
+
server_objects.each { |server| server.destroy }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '.detect' do
|
16
|
+
context 'valid instance' do
|
17
|
+
let(:results) { described_class.detect(pattern, compute) }
|
18
|
+
let(:pattern) { 'group' }
|
19
|
+
|
20
|
+
context 'one host is found' do
|
21
|
+
before(:each) { server_objects.first.groups << pattern }
|
22
|
+
|
23
|
+
it 'returns the array with one element' do
|
24
|
+
results.hosts.count.should == 1
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'returns an array of FogHosts' do
|
28
|
+
results.hosts.select { |host| host.class == Slugforge::FogHost }.count.should == results.hosts.count
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'multiple hosts are found' do
|
33
|
+
before(:each) { server_objects.each { |s| s.groups << pattern } }
|
34
|
+
|
35
|
+
it 'returns the array with all hosts' do
|
36
|
+
results.hosts.count.should == server_objects.count
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'returns an array of FogHosts' do
|
40
|
+
results.hosts.select { |host| host.class == Slugforge::FogHost }.count.should == results.hosts.count
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'no hosts are found' do
|
46
|
+
let(:results) { described_class.detect(pattern, compute) }
|
47
|
+
let(:pattern) { 'name=value' }
|
48
|
+
it { results.should be_nil }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'slugforge/models/tag_manager'
|
3
|
+
|
4
|
+
describe Slugforge::TagManager do
|
5
|
+
|
6
|
+
let(:s3) { ::Fog::Storage.new(
|
7
|
+
:aws_access_key_id => 'aws_access_key_id',
|
8
|
+
:aws_secret_access_key => 'aws_secret_access_key',
|
9
|
+
:provider => 'AWS',
|
10
|
+
:region => 'us-east-1')
|
11
|
+
}
|
12
|
+
let(:tm) { Slugforge::TagManager.new(:s3 => s3, :bucket => 'tj-slugforge') }
|
13
|
+
let!(:bucket) { s3.directories.create(:key => 'tj-slugforge') }
|
14
|
+
|
15
|
+
context "projects" do
|
16
|
+
it "list of projects should start empty" do
|
17
|
+
tm.projects.should be_empty
|
18
|
+
end
|
19
|
+
|
20
|
+
context "with a project" do
|
21
|
+
before(:each) do
|
22
|
+
bucket.files.create(:key => 'project1/test.slug')
|
23
|
+
bucket.files.create(:key => 'project2/test.slug')
|
24
|
+
end
|
25
|
+
|
26
|
+
it "list of projects should include all projects" do
|
27
|
+
tm.projects.should include("project1")
|
28
|
+
tm.projects.should include("project2")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "tags" do
|
34
|
+
before(:each) do
|
35
|
+
bucket.files.create(:key => 'project1/test.slug')
|
36
|
+
bucket.files.create(:key => 'project2/test.slug')
|
37
|
+
bucket.files.create(
|
38
|
+
:key => 'project2/tags/prod',
|
39
|
+
:body => "test.slug\nold.slug"
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
context "invalid project" do
|
44
|
+
it "lists no tags" do
|
45
|
+
tm.tags('bad_project').should be_empty
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "with no tags" do
|
50
|
+
it "lists no tags" do
|
51
|
+
tm.tags('project1').should be_empty
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "with tags" do
|
56
|
+
it "lists the tags" do
|
57
|
+
tm.tags('project2').should include('prod')
|
58
|
+
end
|
59
|
+
|
60
|
+
it "lists only the current slug for the tag" do
|
61
|
+
tm.slug_for_tag('project2', 'prod').should include('test.slug')
|
62
|
+
tm.slug_for_tag('project2', 'prod').should_not include('old.slug')
|
63
|
+
end
|
64
|
+
|
65
|
+
it "lists the slug history for the tag" do
|
66
|
+
tm.slugs_for_tag('project2', 'prod').should include('old.slug')
|
67
|
+
end
|
68
|
+
|
69
|
+
it "lists the tags for a slug" do
|
70
|
+
tm.tags_for_slug('project2', 'test.slug').should include('prod')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
if ENV['COVERAGE']
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start do
|
4
|
+
add_filter "/spec/"
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
$: << File.expand_path('../../lib', __FILE__)
|
9
|
+
require 'slugforge'
|
10
|
+
require 'fakefs/spec_helpers'
|
11
|
+
|
12
|
+
ENV["THOR_DEBUG"] = "1"
|
13
|
+
|
14
|
+
# Load support files
|
15
|
+
Dir[File.expand_path('../support/**/*.rb', __FILE__)].each { |f| require f }
|
16
|
+
|
17
|
+
def fixture_file(file)
|
18
|
+
File.expand_path(File.join("../fixtures", file), __FILE__)
|
19
|
+
end
|
20
|
+
|
21
|
+
def with_env(env)
|
22
|
+
old_env = env.reduce({}) { |old,(k,v)| old[k] = ENV[k]; ENV[k] = v; old }
|
23
|
+
yield
|
24
|
+
old_env.each { |k,v| ENV[k] = v }
|
25
|
+
end
|
26
|
+
|
27
|
+
RSpec.configure do |config|
|
28
|
+
config.before(:all) do
|
29
|
+
Slugforge::Configuration.configuration_files = [ 'slugforge.yml' ]
|
30
|
+
|
31
|
+
sanitize_environment! /AWS_|EC2_|S3_|JENKINS_|SLUGFORGE_/
|
32
|
+
end
|
33
|
+
|
34
|
+
config.include FakeFS::SpecHelpers
|
35
|
+
config.include HelperProvider
|
36
|
+
config.include ConfigurationWriter
|
37
|
+
end
|
data/spec/support/env.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module ConfigurationWriter
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
def write_config_file(configuration)
|
8
|
+
File.open(Slugforge::Configuration.configuration_files.first, "w+") do |f|
|
9
|
+
f.write configuration.to_yaml
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Only do this configuration when specifically requested by setting :config => true on the example group
|
14
|
+
before(:each, :config => true) do
|
15
|
+
write_config_file 'aws' => {
|
16
|
+
'access_key' => 'zzz-access-key',
|
17
|
+
'secret_key' => 'zzz-secret-key',
|
18
|
+
'slug_bucket' => 'tj-slugforge'
|
19
|
+
}
|
20
|
+
|
21
|
+
helpers.s3.directories.create({key: 'tj-slugforge'})
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
def create_slug(project, name, content = "blag")
|
2
|
+
bucket = helpers.s3.directories.new(:key => 'tj-slugforge')
|
3
|
+
bucket.files.create :key => "#{project}/#{name}.slug", :body => "blah"
|
4
|
+
end
|
5
|
+
|
6
|
+
def create_tag(project, tag, slug)
|
7
|
+
bucket = helpers.s3.directories.new(:key => 'tj-slugforge')
|
8
|
+
bucket.files.create :key => "#{project}/tags/#{tag}", :body => "#{project}/#{slug}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def build_sts_response
|
12
|
+
double('STS Response', :body => {
|
13
|
+
'AccessKeyId' => "access-#{rand(1000)}",
|
14
|
+
'AwsRegion' => "region-#{rand(1000)}",
|
15
|
+
'SecretAccessKey' => "secret-#{rand(1000)}",
|
16
|
+
'SessionToken' => "session-#{rand(1000)}"
|
17
|
+
})
|
18
|
+
end
|
data/spec/support/fog.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
RSpec.configure do |config|
|
2
|
+
config.before(:all) do
|
3
|
+
Fog.mock!
|
4
|
+
Fog::Mock.delay = 0
|
5
|
+
end
|
6
|
+
|
7
|
+
config.before(:each) do
|
8
|
+
# Fog does not provide STS mocks yet
|
9
|
+
::Fog::AWS::STS.stub(:new) {
|
10
|
+
double(::Fog::AWS::STS,
|
11
|
+
:get_session_token => build_sts_response,
|
12
|
+
:get_federation_token => build_sts_response)
|
13
|
+
}
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class SpecHelpers
|
2
|
+
def self.class_option(*); end
|
3
|
+
def self.source_root(*); end
|
4
|
+
|
5
|
+
include Slugforge::Helper
|
6
|
+
|
7
|
+
def initialize(args=[], options={}, config={})
|
8
|
+
@config = Slugforge::Configuration.new(options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def config; @config; end
|
12
|
+
|
13
|
+
def options; {}; end
|
14
|
+
|
15
|
+
def metadata
|
16
|
+
parse_metadata_file
|
17
|
+
end
|
18
|
+
end
|