Skip to content

Commit

Permalink
Add option to store machine data in node attribute rather than file.
Browse files Browse the repository at this point in the history
To use this option, specify a driver url of 'ssh:chef'

In support of above:
1. Some refactoring of driver.rb.
2. Fixes some warnings with gemspec.
3. Fixes tests for changed location of vagrant insecure key in vagrant 1.7+.
4. Updated readme for above and some out of date info.

Also fixes #29 by using stored transport options if none specified
  • Loading branch information
christine_draper committed May 8, 2016
1 parent ef89ed0 commit 2ccebb8
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 95 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ test/.chef/provisioning
test/nodes
test/clients
test/.chef/local-mode-cache
.project
vendor/
Gemfile.lock
*.gem
32 changes: 24 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# Chef::Provisioning::Ssh

TODO: Write a gem description
Provisions existing machines using SSH.

## Installation

Expand All @@ -22,13 +22,25 @@ Or install it yourself as:

## Usage

The `machine_options` for provisioning ssh now use the key `transport_options` which line up directly with the `transport_options` for chef-provisioning proper.
### driver_url

`with_driver 'ssh'` will store machine data in a file in the directory `.chef/provisioning/ssh`
on the provisioning machine, with a reference
to the file in the node attribute `node['chef_provisioning']['reference']['ssh_machine_file']`

The `transport_options` key must be a *symbol*.
`with_driver 'ssh:/some/path'` will store machine data in the specified directory, with a
reference to the file as above.

Sub-keys should be *strings*.
`with_driver 'ssh:chef'` will store all machine data in the node attribute
`node['chef_provisioning']['reference']`.

The transport_options can be viewed in the code for chef-provisioning here:
### machine_options

The `machine_options` for provisioning ssh now use the key `transport_options` which line up directly with the `transport_options` for chef-provisioning proper.

The `transport_options` key and sub-keys may be strings or symbols.

The `transport_options` can be viewed in the code for chef-provisioning here:

https://github.com/chef/chef-provisioning/blob/master/lib/chef/provisioning/transport/ssh.rb#L17-L34

Expand Down Expand Up @@ -161,7 +173,7 @@ In addition to host, ip_address and hostname are also additional options.

To test it out, clone the repo:

`git clone https://github.com/double-z/chef-provisioning-ssh.git`
`git clone https://github.com/chef/chef-provisioning-ssh.git`

in the test directory there is a Vagrantfile with 2 nodes.

Expand All @@ -175,17 +187,21 @@ Then run from the test directory:

`chef-client -z -o vagrant::test_ssh`

NOTE: if the second machine fails it will be a result of issues with your vagrant key.
NOTE: if the first machine fails it will likely be a result of issues with your vagrant key.

This will run chef-provisioning on each of the two vagrant nodes.

thats it.

party on wayne.

Be aware, the `test_ssh` recipe is designed for testing, not to illustrate good practice. For example, you
do not need to list all three actions `[ :ready, :setup, :converge ]` or specify `converge true`
if you want the normal 'bootstrap if needed, converge if changed' behavior.

## Contributing

