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.
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'vagrant/action/builder'
4
+
5
+ module VagrantPlugins
6
+ module StartCloud
7
+ module Action
8
+ include Vagrant::Action::Builtin
9
+
10
+ def self.configure_machine
11
+ Vagrant::Action::Builder.new.tap do |b|
12
+ b.use ConfigValidate
13
+ b.use Call, IsCreated do |env, b2|
14
+ unless env[:result]
15
+ b2.use HandleBox
16
+ b2.use Import
17
+ end
18
+
19
+ b2.use Call, IsVirtualBox do |env2, b3|
20
+ if env2[:result]
21
+ b3.use ConfigureNetworks, provider: :virtualbox
22
+ else
23
+ b3.use ConfigureNetworks, provider: :zone
24
+ end
25
+ end
26
+
27
+ b2.use Provision
28
+ end
29
+ end
30
+ end
31
+
32
+ def self.action_halt
33
+ Vagrant::Action::Builder.new.tap do |b|
34
+ b.use ConfigValidate
35
+ b.use Call, IsCreated do |env, b2|
36
+ if env[:result]
37
+ b2.use Call, IsVirtualBox do |env2, b3|
38
+ if env2[:result]
39
+ b3.use VagrantPlugins::ProviderVirtualBox::Action::GracefulHalt
40
+ else
41
+ b3.use VagrantPlugins::ProviderZone::Action::GracefulHalt
42
+ end
43
+ end
44
+ b2.use PowerOff
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ def self.action_destroy
51
+ Vagrant::Action::Builder.new.tap do |b|
52
+ b.use ConfigValidate
53
+ b.use Call, IsCreated do |env, b2|
54
+ if env[:result]
55
+ b2.use action_halt
56
+ b2.use Call, IsVirtualBox do |env2, b3|
57
+ if env2[:result]
58
+ b3.use VagrantPlugins::ProviderVirtualBox::Action::Destroy
59
+ else
60
+ b3.use VagrantPlugins::ProviderZone::Action::Destroy
61
+ end
62
+ end
63
+ b2.use CleanupNetworks
64
+ b2.use ProvisionerCleanup
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ # Load middleware classes
71
+ action_root = Pathname.new(File.expand_path('action', __dir__))
72
+ autoload :ConfigureNetworks, action_root.join('configure_networks')
73
+ autoload :IsCreated, action_root.join('is_created')
74
+ autoload :IsVirtualBox, action_root.join('is_virtualbox')
75
+
76
+ # Helper methods
77
+ def self.with_target_vms(machine_name = nil, **opts, &block)
78
+ env = opts.fetch(:env, Vagrant::Environment.new)
79
+ env.machine_names = [machine_name] if machine_name
80
+
81
+ env.batch(parallel: true) do |batch|
82
+ env.active_machines.each do |name, provider|
83
+ batch.action(env.machine(name, provider), :up, &block)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module VagrantPlugins
6
+ module StartCloud
7
+ class Command < Vagrant.plugin('2', :command)
8
+ def initialize(argv, env)
9
+ super
10
+ @env = env
11
+ end
12
+
13
+ def execute
14
+ config_file = 'Hosts.yml'
15
+
16
+ unless File.exist?(config_file)
17
+ @env.ui.error(I18n.t('vagrant_startcloud.config.config_not_found', path: config_file))
18
+ return 1
19
+ end
20
+
21
+ begin
22
+ config = YAML.load_file(config_file)
23
+ return 1 unless config && config['hosts']
24
+
25
+ config['hosts'].each do |host|
26
+ name = "#{host['settings']['server_id']}--#{host['settings']['hostname']}.#{host['settings']['domain']}"
27
+ @env.ui.info(I18n.t('vagrant_startcloud.command.configuring', name: name))
28
+
29
+ with_target_vms([name]) do |vm|
30
+ vm.config.startcloud.settings = host['settings']
31
+ vm.config.startcloud.networks = host['networks']
32
+ vm.config.startcloud.disks = host['disks']
33
+ vm.config.startcloud.provisioning = host['provisioning']
34
+ vm.config.startcloud.folders = host['folders']
35
+ vm.config.startcloud.roles = host['roles']
36
+ vm.config.startcloud.vars = host['vars']
37
+ vm.config.startcloud.plugins = host['plugins']
38
+ vm.config.startcloud.zones = host['zones']
39
+ end
40
+ end
41
+
42
+ 0
43
+ rescue StandardError => e
44
+ @env.ui.error(I18n.t('vagrant_startcloud.config.yaml_error', error: e.message))
45
+ 1
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantPlugins
4
+ module StartCloud
5
+ class Config < Vagrant.plugin('2', :config)
6
+ attr_accessor :config_path, :settings, :networks, :disks, :provisioning, :folders, :roles, :vars, :plugins, :zones
7
+
8
+ def initialize
9
+ super
10
+ @config_path = UNSET_VALUE
11
+ @settings = {}
12
+ @networks = []
13
+ @disks = {}
14
+ @provisioning = {}
15
+ @folders = []
16
+ @roles = []
17
+ @vars = {}
18
+ @plugins = {}
19
+ @zones = {}
20
+ end
21
+
22
+ def finalize!
23
+ @config_path = nil if @config_path == UNSET_VALUE
24
+ end
25
+
26
+ def validate(_machine)
27
+ errors = _detected_errors
28
+
29
+ errors << I18n.t('vagrant_startcloud.config.config_not_found', path: @config_path) if @config_path && !File.exist?(@config_path)
30
+
31
+ if @settings.empty?
32
+ errors << I18n.t('vagrant_startcloud.config.no_settings')
33
+ return { 'StartCloud configuration' => errors }
34
+ end
35
+
36
+ required_settings = %w[hostname domain server_id box]
37
+ missing_settings = required_settings - @settings.keys
38
+
39
+ errors << I18n.t('vagrant_startcloud.config.missing_required', fields: missing_settings.join(', ')) unless missing_settings.empty?
40
+
41
+ { 'StartCloud configuration' => errors }
42
+ end
43
+
44
+ def load_config(machine)
45
+ return unless @config_path && File.exist?(@config_path)
46
+
47
+ config = YAML.load_file(@config_path)
48
+ return unless config && config['hosts']
49
+
50
+ host = config['hosts'].first
51
+ @settings = host['settings'] || {}
52
+ @networks = host['networks'] || []
53
+ @disks = host['disks'] || {}
54
+ @provisioning = host['provisioning'] || {}
55
+ @folders = host['folders'] || []
56
+ @roles = host['roles'] || []
57
+ @vars = host['vars'] || {}
58
+ @plugins = host['plugins'] || {}
59
+ @zones = host['zones'] || {}
60
+ rescue StandardError => e
61
+ machine.ui.error(I18n.t('vagrant_startcloud.config.yaml_error', error: e.message))
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'log4r'
4
+
5
+ module VagrantPlugins
6
+ module StartCloud
7
+ class ConfigBuilder
8
+ class << self
9
+ def load_config(config_path)
10
+ require 'yaml'
11
+ logger = Log4r::Logger.new('vagrant_startcloud::config_builder')
12
+ config = Config.new
13
+
14
+ if File.exist?(config_path)
15
+ logger.debug("Loading configuration from #{config_path}")
16
+ yaml_config = YAML.load_file(config_path)
17
+
18
+ if yaml_config && yaml_config['hosts']&.first
19
+ host = yaml_config['hosts'].first
20
+ config.settings = host['settings'] || {}
21
+ config.networks = host['networks'] || []
22
+ config.disks = host['disks'] || {}
23
+ config.provisioning = host['provisioning'] || {}
24
+ config.folders = host['folders'] || []
25
+ config.roles = host['roles'] || []
26
+ config.vars = host['vars'] || {}
27
+ config.plugins = host['plugins'] || {}
28
+ config.zones = host['zones'] || {}
29
+ config.config_path = config_path
30
+ logger.info('Configuration loaded successfully')
31
+ else
32
+ logger.warn('No hosts defined in configuration')
33
+ raise Errors::MissingHosts
34
+ end
35
+ else
36
+ logger.error("Configuration file not found: #{config_path}")
37
+ raise Errors::ConfigurationFileNotFound, path: config_path
38
+ end
39
+
40
+ config
41
+ rescue StandardError => e
42
+ logger.error("Error loading configuration: #{e.message}")
43
+ logger.error(e.backtrace.join("\n"))
44
+ raise Errors::InvalidConfigurationFormat, error: e.message
45
+ end
46
+
47
+ def validate_config(config)
48
+ logger = Log4r::Logger.new('vagrant_startcloud::config_builder')
49
+ errors = []
50
+
51
+ if config.settings.empty?
52
+ logger.error('No settings defined in configuration')
53
+ errors << I18n.t('vagrant_startcloud.config.no_settings')
54
+ else
55
+ required = %w[hostname domain server_id box]
56
+ missing = required.reject { |key| config.settings.key?(key) && !config.settings[key].to_s.empty? }
57
+ if missing.any?
58
+ logger.error("Missing required settings: #{missing.join(', ')}")
59
+ errors << I18n.t('vagrant_startcloud.config.missing_required', fields: missing.join(', '))
60
+ end
61
+ end
62
+
63
+ if config.networks&.any?
64
+ config.networks.each_with_index do |network, index|
65
+ unless network['type']
66
+ logger.error("Network type required for network at index #{index}")
67
+ errors << I18n.t('vagrant_startcloud.config.network_type_required', index: index)
68
+ end
69
+ end
70
+ end
71
+
72
+ errors << I18n.t('vagrant_startcloud.config.boot_disk_size_required') if config.disks&.any? && config.disks['boot'] && !config.disks['boot']['size']
73
+
74
+ if config.folders&.any?
75
+ config.folders.each_with_index do |folder, index|
76
+ unless folder['map'] && folder['to']
77
+ logger.error("Both 'map' and 'to' paths are required for folder at index #{index}")
78
+ errors << I18n.t('vagrant_startcloud.config.folder_paths_required', index: index)
79
+ end
80
+ end
81
+ end
82
+
83
+ errors
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantPlugins
4
+ module StartCloud
5
+ module Errors
6
+ class StartCloudError < Vagrant::Errors::VagrantError
7
+ error_namespace('vagrant_startcloud.errors')
8
+ end
9
+
10
+ class ConfigurationFileNotFound < StartCloudError
11
+ error_key(:config_not_found)
12
+ end
13
+
14
+ class YAMLParseError < StartCloudError
15
+ error_key(:yaml_error)
16
+ end
17
+
18
+ class NoSettingsError < StartCloudError
19
+ error_key(:no_settings)
20
+ end
21
+
22
+ class MissingRequiredSettings < StartCloudError
23
+ error_key(:missing_required_setting)
24
+ end
25
+
26
+ class MissingHosts < StartCloudError
27
+ error_key(:missing_hosts)
28
+ end
29
+
30
+ class MissingSettings < StartCloudError
31
+ error_key(:missing_settings)
32
+ end
33
+
34
+ class NetworkTypeRequired < StartCloudError
35
+ error_key(:network_type_required)
36
+ end
37
+
38
+ class BootDiskSizeRequired < StartCloudError
39
+ error_key(:boot_disk_size_required)
40
+ end
41
+
42
+ class FolderPathsRequired < StartCloudError
43
+ error_key(:folder_paths_required)
44
+ end
45
+
46
+ class InvalidConfigurationFormat < StartCloudError
47
+ error_key(:invalid_format)
48
+ end
49
+
50
+ class MachineNotFound < StartCloudError
51
+ error_key(:machine_not_found)
52
+ end
53
+
54
+ # Provisioner errors
55
+ class VMConfigurationError < StartCloudError
56
+ error_key(:configuring_vm)
57
+ end
58
+
59
+ class SSHConfigurationError < StartCloudError
60
+ error_key(:configuring_ssh)
61
+ end
62
+
63
+ class NetworkConfigurationError < StartCloudError
64
+ error_key(:configuring_networks)
65
+ end
66
+
67
+ class ProviderConfigurationError < StartCloudError
68
+ error_key(:configuring_provider)
69
+ end
70
+
71
+ class FolderConfigurationError < StartCloudError
72
+ error_key(:configuring_folders)
73
+ end
74
+
75
+ class ShellProvisionerError < StartCloudError
76
+ error_key(:configuring_shell)
77
+ end
78
+
79
+ class AnsibleProvisionerError < StartCloudError
80
+ error_key(:configuring_ansible)
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'vagrant'
4
+
5
+ module VagrantPlugins
6
+ module StartCloud
7
+ class Plugin < Vagrant.plugin('2')
8
+ @registered = []
9
+
10
+ name 'StartCloud'
11
+ description 'Provides support for provisioning machines using YAML configuration'
12
+
13
+ # Register config
14
+ config(:startcloud) do
15
+ require_relative 'config'
16
+ Config
17
+ end
18
+
19
+ # Register provisioner
20
+ config(:startcloud, :provisioner) do
21
+ require_relative 'config'
22
+ Config
23
+ end
24
+
25
+ provisioner(:startcloud) do
26
+ require_relative 'provisioner'
27
+ Provisioner
28
+ end
29
+
30
+ # Register command
31
+ command(:startcloud) do
32
+ require_relative 'command'
33
+ [VagrantPlugins::StartCloud::Command, { primary: true }]
34
+ end
35
+
36
+ # Register action hooks
37
+ action_hook(:startcloud_configure, :machine_action_up) do |hook|
38
+ require_relative 'action'
39
+ hook.before(Vagrant::Action::Builtin::ConfigValidate, Action.configure_machine)
40
+ end
41
+
42
+ # Register provider capabilities
43
+ provider_capability(:virtualbox, :configure_networks) do
44
+ require_relative 'action/configure_networks'
45
+ Action::ConfigureNetworks
46
+ end
47
+
48
+ provider_capability(:zones, :configure_networks) do
49
+ require_relative 'action/configure_networks'
50
+ Action::ConfigureNetworks
51
+ end
52
+
53
+ # Plugin registration
54
+ class << self
55
+ def registered
56
+ @registered ||= []
57
+ end
58
+
59
+ def registered?(name)
60
+ registered.include?(name)
61
+ end
62
+
63
+ def register(name)
64
+ registered << name unless registered?(name)
65
+ end
66
+
67
+ def version
68
+ VERSION
69
+ end
70
+
71
+ def components
72
+ @components ||= begin
73
+ c = Vagrant::Plugin::V2::Components.new
74
+ c.configs[:top].register(:startcloud) { Config }
75
+ c.configs[:provisioner].register(:startcloud) { Config }
76
+ c.commands.register(:startcloud) { [Command, { primary: true }] }
77
+ c.provisioners.register(:startcloud) { Provisioner }
78
+ c
79
+ end
80
+ end
81
+
82
+ def config_builder
83
+ require_relative 'config_builder'
84
+ ConfigBuilder
85
+ end
86
+ end
87
+
88
+ # Register plugin with Vagrant
89
+ register('vagrant-startcloud')
90
+
91
+ # Initialize I18n
92
+ def self.setup_i18n
93
+ I18n.load_path << File.expand_path('../../locales/en.yml', __dir__)
94
+ I18n.reload!
95
+ end
96
+
97
+ # Initialize plugin
98
+ def self.init!
99
+ setup_i18n
100
+ end
101
+
102
+ # Call initialization
103
+ init!
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantPlugins
4
+ module StartCloud
5
+ class Provisioner < Vagrant.plugin('2', :provisioner)
6
+ def configure
7
+ settings = config.settings || {}
8
+ networks = config.networks || []
9
+ provisioning = config.provisioning || {}
10
+ folders = config.folders || []
11
+
12
+ configure_basic_settings(machine.config, settings)
13
+ configure_ssh_settings(machine.config, settings)
14
+ configure_networks(machine.config, networks)
15
+ configure_provider(machine.config, settings)
16
+ configure_shared_folders(machine.config, folders)
17
+ configure_provisioners(machine.config, provisioning)
18
+ end
19
+
20
+ private
21
+
22
+ def configure_basic_settings(root_config, settings)
23
+ root_config.vm.hostname = "#{settings['hostname']}.#{settings['domain']}"
24
+ root_config.vm.boot_timeout = settings['boot_timeout'] if settings['boot_timeout']
25
+ end
26
+
27
+ def configure_ssh_settings(root_config, settings)
28
+ root_config.ssh.insert_key = false
29
+ root_config.ssh.forward_agent = true
30
+ root_config.ssh.username = settings['vagrant_user'] if settings['vagrant_user']
31
+ root_config.ssh.private_key_path = settings['vagrant_user_private_key_path'] if settings['vagrant_user_private_key_path']
32
+ end
33
+
34
+ def configure_networks(root_config, networks)
35
+ networks.each do |network|
36
+ options = {
37
+ ip: network['address'],
38
+ netmask: network['netmask'],
39
+ gateway: network['gateway'],
40
+ dhcp: network['dhcp4'],
41
+ auto_config: network['autoconf']
42
+ }
43
+
44
+ root_config.vm.network network['type'], **options
45
+ end
46
+ end
47
+
48
+ def configure_provider(root_config, settings)
49
+ provider_type = settings['provider_type']&.to_sym
50
+ return unless provider_type
51
+
52
+ root_config.vm.provider(provider_type) do |provider|
53
+ provider.memory = settings['memory'] if settings['memory']
54
+ provider.cpus = settings['vcpus'] if settings['vcpus']
55
+
56
+ case provider_type
57
+ when :virtualbox
58
+ configure_virtualbox(provider, settings)
59
+ when :zones
60
+ configure_zones(provider, settings)
61
+ end
62
+ end
63
+ end
64
+
65
+ def configure_virtualbox(provider, settings)
66
+ provider.gui = settings['show_console'] if settings.key?('show_console')
67
+ provider.customize ['modifyvm', :id, '--ostype', settings['os_type']] if settings['os_type']
68
+ end
69
+
70
+ def configure_zones(provider, settings)
71
+ provider.brand = settings['brand'] if settings['brand']
72
+ provider.autostart = settings['autostart'] if settings.key?('autostart')
73
+ end
74
+
75
+ def configure_shared_folders(root_config, folders)
76
+ folders.each do |folder|
77
+ options = {
78
+ type: folder['type'],
79
+ owner: folder['owner'],
80
+ group: folder['group']
81
+ }.compact
82
+
83
+ root_config.vm.synced_folder folder['map'], folder['to'], **options
84
+ end
85
+ end
86
+
87
+ def configure_provisioners(root_config, provisioning)
88
+ configure_shell_provisioner(root_config, provisioning['shell']) if provisioning['shell']
89
+ configure_ansible_provisioner(root_config, provisioning['ansible']) if provisioning['ansible']
90
+ end
91
+
92
+ def configure_shell_provisioner(root_config, shell_config)
93
+ root_config.vm.provision 'shell', **shell_config
94
+ end
95
+
96
+ def configure_ansible_provisioner(root_config, ansible_config)
97
+ root_config.vm.provision 'ansible_local', **ansible_config
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantPlugins
4
+ module StartCloud
5
+ VERSION = '0.1.2'
6
+ end
7
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require 'i18n'
5
+ require 'vagrant'
6
+
7
+ module VagrantPlugins
8
+ module StartCloud
9
+ lib_path = Pathname.new(File.expand_path('vagrant-startcloud', __dir__))
10
+
11
+ # Load all plugin files
12
+ require lib_path.join('version')
13
+ require lib_path.join('errors')
14
+ require lib_path.join('config')
15
+ require lib_path.join('command')
16
+ require lib_path.join('provisioner')
17
+ require lib_path.join('config_builder')
18
+ require lib_path.join('action')
19
+
20
+ # This returns the path to the source of this plugin.
21
+ def self.source_root
22
+ @source_root ||= Pathname.new(File.expand_path('..', __dir__))
23
+ end
24
+
25
+ # This initializes the internationalization strings.
26
+ def self.init_i18n
27
+ I18n.load_path << File.expand_path('locales/en.yml', source_root)
28
+ I18n.reload!
29
+ rescue Errno::ENOENT
30
+ # During testing, the locale file might not be in the expected location
31
+ return if ENV['VAGRANT_ENV'] == 'test'
32
+
33
+ raise
34
+ end
35
+
36
+ # This initializes the logging system.
37
+ def self.init_logging
38
+ require 'log4r'
39
+
40
+ level = nil
41
+ begin
42
+ level = Log4r.const_get(ENV['VAGRANT_LOG'].upcase)
43
+ rescue NameError
44
+ level = Log4r::INFO
45
+ end
46
+
47
+ logger = Log4r::Logger.new('vagrant_startcloud')
48
+ logger.outputters = Log4r::Outputter.stderr
49
+ logger.level = level
50
+
51
+ logger
52
+ end
53
+
54
+ # This sets up our log level to be whatever VAGRANT_LOG is.
55
+ def self.setup_logging
56
+ level = nil
57
+ begin
58
+ level = Log4r.const_get(ENV['VAGRANT_LOG'].upcase)
59
+ rescue NameError
60
+ # This means that the logging constant wasn't found,
61
+ # which is fine, we just keep `level` as `nil`. But
62
+ # we tell the user.
63
+ level = nil
64
+ end
65
+
66
+ # Some constants, such as "true" resolve to booleans, so the
67
+ # above error checking doesn't catch it. This will check to make
68
+ # sure that the log level is an integer, as Log4r requires.
69
+ level = nil unless level.is_a?(Integer)
70
+
71
+ # Set the logging level on all "vagrant" namespaced
72
+ # logs as long as we have a valid level.
73
+ return unless level
74
+
75
+ logger = Log4r::Logger.new('vagrant_startcloud')
76
+ logger.outputters = Log4r::Outputter.stderr
77
+ logger.level = level
78
+ logger
79
+ end
80
+
81
+ # This initializes the plugin.
82
+ def self.init!
83
+ init_i18n
84
+ init_logging
85
+ setup_logging
86
+ end
87
+ end
88
+ end
89
+
90
+ # Load the plugin
91
+ require 'vagrant-startcloud/plugin'
92
+
93
+ # Initialize the plugin
94
+ VagrantPlugins::StartCloud.init!