opsicle 0.1.0 → 0.2.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 (46) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +3 -0
  3. data/.travis.yml +6 -1
  4. data/Gemfile +3 -0
  5. data/Guardfile +5 -0
  6. data/README.markdown +44 -17
  7. data/bin/opsicle +36 -3
  8. data/lib/opsicle.rb +2 -3
  9. data/lib/opsicle/commands.rb +6 -0
  10. data/lib/opsicle/commands/deploy.rb +39 -0
  11. data/lib/opsicle/{list.rb → commands/list.rb} +0 -2
  12. data/lib/opsicle/{ssh.rb → commands/ssh.rb} +0 -3
  13. data/lib/opsicle/commands/ssh_key.rb +40 -0
  14. data/lib/opsicle/config.rb +1 -0
  15. data/lib/opsicle/deployment.rb +59 -0
  16. data/lib/opsicle/deployments.rb +22 -0
  17. data/lib/opsicle/monitor.rb +12 -0
  18. data/lib/opsicle/monitor/app.rb +147 -0
  19. data/lib/opsicle/monitor/panel.rb +98 -0
  20. data/lib/opsicle/monitor/panels/deployments.rb +42 -0
  21. data/lib/opsicle/monitor/panels/header.rb +48 -0
  22. data/lib/opsicle/monitor/panels/help.rb +33 -0
  23. data/lib/opsicle/monitor/screen.rb +83 -0
  24. data/lib/opsicle/monitor/spy/dataspyable.rb +19 -0
  25. data/lib/opsicle/monitor/spy/deployments.rb +53 -0
  26. data/lib/opsicle/monitor/subpanel.rb +55 -0
  27. data/lib/opsicle/monitor/translatable.rb +33 -0
  28. data/lib/opsicle/stack.rb +22 -0
  29. data/lib/opsicle/version.rb +1 -1
  30. data/opsicle.gemspec +1 -1
  31. data/spec/opsicle/client_spec.rb +6 -6
  32. data/spec/opsicle/commands/deploy_spec.rb +50 -0
  33. data/spec/opsicle/{list_spec.rb → commands/list_spec.rb} +7 -6
  34. data/spec/opsicle/commands/ssh_key_spec.rb +75 -0
  35. data/spec/opsicle/{ssh_spec.rb → commands/ssh_spec.rb} +24 -24
  36. data/spec/opsicle/config_spec.rb +12 -11
  37. data/spec/opsicle/monitor/app_spec.rb +63 -0
  38. data/spec/opsicle/monitor/panel_spec.rb +162 -0
  39. data/spec/opsicle/monitor/screen_spec.rb +121 -0
  40. data/spec/opsicle/monitor/spy/deployments_spec.rb +41 -0
  41. data/spec/opsicle/monitor/subpanel_spec.rb +199 -0
  42. data/spec/spec_helper.rb +2 -1
  43. metadata +44 -16
  44. data/Gemfile.lock +0 -75
  45. data/lib/opsicle/deploy.rb +0 -25
  46. data/spec/opsicle/deploy_spec.rb +0 -29
@@ -0,0 +1,22 @@
1
+ module Opsicle
2
+ class Stack
3
+
4
+ def initialize(client)
5
+ @client = client
6
+ end
7
+
8
+ def stack_summary(options={})
9
+ # Only call the API again if you need to
10
+ @stack_summary = nil if options[:reload]
11
+ @deployment ||= @client.api_call('describe_stack_summary',
12
+ :stack_id => @client.config.opsworks_config[:stack_id]
13
+ )[:stack_summary]
14
+ end
15
+ private :stack_summary
16
+
17
+ def name
18
+ stack_summary[:name]
19
+ end
20
+
21
+ end
22
+ end
@@ -1,3 +1,3 @@
1
1
  module Opsicle
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/opsicle.gemspec CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.add_development_dependency "bundler", "~> 1.3"
26
26
  spec.add_development_dependency "rake"
27
- spec.add_development_dependency "rspec"
27
+ spec.add_development_dependency "rspec", "3.0.0.beta1" # Pinning to beta1 until https://github.com/guard/guard-rspec/pull/250 is closed
28
28
  spec.add_development_dependency "guard"
29
29
  spec.add_development_dependency "guard-rspec"
30
30
  end
@@ -8,16 +8,16 @@ module Opsicle
8
8
  let(:config) { double }
9
9
  before do
10
10
  ow_stub = double
