diff --git a/CHANGELOG.md b/CHANGELOG.md index 27e76c483..24ea0614d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,8 @@ IMPROVEMENTS: - providers/virtualbox: customizations via VBoxManage are retried, avoiding VirtualBox flakiness [GH-2483] - provisioners/ansible: allow files for extra vars [GH-2366] + - provisioners/puppet: client cert and private key can now be specified + for the puppet server provisioner. [GH-902] - provisioners/shell: Added `keep_color` option to not automatically color output based on stdout/stderr. [GH-2505] - provisioners/shell: Arguments can now be an array of args. [GH-1949] diff --git a/plugins/provisioners/puppet/config/puppet_server.rb b/plugins/provisioners/puppet/config/puppet_server.rb index 3dc5b5d59..1ca1f8da3 100644 --- a/plugins/provisioners/puppet/config/puppet_server.rb +++ b/plugins/provisioners/puppet/config/puppet_server.rb @@ -2,26 +2,67 @@ module VagrantPlugins module Puppet module Config class PuppetServer < Vagrant.plugin("2", :config) + attr_accessor :client_cert_path + attr_accessor :client_private_key_path + attr_accessor :facter + attr_accessor :options attr_accessor :puppet_server attr_accessor :puppet_node - attr_accessor :options - attr_accessor :facter def initialize super - @facter = {} - @options = [] - @puppet_node = UNSET_VALUE - @puppet_server = UNSET_VALUE + @client_cert_path = UNSET_VALUE + @client_private_key_path = UNSET_VALUE + @facter = {} + @options = [] + @puppet_node = UNSET_VALUE + @puppet_server = UNSET_VALUE end def finalize! super + @client_cert_path = nil if @client_cert_path == UNSET_VALUE + @client_private_key_path = nil if @client_private_key_path == UNSET_VALUE @puppet_node = nil if @puppet_node == UNSET_VALUE @puppet_server = "puppet" if @puppet_server == UNSET_VALUE end + + def validate(machine) + errors = _detected_errors + + if (client_cert_path && !client_private_key_path) || + (client_private_key_path && !client_cert_path) + errors << I18n.t( + "vagrant.provisioners.puppet_server.client_cert_and_private_key") + end + + if client_cert_path + path = Pathname.new(client_cert_path). + expand_path(machine.env.root_path) + if !path.file? + errors << I18n.t( + "vagrant.provisioners.puppet_server.client_cert_not_found") + end + end + + if client_private_key_path + path = Pathname.new(client_private_key_path). + expand_path(machine.env.root_path) + if !path.file? + errors << I18n.t( + "vagrant.provisioners.puppet_server.client_private_key_not_found") + end + end + + if !puppet_node && (client_cert_path || client_private_key_path) + errors << I18n.t( + "vagrant.provisioners.puppet_server.cert_requires_node") + end + + { "puppet server provisioner" => errors } + end end end end diff --git a/plugins/provisioners/puppet/provisioner/puppet_server.rb b/plugins/provisioners/puppet/provisioner/puppet_server.rb index b374f9773..e575b6fc8 100644 --- a/plugins/provisioners/puppet/provisioner/puppet_server.rb +++ b/plugins/provisioners/puppet/provisioner/puppet_server.rb @@ -41,6 +41,27 @@ module VagrantPlugins # Add the certname option if there is one options += ["--certname", cn] if cn + # A shortcut to make things easier + comm = @machine.communicate + + # If we have client certs specified, then upload them + if config.client_cert_path && config.client_private_key_path + @machine.ui.info( + I18n.t("vagrant.provisioners.puppet_server.uploading_client_cert")) + dirname = "/tmp/puppet-#{Time.now.to_i}-#{rand(1000)}" + comm.sudo("mkdir -p #{dirname}") + comm.sudo("mkdir -p #{dirname}/certs") + comm.sudo("mkdir -p #{dirname}/private_keys") + comm.sudo("chmod -R 0777 #{dirname}") + comm.upload(config.client_cert_path, "#{dirname}/certs/#{cn}.pem") + comm.upload(config.client_private_key_path, + "#{dirname}/private_keys/#{cn}.pem") + + # Setup the options so that they point to our directories + options << "--certdir=#{dirname}/certs" + options << "--privatekeydir=#{dirname}/private_keys" + end + # Disable colors if we must if !@machine.env.ui.is_a?(Vagrant::UI::Colored) options << "--color=false" @@ -59,9 +80,10 @@ module VagrantPlugins facter = "#{facts.join(" ")} " end - command = "#{facter}puppet agent #{options} --server #{config.puppet_server} --detailed-exitcodes || [ $? -eq 2 ]" + command = "#{facter}puppet agent #{options} --server " + + "#{config.puppet_server} --detailed-exitcodes || [ $? -eq 2 ]" - @machine.env.ui.info I18n.t("vagrant.provisioners.puppet_server.running_puppetd") + @machine.ui.info I18n.t("vagrant.provisioners.puppet_server.running_puppetd") @machine.communicate.sudo(command) do |type, data| if !data.empty? @machine.env.ui.info(data, :new_line => false, :prefix => false) diff --git a/templates/locales/en.yml b/templates/locales/en.yml index f1d086771..21a865b41 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -1258,12 +1258,22 @@ en: module_path_missing: "The configured module path doesn't exist: %{path}" puppet_server: + cert_requires_node: |- + "puppet_node" is required when a client cert or key is specified + client_cert_and_private_key: |- + Both a client certificate and private key must be specified, if any + client_cert_not_found: |- + The specified client cert path could not be found + client_private_key_not_found: |- + The specified client private key path could not be found not_detected: |- The `%{binary}` binary appears to not be in the PATH of the guest. This could be because the PATH is not properly setup or perhaps Puppet is not installed on this guest. Puppet provisioning can not continue without Puppet properly installed. running_puppetd: "Running Puppet agent..." + uploading_client_cert: |- + Uploading client certificate and private key... shell: args_bad_type: "Shell provisioner `args` must be a string or array." diff --git a/website/docs/source/v2/provisioning/puppet_agent.html.md b/website/docs/source/v2/provisioning/puppet_agent.html.md index dd91ad78f..960e7dff4 100644 --- a/website/docs/source/v2/provisioning/puppet_agent.html.md +++ b/website/docs/source/v2/provisioning/puppet_agent.html.md @@ -21,6 +21,32 @@ the set of modules and manifests from there.

+## Options + +The `puppet_server` provisioner takes various options. None are strictly +required. They are listed below: + +* `client_cert_path` (string) - Path to the client certificate for the + node on your disk. This defaults to nothing, in which case a client + cert won't be uploaded. + +* `client_private_key_path` (string) - Path to the client private key for + the node on your disk. This defaults to nothing, in which case a client + private key won't be uploaded. + +* `facter` (hash) - Additional Facter facts to make available to the + Puppet run. + +* `options` (string or array) - Additional command line options to pass + to `puppet agent` when Puppet is ran. + +* `puppet_node` (string) - The name of the node. If this isn't set, + this will attempt to use a hostname if set via `config.vm.hostname`. + Otherwise, the box name will be used. + +* `puppet_server` (string) - Hostname of the Puppet server. By default + "puppet" will be used. + ## Specifying the Puppet Master The quickest way to get started with the Puppet agent provisioner is to just