Class: DevboxLauncher::Box

Inherits:
Object
  • Object
show all
Defined in:
lib/devbox_launcher/models/box.rb

Constant Summary collapse

WAIT_BOOT_RESCUED_EXCEPTIONS =
[
  Net::SSH::ConnectionTimeout,
  Net::SSH::Disconnect,
  Errno::ECONNRESET,
  Errno::ETIMEDOUT,
  Errno::ECONNREFUSED,
]
WAIT_BOOT_IN_SECONDS =
10.freeze
MAX_BOOT_RETRIES =
20
SSH_CONFIG_PATH =
File.expand_path("~/.ssh/config").freeze
CONFIG_PATH =
File.expand_path("~/.devbox_launcher.yml").freeze
CONFIG =
YAML.load_file(CONFIG_PATH).freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(account_and_box_name, options) ⇒ Box

Returns a new instance of Box.



19
20
21
22
# File 'lib/devbox_launcher/models/box.rb', line 19

def initialize(, options)
  @account_and_box_name = 
  @options = options
end

Instance Attribute Details

#account_and_box_nameObject (readonly)

Returns the value of attribute account_and_box_name.



17
18
19
# File 'lib/devbox_launcher/models/box.rb', line 17

def 
  @account_and_box_name
end

#optionsObject (readonly)

Returns the value of attribute options.



17
18
19
# File 'lib/devbox_launcher/models/box.rb', line 17

def options
  @options
end

Instance Method Details

#accountObject



24
25
26
# File 'lib/devbox_launcher/models/box.rb', line 24

def 
  @account ||= @account_and_box_name.split("/")[0]
end

#account_configObject



153
154
155
156
157
158
159
160
161
# File 'lib/devbox_launcher/models/box.rb', line 153

def 
  return @account_config if @account_config

  if not CONFIG.has_key?()
    fail "No config in #{CONFIG_PATH} found for #{}"
  end

  @account_config = AccountConfig.new(, CONFIG[])
end

#box_configObject



163
164
165
# File 'lib/devbox_launcher/models/box.rb', line 163

def box_config
  .find_box_config(name)
end

#box_name_from_configObject



118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/devbox_launcher/models/box.rb', line 118

def box_name_from_config
  passed_in_box_name = @account_and_box_name.split("/")[1]

  case .count
  when 0
    fail "You have to specify box configuration"
  when 1
    .first[:box]
  else
    [name]
  end
end

#cmd_args_for(method) ⇒ Object



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/devbox_launcher/models/box.rb', line 171

def cmd_args_for(method)
  args = {
    project: box_config.project,
    account: ,
    zone: box_config.zone,
  }.each_with_object([]) do |(key, val), arr|
    next if val.blank?
    arr << ["--#{key}", val].join("=")
  end.join(" ")

  [
    "gcloud",
    "compute",
    "instances",
    method,
    name,
    args
  ].join(" ")
end

#connect_moshObject



45
46
47
48
49
50
# File 'lib/devbox_launcher/models/box.rb', line 45

def connect_mosh
  return if options[:mosh].nil?

  mosh_cmd = %Q(mosh #{hostname})
  system(mosh_cmd)
end

#connect_sshObject



52
53
54
55
56
57
# File 'lib/devbox_launcher/models/box.rb', line 52

def connect_ssh
  return if options[:ssh].nil?

  ssh_cmd = %Q(ssh #{hostname})
  system(ssh_cmd)
end

#describe_cmdObject



96
97
98
# File 'lib/devbox_launcher/models/box.rb', line 96

def describe_cmd
  cmd_args_for('describe')
end

#description(reload: false) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/devbox_launcher/models/box.rb', line 79

def description(reload: false)
  return @description if !reload && @description

  describe_stdout, describe_stderr, describe_status =
    Open3.capture3(describe_cmd)

  if !describe_status.success?
    msg = "Problem fetching the description of #{name}. "
    msg += "Please ensure you can call `#{describe_cmd}`.\n"
    msg += "Error:\n"
    msg += describe_stderr
    fail msg
  end

  @description = Description.new(describe_stdout)
end

#hostnameObject



145
146
147
# File 'lib/devbox_launcher/models/box.rb', line 145

def hostname
  [name, username, "devbox"].join("-")
end

#labelObject



114
115
116
# File 'lib/devbox_launcher/models/box.rb', line 114

def label
  "#{username}=#{name}"
end

#mutagen_configObject



167
168
169
# File 'lib/devbox_launcher/models/box.rb', line 167

def mutagen_config
  @mutagen_config ||= box_config.mutagen_config
end

#mutagen_sessionObject



191
192
193
194
195
196
197
# File 'lib/devbox_launcher/models/box.rb', line 191

def mutagen_session
  @mutagen_session ||= MutagenSession.new(
    label: label,
    config: mutagen_config,
    hostname: hostname,
  )
end

#nameObject



131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/devbox_launcher/models/box.rb', line 131

def name
  return @name if @name
  passed_in_box_name = @account_and_box_name.split("/")[1]

  name = passed_in_box_name.presence || box_name_from_config

  if name.blank?
    fail "box name must be given either in the CLI or in config. " \
      "See README.md."
  end

  @name = name
end

#set_ssh_config!Object



100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/devbox_launcher/models/box.rb', line 100

def set_ssh_config!
  FileUtils.touch(SSH_CONFIG_PATH)
  ssh_config = ConfigFile.new
  args = {
    "HostName" => description.ip,
    "User" => username,
    "IdentityFile" => box_config.identity_file,
  }
  args.each do |key, value|
    ssh_config.set(hostname, key, value)
  end
  ssh_config.save
end

#startObject



28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/devbox_launcher/models/box.rb', line 28

def start
  start_stdout, start_stderr, start_status =
    Open3.capture3(start_cmd)

  set_ssh_config!

  wait_boot

  mutagen_session.reset

  connect_mosh || connect_ssh
end

#start_cmdObject



41
42
43
# File 'lib/devbox_launcher/models/box.rb', line 41

def start_cmd
  cmd_args_for('start')
end

#usernameObject



149
150
151
# File 'lib/devbox_launcher/models/box.rb', line 149

def username
  @username ||= box_config.user || .gsub(/\W/, "_")
end

#wait_boot(tries: 1) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/devbox_launcher/models/box.rb', line 59

def wait_boot(tries: 1)
  Net::SSH.start(hostname, username, timeout: WAIT_BOOT_IN_SECONDS) do |ssh|
    puts "[#{ssh.exec!('date').chomp}] Machine booted"
  end
rescue *WAIT_BOOT_RESCUED_EXCEPTIONS
  puts "Not booted. Waiting #{WAIT_BOOT_IN_SECONDS} seconds before trying again..."

  sleep WAIT_BOOT_IN_SECONDS

  if !description(reload: true).running?
    puts "Detected that the machine is not running " \
      "(status is #{description.status}). Booting it..."
    start
  end

  fail if tries >= MAX_BOOT_RETRIES

  wait_boot tries: tries+1
end