slugforge 4.0.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/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
|