1. Fork it ( http://github.com/double-z/chef-provisioning-ssh/fork )
1. Fork it ( http://github.com/chef/chef-provisioning-ssh/fork )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
Expand Down
8 changes: 4 additions & 4 deletions chef-provisioning-ssh.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ Gem::Specification.new do |s|
s.extra_rdoc_files = ['README.md', 'LICENSE' ]
s.summary = 'Provisioner for managing servers using ssh in Chef Provisioning.'
s.description = s.summary
s.homepage = 'https://github.com/double-z/chef-provisioning-ssh'
s.homepage = 'https://github.com/chef/chef-provisioning-ssh'

s.require_path = "lib"
s.bindir = "bin"
s.executables = %w( )
s.files = %w(Rakefile LICENSE README.md Gemfile) + Dir.glob("*.gemspec") +
Dir.glob("{distro,lib,tasks,spec}/**/*", File::FNM_DOTMATCH).reject {|f| File.directory?(f) }

s.add_dependency 'chef-provisioning'
s.add_runtime_dependency 'chef-provisioning', "~> 1.0"

s.add_development_dependency "bundler", "~> 1.5"
s.add_development_dependency "rspec"
s.add_development_dependency "rake"
s.add_development_dependency "rspec", "~> 0"
s.add_development_dependency "rake", "~> 0"
end
151 changes: 78 additions & 73 deletions lib/chef/provisioning/ssh_driver/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ class Driver < Chef::Provisioning::Driver

include Chef::Provisioning::SshDriver::Helpers

# cluster_path is where the driver stores machine data unless use_chef_store is true
attr_reader :cluster_path

# use_chef_store is true if the driver_url is 'ssh:chef'
# In this case, machine data is stored in chef
# under node['chef_provisioning']['reference']['machine_options']
attr_reader :use_chef_store

def self.from_url(driver_url, config)
Driver.new(driver_url, config)
Expand All @@ -32,43 +38,44 @@ def initialize(driver_url, config)
super(driver_url, config)
scheme, cluster_path = driver_url.split(':', 2)
@cluster_path = cluster_path
@use_chef_store = cluster_path == 'chef'
end

def self.canonicalize_url(driver_url, config)
scheme, cluster_path = driver_url.split(':', 2)
cluster_path = File.expand_path(cluster_path || File.join(Chef::Config.config_dir, 'provisioning/ssh'))
unless cluster_path == 'chef'
cluster_path = File.expand_path(cluster_path || File.join(Chef::Config.config_dir, 'provisioning/ssh'))
end
"ssh:#{cluster_path}"
end

def allocate_machine(action_handler, machine_spec, machine_options)
existing_machine = ssh_machine_exists?(machine_spec)
ssh_machine_file_updated = create_machine(action_handler, machine_spec, machine_options)

if !existing_machine || !machine_spec.location
machine_spec.location = {
ssh_machine_options = prepare_machine_options(action_handler, machine_spec, machine_options)
log_info("current_machine_options = #{ssh_machine_options.to_s}")
unless ssh_machine_exists?(machine_spec)
machine_spec.reference = {
'driver_url' => driver_url,
'driver_version' => Chef::Provisioning::SshDriver::VERSION,
'target_name' => machine_spec.name,
'ssh_machine_file' => ssh_machine_file_updated,
'allocated_at' => Time.now.utc.to_s,
'updated_at' => Time.now.utc.to_s,
'host' => action_handler.host_node
}
elsif ssh_machine_file_updated
machine_spec.location['updated_at'] = Time.now.utc.to_s
end

update_ssh_machine(action_handler, machine_spec, ssh_machine_options)

if machine_spec.location && (machine_spec.location['driver_version'] != Chef::Provisioning::SshDriver::VERSION)
machine_spec.location['driver_version'] = Chef::Provisioning::SshDriver::VERSION
if machine_spec.reference &&
(machine_spec.reference['driver_version'] != Chef::Provisioning::SshDriver::VERSION)
machine_spec.reference['driver_version'] = Chef::Provisioning::SshDriver::VERSION
end

end

def ready_machine(action_handler, machine_spec, machine_options)
ssh_machine = existing_ssh_machine_to_sym(machine_spec)

if !ssh_machine
raise "SSH Machine #{machine_spec.name} does not have a machine file associated with it!"
unless ssh_machine
raise "SSH Machine #{machine_spec.name} does not have machine options associated with it!"
end

wait_for_transport(action_handler, ssh_machine, machine_spec, machine_options)
Expand All @@ -83,25 +90,25 @@ def connect_to_machine(machine_spec, machine_options)
def destroy_machine(action_handler, machine_spec, machine_options)
ssh_machine = ssh_machine_exists?(machine_spec)

if !ssh_machine || !::File.exists?(machine_spec.location['ssh_machine_file'])
raise "SSH Machine #{machine_spec.name} does not have a machine file associated with it!"
else
unless ssh_machine
raise "SSH Machine #{machine_spec.name} does not have machine options associated with it!"
end

unless use_chef_store
Chef::Provisioning.inline_resource(action_handler) do
file machine_spec.location['ssh_machine_file'] do
action :delete
backup false
end
end
end


file machine_spec.reference['ssh_machine_file'] do
action :delete
backup false
end
end
end
end

def stop_machine(action_handler, machine_spec, machine_options)
ssh_machine = existing_ssh_machine_to_sym(machine_spec)

if !ssh_machine
raise "SSH Machine #{machine_spec.name} does not have a machine file associated with it!"
unless ssh_machine && machine_spec.reference['machine_options']
raise "SSH Machine #{machine_spec.name} does not have machine options associated with it!"
end

action_handler.report_progress("SSH Machine #{machine_spec.name} is existing hardware login and power off.")
Expand All @@ -110,8 +117,8 @@ def stop_machine(action_handler, machine_spec, machine_options)
def machine_for(machine_spec, machine_options)
ssh_machine = existing_ssh_machine_to_sym(machine_spec)

if !ssh_machine
raise "SSH Machine #{machine_spec.name} does not have a machine file associated with it!"
unless ssh_machine
raise "SSH Machine #{machine_spec.name} does not have machine options associated with it!"
end

if ssh_machine[:transport_options][:is_windows]
Expand Down Expand Up @@ -192,8 +199,8 @@ def wait_for_transport(action_handler, ssh_machine, machine_spec, machine_option
def validate_machine_options(action_handler, machine_spec, machine_options)
error_msgs = []
valid = true

if !machine_options[:transport_options]
unless machine_options[:transport_options]
error_msgs << ":transport_options required."
valid = false
else
Expand Down Expand Up @@ -292,42 +299,14 @@ def ensure_ssh_cluster(action_handler)
end
end

def create_machine(action_handler, machine_spec, machine_options)
def create_machine_file(action_handler, machine_spec, machine_options_hash)
ensure_ssh_cluster(action_handler)

machine_options_hash_for_sym = deep_hashify(machine_options)
symbolized_machine_options = symbolize_keys(machine_options_hash_for_sym)
validate_machine_options(action_handler, machine_spec, symbolized_machine_options)
# end


# def create_ssh_machine(action_handler, machine_spec, machine_options)
log_info("File is = #{ssh_machine_file(machine_spec)}")
log_info("current_machine_options = #{machine_options.to_s}")

machine_options_hash_for_s = deep_hashify(machine_options)
stringy_machine_options = stringify_keys(machine_options_hash_for_s)
given_machine_options = create_machine_hash(stringy_machine_options)

if ssh_machine_exists?(machine_spec)
existing_machine_hash = existing_ssh_machine(machine_spec)
if !existing_machine_hash.eql?(given_machine_options)
create_machine_file(action_handler, machine_spec, given_machine_options)
else
return false
end
else
file_updated = create_machine_file(action_handler, machine_spec, given_machine_options)
file_updated
end
end

def create_machine_file(action_handler, machine_spec, machine_options)
file_path = ssh_machine_file(machine_spec)
machine_options_hash = deep_hashify(machine_options)
stringy_machine_options = stringify_keys(machine_options_hash)
options_parsed = ::JSON.parse(stringy_machine_options.to_json)
json_machine_options = ::JSON.pretty_generate(options_parsed)
log_info("File is = #{file_path}")
Chef::Provisioning.inline_resource(action_handler) do
file file_path do
content json_machine_options
Expand All @@ -347,12 +326,15 @@ def delete_ssh_machine(action_handler, machine_spec)
end

def existing_ssh_machine(machine_spec)
if ssh_machine_exists?(machine_spec)
existing_machine_hash = JSON.parse(File.read(ssh_machine_file(machine_spec)))
existing_machine_hash.to_hash
else
unless ssh_machine_exists?(machine_spec)
return {}
end

if use_chef_store
machine_spec.reference['machine_options']
else
JSON.parse(File.read(ssh_machine_file(machine_spec))).to_hash
end
end

def existing_ssh_machine_to_sym(machine_spec)
Expand All @@ -365,22 +347,47 @@ def existing_ssh_machine_to_sym(machine_spec)
end

def ssh_machine_exists?(machine_spec)
if machine_spec.location
::File.exists?(ssh_machine_file(machine_spec))
if use_chef_store
machine_spec.reference && machine_spec.reference['machine_options']
else
false
machine_spec.reference && ::File.exists?(ssh_machine_file(machine_spec))
end
end

def ssh_machine_file(machine_spec)
if machine_spec.location && machine_spec.location['ssh_machine_file']
machine_spec.location['ssh_machine_file']
if machine_spec.reference && machine_spec.reference['ssh_machine_file']
machine_spec.reference['ssh_machine_file']
else
ssh_machine_file = ::File.join(@cluster_path, "#{machine_spec.name}.json")
ssh_machine_file
end
end

def prepare_machine_options(action_handler, machine_spec, machine_options)
options_hash = symbolize_keys(deep_hashify(machine_options))

# if no transport options are specified, use the existing ones
unless options_hash[:transport_options]
ssh_machine = existing_ssh_machine_to_sym(machine_spec) || {}
options_hash[:transport_options] = ssh_machine[:transport_options] || {}
end

validate_machine_options(action_handler, machine_spec, options_hash)
create_machine_hash(stringify_keys(options_hash))
end

def update_ssh_machine(action_handler, machine_spec, ssh_machine_options)
unless existing_ssh_machine(machine_spec).eql? ssh_machine_options
if use_chef_store
machine_spec.reference['machine_options'] = ssh_machine_options
else
machine_spec.reference['ssh_machine_file'] =
create_machine_file(action_handler, machine_spec, ssh_machine_options)
end
machine_spec.reference['updated_at'] = Time.now.utc.to_s
end
end

def create_machine_hash(machine_options)
if !machine_options['transport_options']['host']
machine_options['transport_options']['host'] = machine_options['transport_options']['ip_address'] ||
Expand Down Expand Up @@ -441,8 +448,6 @@ def validate_transport_options_host(target_host)

raise 'Host is not a Valid IP or Resolvable Hostname' unless ( valid_ip || in_hosts_file || in_dns )
end


end
end
end
Expand Down
15 changes: 8 additions & 7 deletions test/Vagrantfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
Vagrant.configure("2") do |c|
c.ssh.insert_key = false

c.vm.define "provisioner" do |ne|
ne.vm.box = "opscode-ubuntu-12.04"
ne.vm.box_url = "https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-12.04_chef-provisionerless.box"
c.vm.define "provisioner" do |ne|
ne.vm.box = "opscode-ubuntu-14.04"
ne.vm.box_url = "https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-14.04_chef-provisionerless.box"
ne.vm.hostname = "target.vagrantup.com"
ne.vm.synced_folder "../../.", "/vagrant"
ne.vm.network(:private_network, {:ip=>"192.168.33.171"})
Expand All @@ -12,8 +13,8 @@ Vagrant.configure("2") do |c|
end

c.vm.define "ssh-one" do |one|
one.vm.box = "opscode-ubuntu-12.04"
one.vm.box_url = "https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-12.04_chef-provisionerless.box"
one.vm.box = "opscode-ubuntu-14.04"
one.vm.box_url = "https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-14.04_chef-provisionerless.box"
one.vm.hostname = "target01.vagrantup.com"
one.vm.synced_folder ".", "/vagrant", disabled: true
one.vm.network(:private_network, {:ip=>"192.168.33.122"})
Expand All @@ -23,8 +24,8 @@ Vagrant.configure("2") do |c|
end

c.vm.define "ssh-two" do |two|
two.vm.box = "opscode-ubuntu-12.04"
two.vm.box_url = "https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-12.04_chef-provisionerless.box"
two.vm.box = "opscode-ubuntu-14.04"
two.vm.box_url = "https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-14.04_chef-provisionerless.box"
two.vm.hostname = "target02.vagrantup.com"
two.vm.synced_folder ".", "/vagrant"
two.vm.network(:private_network, {:ip=>"192.168.33.123"})
Expand Down
Loading

0 comments on commit 2ccebb8

Please sign in to comment.