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.
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