# # Author:: Philip (flip) Kromer () # Copyright:: Copyright (c) 2011 Infochimps, Inc # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef class Knife # # Vagrant support is VERY rough. It will # # * change the ui/commandline args, at the worst possible moment for you to adapt # * show you info that is inaccurate, even beyond the obvious fact that it reports AWS properties. # * translate your documents into swahili, make your TV record Gigli -- [tracker issue here](http://bit.ly/vrsalert) # # Vagrant has a really strong narcissistic streak, even more so than chef # and ironfan already do. I don't want to fight (it's probably that way for # good reasons), so the following oddball procedure may persist until it a) # stops working well or b) someone recommends a better approach. # # when you run `knife cluster vagrant command cluster-[facet-[indexes]]` we: # # * identify all servers defined on that cluster # * find (or make) the directory config.vagrant_path # - if unset, will use `{homebase}/vagrants/{cluster_name}` # * copy a special-purpose vagrantfile into that directory # - it is called vagrantfile, but won't work in a standalone way. # # class ClusterVagrant < Knife IRONFAN_DIR = File.dirname(File.realdirpath(__FILE__)) require File.expand_path('ironfan_knife_common', IRONFAN_DIR) include Ironfan::KnifeCommon deps do Ironfan::KnifeCommon.load_deps require 'vagrant' require File.expand_path('vagrant/ironfan_environment', IRONFAN_DIR) require File.expand_path('vagrant/ironfan_provisioners', IRONFAN_DIR) end banner "knife cluster vagrant CMD CLUSTER-[FACET-[INDEXES]] (options) - runs the given command against a vagrant environment created from your cluster definition. EARLY, use at your own risk" option :cloud, :long => "--cloud PROVIDER", :short => "-p", :description => "cloud provider to target, or 'false' to skip cloud-targeted steps. (default false)", :default => false, :boolean => false def run # The subnet for this world Chef::Config.host_network_blk ||= '33.33.33' # Location that cookbooks, roles, etc will be mounted on vm # set to false to skip Chef::Config.homebase_on_vm_dir "/homebase" if Chef::Config.homebase_on_vm_dir.nil? # yuck. necessary until cloud agnosticism shows up Chef::Config[:cloud] = config[:cloud] = false # TODO: make this customizable Chef::Config[:vagrant_path] = File.expand_path("vagrants", Chef::Config.homebase) # standard ironfan knife preamble load_ironfan die("Missing command or slice:\n#{banner}") if @name_args.length < 2 die("Too many args:\n#{banner}") if @name_args.length > 2 configure_dry_run ui.warn "Vagrant support is VERY rough: the ui will change, displays are inaccurate, may translate your documents into swahili" # # Load the servers. Note carefully: this is subtly different from all # the other `knife cluster` commands. Vagrant provides idempotency, but # we want the vagrant file to be invariant to the particular servers # you're asking it to diddle. # # So we configure VMs for all servers in the cluster, but only issue the # cli command against the ones given by the server slice. # vagrant_command, slice_string = @name_args target = get_slice(slice_string) all_servers = target.cluster.servers display(target) # Pre-populate information in chef section("Sync'ing to chef and cloud") target.sync_to_chef # FIXME: I read somewhere that global variables are a smell for something $ironfan_target = all_servers # # Prepare vagrant # section("Configuring vagrant", :green) cluster_vagrant_dir = File.expand_path(target.cluster.name.to_s, Chef::Config.vagrant_path) skeleton_vagrantfile = File.expand_path('vagrant/skeleton_vagrantfile.rb', IRONFAN_DIR) # using ':vagrantfile_name => skeleton_vagrantfile' doesn't seem to work # in vagrant - the VM comes out incompletely configured in a way I don't # totally understand. Plus it wants its own directory anyhow. So, make a # directory to hold vagrantfiles and push the skeleton in there FileUtils.mkdir_p cluster_vagrant_dir FileUtils.cp skeleton_vagrantfile, File.join(cluster_vagrant_dir, 'vagrantfile') log_level = [0, (3 - config.verbosity)].max env = Vagrant::IronfanEnvironment.new( :ui_class => Vagrant::UI::Colored, :cwd => cluster_vagrant_dir, :log_level => log_level, ) # # Run command # section("issuing command 'vagrant #{vagrant_command}'", :green) target.servers.each do |server| env.cli(vagrant_command, server.fullname) end end def display(target) super(target.cluster.servers, ["Name", "InstanceID", "State", "Flavor", "Image", "AZ", "Public IP", "Private IP", "Created At", 'Volumes', 'Elastic IP']) do |svr| { 'targeted?' => (target.include?(svr) ? "[blue]true[reset]" : '-' ), } end end end end end