vagrant-terraform 0.1.3

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.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/Gemfile +11 -0
  4. data/Gemfile.lock +11 -0
  5. data/LICENSE +21 -0
  6. data/README.md +120 -0
  7. data/example_box/README.md +12 -0
  8. data/example_box/Vagrantfile +14 -0
  9. data/example_box/dummy.box +0 -0
  10. data/example_box/metadata.json +4 -0
  11. data/lib/vagrant-terraform/action/create_vm.rb +205 -0
  12. data/lib/vagrant-terraform/action/destroy_vm.rb +39 -0
  13. data/lib/vagrant-terraform/action/halt_vm.rb +39 -0
  14. data/lib/vagrant-terraform/action/is_created.rb +19 -0
  15. data/lib/vagrant-terraform/action/is_running.rb +19 -0
  16. data/lib/vagrant-terraform/action/read_ssh_info.rb +44 -0
  17. data/lib/vagrant-terraform/action/read_state.rb +67 -0
  18. data/lib/vagrant-terraform/action/setup_terraform.rb +42 -0
  19. data/lib/vagrant-terraform/action/start_vm.rb +51 -0
  20. data/lib/vagrant-terraform/action/wait_for_vm_up.rb +99 -0
  21. data/lib/vagrant-terraform/action.rb +186 -0
  22. data/lib/vagrant-terraform/config.rb +90 -0
  23. data/lib/vagrant-terraform/errors.rb +32 -0
  24. data/lib/vagrant-terraform/plugin.rb +75 -0
  25. data/lib/vagrant-terraform/provider.rb +76 -0
  26. data/lib/vagrant-terraform/util/machine_names.rb +23 -0
  27. data/lib/vagrant-terraform/util/terraform_execute.rb +32 -0
  28. data/lib/vagrant-terraform/util/timer.rb +17 -0
  29. data/lib/vagrant-terraform/util/update_vm_state.rb +29 -0
  30. data/lib/vagrant-terraform/util.rb +8 -0
  31. data/lib/vagrant-terraform/version.rb +6 -0
  32. data/lib/vagrant-terraform.rb +21 -0
  33. data/locales/en.yml +50 -0
  34. data/vagrant-terraform.gemspec +21 -0
  35. metadata +91 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2522d1ea4a52b4c972f6abf6a66f11f361ec801a26fb5bcb089d286a6cd292df
