slugforge 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +316 -0
  3. data/bin/slugforge +9 -0
  4. data/lib/slugforge.rb +19 -0
  5. data/lib/slugforge/build.rb +4 -0
  6. data/lib/slugforge/build/build_project.rb +31 -0
  7. data/lib/slugforge/build/export_upstart.rb +85 -0
  8. data/lib/slugforge/build/package.rb +63 -0
  9. data/lib/slugforge/cli.rb +125 -0
  10. data/lib/slugforge/commands.rb +130 -0
  11. data/lib/slugforge/commands/build.rb +20 -0
  12. data/lib/slugforge/commands/config.rb +24 -0
  13. data/lib/slugforge/commands/deploy.rb +383 -0
  14. data/lib/slugforge/commands/project.rb +21 -0
  15. data/lib/slugforge/commands/tag.rb +148 -0
  16. data/lib/slugforge/commands/wrangler.rb +142 -0
  17. data/lib/slugforge/configuration.rb +125 -0
  18. data/lib/slugforge/helper.rb +186 -0
  19. data/lib/slugforge/helper/build.rb +46 -0
  20. data/lib/slugforge/helper/config.rb +37 -0
  21. data/lib/slugforge/helper/enumerable.rb +46 -0
  22. data/lib/slugforge/helper/fog.rb +90 -0
  23. data/lib/slugforge/helper/git.rb +89 -0
  24. data/lib/slugforge/helper/path.rb +76 -0
  25. data/lib/slugforge/helper/project.rb +86 -0
  26. data/lib/slugforge/models/host.rb +233 -0
  27. data/lib/slugforge/models/host/fog_host.rb +33 -0
  28. data/lib/slugforge/models/host/hostname_host.rb +9 -0
  29. data/lib/slugforge/models/host/ip_address_host.rb +9 -0
  30. data/lib/slugforge/models/host_group.rb +65 -0
  31. data/lib/slugforge/models/host_group/aws_tag_group.rb +22 -0
  32. data/lib/slugforge/models/host_group/ec2_instance_group.rb +21 -0
  33. data/lib/slugforge/models/host_group/hostname_group.rb +16 -0
  34. data/lib/slugforge/models/host_group/ip_address_group.rb +16 -0
  35. data/lib/slugforge/models/host_group/security_group_group.rb +20 -0
  36. data/lib/slugforge/models/logger.rb +36 -0
  37. data/lib/slugforge/models/tag_manager.rb +125 -0
  38. data/lib/slugforge/slugins.rb +125 -0
  39. data/lib/slugforge/version.rb +9 -0
  40. data/scripts/post-install.sh +143 -0
  41. data/scripts/unicorn-shepherd.sh +305 -0
  42. data/spec/fixtures/array.yaml +3 -0
  43. data/spec/fixtures/fog_credentials.yaml +4 -0
  44. data/spec/fixtures/invalid_syntax.yaml +1 -0
  45. data/spec/fixtures/one.yaml +3 -0
  46. data/spec/fixtures/two.yaml +3 -0
  47. data/spec/fixtures/valid.yaml +4 -0
  48. data/spec/slugforge/commands/deploy_spec.rb +72 -0
  49. data/spec/slugforge/commands_spec.rb +33 -0
  50. data/spec/slugforge/configuration_spec.rb +200 -0
  51. data/spec/slugforge/helper/fog_spec.rb +81 -0
  52. data/spec/slugforge/helper/git_spec.rb +152 -0
  53. data/spec/slugforge/models/host_group/aws_tag_group_spec.rb +54 -0
  54. data/spec/slugforge/models/host_group/ec2_instance_group_spec.rb +51 -0
  55. data/spec/slugforge/models/host_group/hostname_group_spec.rb +20 -0
  56. data/spec/slugforge/models/host_group/ip_address_group_spec.rb +54 -0
  57. data/spec/slugforge/models/host_group/security_group_group_spec.rb +52 -0
  58. data/spec/slugforge/models/tag_manager_spec.rb +75 -0
  59. data/spec/spec_helper.rb +37 -0
  60. data/spec/support/env.rb +3 -0
  61. data/spec/support/example_groups/configuration_writer.rb +24 -0
  62. data/spec/support/example_groups/helper_provider.rb +10 -0
  63. data/spec/support/factories.rb +18 -0
  64. data/spec/support/fog.rb +15 -0
  65. data/spec/support/helpers.rb +18 -0
  66. data/spec/support/mock_logger.rb +6 -0
  67. data/spec/support/ssh.rb +8 -0
  68. data/spec/support/streams.rb +13 -0
  69. data/templates/foreman/master.conf.erb +21 -0
  70. data/templates/foreman/process-master.conf.erb +2 -0
  71. data/templates/foreman/process.conf.erb +19 -0
  72. 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
@@ -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
@@ -0,0 +1,3 @@
1
+ def sanitize_environment! (pattern)
2
+ ENV.keys.select { |k| k =~ pattern }.each { |k| ENV[k] = nil }
3
+ end
@@ -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,10 @@
1
+ require 'active_support/concern'
2
+
3
+ module HelperProvider
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ let(:helpers) { SpecHelpers.new }
8
+ end
9
+ end
10
+
@@ -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
@@ -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