pipely 0.8.3 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pipely/build.rb +2 -16
  3. data/lib/pipely/build/daily_scheduler.rb +1 -1
  4. data/lib/pipely/build/definition.rb +30 -2
  5. data/lib/pipely/build/environment_config.rb +24 -1
  6. data/lib/pipely/build/s3_path_builder.rb +65 -33
  7. data/lib/pipely/deploy/bootstrap.rb +17 -14
  8. data/lib/pipely/deploy/bootstrap_context.rb +87 -10
  9. data/lib/pipely/deploy/bootstrap_registry.rb +45 -0
  10. data/lib/pipely/deploy/client.rb +33 -18
  11. data/lib/pipely/deploy/json_definition.rb +51 -0
  12. data/lib/pipely/pipeline_date_time/pipeline_date.rb +62 -0
  13. data/lib/pipely/pipeline_date_time/pipeline_date_pattern.rb +42 -0
  14. data/lib/pipely/pipeline_date_time/pipeline_date_range_base.rb +44 -0
  15. data/lib/pipely/pipeline_date_time/pipeline_day_range.rb +14 -0
  16. data/lib/pipely/pipeline_date_time/pipeline_month_range.rb +26 -0
  17. data/lib/pipely/pipeline_date_time/pipeline_year_range.rb +25 -0
  18. data/lib/pipely/tasks/definition.rb +7 -0
  19. data/lib/pipely/tasks/deploy.rb +7 -0
  20. data/lib/pipely/tasks/upload_pipeline_as_gem.rb +19 -9
  21. data/lib/pipely/version.rb +1 -1
  22. data/spec/fixtures/bootstrap_contexts/green.rb +9 -0
  23. data/spec/fixtures/bootstrap_contexts/simple.rb +9 -0
  24. data/spec/fixtures/templates/bootstrap.sh.erb +4 -0
  25. data/spec/lib/pipely/build/environment_config_spec.rb +58 -0
  26. data/spec/lib/pipely/build/s3_path_builder_spec.rb +34 -2
  27. data/spec/lib/pipely/build/template_spec.rb +10 -10
  28. data/spec/lib/pipely/build_spec.rb +29 -0
  29. data/spec/lib/pipely/deploy/bootstrap_context_spec.rb +102 -14
  30. data/spec/lib/pipely/deploy/bootstrap_registry_spec.rb +32 -0
  31. data/spec/lib/pipely/deploy/bootstrap_spec.rb +41 -24
  32. data/spec/lib/pipely/pipeline_date_time/pipeline_date_pattern_spec.rb +181 -0
  33. data/spec/lib/pipely/pipeline_date_time/pipeline_date_range_base_spec.rb +39 -0
  34. data/spec/lib/pipely/pipeline_date_time/pipeline_date_spec.rb +110 -0
  35. data/spec/lib/pipely/pipeline_date_time/pipeline_day_range_spec.rb +23 -0
  36. data/spec/lib/pipely/pipeline_date_time/pipeline_month_range_spec.rb +93 -0
  37. data/spec/lib/pipely/pipeline_date_time/pipeline_year_range_spec.rb +93 -0
  38. data/spec/lib/pipely/tasks/upload_pipeline_as_gem_spec.rb +59 -0
  39. metadata +49 -3
@@ -1,3 +1,3 @@
1
1
  module Pipely
2
- VERSION = "0.8.3" unless defined?(::Pipely::VERSION)
2
+ VERSION = '0.10.0' unless defined?(::Pipely::VERSION)
3
3
  end
