vagrant-startcloud 0.1.2

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.
data/README.md ADDED
@@ -0,0 +1,179 @@
1
+ # Vagrant StartCloud Plugin
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/vagrant-startcloud.svg)](https://badge.fury.io/rb/vagrant-startcloud)
4
+ [![Build Status](https://github.com/STARTcloud/vagrant-startcloud/workflows/CI/badge.svg)](https://github.com/STARTcloud/vagrant-startcloud/actions)
5
+
6
+ A Vagrant plugin that provides support for provisioning machines using YAML configuration with multi-provider support.
7
+
8
+ ## Features
9
+
10
+ - YAML-based configuration for Vagrant machines
11
+ - Support for multiple providers (VirtualBox, Zones, etc.)
12
+ - Network configuration management
13
+ - Shared folder configuration
14
+ - Plugin management
15
+ - Provisioner configuration (Shell, Ansible)
16
+ - Provider-specific settings
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ vagrant plugin install vagrant-startcloud
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ 1. Create a `Hosts.yml` file in your project directory:
27
+
28
+ ```yaml
29
+ hosts:
30
+ - settings:
31
+ hostname: web
32
+ domain: example.com
33
+ server_id: '1001'
34
+ box: ubuntu/focal64
35
+ provider_type: virtualbox
36
+ virtualbox:
37
+ memory: 2048
38
+ cpus: 2
39
+ networks:
40
+ - type: private_network
41
+ ip: 192.168.50.10
42
+ folders:
43
+ - host: ./app
44
+ guest: /var/www/app
45
+ options:
46
+ type: rsync
47
+ provisioning:
48
+ shell:
49
+ path: setup.sh
50
+ ```
51
+
52
+ 2. Run Vagrant with the StartCloud plugin:
53
+
54
+ ```bash
55
+ vagrant startcloud up
56
+ ```
57
+
58
+ ## Configuration Reference
59
+
60
+ ### Settings
61
+
62
+ - `hostname`: Machine hostname
63
+ - `domain`: Domain name
64
+ - `server_id`: Unique identifier for the machine
65
+ - `box`: Vagrant box name
66
+ - `box_version`: (Optional) Specific box version
67
+ - `box_url`: (Optional) Custom box URL
68
+ - `provider_type`: Provider to use (virtualbox, zones)
69
+
70
+ ### Networks
71
+
72
+ ```yaml
73
+ networks:
74
+ - type: private_network
75
+ ip: 192.168.50.10
76
+ netmask: 255.255.255.0
77
+ gateway: 192.168.50.1
78
+ dhcp: true
79
+ auto_config: true
80
+ ```
81
+
82
+ ### Shared Folders
83
+
84
+ ```yaml
85
+ folders:
86
+ - host: ./app
87
+ guest: /var/www/app
88
+ options:
89
+ type: rsync
90
+ owner: www-data
91
+ group: www-data
92
+ ```
93
+
94
+ ### Provisioners
95
+
96
+ ```yaml
97
+ provisioning:
98
+ shell:
99
+ path: setup.sh
100
+ args: ['--debug']
101
+ ansible:
102
+ playbook: playbook.yml
103
+ install_mode: pip
104
+ ```
105
+
106
+ ### Provider Settings
107
+
108
+ #### VirtualBox
109
+
110
+ ```yaml
111
+ virtualbox:
112
+ memory: 2048
113
+ cpus: 2
114
+ gui: false
115
+ customize:
116
+ - ['modifyvm', '{{.Name}}', '--cpuexecutioncap', '50']
117
+ ```
118
+
119
+ #### Zones
120
+
121
+ ```yaml
122
+ zones:
123
+ memory: 2048
124
+ cpus: 2
125
+ brand: lx
126
+ image: ubuntu-20.04
127
+ ```
128
+
129
+ ## Commands
130
+
131
+ - `vagrant startcloud`: Main command
132
+ - `vagrant startcloud --list`: List configured machines
133
+ - `vagrant startcloud --config FILE`: Use custom config file
134
+ - `vagrant startcloud MACHINE`: Configure specific machine
135
+
136
+ ## Development
137
+
138
+ 1. Clone the repository:
139
+ ```bash
140
+ git clone https://github.com/STARTcloud/vagrant-startcloud.git
141
+ cd vagrant-startcloud
142
+ ```
143
+
144
+ 2. Install dependencies:
145
+ ```bash
146
+ bundle install
147
+ ```
148
+
149
+ 3. Run tests:
150
+ ```bash
151
+ bundle exec rake
152
+ ```
153
+
154
+ 4. Build and install locally:
155
+ ```bash
156
+ bundle exec rake gem:install
157
+ ```
158
+
159
+ ## Contributing
160
+
161
+ 1. Fork it
162
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
163
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
164
+ 4. Push to the branch (`git push origin my-new-feature`)
165
+ 5. Create new Pull Request
166
+
167
+ ## License
168
+
169
+ This project is licensed under the AGPL-3.0 License - see the [LICENSE](LICENSE) file for details.
170
+
171
+ ## Authors
172
+
173
+ - Mark Gilbert ([@Makr91](https://github.com/Makr91))
174
+ - Mark Gilbert ([@MarkProminic](https://github.com/MarkProminic))
175
+
176
+ ## Acknowledgments
177
+
178
+ - Based on the core_provisioner concept
179
+ - Inspired by vagrant-zones and vagrant-scp-sync plugins
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantPlugins
4
+ module StartCloud
5
+ module Action
6
+ class ConfigureNetworks
7
+ def initialize(app, env)
8
+ @app = app
9
+ @provider = env[:provider] || :virtualbox
10
+ end
11
+
12
+ def call(env)
13
+ machine = env[:machine]
14
+ config = machine.config.startcloud
15
+
16
+ if config.networks&.any?
17
+ machine.ui.info I18n.t('vagrant_startcloud.provisioner.configuring_networks')
18
+
19
+ config.networks.each do |network|
20
+ options = {
21
+ ip: network['address'],
22
+ netmask: network['netmask'],
23
+ gateway: network['gateway'],
24
+ dhcp: network['dhcp4'],
25
+ auto_config: network['autoconf']
26
+ }
27
+
28
+ case network['type']
29
+ when 'host'
30
+ configure_network(machine, 'private_network', options)
31
+ else
32
+ configure_network(machine, network['type'], options)
33
+ end
34
+ rescue StandardError => e
35
+ raise Errors::NetworkConfigurationError, error: e.message
36
+ end
37
+ end
38
+
39
+ @app.call(env)
40
+ end
41
+
42
+ private
43
+
44
+ def configure_network(machine, type, options)
45
+ case @provider
46
+ when :virtualbox
47
+ configure_virtualbox_network(machine, type, options)
48
+ when :zone
49
+ configure_zones_network(machine, type, options)
50
+ end
51
+ end
52
+
53
+ def configure_virtualbox_network(machine, type, options)
54
+ case type
55
+ when 'private_network'
56
+ machine.provider.capability(
57
+ :configure_networks,
58
+ [{
59
+ type: :static,
60
+ adapter: machine.provider.driver.read_network_interfaces.length + 1,
61
+ ip: options[:ip],
62
+ netmask: options[:netmask],
63
+ auto_config: options[:auto_config]
64
+ }]
65
+ )
66
+ when 'public_network'
67
+ machine.provider.capability(
68
+ :configure_networks,
69
+ [{
70
+ type: :bridged,
71
+ adapter: machine.provider.driver.read_network_interfaces.length + 1,
72
+ ip: options[:ip],
73
+ netmask: options[:netmask],
74
+ auto_config: options[:auto_config]
75
+ }]
76
+ )
77
+ end
78
+ end
79
+
80
+ def configure_zones_network(machine, type, options)
81
+ case type
82
+ when 'private_network'
83
+ machine.provider.capability(
84
+ :configure_networks,
85
+ [{
86
+ type: :vnic,
87
+ ip: options[:ip],
88
+ netmask: options[:netmask],
89
+ gateway: options[:gateway],
90
+ dhcp4: options[:dhcp],
91
+ autoconf: options[:auto_config]
92
+ }]
93
+ )
94
+ when 'public_network'
95
+ machine.provider.capability(
96
+ :configure_networks,
97
+ [{
98
+ type: :vnic,
99
+ public: true,
100
+ ip: options[:ip],
101
+ netmask: options[:netmask],
102
+ gateway: options[:gateway],
103
+ dhcp4: options[:dhcp],
104
+ autoconf: options[:auto_config]
105
+ }]
106
+ )
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantPlugins
4
+ module StartCloud
5
+ module Action
6
+ class ConfigureProviders
7
+ def initialize(app, _env)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+ machine = env[:machine]
13
+ config = machine.config.startcloud
14
+ settings = config.settings
15
+
16
+ if settings && settings['provider_type']
17
+ env[:ui].info(I18n.t('vagrant_startcloud.configuring.provider',
18
+ provider: settings['provider_type']))
19
+
20
+ case settings['provider_type']
21
+ when 'virtualbox'
22
+ configure_virtualbox(machine, settings)
23
+ when 'zones'
24
+ configure_zones(machine, settings, config.zones)
25
+ end
26
+ end
27
+
28
+ @app.call(env)
29
+ end
30
+
31
+ private
32
+
33
+ def configure_virtualbox(machine, settings)
34
+ machine.config.vm.provider :virtualbox do |vb|
35
+ memory = if settings['memory'].to_s =~ /gb|g/i
36
+ 1024 * settings['memory'].to_s.tr('^0-9', '').to_i
37
+ else
38
+ settings['memory'].to_s.tr('^0-9', '').to_i
39
+ end
40
+
41
+ vb.name = "#{settings['server_id']}--#{settings['hostname']}.#{settings['domain']}"
42
+ vb.gui = settings['show_console']
43
+ vb.customize ['modifyvm', :id, '--ostype', settings['os_type']] if settings['os_type']
44
+ vb.customize ['modifyvm', :id, '--vrdeport', settings['consoleport']] if settings['consoleport']
45
+ vb.customize ['modifyvm', :id, '--vrdeaddress', settings['consolehost']] if settings['consolehost']
46
+ vb.customize ['modifyvm', :id, '--cpus', settings['vcpus']] if settings['vcpus']
47
+ vb.customize ['modifyvm', :id, '--memory', memory] if memory.positive?
48
+ vb.customize ['modifyvm', :id, '--firmware', 'efi'] if settings['firmware_type'] == 'UEFI'
49
+
50
+ # Default VirtualBox settings
51
+ vb.customize ['modifyvm', :id, '--vrde', 'on']
52
+ vb.customize ['modifyvm', :id, '--natdnsproxy1', 'off']
53
+ vb.customize ['modifyvm', :id, '--natdnshostresolver1', 'off']
54
+ vb.customize ['modifyvm', :id, '--accelerate3d', 'off']
55
+ vb.customize ['modifyvm', :id, '--vram', '256']
56
+
57
+ # Apply custom VirtualBox directives if specified
58
+ if settings['vbox']&.dig('directives')
59
+ settings['vbox']['directives'].each do |directive|
60
+ vb.customize ['modifyvm', :id, "--#{directive['directive']}", directive['value']]
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def configure_zones(machine, settings, zones_config)
67
+ machine.config.vm.provider :zones do |zones|
68
+ zones.hostname = "#{settings['subdomain']}.#{settings['domain']}"
69
+ zones.name = "#{settings['partition_id']}--#{settings['subdomain']}.#{settings['domain']}"
70
+ zones.partition_id = settings['server_id']
71
+
72
+ # Map settings to zones provider configuration
73
+ zones_config.each do |key, value|
74
+ zones.send("#{key}=", value) if zones.respond_to?("#{key}=")
75
+ end
76
+
77
+ # Set required settings
78
+ zones.memory = settings['memory']
79
+ zones.cpus = settings['vcpus']
80
+ zones.brand = zones_config['brand']
81
+ zones.image = zones_config['image']
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantPlugins
4
+ module StartCloud
5
+ module Action
6
+ class ConfigureProvisioners
7
+ def initialize(app, _env)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+ machine = env[:machine]
13
+ config = machine.config.startcloud
14
+ provisioning = config.provisioning
15
+
16
+ if provisioning && !provisioning.empty?
17
+ env[:ui].info(I18n.t('vagrant_startcloud.configuring.provisioners'))
18
+
19
+ begin
20
+ configure_shell_provisioner(machine, provisioning['shell']) if provisioning['shell']&.dig('enabled')
21
+ configure_ansible_provisioner(machine, provisioning['ansible']) if provisioning['ansible']&.dig('enabled')
22
+ configure_docker_provisioner(machine, provisioning['docker']) if provisioning['docker']&.dig('enabled')
23
+ rescue StandardError => e
24
+ raise Errors::ProvisionerConfigurationFailed, error: e.message
25
+ end
26
+ end
27
+
28
+ @app.call(env)
29
+ end
30
+
31
+ private
32
+
33
+ def configure_shell_provisioner(machine, shell_config)
34
+ return unless shell_config['scripts']
35
+
36
+ shell_config['scripts'].each do |script|
37
+ machine.config.vm.provision 'shell', path: script
38
+ end
39
+ end
40
+
41
+ def configure_ansible_provisioner(machine, ansible_config)
42
+ return unless ansible_config['playbooks']
43
+
44
+ ansible_config['playbooks'].each do |playbooks|
45
+ configure_ansible_local(machine, playbooks['local']) if playbooks['local']
46
+
47
+ configure_ansible_remote(machine, playbooks['remote']) if playbooks['remote']
48
+ end
49
+ end
50
+
51
+ def configure_ansible_local(machine, playbooks)
52
+ playbooks.each do |playbook|
53
+ run_value = case playbook['run']
54
+ when 'always'
55
+ :always
56
+ when 'not_first'
57
+ File.exist?(File.join(Dir.pwd, 'results.yml')) ? :always : :never
58
+ else
59
+ :once
60
+ end
61
+
62
+ machine.config.vm.provision :ansible_local, run: run_value do |ansible|
63
+ ansible.playbook = playbook['playbook']
64
+ ansible.compatibility_mode = playbook['compatibility_mode'].to_s
65
+ ansible.install_mode = 'pip' if playbook['install_mode'] == 'pip'
66
+ ansible.verbose = playbook['verbose']
67
+ ansible.config_file = '/vagrant/ansible/ansible.cfg'
68
+ ansible.galaxy_roles_path = '/vagrant'
69
+
70
+ if playbook['remote_collections']
71
+ ansible.galaxy_role_file = '/vagrant/ansible/requirements.yml'
72
+ ansible.galaxy_roles_path = '/vagrant/ansible/ansible_collections'
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ def configure_ansible_remote(machine, playbooks)
79
+ playbooks.each do |playbook|
80
+ run_value = case playbook['run']
81
+ when 'always'
82
+ :always
83
+ when 'once'
84
+ File.exist?(File.join(Dir.pwd, 'results.yml')) ? :never : :once
85
+ when 'not_first'
86
+ File.exist?(File.join(Dir.pwd, 'results.yml')) ? :always : :never
87
+ else
88
+ :once
89
+ end
90
+
91
+ machine.config.vm.provision :ansible, run: run_value do |ansible|
92
+ ansible.playbook = playbook['playbook']
93
+ ansible.compatibility_mode = playbook['compatibility_mode'].to_s
94
+ ansible.verbose = playbook['verbose']
95
+
96
+ if playbook['remote_collections']
97
+ ansible.galaxy_role_file = 'requirements.yml'
98
+ ansible.galaxy_roles_path = './ansible/ansible_collections'
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ def configure_docker_provisioner(machine, docker_config)
105
+ machine.config.vm.provision 'docker'
106
+
107
+ return unless docker_config['docker_compose']
108
+
109
+ docker_config['docker_compose'].each do |file|
110
+ machine.config.vm.provision :docker_compose, yml: file, run: 'always'
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantPlugins
4
+ module StartCloud
5
+ module Action
6
+ class ConfigureSharedFolders
7
+ def initialize(app, _env)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+ machine = env[:machine]
13
+ config = machine.config.startcloud
14
+
15
+ if config.folders&.any?
16
+ env[:ui].info('Configuring shared folders...')
17
+
18
+ config.folders.each do |folder|
19
+ configure_folder(machine, folder)
20
+ end
21
+ end
22
+
23
+ @app.call(env)
24
+ end
25
+
26
+ private
27
+
28
+ def configure_folder(machine, folder)
29
+ mount_opts = folder['type'] == 'virtualbox' ? ['actimeo=1'] : []
30
+
31
+ machine.config.vm.synced_folder(
32
+ folder['map'],
33
+ folder['to'],
34
+ type: folder['type'],
35
+ owner: folder['owner'] || machine.config.startcloud.settings['vagrant_user'],
36
+ group: folder['group'] || machine.config.startcloud.settings['vagrant_user'],
37
+ mount_options: mount_opts,
38
+ automount: folder['automount'] || true,
39
+ scp__args: folder['args'],
40
+ rsync__args: folder['args'] || ['--verbose', '--archive', '-z', '--copy-links'],
41
+ rsync__chown: folder['chown'] || false,
42
+ create: folder['create'] || false,
43
+ rsync__rsync_ownership: folder['rsync_ownership'] || true,
44
+ disabled: folder['disabled'] || false
45
+ )
46
+
47
+ # Configure syncback if enabled and scp-sync plugin is available
48
+ return unless folder['syncback'] && Vagrant.has_plugin?('vagrant-scp-sync')
49
+
50
+ configure_syncback(machine, folder)
51
+ end
52
+
53
+ def configure_syncback(machine, folder)
54
+ settings = machine.config.startcloud.settings
55
+ prefix = "==> #{settings['server_id']}--#{settings['hostname']}.#{settings['domain']}:"
56
+
57
+ machine.config.trigger.after :rsync, type: :command do |trigger|
58
+ trigger.info = 'Using SCP to sync from Guest to Host'
59
+ trigger.ruby do |_env, _machine|
60
+ guest_path = folder['to']
61
+ host_path = folder['map'].split(%r{(?<=/)[^/]*$}).last
62
+ transfer_cmd = "vagrant scp :#{guest_path} #{host_path}"
63
+ puts "#{prefix} #{transfer_cmd}"
64
+ system(transfer_cmd)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantPlugins
4
+ module StartCloud
5
+ module Action
6
+ class IsCreated
7
+ def initialize(app, _env)
8
+ @app = app
9
+ @logger = Log4r::Logger.new('vagrant_startcloud::action::is_created')
10
+ end
11
+
12
+ def call(env)
13
+ machine = env[:machine]
14
+ state_id = machine.state.id
15
+
16
+ @logger.debug("Checking if machine '#{machine.name}' is created (state: #{state_id})")
17
+
18
+ # Consider the machine created if it's in any state other than :not_created
19
+ env[:result] = state_id != :not_created
20
+
21
+ @logger.info("Machine '#{machine.name}' is #{env[:result] ? '' : 'not '}created")
22
+
23
+ # Call the next middleware
24
+ @app.call(env)
25
+ rescue StandardError => e
26
+ @logger.error("Error checking machine state: #{e.message}")
27
+ @logger.error(e.backtrace.join("\n"))
28
+ raise Errors::VMConfigurationError, error: e.message
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantPlugins
4
+ module StartCloud
5
+ module Action
6
+ class IsVirtualBox
7
+ def initialize(app, _env)
8
+ @app = app
9
+ @logger = Log4r::Logger.new('vagrant_startcloud::action::is_virtualbox')
10
+ end
11
+
12
+ def call(env)
13
+ machine = env[:machine]
14
+ provider_name = machine.provider_name
15
+
16
+ @logger.debug("Checking if machine '#{machine.name}' uses VirtualBox provider (current: #{provider_name})")
17
+
18
+ # Check if the provider is VirtualBox
19
+ env[:result] = provider_name == :virtualbox
20
+
21
+ @logger.info("Machine '#{machine.name}' is #{env[:result] ? '' : 'not '}using VirtualBox provider")
22
+
23
+ # Call the next middleware
24
+ @app.call(env)
25
+ rescue StandardError => e
26
+ @logger.error("Error checking provider: #{e.message}")
27
+ @logger.error(e.backtrace.join("\n"))
28
+ raise Errors::ProviderConfigurationError, error: e.message
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module VagrantPlugins
6
+ module StartCloud
7
+ module Action
8
+ class LoadConfig
9
+ def initialize(app, _env)
10
+ @app = app
11
+ end
12
+
13
+ def call(env)
14
+ machine = env[:machine]
15
+ config = machine.config.startcloud
16
+
17
+ if config.config_path && File.file?(config.config_path)
18
+ yaml_config = YAML.load_file(config.config_path)
19
+ host = yaml_config['hosts']&.find do |h|
20
+ name = "#{h['settings']['server_id']}--#{h['settings']['hostname']}.#{h['settings']['domain']}"
21
+ name == machine.name.to_s
22
+ end
23
+
24
+ if host
25
+ config.settings = host['settings']
26
+ config.networks = host['networks'] || []
27
+ config.disks = host['disks'] || {}
28
+ config.provisioning = host['provisioning'] || {}
29
+ config.folders = host['folders'] || []
30
+ config.roles = host['roles'] || []
31
+ config.vars = host['vars'] || {}
32
+ config.plugins = host['plugins'] || {}
33
+ config.zones = host['zones'] || {}
34
+
35
+ env[:ui].info(I18n.t('vagrant_startcloud.config.loaded', name: machine.name))
36
+ else
37
+ env[:ui].warn(I18n.t('vagrant_startcloud.config.machine_not_found', name: machine.name))
38
+ end
39
+ else
40
+ env[:ui].warn(I18n.t('vagrant_startcloud.config.not_found', path: config.config_path))
41
+ end
42
+
43
+ @app.call(env)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end