4
+ data.tar.gz: 703b30e9a3cb591a6022ad372ed5a1e9932bb319c170a07eda801659a14d734b
5
+ SHA512:
6
+ metadata.gz: e361ce86c637c40b6b72510686a6cf9a77238f36fb7f8848965aeacac3acd95356d4928ddc6678d5a5c93108b8aaab38eb9d3eb6e74591706d461a6a0eeb356a
7
+ data.tar.gz: 448483359a5f6d30a4e95a5463edcb297504dae0ed4f42b65ede44a2834a1c9d9821c59bd123e9f2ae5132ce875e44fcf55754a085031ba72edcfae00442e1cb
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ *.gem
2
+ .bundle
3
+ *.log
4
+ /Vagrantfile*
5
+ .vagrant
6
+ /.idea
7
+ *.gem
8
+ /manifests
9
+ vendor
10
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # gem "rails"
8
+ #group :plugins do
9
+ # gem 'vagrant-terraform', :path => '.'
10
+ #end
11
+
data/Gemfile.lock ADDED
@@ -0,0 +1,11 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+
5
+ PLATFORMS
6
+ x86_64-linux-musl
7
+
8
+ DEPENDENCIES
9
+
10
+ BUNDLED WITH
11
+ 2.2.33
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 <copyright holders>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # Vagrant Terraform Provider
2
+
3
+ This is an experimental [Vagrant](http://www.vagrantup.com) plugin for Proxmox that uses
4
+ Terraform under the hood to operate the virtual machines.
5
+
6
+ Could be extended to support other Terraform providers in addition to Proxmox but at
7
+ the moment I'm not planning to.
8
+
9
+ Things I'm _not_ planning to do (due to lack of time and resources):
10
+ * Put this in rubygems.org. If you want to use this, roll your own gem like instructed below.
11
+ * Support other Terraform providers unless I have to move away from Proxmox to something else.
12
+ * Add support for suspend / snapshots.
13
+ * Support / test anything other than Ubuntu/Fedora
14
+ * Support Ruby 2.x
15
+
16
+ ## Installation
17
+
18
+ ```
19
+ $ gem build *.gemspec
20
+ $ vagrant plugin install *.gem
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ### Prerequisites
26
+
27
+ #### Requirements
28
+
29
+ Tested on Ubuntu 22.04 and Fedora 40.
30
+
31
+ [Terraform](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli)
32
+
33
+ #### Configuration
34
+
35
+ Create API user
36
+ https://registry.terraform.io/providers/Telmate/proxmox/latest/docs#creating-the-proxmox-user-and-role-for-terraform
37
+ ```
38
+ pveum role add Terraform -privs "Datastore.AllocateSpace Datastore.AllocateTemplate Datastore.Audit Pool.Allocate Sys.Audit Sys.Console Sys.Modify VM.Allocate VM.Audit VM.Clone VM.Config.CDROM VM.Config.Cloudinit VM.Config.CPU VM.Config.Disk VM.Config.HWType VM.Config.Memory VM.Config.Network VM.Config.Options VM.Migrate VM.Monitor VM.PowerMgmt SDN.Use"
39
+
40
+ pveum user add terraform@pve
41
+ pveum aclmod / -user terraform@pve -role Terraform
42
+ pveum user token add terraform@pve automation --privsep 0
43
+ ```
44
+ The value on the last line is your API token secret you are going to need in your Vagrantfile.
45
+
46
+ Privilege separation needs to be disabled from API key due to:
47
+ https://github.com/Telmate/terraform-provider-proxmox/issues/784
48
+
49
+ #### Prepare a VM template on your Proxmox PVE
50
+
51
+ Download cloud image
52
+ ```
53
+ wget https://cloud-images.ubuntu.com/noble/current/SHA256SUMS https://cloud-images.ubuntu.com/noble/current/SHA256SUMS.gpg https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
54
+ gpg --keyid-format long --verify SHA256SUMS.gpg SHA256SUMS # Requires public key "UEC Image Automatic Signing Key <cdimage@ubuntu.com>"
55
+ sha256sum -c --ignore-missing SHA256SUMS
56
+ ```
57
+
58
+ Customize it the way you want with virt-customize
59
+ ```
60
+ export LIBGUESTFS_BACKEND=direct; virt-customize -a noble-server-cloudimg-amd64.img --install qemu-guest-agent --run-command "truncate -s 0 /etc/machine-id /var/lib/dbus/machine-id"
61
+ ```
62
+
63
+ Create a VM and convert it to template
64
+ ```
65
+ qm create 9001 --memory 1024 --net0 virtio,bridge=vmbr0 --scsihw virtio-scsi-pci --name ubuntu-noble-template --machine q35,viommu=virtio --ostype l26 --rng0 /dev/urandom
66
+ qm set 9001 --virtio0 [STORAGE_NAME]:0,import-from=/root/noble-server-cloudimg-amd64.img
67
+ qm set 9001 --ide2 [STORAGE_NAME]:cloudinit --boot c --bootdisk virtio0
68
+ qm template 9001
69
+ ```
70
+
71
+
72
+ ### Supported Commands
73
+
74
+ 1. `vagrant up`
75
+ 1. `vagrant destroy`
76
+ 1. `vagrant ssh [-c 'command']`
77
+ 1. `vagrant ssh-config`
78
+ 1. `vagrant halt`
79
+ 1. `vagrant reload`
80
+ 1. `vagrant status`
81
+
82
+ ### Simple configuration example with two network interfaces
83
+
84
+ ```
85
+ Vagrant.configure("2") do |config|
86
+ config.vm.box = 'dummy'
87
+ config.vm.box_url = 'https://github.com/mika-b/vagrant-terraform/blob/main/example_box/dummy.box?raw=true'
88
+
89
+ config.vm.hostname = "example-vm.local"
90
+
91
+ config.vm.synced_folder './', '/vagrant', type: 'rsync', rsync__exclude: ["log/", ".git/"] # , rsync__verbose: false
92
+ config.ssh.forward_agent = true
93
+
94
+ config.vm.network :private_network,
95
+ :terraform__network_name => 'testmgmt' # DHCP
96
+ config.vm.network :private_network,
97
+ :terraform__network_name => 'vmbr0', :terraform__ip => '192.168.0.51/24', :terraform__gateway => '192.168.0.1'
98
+
99
+ config.vm.provider :terraform do |terraform|
100
+ terraform.api_url = 'https://[PVE_ADDRESS]:8006/api2/json'
101
+ terraform.api_token_id = "terraform@pve!automation"
102
+ terraform.api_token_secret = "[API_TOKEN]"
103
+ terraform.insecure = true
104
+ terraform.debug = false
105
+ terraform.description = "Created by: #{ENV['USER']}"
106
+ terraform.template = 'ubuntu-noble-template'
107
+ terraform.cpu_cores = 2
108
+ terraform.memory_size = '4 GiB'
109
+ terraform.disk_size = '15 GB'
110
+ terraform.target_node = 'pve1'
111
+ terraform.storage_domain = '[STORAGE_NAME]'
112
+ terraform.nameserver = '1.1.1.1 1.0.0.1'
113
+ terraform.searchdomain = 'example.com'
114
+ end
115
+
116
+ config.vm.provision "shell", inline: <<-SHELL
117
+ ip a
118
+ SHELL
119
+ end
120
+ ```
@@ -0,0 +1,12 @@
1
+ # Vagrant Terraform dummy box
2
+
3
+ To turn this into a box:
4
+
5
+ ```
6
+ $ tar cvzf dummy.box ./metadata.json ./Vagrantfile
7
+ ```
8
+
9
+ Use it
10
+ ```
11
+ vagrant --provider=terraform box add --name terraform dummy.box
12
+ ```
@@ -0,0 +1,14 @@
1
+ # -*- mode: ruby -*-
2
+ # vi: set ft=ruby :
3
+ Vagrant.configure("2") do |config|
4
+ config.vm.box = 'terraform'
5
+ #config.vm.box_url = 'https://github.com/mika-b/vagrant-terraform/blob/master/example_box/dummy.box?raw=true'
6
+
7
+ config.vm.provider :terraform do |terraform|
8
+ terraform.api_url = 'https://server:8006/api2/json'
9
+ terraform.api_token_id = "terraform@pam!automation"
10
+ terraform.api_token_secret = "password"
11
+ terraform.insecure = true
12
+ terraform.debug = true
13
+ end
14
+ end
Binary file
@@ -0,0 +1,4 @@
1
+ {
2
+ "provider": "terraform"
3
+ }
4
+
@@ -0,0 +1,205 @@
1
+ require 'log4r'
2
+ require 'vagrant-terraform/util/machine_names'
3
+ require 'vagrant-terraform/util/terraform_execute'
4
+ require 'vagrant/util/retryable'
5
+
6
+ module VagrantPlugins
7
+ module TerraformProvider
8
+ module Action
9
+ class CreateVM
10
+ include Util::TerraformExecute
11
+ include Util::MachineNames
12
+ include Vagrant::Util::Retryable
13
+
14
+ def initialize(app, env)
15
+ @logger = Log4r::Logger.new("vagrant_terraform::action::create_vm")
16
+ @app = app
17
+ end
18
+
19
+ def call(env)
20
+ # Get config.
21
+ config = env[:machine].provider_config
22
+ if config.target_node.nil?
23
+ raise "'target_node' must not be empty."
24
+ end
25
+
26
+ if config.storage_domain.nil?
27
+ raise "'storage_domain' must not be empty."
28
+ end
29
+
30
+ if config.disk_size.nil?
31
+ raise "'disk_size' must be set."
32
+ end
33
+
34
+ vmname = machine_vmname(env[:machine])
35
+
36
+ main_tf = <<-END
37
+ provider "proxmox" {
38
+ pm_api_url = "#{config.api_url}"
39
+ pm_api_token_id = "#{config.api_token_id}"
40
+ pm_api_token_secret = "#{config.api_token_secret}"
41
+ pm_tls_insecure = #{config.insecure.to_s}
42
+ pm_debug = #{config.debug}
43
+ }
44
+
45
+ resource "proxmox_vm_qemu" "#{vmname.gsub(/\./, '-')}" {
46
+ name = "#{vmname}"
47
+ target_nodes = ["#{config.target_node}"]
48
+ desc = "#{config.description}"
49
+ vm_state = "stopped"
50
+ clone = "#{config.template}"
51
+ full_clone = "#{config.full_clone}"
52
+ cores = #{config.cpu_cores.to_i}
53
+ memory = #{Filesize.from("#{config.memory_size} B").to_f('MiB').to_i}
54
+ onboot = #{config.onboot}
55
+ agent = 1
56
+ vga {
57
+ type = "#{config.vga}"
58
+ # Between 4 and 512, ignored if type is defined to serial
59
+ memory = 64
60
+ }
61
+ scsihw = "virtio-scsi-pci"
62
+ boot = "order=virtio0"
63
+ bootdisk = "virtio0"
64
+ os_type = "#{config.os_type}"
65
+ disks {
66
+ virtio {
67
+ virtio0 {
68
+ disk {
69
+ backup = false
70
+ storage = "#{config.storage_domain}"
71
+ size = "#{Filesize.from("#{config.disk_size} B").to_f('GB').to_i}G"
72
+ }
73
+ }
74
+ }
75
+ ide {
76
+ ide2 {
77
+ cloudinit {
78
+ storage = "#{config.storage_domain}"
79
+ }
80
+ }
81
+ }
82
+ }
83
+ nameserver = "#{config.nameserver}"
84
+ searchdomain = "#{config.searchdomain}"
85
+ %NETWORKS%
86
+ ciuser = "vagrant"
87
+ sshkeys = <<EOF
88
+ ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key
89
+ EOF
90
+ }
91
+
92
+ # terraform output -raw public_ip
93
+ output "public_ip" {
94
+ value = proxmox_vm_qemu.#{vmname.gsub(/\./, '-')}.default_ipv4_address
95
+ }
96
+
97
+ terraform {
98
+ required_version = ">= 1.8.0"
99
+ required_providers {
100
+ proxmox = {
101
+ source = "telmate/proxmox"
102
+ version = "3.0.1-rc6"
103
+ #version = ">= 3.0.1-rc3"
104
+ }
105
+ }
106
+ }
107
+ END
108
+ network_template = <<-END
109
+ network {
110
+ id = %IDX%
111
+ bridge = "%BRIDGE%"
112
+ firewall = false
113
+ link_down = false
114
+ model = "virtio"
115
+ }
116
+ ipconfig%IDX% = "%IP%"
117
+ END
118
+
119
+ vagrantfile_networks = []
120
+ env[:machine].id = vmname
121
+ env[:machine].config.vm.networks.each_with_index do |network, idx|
122
+ type, options = network
123
+
124
+ # Only private networks are supported
125
+ next unless type == :private_network
126
+ if ! options[:terraform__ip].nil?
127
+ if ! options[:terraform__ip].include? "/"
128
+ raise "IP must be given in CIDR form, for example 192.168.0.10/24"
129
+ end
130
+
131
+ if options[:terraform__gateway].nil?
132
+ network_str = network_template.gsub(/%IP%/, "ip=#{options[:terraform__ip]}")
133
+ else
134
+ network_str = network_template.gsub(/%IP%/, "ip=#{options[:terraform__ip]},gw=#{options[:terraform__gateway]}")
135
+ end
136
+ else
137
+ network_str = network_template.gsub(/%IP%/, "ip=dhcp")
138
+ end
139
+ network_str = network_str.gsub(/%BRIDGE%/, options[:terraform__network_name])
140
+ network_str = network_str.gsub(/%IDX%/, idx.to_s)
141
+ vagrantfile_networks << network_str
142
+ end
143
+ main_tf = main_tf.gsub(/%NETWORKS%/, vagrantfile_networks.join())
144
+
145
+ # Output the settings we're going to use to the user
146
+ env[:ui].info(I18n.t("vagrant_terraform.creating_vm"))
147
+ env[:ui].info(" -- Name: #{vmname}")
148
+ env[:ui].info(" -- Template: #{config.template}")
149
+ env[:ui].info(" -- Description: #{config.description}")
150
+ env[:ui].info(" -- Target node: #{config.target_node}")
151
+ env[:ui].info(" -- Storage domain: #{config.storage_domain}")
152
+ env[:ui].info(" -- CPU Cores: #{config.cpu_cores}")
153
+ env[:ui].info(" -- Memory: #{Filesize.from("#{config.memory_size} B").to_f('MiB').to_i} MB")
154
+ env[:ui].info(" -- Disk: #{Filesize.from("#{config.disk_size} B").to_f('GiB').to_i} GB") unless config.disk_size.nil?
155
+
156
+ terraform_dir = env[:machine_tf_dir]
157
+ terraform_main_file = "#{terraform_dir}/main.tf"
158
+
159
+ File.write(terraform_main_file, main_tf)
160
+ terraform_execute(env, 'terraform init')
161
+
162
+ retryable(on: Errors::TerraformError, tries: 10, sleep: 1) do
163
+ begin
164
+ terraform_execute(env, "terraform apply -auto-approve")
165
+ rescue Errors::TerraformError => e
166
+ # ==> vm_one: terraform stderr: ╷
167
+ # ==> vm_one: │ Error: can't lock file '/var/lock/qemu-server/lock-100.conf' - got timeout
168
+ ansi_escape_regex = /\e\[(?:[0-9]{1,2}(?:;[0-9]{1,2})*)?[m|K]/
169
+ if e.message.gsub(ansi_escape_regex, '').include?("Error: can't lock file")
170
+ env[:ui].info("Proxmox unable to get lock, retrying")
171
+ raise e
172
+ end
173
+
174
+ if e.message.gsub(ansi_escape_regex, '') =~ /.*Error: [0-9 ]*unable to create VM [0-9]*: config file already exists/
175
+ env[:ui].info("Proxmox ID conflict, retrying")
176
+ raise e
177
+ end
178
+
179
+ if config.debug
180
+ raise e
181
+ else
182
+ fault_message = /Error: (.*)/.match(e.message.gsub(ansi_escape_regex, ''))[1] rescue e.message
183
+ raise Errors::CreateVMError,
184
+ :error_message => fault_message
185
+ end
186
+ end
187
+ end
188
+
189
+ @app.call(env)
190
+ end
191
+
192
+ def recover(env)
193
+ # undo
194
+ return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError) # leaves main.tf in .vagrant/terraform/HOSTNAME/
195
+ env[:ui].info(I18n.t("vagrant_terraform.error_recovering"))
196
+ destroy_env = env.dup
197
+ destroy_env.delete(:interrupted)
198
+ destroy_env[:config_validate] = false
199
+ destroy_env[:force_confirm_destroy] = true
200
+ env[:action_runner].run(Action.action_destroy, destroy_env)
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,39 @@
1
+ require 'fileutils'
2
+ require 'log4r'
3
+ require 'vagrant-terraform/errors'
4
+ require 'vagrant-terraform/util/terraform_execute'
5
+
6
+ module VagrantPlugins
7
+ module TerraformProvider
8
+ module Action
9
+
10
+ class DestroyVM
11
+ include Util::TerraformExecute
12
+
13
+ def initialize(app, env)
14
+ @logger = Log4r::Logger.new("vagrant_terraform::action::destroy_vm")
15
+ @app = app
16
+ end
17
+
18
+ def call(env)
19
+ env[:ui].info(I18n.t("vagrant_terraform.destroy_vm"))
20
+
21
+ begin
22
+ # for some reason here the machine_tf_dir is not in env even though read_state has been called
23
+ # multiple times on 'vagrant halt' before we get here.
24
+ terraform_dir = ".vagrant/terraform/#{env[:machine].id}"
25
+
26
+ terraform_execute(env, 'terraform destroy -auto-approve')
27
+ FileUtils.rm_rf(terraform_dir)
28
+ rescue Exception => e
29
+ retry if e.message =~ /Please try again/
30
+
31
+ raise e
32
+ end
33
+
34
+ @app.call(env)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,39 @@
1
+ require 'log4r'
2
+ require 'vagrant-terraform/errors'
3
+ require 'vagrant-terraform/util/terraform_execute'
4
+ require 'vagrant-terraform/util/update_vm_state'
5
+
6
+ module VagrantPlugins
7
+ module TerraformProvider
8
+ module Action
9
+
10
+ class HaltVM
11
+ include Util::TerraformExecute
12
+ include Util::UpdateVmState
13
+
14
+ def initialize(app, env)
15
+ @logger = Log4r::Logger.new("vagrant_terraform::action::halt_vm")
16
+ @app = app
17
+ end
18
+
19
+ def call(env)
20
+
21
+ env[:ui].info(I18n.t("vagrant_terraform.halt_vm"))
22
+
23
+ begin
24
+ terraform_dir = ".vagrant/terraform/#{env[:machine].id}"
25
+ terraform_main_file = "#{terraform_dir}/main.tf"
26
+ update_vm_state(terraform_main_file, "stopped")
27
+ terraform_execute(env, 'terraform apply -auto-approve')
28
+ rescue Exception => e
29
+ # TODO: need to retry in some case?
30
+ # retry if e.message =~ /something/
31
+ raise e
32
+ end
33
+
34
+ @app.call(env)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,19 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module TerraformProvider
5
+ module Action
6
+ class IsCreated
7
+ def initialize(app, env)
8
+ @app = app
9
+ @logger = Log4r::Logger.new("vagrant_terraform::action::is_created")
10
+ end
11
+
12
+ def call(env)
13
+ env[:result] = env[:machine].state.id != :not_created
14
+ @app.call(env)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module TerraformProvider
5
+ module Action
6
+ class IsRunning
7
+ def initialize(app, env)
8
+ @app = app
9
+ @logger = Log4r::Logger.new("vagrant_terraform::action::is_created")
10
+ end
11
+
12
+ def call(env)
13
+ env[:result] = env[:machine].state.id == :running
14
+ @app.call(env)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,44 @@
1
+ require "log4r"
2
+ require 'vagrant-terraform/util/terraform_execute'
3
+
4
+ module VagrantPlugins
5
+ module TerraformProvider
6
+ module Action
7
+ # This action reads the SSH info for the machine and puts it into the
8
+ # `:machine_ssh_info` key in the environment.
9
+ class ReadSSHInfo
10
+ include Util::TerraformExecute
11
+
12
+ def initialize(app, env)
13
+ @app = app
14
+ @logger = Log4r::Logger.new("vagrant_terraform::action::read_ssh_info")
15
+ end
16
+
17
+ def call(env)
18
+ env[:machine_ssh_info] = read_ssh_info(env)
19
+
20
+ @app.call(env)
21
+ end
22
+
23
+ # Returns a hash of SSH connection information if and only if at least one IPv4
24
+ # address associated with the machine in question could be retrieved
25
+ # Otherwise, it returns nil.
26
+ def read_ssh_info(env)
27
+ machine = env[:machine]
28
+
29
+ ip_addr = terraform_execute(env, "terraform output -raw public_ip")
30
+ return nil if ip_addr.nil?
31
+
32
+ return {
33
+ :host => ip_addr,
34
+ :port => machine.config.ssh.guest_port,
35
+ :username => machine.config.ssh.username,
36
+ :private_key_path => machine.config.ssh.private_key_path,
37
+ :forward_agent => machine.config.ssh.forward_agent,
38
+ :forward_x11 => machine.config.ssh.forward_x11,
39
+ }
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,67 @@
1
+ require "log4r"
2
+ require 'vagrant-terraform/util/terraform_execute'
3
+ require 'vagrant-terraform/util/machine_names'
4
+
5
+ $terraform_refreshed = nil
6
+
7
+ module VagrantPlugins
8
+ module TerraformProvider
9
+ module Action
10
+ # This action reads the state of the machine and puts it in the
11
+ # `:machine_state_id` key in the environment.
12
+ class ReadState
13
+ include Util::TerraformExecute
14
+ include Util::MachineNames
15
+
16
+ def initialize(app, env)
17
+ @app = app
18
+ @logger = Log4r::Logger.new("vagrant_terraform::action::read_state")
19
+ end
20
+
21
+ def call(env)
22
+ env[:machine_state_id] = read_state(env)
23
+ @app.call(env)
24
+ end
25
+
26
+ def read_state(env)
27
+ env[:machine_tf_dir] = ".vagrant/terraform/#{machine_vmname(env[:machine])}"
28
+ terraform_state_file = "#{env[:machine_tf_dir]}/terraform.tfstate"
29
+ if File.exist?(env[:machine_tf_dir]) && File.exist?(terraform_state_file)
30
+ # read_state might get called several times. Avoid refreshing 5 times in a row
31
+ # for example during "vagrant up" for no obvious reason.
32
+ if $terraform_refreshed.nil?
33
+ terraform_execute(env, 'terraform refresh')
34
+ $terraform_refreshed = true
35
+ end
36
+
37
+ json_data = File.read(terraform_state_file)
38
+ data = JSON.parse(json_data)
39
+
40
+ # Navigate to the "vm_state" value
41
+ resources = data["resources"]
42
+ return :not_created if resources.nil? || resources.empty?
43
+
44
+ first_resource = resources.first # TODO: find by name
45
+ instances = first_resource["instances"]
46
+ return :not_created if instances.nil? || instances.empty?
47
+
48
+ attributes = instances.first["attributes"]
49
+ return :not_created if attributes.nil?
50
+
51
+ ip_addr = attributes["default_ipv4_address"]
52
+ unless ip_addr.nil?
53
+ env[:ip_address] = ip_addr
54
+ end
55
+
56
+ if attributes["vm_state"].nil?
57
+ return :not_created
58
+ end
59
+ return attributes["vm_state"].to_sym
60
+ else
61
+ return :not_created
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end