pipely 0.8.3 → 0.10.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 (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