@@ -0,0 +1,9 @@
1
+ module Fixtures
2
+ module BootstrapContexts
3
+ module Green
4
+ def green
5
+ "green"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Fixtures
2
+ module BootstrapContexts
3
+ module Simple
4
+ def simple
5
+ "simple"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ one
2
+ two
3
+ three
4
+ <%= bootstrap.simple %>
@@ -0,0 +1,58 @@
1
+ require 'pipely/build/environment_config'
2
+
3
+ describe Pipely::Build::EnvironmentConfig do
4
+
5
+ describe '.load(filename, environment)' do
6
+ let(:filename) { 'path/to/config/yaml.yml' }
7
+
8
+ let(:config) do
9
+ YAML.load(<<-EOS)
10
+ my_env:
11
+ key: 'my_val'
12
+ production:
13
+ key: 'prod_val'
14
+ staging:
15
+ key: 'staging_val'
16
+ EOS
17
+ end
18
+
19
+ before do
20
+ allow(YAML).to receive(:load_file).with(filename) { config }
21
+ end
22
+
23
+ context 'given a custom environment' do
24
+ subject { described_class.load(filename, 'my_env') }
25
+
26
+ it 'loads config from a YAML file' do
27
+ expect(subject[:key]).to eq('my_val')
28
+ end
29
+ end
30
+
31
+ context 'given the "production" environment' do
32
+ subject { described_class.load(filename, 'production') }
33
+
34
+ it 'loads config from a YAML file' do
35
+ expect(subject[:key]).to eq('prod_val')
36
+ end
37
+
38
+ it 'supports legacy defaults' do
39
+ expect(subject[:s3_prefix]).to eq('production/:namespace')
40
+ expect(subject[:scheduler]).to eq('daily')
41
+ end
42
+ end
43
+
44
+ context 'given the "staging" environment' do
45
+ subject { described_class.load(filename, 'staging') }
46
+
47
+ it 'loads config from a YAML file' do
48
+ expect(subject[:key]).to eq('staging_val')
49
+ end
50
+
51
+ it 'supports legacy defaults' do
52
+ expect(subject[:s3_prefix]).to eq('staging/:whoami/:namespace')
53
+ expect(subject[:scheduler]).to eq('now')
54
+ end
55
+ end
56
+ end
57
+
58
+ end
@@ -35,9 +35,13 @@ describe Pipely::Build::S3PathBuilder do
35
35
  should eq("s3://asset-bucket/run-prefix/shared/\#{format(@scheduledStartTime,'YYYY-MM-dd')}")
36
36
  }
37
37
 
38
+ its(:bucket_relative_s3_asset_prefix) {
39
+ should eq("run-prefix/\#{format(@scheduledStartTime,'YYYY-MM-dd_HHmmss')}")
40
+ }
41
+
38
42
  describe "#to_hash" do
39
43
  it 'includes the necessary keys for supplying config to a Template' do
40
- expect(subject.to_hash.keys).to match_array([
44
+ expect(subject.to_hash.keys).to include(
41
45
  :s3_log_prefix,
42
46
  :s3_step_prefix,
43
47
  :s3n_step_prefix,
@@ -45,7 +49,35 @@ describe Pipely::Build::S3PathBuilder do
45
49
  :s3n_asset_prefix,
46
50
  :s3_shared_asset_prefix,
47
51
  :bucket_relative_s3_asset_prefix,
48
- ])
52
+ )
53
+ end
54
+ end
55
+
56
+ context "when a custom template is specified via config" do
57
+ subject {
58
+ described_class.new(
59
+ foo: 'my-value',
60
+ templates: {
61
+ bar: ':protocol://my-bucket/:foo/okay'
62
+ }
63
+ )
64
+ }
65
+
66
+ its(:s3_bar_prefix) {
67
+ should eq('s3://my-bucket/my-value/okay')
68
+ }
69
+
70
+ its(:s3n_bar_prefix) {
71
+ should eq('s3n://my-bucket/my-value/okay')
72
+ }
73
+
74
+ describe "#to_hash" do
75
+ it 'includes the keys for the custom template' do
76
+ expect(subject.to_hash.keys).to include(
77
+ :s3_bar_prefix,
78
+ :s3n_bar_prefix,
79
+ )
80
+ end
49
81
  end
50
82
  end
51
83
 
@@ -17,17 +17,17 @@ describe Pipely::Build::Template do
17
17
  end
18
18
 
19
19
  describe "#streaming_hadoop_step(options)" do
20
- let(:s3_path_builder) {
21
- Pipely::Build::S3PathBuilder.new(
22
- logs: 'log-bucket',
23
- steps: 'step-bucket',
24
- assets: 'asset-bucket',
25
- prefix: 'run-prefix'
26
- )
27
- }
28
-
29
20
  before do
