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.
- checksums.yaml +8 -8
- data/.gitignore +3 -0
- data/.travis.yml +6 -1
- data/Gemfile +3 -0
- data/Guardfile +5 -0
- data/README.markdown +44 -17
- data/bin/opsicle +36 -3
- data/lib/opsicle.rb +2 -3
- data/lib/opsicle/commands.rb +6 -0
- data/lib/opsicle/commands/deploy.rb +39 -0
- data/lib/opsicle/{list.rb → commands/list.rb} +0 -2
- data/lib/opsicle/{ssh.rb → commands/ssh.rb} +0 -3
- data/lib/opsicle/commands/ssh_key.rb +40 -0
- data/lib/opsicle/config.rb +1 -0
- data/lib/opsicle/deployment.rb +59 -0
- data/lib/opsicle/deployments.rb +22 -0
- data/lib/opsicle/monitor.rb +12 -0
- data/lib/opsicle/monitor/app.rb +147 -0
- data/lib/opsicle/monitor/panel.rb +98 -0
- data/lib/opsicle/monitor/panels/deployments.rb +42 -0
- data/lib/opsicle/monitor/panels/header.rb +48 -0
- data/lib/opsicle/monitor/panels/help.rb +33 -0
- data/lib/opsicle/monitor/screen.rb +83 -0
- data/lib/opsicle/monitor/spy/dataspyable.rb +19 -0
- data/lib/opsicle/monitor/spy/deployments.rb +53 -0
- data/lib/opsicle/monitor/subpanel.rb +55 -0
- data/lib/opsicle/monitor/translatable.rb +33 -0
- data/lib/opsicle/stack.rb +22 -0
- data/lib/opsicle/version.rb +1 -1
- data/opsicle.gemspec +1 -1
- data/spec/opsicle/client_spec.rb +6 -6
- data/spec/opsicle/commands/deploy_spec.rb +50 -0
- data/spec/opsicle/{list_spec.rb → commands/list_spec.rb} +7 -6
- data/spec/opsicle/commands/ssh_key_spec.rb +75 -0
- data/spec/opsicle/{ssh_spec.rb → commands/ssh_spec.rb} +24 -24
- data/spec/opsicle/config_spec.rb +12 -11
- data/spec/opsicle/monitor/app_spec.rb +63 -0
- data/spec/opsicle/monitor/panel_spec.rb +162 -0
- data/spec/opsicle/monitor/screen_spec.rb +121 -0
- data/spec/opsicle/monitor/spy/deployments_spec.rb +41 -0
- data/spec/opsicle/monitor/subpanel_spec.rb +199 -0
- data/spec/spec_helper.rb +2 -1
- metadata +44 -16
- data/Gemfile.lock +0 -75
- data/lib/opsicle/deploy.rb +0 -25
- 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
|
data/lib/opsicle/version.rb
CHANGED
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
|
data/spec/opsicle/client_spec.rb
CHANGED
@@ -8,16 +8,16 @@ module Opsicle
|
|
8
8
|
let(:config) { double }
|
9
9
|
before do
|
10
10
|
ow_stub = double
|
11
|
-
config.
|
12
|
-
ow_stub.
|
13
|
-
Config.
|
14
|
-
AWS::OpsWorks.
|
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.
|
20
|
-
aws_client.
|
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.
|
13
|
+
allow(Client).to receive(:new).with('derp').and_return(client)
|
14
14
|
end
|
15
15
|
|
16
|
-
it "
|
17
|
-
subject.
|
18
|
-
subject.
|
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.
|
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.
|
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.
|
16
|
-
subject.
|
17
|
-
subject.
|
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 "
|
21
|
-
subject.
|
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.
|
26
|
+
expect(subject).to receive(:system).with("ssh mrderpyman2014@789.789.789.789")
|
27
27
|
subject.execute
|
28
28
|
end
|
29
29
|
|
30
|
-
it "
|
31
|
-
subject.
|
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.
|
36
|
+
expect(subject).to receive(:system).with("ssh mrderpyman2014@987.987.987.987")
|
37
37
|
subject.execute
|
38
38
|
end
|
39
39
|
|
40
|
-
it "
|
41
|
-
subject.
|
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.
|
46
|
+
expect(subject).to receive(:system).with("ssh mrderpyman2014@132.132.132.132")
|
47
47
|
subject.execute
|
48
48
|
end
|
49
49
|
|
50
|
-
it "
|
51
|
-
subject.
|
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.
|
56
|
-
subject.
|
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.
|
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.
|
70
|
+
expect(client).to receive(:api_call).with(:describe_instances, {stack_id: "1234"})
|
71
71
|
.and_return(api_call)
|
72
|
-
api_call.
|
73
|
-
subject.instances.
|
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.
|
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.
|
81
|
+
expect(subject.ssh_username).to eq("captkirk01")
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
data/spec/opsicle/config_spec.rb
CHANGED
@@ -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.
|
9
|
-
File.
|
10
|
-
YAML.
|
11
|
-
YAML.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
45
|
-
File.
|
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
|