11
- config.stub(:opsworks_config).and_return({ stack_id: 'stack', app_id: 'app' })
12
- ow_stub.stub(:client).and_return(aws_client)
13
- Config.stub(:new).and_return(config)
14
- AWS::OpsWorks.stub(:new).and_return(ow_stub)
11
+ allow(config).to receive(:opsworks_config).and_return({ stack_id: 'stack', app_id: 'app' })
12
+ allow(ow_stub).to receive(:client).and_return(aws_client)
13
+ allow(Config).to receive(:new).and_return(config)
14
+ allow(AWS::OpsWorks).to receive(:new).and_return(ow_stub)
15
15
  end
16
16
 
17
17
  context "#run_command" do
18
18
  it "calls out to the aws client with all the config options" do
19
- config.should_receive(:configure_aws!)
20
- aws_client.should_receive(:create_deployment).with(
19
+ expect(config).to receive(:configure_aws!)
20
+ expect(aws_client).to receive(:create_deployment).with(
21
21
  hash_including(
22
22
  command: { name: 'deploy' },
23
23
  stack_id: 'stack',
@@ -0,0 +1,50 @@
1
+ require "spec_helper"
2
+ require "opsicle/commands/deploy"
3
+ require "opsicle/monitor"
4
+
5
+ module Opsicle
6
+ describe Deploy do
7
+ subject { Deploy.new('derp') }
8
+
9
+ context "#execute" do
10
+ let(:client) { double }
11
+ let(:monitor) { double(:start => nil) }
12
+ before do
13
+ allow(Client).to receive(:new).with('derp').and_return(client)
14
+ allow(client).to receive(:run_command).with('deploy').and_return({deployment_id: 'derp'})
15
+
16
+ allow(Monitor::App).to receive(:new).and_return(monitor)
17
+ allow(monitor).to receive(:start)
18
+ end
19
+
20
+ it "creates a new deployment" do
21
+ expect(client).to receive(:run_command).with('deploy').and_return({deployment_id: 'derp'})
22
+
23
+ subject.execute
24
+ end
25
+
26
+ it "runs the Opsicle Stack Monitor by default" do
27
+ expect(Monitor::App).to receive(:new).and_return(monitor)
28
+ expect(monitor).to receive(:start)
29
+ expect(subject).to_not receive(:open_deploy)
30
+
31
+ subject.execute
32
+ end
33
+
34
+ it "opens the OpsWorks deployments screen if browser option is given" do
35
+ expect(subject).to receive(:open_deploy)
36
+ expect(Monitor::App).to_not receive(:new)
37
+
38
+ subject.execute({browser: true})
39
+ end
40
+ end
41
+
42
+ context "#client" do
43
+ it "generates a new aws client from the given configs" do
44
+ expect(Client).to receive(:new).with('derp')
45
+ subject.client
46
+ end
47
+ end
48
+
49
+ end
50
+ end
@@ -1,5 +1,5 @@
1
1
  require "spec_helper"
2
- require "opsicle/list"
2
+ require "opsicle/commands/list"
3
3
 
4
4
  module Opsicle
5
5
  describe List do
@@ -10,19 +10,20 @@ module Opsicle
10
10
  let(:stack_ids) { [1,2,3] }
11
11
  let(:apps) { [{ name: 'test', stack_id: 1, app_id: 1}, { name: 'test2', stack_id: 2, app_id: 2}, { name: 'test3', stack_id: 3, app_id: 3 }] }
12
12
  before do
13
- Client.stub(:new).with('derp').and_return(client)
13
+ allow(Client).to receive(:new).with('derp').and_return(client)
14
14
  end
15
15
 
16
- it "creates a new deployment" do
17
- subject.should_receive(:get_stacks).and_return(stack_ids)
18
- subject.should_receive(:get_apps).with(stack_ids).and_return(apps)
16
+ it "shows a table with all of the apps/stacks from OpsWorks" do
17
+ expect(subject).to receive(:get_stacks).and_return(stack_ids)
18
+ expect(subject).to receive(:get_apps).with(stack_ids).and_return(apps)
19
+ expect(subject).to receive(:print).with(apps)
19
20
  subject.execute
20
21
  end
21
22
  end
22
23
 
23
24
  context "#client" do
24
25
  it "generates a new aws client from the given configs" do
25
- Client.should_receive(:new).with('derp')
26
+ expect(Client).to receive(:new).with('derp')
26
27
  subject.client
27
28
  end
28
29
  end
@@ -0,0 +1,75 @@
1
+ require "spec_helper"
2
+ require "opsicle/commands/ssh_key"
3
+
4
+ module Opsicle
5
+ describe SSHKey do
6
+ let(:valid_key) { "my-valid-public-key" }
7
+ subject { SSHKey.new("derp", valid_key) }
8
+ let(:client) { double(config: double(opsworks_config: {stack_id: "1234"})) }
9
+ let(:api_call) { double }
10
+
11
+ before do
12
+ allow(Client).to receive(:new).with("derp").and_return(client)
13
+ end
14
+
15
+ context "#execute" do
16
+ context "valid ssh key" do
17
+ it "confirms that the given file is a public ssh key" do
18
+ expect(subject).to receive(:validate!)
19
+ expect(subject).to receive(:say).with(/success/)
20
+ allow(subject).to receive(:update)
21
+ subject.execute
22
+ end
23
+
24
+ it "updates the user's ssh-key on opsworks" do
25
+ allow(subject).to receive(:validate!)
26
+ expect(subject).to receive(:say).with(/success/)
27
+ expect(subject).to receive(:update)
28
+ subject.execute
29
+ end
30
+ end
31
+ end
32
+
33
+ context "#validate!" do
34
+ it "validates a valid ssh key" do
35
+ allow(File).to receive(:exists?).and_return(true)
36
+ allow(subject).to receive(:valid_key_file?).and_return(true)
37
+ allow(subject).to receive(:public_key?).and_return(true)
38
+
39
+ expect { subject.validate! }.to_not raise_error
40
+ end
41
+
42
+ it "raises an error for a private ssh key" do
43
+ allow(File).to receive(:exists?).and_return(true)
44
+ allow(subject).to receive(:valid_key_file?).and_return(true)
45
+ allow(subject).to receive(:public_key?).and_return(false)
46
+
47
+ expect { subject.validate! }.to raise_error
48
+ end
49
+
50
+ it "raises an error for a non-key file" do
51
+ allow(File).to receive(:exists?).and_return(true)
52
+ allow(subject).to receive(:valid_key_file?).and_return(false)
53
+ allow(subject).to receive(:public_key?).and_return(true)
54
+
55
+ expect { subject.validate! }.to raise_error(InvalidKeyFile)
56
+ end
57
+
58
+ it "raises an error if no key file exists" do
59
+ allow(File).to receive(:exists?).and_return(false)
60
+ allow(subject).to receive(:valid_key_file?).and_return(true)
61
+ allow(subject).to receive(:public_key?).and_return(true)
62
+
63
+ expect { subject.validate! }.to raise_error(KeyFileNotFound)
64
+ end
65
+ end
66
+
67
+ context "#update" do
68
+ it "updates the users ssh key via the aws api" do
69
+ allow(subject).to receive(:key).and_return(valid_key)
70
+ expect(client).to receive(:api_call).with(:update_my_user_profile, {ssh_public_key: valid_key }).and_return(api_call)
71
+ subject.update
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,5 +1,5 @@
1
1
  require "spec_helper"
2
- require "opsicle/ssh"
2
+ require "opsicle/commands/ssh"
3
3
 
4
4
  module Opsicle
5
5
  describe SSH do
@@ -7,78 +7,78 @@ module Opsicle
7
7
  let(:client) { double(config: double(opsworks_config: {stack_id: "1234"})) }
8
8
  let(:api_call) { double }
9
9
  before do
10
- Client.stub(:new).with('derp').and_return(client)
10
+ allow(Client).to receive(:new).with('derp').and_return(client)
11
11
  end
12
12
 
13
13
  context "#execute" do
14
14
  before do
15
- subject.stub(:say) { "What instance do you want, huh?" }
16
- subject.stub(:ask).and_return(2)
17
- subject.stub(:ssh_username) {"mrderpyman2014"}
15
+ allow(subject).to receive(:say) { "What instance do you want, huh?" }
16
+ allow(subject).to receive(:ask).and_return(2)
17
+ allow(subject).to receive(:ssh_username) {"mrderpyman2014"}
18
18
  end
19
19
 
20
- it "should execute ssh with a selected Opsworks instance IP" do
21
- subject.stub(:instances) {[
20
+ it "executes ssh with a selected Opsworks instance IP" do
21
+ allow(subject).to receive(:instances) {[
22
22
  { hostname: "host1", elastic_ip: "123.123.123.123" },
23
23
  { hostname: "host2", elastic_ip: "789.789.789.789" }
24
24
  ]}
25
25
 
26
- subject.should_receive(:system).with("ssh mrderpyman2014@789.789.789.789")
26
+ expect(subject).to receive(:system).with("ssh mrderpyman2014@789.789.789.789")
27
27
  subject.execute
28
28
  end
29
29
 
30
- it "should execute ssh with public_ip listings as well as elastic_ip" do
31
- subject.stub(:instances) {[
30
+ it "executes ssh with public_ip listings as well as elastic_ip" do
31
+ allow(subject).to receive(:instances) {[
32
32
  { hostname: "host1", elastic_ip: "678.678.678.678" },
33
33
  { hostname: "host2", public_ip: "987.987.987.987" }
34
34
  ]}
35
35
 
36
- subject.should_receive(:system).with("ssh mrderpyman2014@987.987.987.987")
36
+ expect(subject).to receive(:system).with("ssh mrderpyman2014@987.987.987.987")
37
37
  subject.execute
38
38
  end
39
39
 
40
- it "should execute ssh favoring an elastic_ip over a public_ip if both exist" do
41
- subject.stub(:instances) {[
40
+ it "executes ssh favoring an elastic_ip over a public_ip if both exist" do
41
+ allow(subject).to receive(:instances) {[
42
42
  { hostname: "host1", elastic_ip: "678.678.678.678" },
43
43
  { hostname: "host2", public_ip: "987.987.987.987", elastic_ip: "132.132.132.132" }
44
44
  ]}
45
45
 
46
- subject.should_receive(:system).with("ssh mrderpyman2014@132.132.132.132")
46
+ expect(subject).to receive(:system).with("ssh mrderpyman2014@132.132.132.132")
47
47
  subject.execute
48
48
  end
49
49
 
50
- it "should execute ssh right away if there is only one Opsworks instance available" do
51
- subject.stub(:instances) {[
50
+ it "executes ssh right away if there is only one Opsworks instance available" do
51
+ allow(subject).to receive(:instances) {[
52
52
  { hostname: "host3", elastic_ip: "456.456.456.456" }
53
53
  ]}
54
54
 
55
- subject.should_receive(:system).with("ssh mrderpyman2014@456.456.456.456")
56
- subject.should_not_receive(:ask)
55
+ expect(subject).to receive(:system).with("ssh mrderpyman2014@456.456.456.456")
56
+ expect(subject).not_to receive(:ask)
57
57
  subject.execute
58
58
  end
59
59
  end
60
60
 
61
61
  context "#client" do
62
62
  it "generates a new aws client from the given configs" do
63
- Client.should_receive(:new).with('derp')
63
+ expect(Client).to receive(:new).with('derp')
64
64
  subject.client
65
65
  end
66
66
  end
67
67
 
68
68
  context "#instances" do
69
69
  it "makes a describe_instances API call" do
70
- client.stub(:api_call).with(:describe_instances, {stack_id: "1234"})
70
+ expect(client).to receive(:api_call).with(:describe_instances, {stack_id: "1234"})
71
71
  .and_return(api_call)
72
- api_call.should_receive(:data).and_return(instances: {:foo => :bar})
73
- subject.instances.should == {:foo => :bar}
72
+ expect(api_call).to receive(:data).and_return(instances: {:foo => :bar})
73
+ expect(subject.instances).to eq({:foo => :bar})
74
74
  end
75
75
  end
76
76
 
77
77
  context "#ssh_username" do
78
78
  it "makes a describe_my_user_profile API call" do
79
- client.stub(:api_call).with(:describe_my_user_profile)
79
+ allow(client).to receive(:api_call).with(:describe_my_user_profile)
80
80
  .and_return({user_profile: {:ssh_username => "captkirk01"}})
81
- subject.ssh_username.should == "captkirk01"
81
+ expect(subject.ssh_username).to eq("captkirk01")
82
82
  end
83
83
  end
84
84
 
@@ -1,39 +1,40 @@
1
1
  require "spec_helper"
2
2
  require "opsicle/config"
3
+
3
4
  module Opsicle
4
5
  describe Config do
5
6
  subject { Config.new('derp') }
6
7
  context "with a valid config" do
7
8
  before do
8
- File.stub(:exist?).with(File.expand_path '~/.fog').and_return(true)
9
- File.stub(:exist?).with('./.opsicle').and_return(true)
10
- YAML.stub(:load_file).with(File.expand_path '~/.fog').and_return({'derp' => { 'aws_access_key_id' => 'key', 'aws_secret_access_key' => 'secret'}})
11
- YAML.stub(:load_file).with('./.opsicle').and_return({'derp' => { 'app_id' => 'app', 'stack_id' => 'stack'}})
9
+ allow(File).to receive(:exist?).with(File.expand_path '~/.fog').and_return(true)
10
+ allow(File).to receive(:exist?).with('./.opsicle').and_return(true)
11
+ allow(YAML).to receive(:load_file).with(File.expand_path '~/.fog').and_return({'derp' => { 'aws_access_key_id' => 'key', 'aws_secret_access_key' => 'secret'}})
12
+ allow(YAML).to receive(:load_file).with('./.opsicle').and_return({'derp' => { 'app_id' => 'app', 'stack_id' => 'stack'}})
12
13
  end
13
14
 
14
15
  context "#aws_config" do
15
16
  it "should contain access_key_id" do
16
- subject.aws_config.should have_key(:access_key_id)
17
+ expect(subject.aws_config).to have_key(:access_key_id)
17
18
  end
18
19
 
19
20
  it "should contain secret_access_key" do
20
- subject.aws_config.should have_key(:secret_access_key)
21
+ expect(subject.aws_config).to have_key(:secret_access_key)
21
22
  end
22
23
  end
23
24
 
24
25
  context "#opsworks_config" do
25
26
  it "should contain stack_id" do
26
- subject.opsworks_config.should have_key(:stack_id)
27
+ expect(subject.opsworks_config).to have_key(:stack_id)
27
28
  end
28
29
 
29
30
  it "should contain app_id" do
30
- subject.opsworks_config.should have_key(:app_id)
31
+ expect(subject.opsworks_config).to have_key(:app_id)
31
32
  end
32
33
  end
33
34
 
34
35
  context "#configure_aws!" do
35
36
  it "should load the config into the AWS module" do
36
- AWS.should_receive(:config).with(hash_including(access_key_id: 'key', secret_access_key: 'secret'))
37
+ expect(AWS).to receive(:config).with(hash_including(access_key_id: 'key', secret_access_key: 'secret'))
37
38
  subject.configure_aws!
38
39
  end
39
40
  end
@@ -41,8 +42,8 @@ module Opsicle
41
42
 
42
43
  context "missing configs" do
43
44
  before do
44
- File.stub(:exist?).with(File.expand_path '~/.fog').and_return(false)
45
- File.stub(:exist?).with('./.opsicle').and_return(false)
45
+ allow(File).to receive(:exist?).with(File.expand_path '~/.fog').and_return(false)
46
+ allow(File).to receive(:exist?).with('./.opsicle').and_return(false)
46
47
  end
47
48
 
48
49
  context "#aws_config" do
@@ -0,0 +1,63 @@
1
+ require "spec_helper"
2
+ require "opsicle/monitor/app"
3
+ require "opsicle/monitor/screen"
4
+
5
+ describe Opsicle::Monitor::App do
6
+
7
+ before do
8
+ @screen = double(
9
+ :close => nil,
10
+ :refresh => nil,
11
+ :next_key => nil,
12
+ :refresh_spies => nil,
13
+ :missized? => nil
14
+ )
15
+
16
+ @client = double
17
+
18
+ allow(Opsicle::Monitor::Screen).to receive(:new).and_return(@screen)
19
+ allow(Opsicle::Client).to receive(:new).and_return(@client)
20
+
21
+ @app = Opsicle::Monitor::App.new("staging", {})
22
+ end
23
+
24
+ it "sets status not-running" do
25
+ expect(@app.running).to equal(false)
26
+ end
27
+
28
+ it "sets status not-restarting" do
29
+ expect(@app.restarting).to equal(false)
30
+ end
31
+
32
+ describe "#restart" do
33
+ before do
34
+ @app.instance_variable_set(:@restarting, false)
35
+ end
36
+
37
+ it "sets status restarting" do
38
+ @app.restart
39
+
40
+ expect(@app.restarting).to equal(true)
41
+ end
42
+ end
43
+
44
+ describe "#do_command" do
45
+ before do
46
+ @app.instance_variable_set(:@running, true)
47
+ @app.instance_variable_set(:@screen, @screen)
48
+ end
49
+
50
+ it "<d> switches to Deployments panel" do
51
+ expect(@screen).to receive(:panel_main=).with(:deployments)
52
+
53
+ @app.do_command('d')
54
+ end
55
+
56
+ it "<h> switches to Help panel" do
57
+ expect(@screen).to receive(:panel_main=).with(:help)
58
+
59
+ @app.do_command('h')
60
+ end
61
+ end
62
+
63
+ end