30
- subject.apply_config(s3_path_builder.to_hash)
21
+ # emulate applying config from S3PathBuilder, as done in Definition#to_json
22
+ subject.apply_config({
23
+ s3_log_prefix: "s3://log-bucket/run-prefix/\#{format(@scheduledStartTime,'YYYY-MM-dd_HHmmss')}",
24
+ s3_step_prefix: "s3://step-bucket/run-prefix",
25
+ s3n_step_prefix: "s3n://step-bucket/run-prefix",
26
+ s3_asset_prefix: "s3://asset-bucket/run-prefix/\#{format(@scheduledStartTime,'YYYY-MM-dd_HHmmss')}",
27
+ s3n_asset_prefix: "s3n://asset-bucket/run-prefix/\#{format(@scheduledStartTime,'YYYY-MM-dd_HHmmss')}",
28
+ s3_shared_asset_prefix: "s3://asset-bucket/run-prefix/shared/\#{format(@scheduledStartTime,'YYYY-MM-dd')}",
29
+ bucket_relative_s3_asset_prefix: "run-prefix/\#{format(@scheduledStartTime,'YYYY-MM-dd_HHmmss')}",
30
+ })
31
31
  end
32
32
 
33
33
  it "builds a streaming hadoop step" do
@@ -1,3 +1,32 @@
1
+ require 'pipely/build'
2
+
1
3
  describe Pipely::Build do
2
4
 
5
+ describe '.build_definition(template, environment, config_path)' do
6
+
7
+ let(:template) { double }
8
+ let(:environment) { 'production' }
9
+ let(:config_path) { 'path/to/config' }
10
+
11
+ let(:config) { double }
12
+
13
+ before do
14
+ allow(Pipely::Build::EnvironmentConfig).to receive(:load).
15
+ with(config_path, environment.to_sym).
16
+ and_return(config)
17
+ end
18
+
19
+ it 'builds a Definition' do
20
+ expect(
21
+ described_class.build_definition(template, environment, config_path)
22
+ ).to eq(
23
+ Pipely::Build::Definition.new(
24
+ template,
25
+ environment.to_sym,
26
+ config
27
+ )
28
+ )
29
+ end
30
+ end
31
+
3
32
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'spec_helper'
4
4
  require 'pipely/deploy/bootstrap_context'
5
- require 'fileutils'
6
5
 
7
6
  describe Pipely::Deploy::BootstrapContext do
8
7
  subject do
@@ -11,9 +10,20 @@ describe Pipely::Deploy::BootstrapContext do
11
10
  end
12
11
  end
13
12
 
14
- describe "#install_gems_script" do
15
- it "defaults to hadoop fs" do
16
- expect(subject.install_gems_script).to eql "
13
+ let(:aws_install_gems_script) do
14
+ "
15
+ # one.gem
16
+ aws s3 cp one.gem one.gem
17
+ gem install --force --local one.gem --no-ri --no-rdoc
18
+
19
+ # two.gem
20
+ aws s3 cp two.gem two.gem
21
+ gem install --force --local two.gem --no-ri --no-rdoc
22
+ "
23
+ end
24
+
25
+ let(:hadoop_install_gems_script) do
26
+ "
17
27
  # one.gem
18
28
  hadoop fs -copyToLocal one.gem one.gem
19
29
  gem install --force --local one.gem --no-ri --no-rdoc
@@ -22,35 +32,113 @@ gem install --force --local one.gem --no-ri --no-rdoc
22
32
  hadoop fs -copyToLocal two.gem two.gem
23
33
  gem install --force --local two.gem --no-ri --no-rdoc
24
34
  "
35
+ end
36
+
37
+ describe "#install_gems_script" do
38
+ it "with hadoop fs" do
39
+ expect(subject.install_gems_script(:hadoop_fs)).to eql(
40
+ hadoop_install_gems_script)
25
41
  end
26
42
 
27
43
  context "with aws cli" do
28
44
  it "should build script for aws cli" do
29
- expect(subject.install_gems_script(:awscli) ).to eql "
45
+ expect(subject.install_gems_script(:awscli) ).to eql(
46
+ aws_install_gems_script)
47
+ end
48
+ end
49
+
50
+ context "with yield" do
51
+ it "should build script for aws cli" do
52
+ expect(subject.install_gems_script(:awscli) do |command,file,filename|
53
+ "custom command - #{file} #{filename} #{command}"
54
+ end).to eql "
30
55
  # one.gem
