-
-
Save jingkaihe/e2cfb49fb238cdaa761497335d43b122 to your computer and use it in GitHub Desktop.
Poor boy's terraform ⚠️ only works on SmartOS. ATM only supports native container (SmartOS & LX brand) provisioning & bootstrapping. You can provision KVM, however you can't run cmd against it
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require "pty" | |
require 'optparse' | |
$stdout.sync = true | |
$stderr.sync = true | |
class Hypervisor | |
attr_accessor :admin, :addr | |
def self.parse!(args) | |
new.tap do |hypervisor| | |
OptionParser.new do |opts| | |
opts.banner = "Usage: vm_sync [options] provision|destroy vm1 vm2 vm3..." | |
opts.on("-a", "--admin admin", "hypervisor admin running the command") do |admin| | |
hypervisor.admin = admin | |
end | |
opts.on("-h", "--addr addr", "hypervisor address to connect to") do |addr| | |
hypervisor.addr = addr | |
end | |
opts.on("-c", "--config config.yml", "hypervisor configuration") do |cf| | |
require "yaml" | |
YAML.load_file(cf).tap do |config| | |
hypervisor.admin = admin = config["admin"] | |
hypervisor.addr = config["addr"] | |
end | |
end | |
end.parse!(args) | |
end | |
end | |
def rpc_addr | |
"#{admin}@#{addr}" | |
end | |
def pty_spawn(*cmd, &block) | |
spawn_cmd = "ssh #{rpc_addr} #{cmd.join(" ")}" | |
process_line("Execute #{spawn_cmd.inspect}...", &block) | |
PTY.spawn(spawn_cmd) do |out, _in, _pid| | |
begin | |
while line = out.gets | |
process_line(line, &block) | |
end | |
rescue Errno::EIO | |
end | |
end | |
rescue PTY::ChildExited | |
process_line("#{spawn_cmd.inspect} finished", &block) | |
end | |
def run(*cmd) | |
`ssh #{rpc_addr} #{cmd.join(" ")}` | |
end | |
private | |
def process_line(line) | |
if block_given? | |
yield(line) | |
else | |
puts(line) | |
end | |
end | |
end | |
hypervisor = Hypervisor.parse!(ARGV) | |
class VM | |
attr_reader :name, :hypervisor | |
def initialize(name, hypervisor:) | |
@name = name | |
@hypervisor = hypervisor | |
end | |
def manifest | |
"#{name}.json" | |
end | |
def manifest_path | |
File.join File.dirname(__FILE__), manifest | |
end | |
def remote_path | |
"/tmp/#{manifest}" | |
end | |
def exist? | |
File.exist?(manifest_path) | |
end | |
def zlogin_exec(*cmd) | |
hypervisor.pty_spawn("zlogin", uuid, *cmd) do |line| | |
vmputs(line) | |
end | |
end | |
def provision | |
vmputs "provisioning..." | |
`scp #{manifest_path} #{hypervisor.rpc_addr}:#{remote_path}` | |
hypervisor.run("vmadm create -f #{remote_path}") | |
end | |
def provisioned? | |
uuid != "" | |
end | |
def uuid | |
hypervisor.run("vmadm list -Ho uuid alias=#{name}").chomp | |
end | |
def destroy | |
vmputs "Delete #{uuid}..." | |
hypervisor.run("vmadm delete #{uuid}") | |
end | |
def vmputs(line) | |
puts "[#{name}] #{line}" | |
end | |
end | |
class VMS | |
attr_reader :vms | |
def initialize(*names, hypervisor:) | |
@vms = names.map do |name| | |
VM.new(name, hypervisor: hypervisor) | |
end | |
end | |
def provision_each | |
parallel_each do |vm| | |
vm.provision unless vm.provisioned? | |
yield(vm) if block_given? | |
end | |
end | |
def destroy_all | |
parallel_each { |vm| vm.destroy } | |
end | |
def parallel_each | |
vms.each do |vm| | |
fork do | |
yield(vm) | |
end | |
end | |
Process.waitall | |
end | |
end | |
action = ARGV.shift | |
vm_names = ARGV | |
vms = VMS.new(*vm_names, hypervisor: hypervisor) | |
case action | |
when "provision" | |
vms.provision_each do |vm| | |
[ | |
"apt-get -y update", | |
"DEBIAN_FRONTEND=noninteractive apt-get -y upgrade", | |
"apt-get -y install puppet", | |
].each do |cmd| | |
vm.zlogin_exec(cmd) | |
end | |
end | |
when "destroy" | |
vms.destroy_all | |
when "rpc" | |
hypervisor.pty_spawn(*ARGV) | |
else | |
$stderr.puts "Support provision|destroy|rpc" | |
exit 1 | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment