Class: Vagrantomatic::Instance

Inherits:
Object
  • Object
show all
Defined in:
lib/vagrantomatic/instance.rb

Constant Summary collapse

VAGRANTFILE =
"Vagrantfile"
VAGRANTFILE_JSON =
"#{VAGRANTFILE}.json"
MASTER_VAGRANTFILE =

We ship our own Vagrantfile with all variables externalised inside this gem and get it into position by symlinking B-)

File.join(File.dirname(File.expand_path(__FILE__)), "../../res/#{VAGRANTFILE}")

Instance Method Summary collapse

Constructor Details

#initialize(vagrant_vm_dir, name, logger: nil, config: nil) ⇒ Instance

Returns a new instance of Instance.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/vagrantomatic/instance.rb', line 65

def initialize(vagrant_vm_dir, name, logger:nil, config:nil)
  @name           = name
  @vagrant_vm_dir = vagrant_vm_dir
  @logger         = Logger.new(logger).logger

  # if we encounter conditions such as missing or damaged files we may need
  # to force a save that would normally not be detected - eg if we load bad
  # json it gets fixed up to an empty hash which would them be compared to a
  # fresh read of the file (also results in empty hash) - so we must supply
  # another way to force the save here
  @force_save     = false

  # use supplied config if present, otherwise load from file
  if config
    # validate a user-supplied config now, at the point of insertion
    # by this point we have a config either from file or supplied by user it
    # must be valid for us to proceed!
    set_config(config)
  else

    # this passed-in config could still be bad - we will validate it before
    # use on either save() or get_vm().  We DONT validate it right away
    # because this would cause the constructor to explode when we are trying
    # to introspec intances - we would never be able to fix anything
    # automatically
    @config = configfile_hash
  end

  @logger.debug "initialized vagrantomatic instance for #{name}"
end

Instance Method Details

#add_shared_folder(folders) ⇒ Object

Add a new folder to @config in the correct place. Folders must be specified as colon delimited strings: ‘HOST_PATH:VM_PATH`, eg `/home/geoff:/stuff` would mount `/home/geoff` from the main computer and would mount it inside the VM at /stuff. Vagrant expects the `HOST_PATH` to be an absolute path, however, you may specify a relative path here and vagrantomatic will attempt to extract a fully qualified path by prepending the present working directory. If this is incorrect its up to the programmer to fix this by passing in a fully qualified path in the first place



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/vagrantomatic/instance.rb', line 45

def add_shared_folder(folders)
  folders=Array(folders)
  # can't use dig() might not be ruby 2.3
  if ! @config.has_key?("folders")
    @config["folders"] = []
  end


  # all paths must be fully qualified.  If we were asked to do a relative path, change
  # it to the current directory since that's probably what the user wanted.  Not right?
  # user supply correct path!
  folders.each { |folder|
    if ! folder.start_with? '/'
      folder = "#{Dir.pwd}/#{folder}"
    end

    @config["folders"] << folder
  }
end

#configObject



12
13
14
# File 'lib/vagrantomatic/instance.rb', line 12

def config
  @config
end

#configfileObject



104
105
106
# File 'lib/vagrantomatic/instance.rb', line 104

def configfile
  File.join(vm_instance_dir, VAGRANTFILE_JSON)
end

#configfile_hashObject

return a hash of the configfile or empty hash if error encountered



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/vagrantomatic/instance.rb', line 109

def configfile_hash

  config  = {}
  begin
    json    = File.read(configfile)
    config  = JSON.parse(json)
  rescue Errno::ENOENT
    # depending on whether the instance has been saved or not, we may not
    # yet have a configfile - allow to proceed
    @logger.debug "#{configfile} does not exist"
    @force_save = true
  rescue JSON::ParserError
    # swallow parse errors so that we can destroy and recreate automatically
    @logger.debug "JSON parse error in #{configfile}"
    @force_save = true
  end
  config
end

#configured?Boolean

Returns:

  • (Boolean)


128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/vagrantomatic/instance.rb', line 128

def configured?
  configured = true

  if ! Dir.exists? (vm_instance_dir)
    @logger.debug "Vagrant instance directory #{vm_instance_dir} does not exist"
    configured = false
  end

  if ! File.exists?(vagrantfile)
    @logger.debug "#{VAGRANTFILE} not found at #{vagrantfile}"
    configured = false
  end

  if ! File.exists?(configfile)
    @logger.debug "#{VAGRANTFILE_JSON} not found at #{configfile}"
    configured = false
  end

  # check config hash is valid without causing a fatal error if its damaged
  if ! validate_config(false)
    configured = false
  end

  configured
end

#ensure_configObject

Vagrant to be driven from a .json config file, all the parameters are externalised here



167
168
169
170
171
172
173
# File 'lib/vagrantomatic/instance.rb', line 167

def ensure_config
  if ! in_sync?
    File.open(configfile,"w") do |f|
      f.write(@config.to_json)
    end
  end
end

#ensure_vagrantfileObject

The Vagrantfile itself is shipped as part of this module and delivered via pluginsync, so we just need to symlink it for each directory. This gives us the benefit being to update by dropping a new module too



179
180
181
# File 'lib/vagrantomatic/instance.rb', line 179

def ensure_vagrantfile
  FileUtils.ln_sf(MASTER_VAGRANTFILE, vagrantfile)
end

#execute_and_log(op, *args) ⇒ Object



204
205
206
207
208
209
# File 'lib/vagrantomatic/instance.rb', line 204

def execute_and_log(op, *args)
  get_vm.execute(op, args) { |stdout, stderr|
    # only one of these will ever be set at a time, other one is nil
    @logger.debug "#{stdout}#{stderr}".strip
  }.success?
end

#get_vmObject



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/vagrantomatic/instance.rb', line 184

def get_vm
  # Create an instance (represents a Vagrant **installation**)
  if ! in_sync?
    raise "get_vm called for instance #{@name}but it is not in_sync! (call save() first?)"
  end

  validate_config

  vagrant_installation = Derelict.instance(Vagrantomatic::DEFAULT_VAGRANT_DIR)
  result = vagrant_installation.execute('--version') # Derelict::Executer object (vagrant --version)
  if result.success?
    # vagrant present and working, connect to our vm INSTANCE
    vm = vagrant_installation.connect(vm_instance_dir)
  else
    raise "Error connecting to vagrant! (vagrant --version failed)"
  end

  vm
end

#in_sync?Boolean

Returns:

  • (Boolean)


211
212
213
214
215
216
217
218
219
# File 'lib/vagrantomatic/instance.rb', line 211

def in_sync?
  configured  = false
  have_config = configfile_hash
  if (! @force_save) and (have_config == @config )
    configured = true
  end

  configured
end

#purgeObject



229
230
231
232
233
234
# File 'lib/vagrantomatic/instance.rb', line 229

def purge
  execute_and_log(:destroy, '-f')
  if Dir.exists? vm_instance_dir
    FileUtils::rm_rf(vm_instance_dir)
  end
end

#reloadObject



236
237
238
# File 'lib/vagrantomatic/instance.rb', line 236

def reload
  execute_and_log(:reload)
end

#resetObject



240
241
242
243
# File 'lib/vagrantomatic/instance.rb', line 240

def reset
  execute_and_log(:destroy, '-f')
  execute_and_log(:up)
end

#run(command) ⇒ Object



245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/vagrantomatic/instance.rb', line 245

def run(command)
  # arrayify
  command = [command]
  command.unshift("-c")

  messages = []
  vm = get_vm
  # throw the command over the wall to derelect whatever the state of instance
  # for now just support ssh - for windows we could do `powershell -c` or
  # maybe even winRM
  executor = vm.execute(:ssh, command) { |stdout,stderr|
    line = "#{stdout}#{stderr}".strip
    @logger.debug line
    messages << line
  }
  @logger.info("command '#{command}' resulted in #{messages.size} lines (exit status: #{executor.status})")
  return executor.status, messages
end

#saveObject



154
155
156
157
158
159
160
161
162
163
# File 'lib/vagrantomatic/instance.rb', line 154

def save
  @logger.debug("validating settings for save...")
  validate_config
  @logger.debug("saving vm settings...")
  FileUtils.mkdir_p(vm_instance_dir)
  ensure_config
  ensure_vagrantfile

  @force_save = false
end

#set_config(config) ⇒ Object

ruby convention is to use ‘config=` but this doesn’t work properly inside a constructor, it declarates a local variable ‘config`. Calling from outside the constructor works fine…



19
20
21
22
# File 'lib/vagrantomatic/instance.rb', line 19

def set_config(config)
  @config = config
  validate_config
end

#startObject



221
222
223
# File 'lib/vagrantomatic/instance.rb', line 221

def start
  execute_and_log(:up)
end

#stopObject



225
226
227
# File 'lib/vagrantomatic/instance.rb', line 225

def stop
  execute_and_log(:suspend)
end

#vagrantfileObject



100
101
102
# File 'lib/vagrantomatic/instance.rb', line 100

def vagrantfile
  File.join(vm_instance_dir, VAGRANTFILE)
end

#validate_config(fatal = true) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
# File 'lib/vagrantomatic/instance.rb', line 24

def validate_config(fatal = true)
  valid = true
  if ! @config.has_key?("box")
    valid = false
    if fatal
      raise "Node #{@name} must specify box"
    end
  end

  valid
end

#vm_instance_dirObject



96
97
98
# File 'lib/vagrantomatic/instance.rb', line 96

def vm_instance_dir
  File.join(@vagrant_vm_dir, @name)
end