31
- aws s3 cp one.gem one.gem
56
+ custom command - one.gem one.gem aws s3 cp
32
57
  gem install --force --local one.gem --no-ri --no-rdoc
33
58
 
34
59
  # two.gem
35
- aws s3 cp two.gem two.gem
60
+ custom command - two.gem two.gem aws s3 cp
36
61
  gem install --force --local two.gem --no-ri --no-rdoc
37
62
  "
38
63
  end
39
64
  end
40
65
 
41
- context "with yield" do
42
- it "should build script for aws cli" do
43
- expect(subject.install_gems_script(:awscli) do |file,filename,command|
44
- "custom command - #{file} #{filename} #{command}"
45
- end).to eql "
66
+ context "using the emr context" do
67
+ describe "#install_gems_script" do
68
+ it "build script using hadoop fs" do
69
+ expect(subject.install_gems_script(:hadoop_fs)).to eql "
46
70
  # one.gem
47
- custom command - one.gem one.gem aws s3 cp one.gem one.gem
71
+ hadoop fs -copyToLocal one.gem one.gem
48
72
  gem install --force --local one.gem --no-ri --no-rdoc
49
73
 
50
74
  # two.gem
51
- custom command - two.gem two.gem aws s3 cp two.gem two.gem
75
+ hadoop fs -copyToLocal two.gem two.gem
52
76
  gem install --force --local two.gem --no-ri --no-rdoc
53
77
  "
78
+ end
79
+ end
80
+ end
81
+
82
+ context "using the emr context" do
83
+ let(:emr) { subject.emr }
84
+
85
+ describe '#install_gems_script' do
86
+ it 'should be same as parent hadoop install script' do
87
+ expect(emr.install_gems_script).to eq(hadoop_install_gems_script)
88
+ end
89
+ end
90
+ end
91
+
92
+ context "using the ec2 context" do
93
+ let(:ec2) { subject.ec2 }
94
+
95
+ describe '#install_gems_script' do
96
+ it 'should be same as parent aws install script' do
97
+ expect(ec2.install_gems_script).to eq(aws_install_gems_script)
98
+ end
99
+ end
100
+
101
+ describe "#as_root" do
102
+
103
+ context "on first run" do
104
+ it "should build script with ssh init" do
105
+ expect(ec2.as_root { "Custom Script here" }).to eql "
106
+ # Set up ssh access
107
+ if [ ! -f ~/.ssh/id_rsa ]; then
108
+ mkdir -p ~/.ssh
109
+ ssh-keygen -P '' -f ~/.ssh/id_rsa
110
+ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
111
+ chmod 600 ~/.ssh/authorized_keys
112
+ fi
113
+
114
+ # Use ssh to bypass the sudo \"require tty\" setting
115
+ ssh -o \"StrictHostKeyChecking no\" -t -t ec2-user@localhost <<- EOF
116
+ sudo su -;
117
+ Custom Script here
118
+ # exit twice, once for su and once for ssh
119
+ exit;
120
+ exit;
121
+ EOF
122
+ "
123
+ end
124
+ end
125
+
126
+ context "on consective runs" do
127
+ it "should build script" do
128
+ ec2.as_root { "First run" }
129
+
130
+ expect(ec2.as_root { "Second run" }).to eql "
131
+ # Use ssh to bypass the sudo \"require tty\" setting
132
+ ssh -o \"StrictHostKeyChecking no\" -t -t ec2-user@localhost <<- EOF
133
+ sudo su -;
134
+ Second run
135
+ # exit twice, once for su and once for ssh
136
+ exit;
137
+ exit;
138
+ EOF
139
+ "
140
+ end
141
+ end
54
142
  end
55
143
  end
56
144
  end
@@ -0,0 +1,32 @@
1
+ # Copyright Swipely, Inc. All rights reserved.
2
+
3
+ require 'spec_helper'
4
+ require 'pipely/deploy/bootstrap_registry'
5
+
6
+ describe Pipely::Deploy::BootstrapRegistry do
7
+
8
+ subject { described_class }
9
+
10
+ describe "#mixins" do
11
+ it "should default to empty" do
12
+ expect(subject.mixins).to be_empty
13
+ end
14
+ end
15
+
16
+ describe "#register_mixins" do
17
+ context "with a mixin" do
18
+ let(:mixin) { "Fixtures::BootstrapContexts::Green" }
19
+ let(:result) { [mixin] }
20
+ it "should registry mixin" do
21
+ expect(subject.register_mixins(mixin)).to eql(result)
22
+ expect(subject.mixins).to eql(result)
23
+ end
24
+ end
25
+
26
+ context "when a mixin cannot be required" do
27
+ it "should raise" do
28
+ expect { subject.register_mixins('bad::mixin') }.to raise_error
29
+ end
30
+ end
31
+ end
32
+ end
@@ -2,14 +2,15 @@
2
2
 
