bard 1.4.8 → 1.4.9
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 +4 -4
- data/.github/workflows/ci.yml +22 -0
- data/Gemfile +4 -0
- data/bard.gemspec +3 -2
- data/lib/bard/git.rb +5 -1
- data/lib/bard/provision/repo.rb +21 -0
- data/lib/bard/provision/ssh.rb +21 -0
- data/lib/bard/server.rb +2 -2
- data/lib/bard/version.rb +1 -1
- data/spec/bard/cli/ci_spec.rb +139 -0
- data/spec/bard/cli/command_spec.rb +50 -0
- data/spec/bard/cli/data_spec.rb +77 -0
- data/spec/bard/cli/deploy_spec.rb +202 -0
- data/spec/bard/cli/hurt_spec.rb +23 -0
- data/spec/bard/cli/install_spec.rb +25 -0
- data/spec/bard/cli/master_key_spec.rb +52 -0
- data/spec/bard/cli/new_spec.rb +60 -0
- data/spec/bard/cli/open_spec.rb +67 -0
- data/spec/bard/cli/ping_spec.rb +47 -0
- data/spec/bard/cli/provision_spec.rb +42 -0
- data/spec/bard/cli/run_spec.rb +51 -0
- data/spec/bard/cli/setup_spec.rb +76 -0
- data/spec/bard/cli/ssh_spec.rb +55 -0
- data/spec/bard/cli/stage_spec.rb +77 -0
- data/spec/bard/cli/vim_spec.rb +34 -0
- data/spec/bard/command_spec.rb +37 -0
- data/spec/bard/copy_spec.rb +33 -0
- data/spec/bard/git_spec.rb +61 -0
- data/spec/bard/github_pages_spec.rb +80 -0
- data/spec/bard/github_spec.rb +45 -0
- data/spec/bard/ping_spec.rb +31 -0
- data/spec/bard/provision/app_spec.rb +33 -0
- data/spec/bard/provision/apt_spec.rb +39 -0
- data/spec/bard/provision/authorizedkeys_spec.rb +40 -0
- data/spec/bard/provision/data_spec.rb +54 -0
- data/spec/bard/provision/deploy_spec.rb +33 -0
- data/spec/bard/provision/http_spec.rb +57 -0
- data/spec/bard/provision/logrotation_spec.rb +34 -0
- data/spec/bard/provision/masterkey_spec.rb +63 -0
- data/spec/bard/provision/mysql_spec.rb +55 -0
- data/spec/bard/provision/passenger_spec.rb +81 -0
- data/spec/bard/provision/repo_spec.rb +208 -0
- data/spec/bard/provision/rvm_spec.rb +49 -0
- data/spec/bard/provision/ssh_spec.rb +229 -0
- data/spec/bard/provision/swapfile_spec.rb +32 -0
- data/spec/bard/provision/user_spec.rb +103 -0
- data/spec/bard/provision_spec.rb +28 -0
- data/spec/spec_helper.rb +6 -1
- metadata +96 -9
@@ -0,0 +1,81 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "bard/provision"
|
3
|
+
require "bard/provision/passenger"
|
4
|
+
|
5
|
+
describe Bard::Provision::Passenger do
|
6
|
+
let(:server) { double("server", project_name: "test_app") }
|
7
|
+
let(:config) { { production: server } }
|
8
|
+
let(:ssh_url) { "user@example.com" }
|
9
|
+
let(:provision_server) { double("provision_server") }
|
10
|
+
let(:passenger) { Bard::Provision::Passenger.new(config, ssh_url) }
|
11
|
+
|
12
|
+
before do
|
13
|
+
allow(passenger).to receive(:server).and_return(server)
|
14
|
+
allow(passenger).to receive(:provision_server).and_return(provision_server)
|
15
|
+
allow(provision_server).to receive_message_chain(:ssh_uri, :host).and_return("192.168.1.100")
|
16
|
+
allow(passenger).to receive(:print)
|
17
|
+
allow(passenger).to receive(:puts)
|
18
|
+
allow(passenger).to receive(:system)
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#call" do
|
22
|
+
context "when HTTP is not responding" do
|
23
|
+
it "installs nginx and passenger" do
|
24
|
+
allow(passenger).to receive(:http_responding?).and_return(false)
|
25
|
+
allow(passenger).to receive(:app_configured?).and_return(true)
|
26
|
+
|
27
|
+
expect(provision_server).to receive(:run!).with(/grep -qxF.*RAILS_ENV/, home: true)
|
28
|
+
|
29
|
+
passenger.call
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "when app is not configured" do
|
34
|
+
it "creates nginx config" do
|
35
|
+
allow(passenger).to receive(:http_responding?).and_return(true)
|
36
|
+
allow(passenger).to receive(:app_configured?).and_return(false)
|
37
|
+
|
38
|
+
expect(provision_server).to receive(:run!).with("bard setup")
|
39
|
+
|
40
|
+
passenger.call
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "when everything is already set up" do
|
45
|
+
it "skips installation and configuration" do
|
46
|
+
allow(passenger).to receive(:http_responding?).and_return(true)
|
47
|
+
allow(passenger).to receive(:app_configured?).and_return(true)
|
48
|
+
|
49
|
+
expect(provision_server).not_to receive(:run!)
|
50
|
+
|
51
|
+
passenger.call
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
it "prints status messages" do
|
56
|
+
allow(passenger).to receive(:http_responding?).and_return(true)
|
57
|
+
allow(passenger).to receive(:app_configured?).and_return(true)
|
58
|
+
|
59
|
+
expect(passenger).to receive(:print).with("Passenger:")
|
60
|
+
expect(passenger).to receive(:puts).with(" ✓")
|
61
|
+
|
62
|
+
passenger.call
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "#http_responding?" do
|
67
|
+
it "checks if port 80 is responding" do
|
68
|
+
expect(passenger).to receive(:system).with("nc -zv 192.168.1.100 80 2>/dev/null")
|
69
|
+
|
70
|
+
passenger.http_responding?
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "#app_configured?" do
|
75
|
+
it "checks if nginx config exists for the app" do
|
76
|
+
expect(provision_server).to receive(:run).with("[ -f /etc/nginx/sites-enabled/test_app ]", quiet: true)
|
77
|
+
|
78
|
+
passenger.app_configured?
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "bard/provision"
|
3
|
+
require "bard/provision/repo"
|
4
|
+
|
5
|
+
describe Bard::Provision::Repo do
|
6
|
+
let(:ssh_uri) { double("ssh_uri", user: "deploy", host: "example.com") }
|
7
|
+
let(:server) { double("server", ssh_uri: ssh_uri, project_name: "test_project") }
|
8
|
+
let(:config) { { production: server } }
|
9
|
+
let(:ssh_url) { "deploy@example.com" }
|
10
|
+
let(:provision_server) { double("provision_server") }
|
11
|
+
let(:github_api) { double("github_api") }
|
12
|
+
let(:repo_provisioner) { Bard::Provision::Repo.new(config, ssh_url) }
|
13
|
+
|
14
|
+
before do
|
15
|
+
allow(repo_provisioner).to receive(:server).and_return(server)
|
16
|
+
allow(repo_provisioner).to receive(:provision_server).and_return(provision_server)
|
17
|
+
allow(repo_provisioner).to receive(:print)
|
18
|
+
allow(repo_provisioner).to receive(:puts)
|
19
|
+
allow(Bard::Github).to receive(:new).and_return(github_api)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#call" do
|
23
|
+
context "when repository is already cloned" do
|
24
|
+
context "when not on latest master" do
|
25
|
+
it "updates to latest master" do
|
26
|
+
allow(repo_provisioner).to receive(:already_cloned?).and_return(true)
|
27
|
+
allow(repo_provisioner).to receive(:on_latest_master?).and_return(false)
|
28
|
+
|
29
|
+
expect(repo_provisioner).to receive(:update_to_latest_master!)
|
30
|
+
expect(github_api).not_to receive(:add_deploy_key)
|
31
|
+
|
32
|
+
repo_provisioner.call
|
33
|
+
end
|
34
|
+
|
35
|
+
it "prints status message when updating to latest master" do
|
36
|
+
allow(repo_provisioner).to receive(:already_cloned?).and_return(true)
|
37
|
+
allow(repo_provisioner).to receive(:on_latest_master?).and_return(false)
|
38
|
+
allow(repo_provisioner).to receive(:update_to_latest_master!)
|
39
|
+
|
40
|
+
expect(repo_provisioner).to receive(:print).with("Repo:")
|
41
|
+
expect(repo_provisioner).to receive(:print).with(" Updating to latest master,")
|
42
|
+
expect(repo_provisioner).to receive(:puts).with(" ✓")
|
43
|
+
|
44
|
+
repo_provisioner.call
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when already on latest master" do
|
49
|
+
it "skips update" do
|
50
|
+
allow(repo_provisioner).to receive(:already_cloned?).and_return(true)
|
51
|
+
allow(repo_provisioner).to receive(:on_latest_master?).and_return(true)
|
52
|
+
|
53
|
+
expect(repo_provisioner).not_to receive(:update_to_latest_master!)
|
54
|
+
expect(github_api).not_to receive(:add_deploy_key)
|
55
|
+
|
56
|
+
repo_provisioner.call
|
57
|
+
end
|
58
|
+
|
59
|
+
it "only prints repo header and checkbox" do
|
60
|
+
allow(repo_provisioner).to receive(:already_cloned?).and_return(true)
|
61
|
+
allow(repo_provisioner).to receive(:on_latest_master?).and_return(true)
|
62
|
+
|
63
|
+
expect(repo_provisioner).to receive(:print).with("Repo:")
|
64
|
+
expect(repo_provisioner).not_to receive(:print).with(" Updating to latest master,")
|
65
|
+
expect(repo_provisioner).to receive(:puts).with(" ✓")
|
66
|
+
|
67
|
+
repo_provisioner.call
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "when repository is not cloned but can be cloned" do
|
73
|
+
it "clones the repository directly" do
|
74
|
+
allow(repo_provisioner).to receive(:already_cloned?).and_return(false)
|
75
|
+
allow(repo_provisioner).to receive(:can_clone_project?).and_return(true)
|
76
|
+
|
77
|
+
expect(provision_server).to receive(:run!).with("git clone git@github.com:botandrosedesign/test_project", home: true)
|
78
|
+
expect(github_api).not_to receive(:add_deploy_key)
|
79
|
+
|
80
|
+
repo_provisioner.call
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "when repository cannot be cloned and SSH keypair exists" do
|
85
|
+
it "adds deploy key and clones repository" do
|
86
|
+
allow(repo_provisioner).to receive(:already_cloned?).and_return(false)
|
87
|
+
allow(repo_provisioner).to receive(:can_clone_project?).and_return(false)
|
88
|
+
allow(repo_provisioner).to receive(:ssh_keypair?).and_return(true)
|
89
|
+
allow(provision_server).to receive(:run).with("cat ~/.ssh/id_rsa.pub", home: true).and_return("ssh-rsa AAAAB3...")
|
90
|
+
|
91
|
+
expect(github_api).to receive(:add_deploy_key).with(title: "deploy@example.com", key: "ssh-rsa AAAAB3...")
|
92
|
+
expect(provision_server).to receive(:run!).with("git clone git@github.com:botandrosedesign/test_project", home: true)
|
93
|
+
|
94
|
+
repo_provisioner.call
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "when repository cannot be cloned and no SSH keypair exists" do
|
99
|
+
it "generates keypair, adds deploy key, and clones repository" do
|
100
|
+
allow(repo_provisioner).to receive(:already_cloned?).and_return(false)
|
101
|
+
allow(repo_provisioner).to receive(:can_clone_project?).and_return(false)
|
102
|
+
allow(repo_provisioner).to receive(:ssh_keypair?).and_return(false)
|
103
|
+
allow(provision_server).to receive(:run).with("cat ~/.ssh/id_rsa.pub", home: true).and_return("ssh-rsa AAAAB3...")
|
104
|
+
|
105
|
+
expect(provision_server).to receive(:run!).with('ssh-keygen -t rsa -b 2048 -f ~/.ssh/id_rsa -q -N ""', home: true)
|
106
|
+
expect(github_api).to receive(:add_deploy_key).with(title: "deploy@example.com", key: "ssh-rsa AAAAB3...")
|
107
|
+
expect(provision_server).to receive(:run!).with("git clone git@github.com:botandrosedesign/test_project", home: true)
|
108
|
+
|
109
|
+
repo_provisioner.call
|
110
|
+
end
|
111
|
+
|
112
|
+
it "prints status messages during setup" do
|
113
|
+
allow(repo_provisioner).to receive(:already_cloned?).and_return(false)
|
114
|
+
allow(repo_provisioner).to receive(:can_clone_project?).and_return(false)
|
115
|
+
allow(repo_provisioner).to receive(:ssh_keypair?).and_return(false)
|
116
|
+
allow(provision_server).to receive(:run).and_return("ssh-rsa AAAAB3...")
|
117
|
+
allow(provision_server).to receive(:run!)
|
118
|
+
allow(github_api).to receive(:add_deploy_key)
|
119
|
+
|
120
|
+
expect(repo_provisioner).to receive(:print).with("Repo:")
|
121
|
+
expect(repo_provisioner).to receive(:print).with(" Generating keypair in ~/.ssh,")
|
122
|
+
expect(repo_provisioner).to receive(:print).with(" Add public key to GitHub repo deploy keys,")
|
123
|
+
expect(repo_provisioner).to receive(:print).with(" Cloning repo,")
|
124
|
+
expect(repo_provisioner).to receive(:puts).with(" ✓")
|
125
|
+
|
126
|
+
repo_provisioner.call
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
it "always prints success message" do
|
131
|
+
allow(repo_provisioner).to receive(:already_cloned?).and_return(true)
|
132
|
+
allow(repo_provisioner).to receive(:on_latest_master?).and_return(true)
|
133
|
+
|
134
|
+
expect(repo_provisioner).to receive(:print).with("Repo:")
|
135
|
+
expect(repo_provisioner).to receive(:puts).with(" ✓")
|
136
|
+
|
137
|
+
repo_provisioner.call
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "private methods" do
|
142
|
+
describe "#ssh_keypair?" do
|
143
|
+
it "checks if SSH public key exists" do
|
144
|
+
expect(provision_server).to receive(:run).with("[ -f ~/.ssh/id_rsa.pub ]", home: true, quiet: true)
|
145
|
+
|
146
|
+
repo_provisioner.send(:ssh_keypair?)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe "#already_cloned?" do
|
151
|
+
it "checks if git directory exists" do
|
152
|
+
expect(provision_server).to receive(:run).with("[ -d ~/test_project/.git ]", home: true, quiet: true)
|
153
|
+
|
154
|
+
repo_provisioner.send(:already_cloned?)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe "#can_clone_project?" do
|
159
|
+
it "tests if repository can be cloned" do
|
160
|
+
expected_commands = [
|
161
|
+
"needle=$(ssh-keyscan -t ed25519 github.com 2>/dev/null | cut -d \" \" -f 2-3)",
|
162
|
+
"grep -q \"$needle\" ~/.ssh/known_hosts || ssh-keyscan -H github.com >> ~/.ssh/known_hosts 2>/dev/null",
|
163
|
+
"git ls-remote git@github.com:botandrosedesign/test_project"
|
164
|
+
].join("; ")
|
165
|
+
|
166
|
+
expect(provision_server).to receive(:run).with(expected_commands, home: true, quiet: true)
|
167
|
+
|
168
|
+
repo_provisioner.send(:can_clone_project?)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "#project_name" do
|
173
|
+
it "returns the server's project name" do
|
174
|
+
expect(repo_provisioner.send(:project_name)).to eq("test_project")
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "#on_latest_master?" do
|
179
|
+
it "checks if current HEAD matches origin/master after fetching" do
|
180
|
+
expected_command = "cd ~/test_project && git fetch origin && [ $(git rev-parse HEAD) = $(git rev-parse origin/master) ]"
|
181
|
+
expect(provision_server).to receive(:run).with(expected_command, home: true, quiet: true)
|
182
|
+
|
183
|
+
repo_provisioner.send(:on_latest_master?)
|
184
|
+
end
|
185
|
+
|
186
|
+
it "returns true when on latest master" do
|
187
|
+
allow(provision_server).to receive(:run).and_return(true)
|
188
|
+
|
189
|
+
expect(repo_provisioner.send(:on_latest_master?)).to be true
|
190
|
+
end
|
191
|
+
|
192
|
+
it "returns false when not on latest master" do
|
193
|
+
allow(provision_server).to receive(:run).and_return(false)
|
194
|
+
|
195
|
+
expect(repo_provisioner.send(:on_latest_master?)).to be false
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
describe "#update_to_latest_master!" do
|
200
|
+
it "checks out master and resets to origin/master" do
|
201
|
+
expected_command = "cd ~/test_project && git checkout master && git reset --hard origin/master"
|
202
|
+
expect(provision_server).to receive(:run!).with(expected_command, home: true)
|
203
|
+
|
204
|
+
repo_provisioner.send(:update_to_latest_master!)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "bard/provision"
|
3
|
+
require "bard/provision/rvm"
|
4
|
+
|
5
|
+
describe Bard::Provision::RVM do
|
6
|
+
let(:config) { { production: double("production") } }
|
7
|
+
let(:ssh_url) { "user@example.com" }
|
8
|
+
let(:provision_server) { double("provision_server") }
|
9
|
+
let(:rvm) { Bard::Provision::RVM.new(config, ssh_url) }
|
10
|
+
|
11
|
+
before do
|
12
|
+
allow(rvm).to receive(:provision_server).and_return(provision_server)
|
13
|
+
allow(rvm).to receive(:print)
|
14
|
+
allow(rvm).to receive(:puts)
|
15
|
+
allow(File).to receive(:read).with(".ruby-version").and_return("3.2.0\n")
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#call" do
|
19
|
+
context "when RVM is not installed" do
|
20
|
+
it "installs RVM and Ruby" do
|
21
|
+
allow(provision_server).to receive(:run).with("[ -d ~/.rvm ]", quiet: true).and_return(false)
|
22
|
+
|
23
|
+
expect(provision_server).to receive(:run!).with(/sed -i.*bashrc/)
|
24
|
+
expect(provision_server).to receive(:run!).with("rvm install 3.2.0")
|
25
|
+
|
26
|
+
rvm.call
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "when RVM is already installed" do
|
31
|
+
it "skips installation" do
|
32
|
+
allow(provision_server).to receive(:run).with("[ -d ~/.rvm ]", quiet: true).and_return(true)
|
33
|
+
|
34
|
+
expect(provision_server).not_to receive(:run!)
|
35
|
+
|
36
|
+
rvm.call
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "prints status messages" do
|
41
|
+
allow(provision_server).to receive(:run).and_return(true)
|
42
|
+
|
43
|
+
expect(rvm).to receive(:print).with("RVM:")
|
44
|
+
expect(rvm).to receive(:puts).with(" ✓")
|
45
|
+
|
46
|
+
rvm.call
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "bard/provision"
|
3
|
+
require "bard/provision/ssh"
|
4
|
+
|
5
|
+
describe Bard::Provision::SSH do
|
6
|
+
let(:ssh_uri) { double("ssh_uri", host: "example.com", port: 2222) }
|
7
|
+
let(:provision_ssh_uri) { double("provision_ssh_uri", host: "example.com", port: nil) }
|
8
|
+
let(:server) { double("server", ssh_uri: ssh_uri) }
|
9
|
+
let(:config) { { production: server } }
|
10
|
+
let(:ssh_url) { "user@example.com" }
|
11
|
+
let(:provision_server) { double("provision_server", ssh_uri: provision_ssh_uri) }
|
12
|
+
let(:ssh_provisioner) { Bard::Provision::SSH.new(config, ssh_url) }
|
13
|
+
|
14
|
+
before do
|
15
|
+
allow(ssh_provisioner).to receive(:server).and_return(server)
|
16
|
+
allow(ssh_provisioner).to receive(:provision_server).and_return(provision_server)
|
17
|
+
allow(ssh_provisioner).to receive(:print)
|
18
|
+
allow(ssh_provisioner).to receive(:puts)
|
19
|
+
allow(ssh_provisioner).to receive(:system)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#call" do
|
23
|
+
context "when SSH is already available on target port" do
|
24
|
+
it "skips port reconfiguration but checks known hosts" do
|
25
|
+
allow(ssh_provisioner).to receive(:password_auth_enabled?).and_return(false)
|
26
|
+
allow(ssh_provisioner).to receive(:ssh_available?).with(provision_ssh_uri, port: 2222).and_return(true)
|
27
|
+
allow(ssh_provisioner).to receive(:ssh_known_host?).with(provision_ssh_uri).and_return(true)
|
28
|
+
|
29
|
+
expect(provision_server).not_to receive(:run!)
|
30
|
+
|
31
|
+
ssh_provisioner.call
|
32
|
+
end
|
33
|
+
|
34
|
+
it "adds to known hosts if not present" do
|
35
|
+
allow(ssh_provisioner).to receive(:password_auth_enabled?).and_return(false)
|
36
|
+
allow(ssh_provisioner).to receive(:ssh_available?).with(provision_ssh_uri, port: 2222).and_return(true)
|
37
|
+
allow(ssh_provisioner).to receive(:ssh_known_host?).with(provision_ssh_uri).and_return(false)
|
38
|
+
|
39
|
+
expect(ssh_provisioner).to receive(:add_ssh_known_host!).with(provision_ssh_uri)
|
40
|
+
|
41
|
+
ssh_provisioner.call
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "when SSH is not available on target port but available on default port" do
|
46
|
+
it "reconfigures SSH port and adds to known hosts" do
|
47
|
+
allow(ssh_provisioner).to receive(:password_auth_enabled?).and_return(false)
|
48
|
+
allow(ssh_provisioner).to receive(:ssh_available?).with(provision_ssh_uri, port: 2222).and_return(false)
|
49
|
+
allow(ssh_provisioner).to receive(:ssh_available?).with(provision_ssh_uri).and_return(true)
|
50
|
+
allow(ssh_provisioner).to receive(:ssh_known_host?).with(provision_ssh_uri).and_return(false)
|
51
|
+
|
52
|
+
expect(ssh_provisioner).to receive(:add_ssh_known_host!).with(provision_ssh_uri).twice
|
53
|
+
expect(provision_server).to receive(:run!).with(
|
54
|
+
'echo "Port 2222" | sudo tee /etc/ssh/sshd_config.d/port_2222.conf; sudo service ssh restart',
|
55
|
+
home: true
|
56
|
+
)
|
57
|
+
|
58
|
+
ssh_provisioner.call
|
59
|
+
end
|
60
|
+
|
61
|
+
it "prints status messages during reconfiguration" do
|
62
|
+
allow(ssh_provisioner).to receive(:password_auth_enabled?).and_return(false)
|
63
|
+
allow(ssh_provisioner).to receive(:ssh_available?).with(provision_ssh_uri, port: 2222).and_return(false)
|
64
|
+
allow(ssh_provisioner).to receive(:ssh_available?).with(provision_ssh_uri).and_return(true)
|
65
|
+
allow(ssh_provisioner).to receive(:ssh_known_host?).and_return(false)
|
66
|
+
allow(ssh_provisioner).to receive(:add_ssh_known_host!)
|
67
|
+
allow(provision_server).to receive(:run!)
|
68
|
+
|
69
|
+
expect(ssh_provisioner).to receive(:print).with("SSH:")
|
70
|
+
expect(ssh_provisioner).to receive(:print).with(" Adding known host,")
|
71
|
+
expect(ssh_provisioner).to receive(:print).with(" Reconfiguring port to 2222,")
|
72
|
+
expect(ssh_provisioner).to receive(:print).with(" Adding known host,")
|
73
|
+
expect(ssh_provisioner).to receive(:puts).with(" ✓")
|
74
|
+
|
75
|
+
ssh_provisioner.call
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "when SSH is not available on either port" do
|
80
|
+
it "raises an error" do
|
81
|
+
allow(ssh_provisioner).to receive(:password_auth_enabled?).and_return(false)
|
82
|
+
allow(ssh_provisioner).to receive(:ssh_available?).with(provision_ssh_uri, port: 2222).and_return(false)
|
83
|
+
allow(ssh_provisioner).to receive(:ssh_available?).with(provision_ssh_uri).and_return(false)
|
84
|
+
|
85
|
+
expect { ssh_provisioner.call }.to raise_error("can't find SSH on port 2222 or 22")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "when password authentication is enabled" do
|
90
|
+
it "disables password authentication" do
|
91
|
+
allow(ssh_provisioner).to receive(:password_auth_enabled?).and_return(true)
|
92
|
+
allow(ssh_provisioner).to receive(:ssh_available?).with(provision_ssh_uri, port: 2222).and_return(true)
|
93
|
+
allow(ssh_provisioner).to receive(:ssh_known_host?).and_return(true)
|
94
|
+
|
95
|
+
expect(ssh_provisioner).to receive(:disable_password_auth!)
|
96
|
+
|
97
|
+
ssh_provisioner.call
|
98
|
+
end
|
99
|
+
|
100
|
+
it "prints status message when disabling password authentication" do
|
101
|
+
allow(ssh_provisioner).to receive(:password_auth_enabled?).and_return(true)
|
102
|
+
allow(ssh_provisioner).to receive(:disable_password_auth!)
|
103
|
+
allow(ssh_provisioner).to receive(:ssh_available?).with(provision_ssh_uri, port: 2222).and_return(true)
|
104
|
+
allow(ssh_provisioner).to receive(:ssh_known_host?).and_return(true)
|
105
|
+
|
106
|
+
expect(ssh_provisioner).to receive(:print).with("SSH:")
|
107
|
+
expect(ssh_provisioner).to receive(:print).with(" Disabling password authentication,")
|
108
|
+
expect(ssh_provisioner).to receive(:puts).with(" ✓")
|
109
|
+
|
110
|
+
ssh_provisioner.call
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "when password authentication is already disabled" do
|
115
|
+
it "skips disabling password authentication" do
|
116
|
+
allow(ssh_provisioner).to receive(:password_auth_enabled?).and_return(false)
|
117
|
+
allow(ssh_provisioner).to receive(:ssh_available?).with(provision_ssh_uri, port: 2222).and_return(true)
|
118
|
+
allow(ssh_provisioner).to receive(:ssh_known_host?).and_return(true)
|
119
|
+
|
120
|
+
expect(ssh_provisioner).not_to receive(:disable_password_auth!)
|
121
|
+
|
122
|
+
ssh_provisioner.call
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
it "updates the SSH URL with the target port" do
|
127
|
+
allow(ssh_provisioner).to receive(:password_auth_enabled?).and_return(false)
|
128
|
+
allow(ssh_provisioner).to receive(:ssh_available?).with(provision_ssh_uri, port: 2222).and_return(true)
|
129
|
+
allow(ssh_provisioner).to receive(:ssh_known_host?).and_return(true)
|
130
|
+
|
131
|
+
ssh_provisioner.call
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "private methods" do
|
136
|
+
describe "#target_port" do
|
137
|
+
it "returns the server's SSH port" do
|
138
|
+
expect(ssh_provisioner.send(:target_port)).to eq(2222)
|
139
|
+
end
|
140
|
+
|
141
|
+
it "defaults to 22 if no port is specified" do
|
142
|
+
allow(ssh_uri).to receive(:port).and_return(nil)
|
143
|
+
expect(ssh_provisioner.send(:target_port)).to eq(22)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe "#ssh_available?" do
|
148
|
+
it "tests SSH connectivity on specified port" do
|
149
|
+
expect(ssh_provisioner).to receive(:system).with("nc -zv example.com 2222 2>/dev/null")
|
150
|
+
|
151
|
+
ssh_provisioner.send(:ssh_available?, provision_ssh_uri, port: 2222)
|
152
|
+
end
|
153
|
+
|
154
|
+
it "uses SSH URI port if no port specified" do
|
155
|
+
allow(provision_ssh_uri).to receive(:port).and_return(2222)
|
156
|
+
expect(ssh_provisioner).to receive(:system).with("nc -zv example.com 2222 2>/dev/null")
|
157
|
+
|
158
|
+
ssh_provisioner.send(:ssh_available?, provision_ssh_uri)
|
159
|
+
end
|
160
|
+
|
161
|
+
it "defaults to port 22 if no port in URI or parameter" do
|
162
|
+
expect(ssh_provisioner).to receive(:system).with("nc -zv example.com 22 2>/dev/null")
|
163
|
+
|
164
|
+
ssh_provisioner.send(:ssh_available?, provision_ssh_uri)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe "#ssh_known_host?" do
|
169
|
+
it "checks if host is in known_hosts file" do
|
170
|
+
expected_command = 'grep -q "$(ssh-keyscan -t ed25519 -p22 example.com 2>/dev/null | cut -d \' \' -f 2-3)" ~/.ssh/known_hosts'
|
171
|
+
expect(ssh_provisioner).to receive(:system).with(expected_command)
|
172
|
+
|
173
|
+
ssh_provisioner.send(:ssh_known_host?, provision_ssh_uri)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "#add_ssh_known_host!" do
|
178
|
+
it "adds host to known_hosts file" do
|
179
|
+
expected_command = "ssh-keyscan -p22 -H example.com >> ~/.ssh/known_hosts 2>/dev/null"
|
180
|
+
expect(ssh_provisioner).to receive(:system).with(expected_command)
|
181
|
+
|
182
|
+
ssh_provisioner.send(:add_ssh_known_host!, provision_ssh_uri)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe "#password_auth_enabled?" do
|
187
|
+
it "returns true when password authentication is enabled" do
|
188
|
+
expect(provision_server).to receive(:run!).with(
|
189
|
+
%q{grep -E '^\s*PasswordAuthentication\s+yes' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf 2>/dev/null || true},
|
190
|
+
home: true,
|
191
|
+
capture: true
|
192
|
+
).and_return("PasswordAuthentication yes\n")
|
193
|
+
|
194
|
+
expect(ssh_provisioner.send(:password_auth_enabled?)).to be true
|
195
|
+
end
|
196
|
+
|
197
|
+
it "returns false when password authentication is disabled" do
|
198
|
+
expect(provision_server).to receive(:run!).with(
|
199
|
+
%q{grep -E '^\s*PasswordAuthentication\s+yes' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf 2>/dev/null || true},
|
200
|
+
home: true,
|
201
|
+
capture: true
|
202
|
+
).and_return("")
|
203
|
+
|
204
|
+
expect(ssh_provisioner.send(:password_auth_enabled?)).to be false
|
205
|
+
end
|
206
|
+
|
207
|
+
it "returns false when grep returns nil" do
|
208
|
+
expect(provision_server).to receive(:run!).with(
|
209
|
+
%q{grep -E '^\s*PasswordAuthentication\s+yes' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf 2>/dev/null || true},
|
210
|
+
home: true,
|
211
|
+
capture: true
|
212
|
+
).and_return(nil)
|
213
|
+
|
214
|
+
expect(ssh_provisioner.send(:password_auth_enabled?)).to be false
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
describe "#disable_password_auth!" do
|
219
|
+
it "creates sshd config file to disable password authentication and restarts ssh" do
|
220
|
+
expect(provision_server).to receive(:run!).with(
|
221
|
+
%q{echo "PasswordAuthentication no" | sudo tee /etc/ssh/sshd_config.d/disable_password_auth.conf; sudo service ssh restart},
|
222
|
+
home: true
|
223
|
+
)
|
224
|
+
|
225
|
+
ssh_provisioner.send(:disable_password_auth!)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "bard/provision"
|
3
|
+
require "bard/provision/swapfile"
|
4
|
+
|
5
|
+
describe Bard::Provision::Swapfile do
|
6
|
+
let(:config) { { production: double("production") } }
|
7
|
+
let(:ssh_url) { "user@example.com" }
|
8
|
+
let(:provision_server) { double("provision_server") }
|
9
|
+
let(:swapfile) { Bard::Provision::Swapfile.new(config, ssh_url) }
|
10
|
+
|
11
|
+
before do
|
12
|
+
allow(swapfile).to receive(:provision_server).and_return(provision_server)
|
13
|
+
allow(swapfile).to receive(:print)
|
14
|
+
allow(swapfile).to receive(:puts)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#call" do
|
18
|
+
it "sets up swapfile on the server" do
|
19
|
+
expect(provision_server).to receive(:run!).with(/if \[ ! -f \/swapfile \]/)
|
20
|
+
|
21
|
+
swapfile.call
|
22
|
+
end
|
23
|
+
|
24
|
+
it "prints status messages" do
|
25
|
+
allow(provision_server).to receive(:run!)
|
26
|
+
expect(swapfile).to receive(:print).with("Swapfile:")
|
27
|
+
expect(swapfile).to receive(:puts).with(" ✓")
|
28
|
+
|
29
|
+
swapfile.call
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|