devbox_launcher 0.4.0 → 0.6.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 +4 -4
- data/.gitignore +0 -0
- data/.rspec +0 -0
- data/.travis.yml +0 -0
- data/CHANGELOG.md +19 -0
- data/CODE_OF_CONDUCT.md +0 -0
- data/Gemfile +0 -0
- data/Gemfile.lock +16 -17
- data/LICENSE.txt +0 -0
- data/README.md +16 -13
- data/Rakefile +0 -0
- data/devbox_launcher.gemspec +1 -1
- data/lib/devbox_launcher/cli.rb +1 -0
- data/lib/devbox_launcher/models/account_config.rb +22 -0
- data/lib/devbox_launcher/models/box.rb +75 -45
- data/lib/devbox_launcher/models/box_config.rb +23 -0
- data/lib/devbox_launcher/version.rb +1 -1
- data/lib/devbox_launcher.rb +3 -0
- metadata +7 -6
- data/lib/devbox_launcher/box.rb +0 -214
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c48092de409744a2c52020e6caf65cf2d06377ab686aca517ede0b9790b0d414
|
|
4
|
+
data.tar.gz: 7cdae080f466ead661718d23bc320f424049cc118887aab9402d5043f0d05184
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 32574d16bae08ed7470449b7c36cdd6acc9f76b176e9e5342b9a70a49da9001debee5ee4793bb0295221606e07158e6aa20984f7d762d462bd8fbfb69ec36923
|
|
7
|
+
data.tar.gz: 94fee4aa07ef525c2b5c4de214bc2452aa7c8c8f9a2e7a43103d0a0c6e9c3a6c72f76bbe66a93601006d8532efe46b42a211a9d2fbdc96c194907f800d8ff257
|
data/.gitignore
CHANGED
|
File without changes
|
data/.rspec
CHANGED
|
File without changes
|
data/.travis.yml
CHANGED
|
File without changes
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,25 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.6.0] - 2021-09-21
|
|
8
|
+
### Changed
|
|
9
|
+
- Ability to configure multiple boxes under one account
|
|
10
|
+
|
|
11
|
+
## [0.5.2] - 2021-05-31
|
|
12
|
+
### Added
|
|
13
|
+
- Ignore VCS as recommended by mutagen
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- Retry on `Errno::ECONNREFUSED`
|
|
17
|
+
|
|
18
|
+
## [0.5.1] - 2021-04-08
|
|
19
|
+
### Fixed
|
|
20
|
+
- Use configured `zone` for describe as well
|
|
21
|
+
|
|
22
|
+
## [0.5.0] - 2021-04-06
|
|
23
|
+
### Added
|
|
24
|
+
- Ability to specify `zone` in config
|
|
25
|
+
|
|
7
26
|
## [0.4.0]
|
|
8
27
|
### Added
|
|
9
28
|
- Sync mutagen with two-way-resolved
|
data/CODE_OF_CONDUCT.md
CHANGED
|
File without changes
|
data/Gemfile
CHANGED
|
File without changes
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
devbox_launcher (0.
|
|
4
|
+
devbox_launcher (0.6.0)
|
|
5
5
|
activesupport (~> 6.0)
|
|
6
6
|
bcrypt_pbkdf (~> 1.0)
|
|
7
7
|
ed25519 (~> 1.2)
|
|
@@ -14,22 +14,22 @@ PATH
|
|
|
14
14
|
GEM
|
|
15
15
|
remote: https://rubygems.org/
|
|
16
16
|
specs:
|
|
17
|
-
activesupport (6.
|
|
17
|
+
activesupport (6.1.4.1)
|
|
18
18
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
19
|
-
i18n (>=
|
|
20
|
-
minitest (
|
|
21
|
-
tzinfo (~>
|
|
22
|
-
zeitwerk (~> 2.
|
|
23
|
-
bcrypt_pbkdf (1.0
|
|
19
|
+
i18n (>= 1.6, < 2)
|
|
20
|
+
minitest (>= 5.1)
|
|
21
|
+
tzinfo (~> 2.0)
|
|
22
|
+
zeitwerk (~> 2.3)
|
|
23
|
+
bcrypt_pbkdf (1.1.0)
|
|
24
24
|
byebug (11.0.1)
|
|
25
25
|
coderay (1.1.2)
|
|
26
|
-
concurrent-ruby (1.1.
|
|
26
|
+
concurrent-ruby (1.1.9)
|
|
27
27
|
diff-lcs (1.3)
|
|
28
28
|
ed25519 (1.2.4)
|
|
29
|
-
i18n (1.8.
|
|
29
|
+
i18n (1.8.10)
|
|
30
30
|
concurrent-ruby (~> 1.0)
|
|
31
31
|
method_source (0.9.2)
|
|
32
|
-
minitest (5.14.
|
|
32
|
+
minitest (5.14.4)
|
|
33
33
|
net-ssh (5.2.0)
|
|
34
34
|
os (1.1.1)
|
|
35
35
|
pry (0.12.2)
|
|
@@ -38,7 +38,7 @@ GEM
|
|
|
38
38
|
pry-byebug (3.7.0)
|
|
39
39
|
byebug (~> 11.0)
|
|
40
40
|
pry (~> 0.10)
|
|
41
|
-
rake (
|
|
41
|
+
rake (13.0.1)
|
|
42
42
|
rspec (3.9.0)
|
|
43
43
|
rspec-core (~> 3.9.0)
|
|
44
44
|
rspec-expectations (~> 3.9.0)
|
|
@@ -54,11 +54,10 @@ GEM
|
|
|
54
54
|
rspec-support (3.9.0)
|
|
55
55
|
ruby-watchman (0.0.2)
|
|
56
56
|
ssh-config (0.1.3)
|
|
57
|
-
thor (1.0
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
zeitwerk (2.4.1)
|
|
57
|
+
thor (1.1.0)
|
|
58
|
+
tzinfo (2.0.4)
|
|
59
|
+
concurrent-ruby (~> 1.0)
|
|
60
|
+
zeitwerk (2.4.2)
|
|
62
61
|
|
|
63
62
|
PLATFORMS
|
|
64
63
|
ruby
|
|
@@ -67,7 +66,7 @@ DEPENDENCIES
|
|
|
67
66
|
bundler (~> 2.0)
|
|
68
67
|
devbox_launcher!
|
|
69
68
|
pry-byebug
|
|
70
|
-
rake (~>
|
|
69
|
+
rake (~> 13.0)
|
|
71
70
|
rspec (~> 3.0)
|
|
72
71
|
|
|
73
72
|
BUNDLED WITH
|
data/LICENSE.txt
CHANGED
|
File without changes
|
data/README.md
CHANGED
|
@@ -4,13 +4,9 @@ Start devboxes quickly
|
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
-
Install the gem:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
gem install devbox_launcher
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
Setup gcloud init with the project that contains your VM.
|
|
7
|
+
- Install the gem: `gem install devbox_launcher`
|
|
8
|
+
- Setup `gcloud init` with the project that contains your VM
|
|
9
|
+
- Install [mutagen](https://mutagen.io)
|
|
14
10
|
|
|
15
11
|
## Usage
|
|
16
12
|
|
|
@@ -20,11 +16,12 @@ Create the config file at `~/.devbox_launcher.yml` so you type less. This is an
|
|
|
20
16
|
|
|
21
17
|
```yml
|
|
22
18
|
ramon@email.com:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
- box: your-instance-name
|
|
20
|
+
project: general-192303
|
|
21
|
+
zone: us-central1-a
|
|
22
|
+
mutagen:
|
|
23
|
+
alpha: /mnt/c/Users/me/src # local machine
|
|
24
|
+
beta: ~/src # remote machine
|
|
28
25
|
ramon@company.com:
|
|
29
26
|
project: development-254604
|
|
30
27
|
box: ramon
|
|
@@ -36,7 +33,13 @@ To start and create the mutagen session:
|
|
|
36
33
|
devbox start your-username
|
|
37
34
|
```
|
|
38
35
|
|
|
39
|
-
|
|
36
|
+
- Want to ssh in immediately?
|
|
37
|
+
- Add `--ssh` switch
|
|
38
|
+
- Want to mosh in immediately?
|
|
39
|
+
- Add `--mosh` switch. Mosh needs to be [installed](https://mosh.org/) in your development machine.
|
|
40
|
+
- More than one box with the same Google Cloud account?
|
|
41
|
+
- Pass in the box in your command, via `devbox start user@domain.com/box-name`
|
|
42
|
+
- No need to configure `box:` in the YAML file
|
|
40
43
|
|
|
41
44
|
Note: Linux users that sync mutagen sessions need to install [Watchman](https://facebook.github.io/watchman/).
|
|
42
45
|
|
data/Rakefile
CHANGED
|
File without changes
|
data/devbox_launcher.gemspec
CHANGED
|
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
|
|
|
30
30
|
spec.require_paths = ["lib"]
|
|
31
31
|
|
|
32
32
|
spec.add_development_dependency "bundler", "~> 2.0"
|
|
33
|
-
spec.add_development_dependency "rake", "~>
|
|
33
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
34
34
|
spec.add_development_dependency "rspec", "~> 3.0"
|
|
35
35
|
|
|
36
36
|
spec.add_runtime_dependency "thor", "~> 1.0"
|
data/lib/devbox_launcher/cli.rb
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module DevboxLauncher
|
|
2
|
+
class AccountConfig
|
|
3
|
+
|
|
4
|
+
attr_reader :account_name
|
|
5
|
+
|
|
6
|
+
def initialize(account_name, config)
|
|
7
|
+
@account_name = account_name
|
|
8
|
+
@config = config
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def find_box_config(box_name)
|
|
12
|
+
box_config = @config.find { |c| c["box"] == box_name }
|
|
13
|
+
|
|
14
|
+
if box_config.nil?
|
|
15
|
+
fail "No box config found for #{box_name} under account #{account_name}"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
BoxConfig.new(box_config)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -6,21 +6,26 @@ module DevboxLauncher
|
|
|
6
6
|
Net::SSH::Disconnect,
|
|
7
7
|
Errno::ECONNRESET,
|
|
8
8
|
Errno::ETIMEDOUT,
|
|
9
|
+
Errno::ECONNREFUSED,
|
|
9
10
|
]
|
|
10
11
|
WAIT_BOOT_IN_SECONDS = 10.freeze
|
|
11
|
-
MAX_BOOT_RETRIES =
|
|
12
|
+
MAX_BOOT_RETRIES = 20
|
|
12
13
|
DEFAULT_IDENTIFY_FILE_PATH = "~/.ssh/google_compute_engine".freeze
|
|
13
14
|
SSH_CONFIG_PATH = File.expand_path("~/.ssh/config").freeze
|
|
14
15
|
CONFIG_PATH = File.expand_path("~/.devbox_launcher.yml").freeze
|
|
15
16
|
CONFIG = YAML.load_file(CONFIG_PATH).freeze
|
|
16
17
|
|
|
17
|
-
attr_reader :
|
|
18
|
+
attr_reader :account_and_box_name, :options
|
|
18
19
|
|
|
19
|
-
def initialize(
|
|
20
|
-
@
|
|
20
|
+
def initialize(account_and_box_name, options)
|
|
21
|
+
@account_and_box_name = account_and_box_name
|
|
21
22
|
@options = options
|
|
22
23
|
end
|
|
23
24
|
|
|
25
|
+
def account
|
|
26
|
+
@account ||= @account_and_box_name.split("/")[0]
|
|
27
|
+
end
|
|
28
|
+
|
|
24
29
|
def start
|
|
25
30
|
start_stdout, start_stderr, start_status =
|
|
26
31
|
Open3.capture3(start_cmd)
|
|
@@ -31,25 +36,11 @@ module DevboxLauncher
|
|
|
31
36
|
|
|
32
37
|
reset_mutagen_session
|
|
33
38
|
|
|
34
|
-
connect_mosh
|
|
39
|
+
connect_mosh || connect_ssh
|
|
35
40
|
end
|
|
36
41
|
|
|
37
42
|
def start_cmd
|
|
38
|
-
|
|
39
|
-
project: config[:project],
|
|
40
|
-
account: account,
|
|
41
|
-
}.map do |(key, val)|
|
|
42
|
-
["--#{key}", val].join("=")
|
|
43
|
-
end.join(" ")
|
|
44
|
-
|
|
45
|
-
[
|
|
46
|
-
"gcloud",
|
|
47
|
-
"compute",
|
|
48
|
-
"instances",
|
|
49
|
-
"start",
|
|
50
|
-
name,
|
|
51
|
-
args
|
|
52
|
-
].join(" ")
|
|
43
|
+
cmd_args_for('start')
|
|
53
44
|
end
|
|
54
45
|
|
|
55
46
|
def connect_mosh
|
|
@@ -59,6 +50,13 @@ module DevboxLauncher
|
|
|
59
50
|
system(mosh_cmd)
|
|
60
51
|
end
|
|
61
52
|
|
|
53
|
+
def connect_ssh
|
|
54
|
+
return if options[:ssh].nil?
|
|
55
|
+
|
|
56
|
+
ssh_cmd = %Q(ssh #{hostname})
|
|
57
|
+
system(ssh_cmd)
|
|
58
|
+
end
|
|
59
|
+
|
|
62
60
|
def wait_boot(tries: 1)
|
|
63
61
|
Net::SSH.start(hostname, username, timeout: WAIT_BOOT_IN_SECONDS) do |ssh|
|
|
64
62
|
puts "[#{ssh.exec!('date').chomp}] Machine booted"
|
|
@@ -82,8 +80,6 @@ module DevboxLauncher
|
|
|
82
80
|
def description(reload: false)
|
|
83
81
|
return @description if !reload && @description
|
|
84
82
|
|
|
85
|
-
puts "Fetching box's description..."
|
|
86
|
-
|
|
87
83
|
describe_stdout, describe_stderr, describe_status =
|
|
88
84
|
Open3.capture3(describe_cmd)
|
|
89
85
|
|
|
@@ -99,35 +95,21 @@ module DevboxLauncher
|
|
|
99
95
|
end
|
|
100
96
|
|
|
101
97
|
def describe_cmd
|
|
102
|
-
|
|
103
|
-
project: config[:project],
|
|
104
|
-
account: account,
|
|
105
|
-
}.map do |(key, val)|
|
|
106
|
-
["--#{key}", val].join("=")
|
|
107
|
-
end.join(" ")
|
|
108
|
-
|
|
109
|
-
[
|
|
110
|
-
"gcloud",
|
|
111
|
-
"compute",
|
|
112
|
-
"instances",
|
|
113
|
-
"describe",
|
|
114
|
-
name,
|
|
115
|
-
args
|
|
116
|
-
].join(" ")
|
|
98
|
+
cmd_args_for('describe')
|
|
117
99
|
end
|
|
118
100
|
|
|
119
101
|
def set_ssh_config!
|
|
120
102
|
FileUtils.touch(SSH_CONFIG_PATH)
|
|
121
|
-
|
|
103
|
+
ssh_config = ConfigFile.new
|
|
122
104
|
args = {
|
|
123
105
|
"HostName" => description.ip,
|
|
124
106
|
"User" => username,
|
|
125
107
|
"IdentityFile" => DEFAULT_IDENTIFY_FILE_PATH,
|
|
126
108
|
}
|
|
127
109
|
args.each do |key, value|
|
|
128
|
-
|
|
110
|
+
ssh_config.set(hostname, key, value)
|
|
129
111
|
end
|
|
130
|
-
|
|
112
|
+
ssh_config.save
|
|
131
113
|
end
|
|
132
114
|
|
|
133
115
|
def reset_mutagen_session
|
|
@@ -192,8 +174,31 @@ module DevboxLauncher
|
|
|
192
174
|
watchman.trigger("mutagen sync flush --label-selector=#{label}")
|
|
193
175
|
end
|
|
194
176
|
|
|
177
|
+
def box_name_from_config
|
|
178
|
+
passed_in_box_name = @account_and_box_name.split("/")[1]
|
|
179
|
+
|
|
180
|
+
case account_config.count
|
|
181
|
+
when 0
|
|
182
|
+
fail "You have to specify box configuration"
|
|
183
|
+
when 1
|
|
184
|
+
account_config.first[:box]
|
|
185
|
+
else
|
|
186
|
+
account_config[name]
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
195
190
|
def name
|
|
196
|
-
@name
|
|
191
|
+
return @name if @name
|
|
192
|
+
passed_in_box_name = @account_and_box_name.split("/")[1]
|
|
193
|
+
|
|
194
|
+
name = passed_in_box_name.presence || box_name_from_config
|
|
195
|
+
|
|
196
|
+
if name.blank?
|
|
197
|
+
fail "box name must be given either in the CLI or in config. " \
|
|
198
|
+
"See README.md."
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
@name = name
|
|
197
202
|
end
|
|
198
203
|
|
|
199
204
|
def hostname
|
|
@@ -204,19 +209,44 @@ module DevboxLauncher
|
|
|
204
209
|
@username ||= account.gsub(/\W/, "_")
|
|
205
210
|
end
|
|
206
211
|
|
|
207
|
-
def
|
|
208
|
-
return @
|
|
212
|
+
def account_config
|
|
213
|
+
return @account_config if @account_config
|
|
209
214
|
|
|
210
215
|
if not CONFIG.has_key?(account)
|
|
211
216
|
fail "No config in #{CONFIG_PATH} found for #{account}"
|
|
212
217
|
end
|
|
213
218
|
|
|
214
|
-
@
|
|
219
|
+
@account_config = AccountConfig.new(account, CONFIG[account])
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def box_config
|
|
223
|
+
account_config.find_box_config(name)
|
|
215
224
|
end
|
|
216
225
|
|
|
217
226
|
def mutagen_config
|
|
218
|
-
@mutagen_config ||=
|
|
227
|
+
@mutagen_config ||= box_config.mutagen_config
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def cmd_args_for(method)
|
|
231
|
+
args = {
|
|
232
|
+
project: box_config.project,
|
|
233
|
+
account: account,
|
|
234
|
+
zone: box_config.zone,
|
|
235
|
+
}.each_with_object([]) do |(key, val), arr|
|
|
236
|
+
next if val.blank?
|
|
237
|
+
arr << ["--#{key}", val].join("=")
|
|
238
|
+
end.join(" ")
|
|
239
|
+
|
|
240
|
+
[
|
|
241
|
+
"gcloud",
|
|
242
|
+
"compute",
|
|
243
|
+
"instances",
|
|
244
|
+
method,
|
|
245
|
+
name,
|
|
246
|
+
args
|
|
247
|
+
].join(" ")
|
|
219
248
|
end
|
|
220
249
|
|
|
250
|
+
|
|
221
251
|
end
|
|
222
252
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module DevboxLauncher
|
|
2
|
+
class BoxConfig
|
|
3
|
+
|
|
4
|
+
attr_reader :config
|
|
5
|
+
|
|
6
|
+
def initialize(config)
|
|
7
|
+
@config = config.with_indifferent_access
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def mutagen_config
|
|
11
|
+
Mutagen.new(config[:mutagen])
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def project
|
|
15
|
+
config[:project]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def zone
|
|
19
|
+
config[:zone]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/devbox_launcher.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "active_support/core_ext/hash/indifferent_access"
|
|
2
|
+
require "active_support/core_ext/object/blank"
|
|
2
3
|
require "ssh-config"
|
|
3
4
|
require "open3"
|
|
4
5
|
require "thor"
|
|
@@ -19,3 +20,5 @@ require "devbox_launcher/watchman"
|
|
|
19
20
|
require "devbox_launcher/models/description"
|
|
20
21
|
require "devbox_launcher/models/mutagen"
|
|
21
22
|
require "devbox_launcher/models/box"
|
|
23
|
+
require "devbox_launcher/models/account_config"
|
|
24
|
+
require "devbox_launcher/models/box_config"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: devbox_launcher
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ramon Tayag
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-
|
|
11
|
+
date: 2021-09-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -30,14 +30,14 @@ dependencies:
|
|
|
30
30
|
requirements:
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
33
|
+
version: '13.0'
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
40
|
+
version: '13.0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: rspec
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -189,9 +189,10 @@ files:
|
|
|
189
189
|
- bin/setup
|
|
190
190
|
- devbox_launcher.gemspec
|
|
191
191
|
- lib/devbox_launcher.rb
|
|
192
|
-
- lib/devbox_launcher/box.rb
|
|
193
192
|
- lib/devbox_launcher/cli.rb
|
|
193
|
+
- lib/devbox_launcher/models/account_config.rb
|
|
194
194
|
- lib/devbox_launcher/models/box.rb
|
|
195
|
+
- lib/devbox_launcher/models/box_config.rb
|
|
195
196
|
- lib/devbox_launcher/models/description.rb
|
|
196
197
|
- lib/devbox_launcher/models/mutagen.rb
|
|
197
198
|
- lib/devbox_launcher/version.rb
|
|
@@ -219,7 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
219
220
|
- !ruby/object:Gem::Version
|
|
220
221
|
version: '0'
|
|
221
222
|
requirements: []
|
|
222
|
-
rubygems_version: 3.1.
|
|
223
|
+
rubygems_version: 3.1.6
|
|
223
224
|
signing_key:
|
|
224
225
|
specification_version: 4
|
|
225
226
|
summary: Conveniently launch your devbox
|
data/lib/devbox_launcher/box.rb
DELETED
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
module DevboxLauncher
|
|
2
|
-
class Box
|
|
3
|
-
|
|
4
|
-
WAIT_BOOT_RESCUED_EXCEPTIONS = [
|
|
5
|
-
Net::SSH::ConnectionTimeout,
|
|
6
|
-
Net::SSH::Disconnect,
|
|
7
|
-
Errno::ECONNRESET,
|
|
8
|
-
Errno::ETIMEDOUT,
|
|
9
|
-
Errno::ECONNREFUSED,
|
|
10
|
-
]
|
|
11
|
-
WAIT_BOOT_IN_SECONDS = 10.freeze
|
|
12
|
-
DEFAULT_IDENTIFY_FILE_PATH = "~/.ssh/google_compute_engine".freeze
|
|
13
|
-
SSH_CONFIG_PATH = File.expand_path("~/.ssh/config").freeze
|
|
14
|
-
CONFIG_PATH = File.expand_path("~/.devbox_launcher.yml").freeze
|
|
15
|
-
CONFIG = YAML.load_file(CONFIG_PATH).freeze
|
|
16
|
-
|
|
17
|
-
attr_reader :account
|
|
18
|
-
|
|
19
|
-
def initialize(account)
|
|
20
|
-
@account = account
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def start
|
|
24
|
-
start_stdout, start_stderr, start_status =
|
|
25
|
-
run_command(start_cmd)
|
|
26
|
-
|
|
27
|
-
set_ssh_config!(hostname, {
|
|
28
|
-
username: username,
|
|
29
|
-
ip: description.ip,
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
wait_boot
|
|
33
|
-
|
|
34
|
-
reset_mutagen_session(
|
|
35
|
-
mutagen_config: config[:mutagen],
|
|
36
|
-
hostname: hostname,
|
|
37
|
-
)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def start_cmd
|
|
41
|
-
args = {
|
|
42
|
-
project: config[:project],
|
|
43
|
-
account: account,
|
|
44
|
-
}.map do |(key, val)|
|
|
45
|
-
["--#{key}", val].join("=")
|
|
46
|
-
end.join(" ")
|
|
47
|
-
|
|
48
|
-
[
|
|
49
|
-
"gcloud",
|
|
50
|
-
"compute",
|
|
51
|
-
"instances",
|
|
52
|
-
"start",
|
|
53
|
-
name,
|
|
54
|
-
args
|
|
55
|
-
].join(" ")
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def wait_boot(tries: 1)
|
|
59
|
-
Net::SSH.start(hostname, username, timeout: WAIT_BOOT_IN_SECONDS) do |ssh|
|
|
60
|
-
puts "[#{ssh.exec!('date').chomp}] Machine booted"
|
|
61
|
-
end
|
|
62
|
-
rescue *WAIT_BOOT_RESCUED_EXCEPTIONS
|
|
63
|
-
puts "Not booted. Waiting #{WAIT_BOOT_IN_SECONDS} seconds before trying again..."
|
|
64
|
-
|
|
65
|
-
sleep WAIT_BOOT_IN_SECONDS
|
|
66
|
-
|
|
67
|
-
description = describe(name)
|
|
68
|
-
if !description.running?
|
|
69
|
-
puts "Detected that the machine is not running " \
|
|
70
|
-
"(status is #{description.status}). Booting it..."
|
|
71
|
-
start_box name, username
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
wait_boot name, username, tries: tries+1
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def description(reload: false)
|
|
78
|
-
return @description if !reload && @description
|
|
79
|
-
|
|
80
|
-
puts "Fetching box's description..."
|
|
81
|
-
|
|
82
|
-
describe_command = %Q(gcloud compute instances describe #{name})
|
|
83
|
-
describe_stdout, describe_stderr, describe_status =
|
|
84
|
-
run_command(describe_command)
|
|
85
|
-
|
|
86
|
-
if !describe_status.success?
|
|
87
|
-
msg = "Problem fetching the description of #{name}. "
|
|
88
|
-
msg += "Please ensure you can call `#{describe_command}`.\n"
|
|
89
|
-
msg += "Error:\n"
|
|
90
|
-
msg += describe_stderr
|
|
91
|
-
fail msg
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
@description = Description.new(describe_stdout)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def set_ssh_config!(hostname, username:, ip:)
|
|
98
|
-
FileUtils.touch(SSH_CONFIG_PATH)
|
|
99
|
-
config = ConfigFile.new
|
|
100
|
-
args = {
|
|
101
|
-
"HostName" => ip,
|
|
102
|
-
"User" => username,
|
|
103
|
-
"IdentityFile" => DEFAULT_IDENTIFY_FILE_PATH,
|
|
104
|
-
}
|
|
105
|
-
args.each do |key, value|
|
|
106
|
-
config.set(hostname, key, value)
|
|
107
|
-
end
|
|
108
|
-
config.save
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def reset_mutagen_session
|
|
112
|
-
mutagen_config = config[:mutagen]
|
|
113
|
-
return if mutagen_config.nil?
|
|
114
|
-
|
|
115
|
-
alpha_dir = mutagen_config[:alpha]
|
|
116
|
-
beta_dir = mutagen_config[:beta]
|
|
117
|
-
|
|
118
|
-
return if alpha_dir.nil? || beta_dir.nil?
|
|
119
|
-
|
|
120
|
-
terminate_mutagen_session
|
|
121
|
-
|
|
122
|
-
create_mutagen_session(
|
|
123
|
-
alpha_dir: alpha_dir,
|
|
124
|
-
beta_dir: beta_dir,
|
|
125
|
-
hostname: hostname,
|
|
126
|
-
username: username,
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
if OS.linux?
|
|
130
|
-
watch_alpha(alpha_dir: alpha_dir, hostname: hostname)
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def terminate_mutagen_session(username)
|
|
135
|
-
puts "Terminating mutagen session..."
|
|
136
|
-
terminate_mutagen_command =
|
|
137
|
-
%Q(mutagen terminate --label-selector=#{username})
|
|
138
|
-
terminate_mutagen_stdout,
|
|
139
|
-
terminate_mutagen_stderr,
|
|
140
|
-
terminate_mutagen_status =
|
|
141
|
-
run_command(terminate_mutagen_command)
|
|
142
|
-
|
|
143
|
-
if not terminate_mutagen_status.success?
|
|
144
|
-
# mutagen prints to stdout and stderr
|
|
145
|
-
msg = "Failed to terminate mutagen sessions: " \
|
|
146
|
-
"#{terminate_mutagen_stdout} -" \
|
|
147
|
-
"#{terminate_mutagen_stderr}"
|
|
148
|
-
fail msg
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def create_mutagen_session(alpha_dir:, beta_dir:, hostname:, username:)
|
|
153
|
-
puts "Create mutagen session syncing local #{alpha_dir} " \
|
|
154
|
-
"with #{hostname} #{beta_dir}"
|
|
155
|
-
|
|
156
|
-
create_mutagen_command = [
|
|
157
|
-
"mutagen sync create",
|
|
158
|
-
alpha_dir,
|
|
159
|
-
"#{hostname}:#{beta_dir}",
|
|
160
|
-
"--label=#{username}",
|
|
161
|
-
"--sync-mode=two-way-resolved",
|
|
162
|
-
]
|
|
163
|
-
create_mutagen_command << "--watch-mode-alpha=no-watch" if OS.linux?
|
|
164
|
-
|
|
165
|
-
create_mutagen_stdout,
|
|
166
|
-
create_mutagen_stderr,
|
|
167
|
-
create_mutagen_status =
|
|
168
|
-
run_command(create_mutagen_command.join(" "))
|
|
169
|
-
|
|
170
|
-
if not create_mutagen_status.success?
|
|
171
|
-
# mutagen prints to stdout and stderr
|
|
172
|
-
msg = "Failed to create mutagen sessions: " \
|
|
173
|
-
"#{create_mutagen_stdout} -" \
|
|
174
|
-
"#{create_mutagen_stderr}"
|
|
175
|
-
fail msg
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
def watch_alpha(alpha_dir:, hostname:)
|
|
180
|
-
watchman = Watchman.new(dir: alpha_dir)
|
|
181
|
-
watchman.trigger("mutagen sync flush --label-selector=#{hostname}")
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
def name
|
|
185
|
-
@name ||= config[:box]
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
def hostname
|
|
189
|
-
[name, username, "devbox"].join("-")
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def username
|
|
193
|
-
@username ||= account.gsub(/\W/, "_")
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
def config
|
|
197
|
-
return @config if @config
|
|
198
|
-
|
|
199
|
-
if not CONFIG.has_key?(account)
|
|
200
|
-
fail "No config in #{CONFIG_PATH} found for #{account}"
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
@config = CONFIG[account].with_indifferent_access
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
def run_command(command, tries: 0)
|
|
207
|
-
Open3.capture3(command)
|
|
208
|
-
rescue *WAIT_BOOT_RESCUED_EXCEPTIONS
|
|
209
|
-
sleep WAIT_BOOT_IN_SECONDS
|
|
210
|
-
run_command(command, tries+1)
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
end
|
|
214
|
-
end
|