3
3
  require 'spec_helper'
4
4
  require 'pipely/deploy/bootstrap'
5
+ require 'pipely/deploy/bootstrap_registry'
5
6
  require 'fileutils'
7
+ require 'fixtures/bootstrap_contexts/simple'
8
+ require 'fixtures/bootstrap_contexts/green'
6
9
 
7
10
  describe Pipely::Deploy::Bootstrap do
8
11
 
9
- subject { described_class.new(s3_uploader) }
10
-
11
- let(:s3_uploader) { double }
12
-
12
+ subject { described_class.new(gem_files, s3_steps_path) }
13
+ let(:s3_steps_path) { 'a/test/path' }
13
14
  let(:gem_files) do
14
15
  {
15
16
  'packaged-gem1' => '/path/to/cache/packaged-gem1.gem',
@@ -17,37 +18,53 @@ describe Pipely::Deploy::Bootstrap do
17
18
  }
18
19
  end
19
20
 
20
- describe "#build_and_upload_gems" do
21
- before do
22
- allow(Pipely::Bundler).to receive(:gem_files) { gem_files }
21
+ describe "#context" do
22
+ context "without any mixins" do
23
+ let(:context) { subject.context }
24
+
25
+ it "should have s3 steps path" do
26
+ expect(context.s3_steps_path).to eq(s3_steps_path)
27
+ end
28
+
29
+ it "builds S3 urls to the uploaded gem files" do
30
+ expect(context.gem_files).to eq(gem_files)
31
+ end
23
32
  end
24
33
 
25
- it 'uploads each gem' do
26
- expect(s3_uploader).to receive(:upload).with(gem_files.values)
34
+ context "with one mixin" do
35
+ let(:context) { subject.context( mixin.name ) }
36
+ let(:mixin) { Fixtures::BootstrapContexts::Simple }
27
37
 
28
- subject.build_and_upload_gems
38
+ it "should have Simple mixin method" do
39
+ expect(context.simple).to eq("simple")
40
+ end
29
41
  end
30
- end
31
42
 
32
- describe "#context" do
33
- let(:context) { subject.context(s3_steps_path) }
34
- let(:s3_steps_path) { 'a/test/path' }
35
- let(:s3_gem_paths) { double }
43
+ context "with multiple mixins" do
44
+ let(:context) { subject.context( mixins.map(&:name) ) }
45
+ let(:mixins) do
46
+ [Fixtures::BootstrapContexts::Simple,Fixtures::BootstrapContexts::Green]
47
+ end
36
48
 
37
- before do
38
- allow(subject).to receive(:gem_files) { gem_files }
49
+ it "should have simple mixin method" do
50
+ expect(context.simple).to eq("simple")
51
+ end
39
52
 
40
- allow(s3_uploader).to receive(:s3_urls).with(gem_files.values) do
41
- s3_gem_paths
53
+ it "should have green mixin method" do
54
+ expect(context.green).to eq("green")
42
55
  end
43
56
  end
44
57
 
45
- it "should have s3 steps path" do
46
- expect(context.s3_steps_path).to eq(s3_steps_path)
47
- end
58
+ context "with mixin from BootstrapRegistry" do
59
+ let(:context) { subject.context }
60
+ before do
61
+ Pipely::Deploy::BootstrapRegistry.instance.register_mixins(
62
+ "Fixtures::BootstrapContexts::Simple")
63
+ end
48
64
 
49
- it "builds S3 urls to the uploaded gem files" do
50
- expect(context.gem_files).to eq(s3_gem_paths)
65
+ it "should have green mixin method" do
66
+ expect(context.green).to eq("green")
67
+ end
51
68
  end
52
69
  end
53
70
  end