virtualmaster 0.0.7 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,6 +1,5 @@
1
1
  *.gem
2
2
  .bundle
3
- Gemfile.lock
4
3
  pkg/*
5
4
  vendor/bundle
6
5
  vendor/cache
@@ -0,0 +1,40 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ virtualmaster (0.1.0)
5
+ commander (~> 4.1.2)
6
+ deltacloud-client (~> 0.5.0)
7
+ net-ssh (~> 2.3.0)
8
+ terminal-table (~> 1.4.4)
9
+
10
+ GEM
11
+ remote: http://rubygems.org/
12
+ specs:
13
+ commander (4.1.2)
14
+ highline (~> 1.6.11)
15
+ deltacloud-client (0.5.0)
16
+ nokogiri (>= 1.4.3)
17
+ rest-client (>= 1.6.1)
18
+ diff-lcs (1.1.3)
19
+ highline (1.6.11)
20
+ mime-types (1.18)
21
+ net-ssh (2.3.0)
22
+ nokogiri (1.5.2)
23
+ rest-client (1.6.7)
24
+ mime-types (>= 1.16)
25
+ rspec (2.9.0)
26
+ rspec-core (~> 2.9.0)
27
+ rspec-expectations (~> 2.9.0)
28
+ rspec-mocks (~> 2.9.0)
29
+ rspec-core (2.9.0)
30
+ rspec-expectations (2.9.1)
31
+ diff-lcs (~> 1.1.3)
32
+ rspec-mocks (2.9.0)
33
+ terminal-table (1.4.5)
34
+
35
+ PLATFORMS
36
+ ruby
37
+
38
+ DEPENDENCIES
39
+ rspec (~> 2)
40
+ virtualmaster!
@@ -1,6 +1,7 @@
1
- # VirtualMaster command line interface
1
+ # Virtualmaster command line interface
2
2
 
3
- Proof of concept of VirtualMaster command line interface
3
+
4
+ Proof of concept of Virtualmaster command line interface
4
5
 
5
6
  # virtualmaster config
6
7
  Running virtualmaster for first time.
@@ -13,13 +14,18 @@ Proof of concept of VirtualMaster command line interface
13
14
 
14
15
  Settings stored within ~/.virtualmaster
15
16
 
16
- # virtualmaster create demo1 --image lucid
17
- Using image 'lucid' with id (999).
18
- Creating 'micro' instance (512 MB memory/5 GB storage).
19
- Instance launch request accepted (instance id 44444).
20
-
17
+ # virtualmaster create demo1 --image ubuntu_lucid
18
+ Using image 'ubuntu_lucid' with ID 124
19
+ Creating 'micro' instance (512 MB memory/10 GB storage)
20
+ Instance launch request accepted. Instance ID 47135
21
+
21
22
  Default password 'AdVc:PBi8&7L'.
22
23
 
24
+ Waiting for instance................
25
+ Instance ready.
26
+
27
+ Try to login using `ssh root@80.79.29.246'
28
+
23
29
  # virtualmaster instances
24
30
  +-------+---------+----------------+
25
31
  | name | state | ip_address |
@@ -33,22 +39,21 @@ The current version of `virtualmaster` command line interface should work with R
33
39
 
34
40
  ## Automatically install SSH keys
35
41
 
36
- VirtualMaster CLI can install your SSH keys to a remote machine automatically using `--copy-id` switch.
42
+ Virtualmaster CLI can install your SSH keys to a remote machine automatically using `--identity IDENTITY_FILE` command-line option.
37
43
 
38
-
39
- virtualmaster create demo1 --image ubuntu_lucid --copy-id
44
+ virtualmaster create demo1 --image ubuntu_lucid --identity ~/.ssh/id_rsa
40
45
  Using image 'ubuntu_lucid' with ID 124
41
46
  Creating 'micro' instance (512 MB memory/10 GB storage)
42
47
  Instance launch request accepted. Instance ID 45387
43
48
 
44
49
  Default password 'vBK7i!kK'
50
+
45
51
  Waiting for instance............
46
- Loading identity file
52
+ Instance ready.
53
+
54
+ Loading identity file /Users/virtualmaster/.ssh/id_rsa
47
55
 
48
- Instance ready!
49
56
  Try to login using `ssh root@195.140.253.130'
50
-
51
- If you want to specify other key (ie. not ~/.ssh/id\_rsa) use option `--identity IDENTITY_FILE`.
52
57
 
53
58
  ## Instance size
54
59
 
@@ -12,19 +12,24 @@ end
12
12
 
13
13
  program :name, "virtualmaster"
14
14
  program :version, VirtualMaster::VERSION
15
- program :description, "VirtualMaster command line interface"
15
+ program :description, "Virtualmaster command line interface"
16
16
  program :help_formatter, :compact
17
17
 
18
18
  default_command :help
19
19
 
20
20
  VirtualMaster::CLI.run do
21
- end
21
+ # load callback
22
+ callback_files = File.join(File.dirname(__FILE__), "../lib/vmaster/callbacks/*.rb")
23
+ Dir.glob(callback_files).each do |f|
24
+ VirtualMaster::Callbacks.load_file(f)
25
+ end
22
26
 
23
- include Commander::UI
24
- include Commander::UI::AskForClass
25
- include Commander::Delegates
27
+ # TODO find callbacks in other rubygems
26
28
 
27
- $terminal.wrap_at = HighLine::SystemExtensions.terminal_size.first - 5 rescue 80 if $stdin.tty?
29
+ # include commands
30
+ require 'vmaster/config_command'
31
+ require 'vmaster/server_commands'
32
+ end
28
33
 
29
34
  Commander::Runner.instance.run!
30
35
 
@@ -4,6 +4,7 @@ require "vmaster/version"
4
4
  require "vmaster/helpers"
5
5
  require "vmaster/cli"
6
6
  require "vmaster/request"
7
+ require "vmaster/callbacks"
7
8
 
8
9
  require 'commander'
9
10
  require 'commander/delegates'
@@ -17,9 +18,3 @@ include Commander::UI::AskForClass
17
18
  include Commander::Delegates
18
19
 
19
20
  $terminal.wrap_at = HighLine::SystemExtensions.terminal_size.first - 5 rescue 80 if $stdin.tty?
20
-
21
-
22
- # include commands
23
- require 'vmaster/config_command'
24
- require 'vmaster/server_commands'
25
-
@@ -0,0 +1,92 @@
1
+ # encoding: utf-8
2
+
3
+ def callback(name, &block)
4
+ cb = VirtualMaster::Callbacks::Callback.new(name, &block)
5
+
6
+ VirtualMaster::CLI.callbacks << cb
7
+ end
8
+
9
+ module VirtualMaster
10
+ module Callbacks
11
+
12
+ class Callback
13
+ attr_reader :name
14
+
15
+ def initialize(name, &block)
16
+ @name = name
17
+ @blocks = {}
18
+ @option = nil
19
+
20
+ instance_eval &block
21
+ end
22
+
23
+ def before(event, &block)
24
+ store_callback(event, :before, &block)
25
+ end
26
+
27
+ def after(event, &block)
28
+ store_callback(event, :after, &block)
29
+ end
30
+
31
+ #
32
+ # command line options provide configuration
33
+ #
34
+ # TODO support for multiple options
35
+ #
36
+ def option(name, type, description)
37
+ @option = {
38
+ :name => name,
39
+ :type => type,
40
+ :description => description
41
+ }
42
+ end
43
+
44
+ def fire(event, phase, options, server)
45
+ @blocks[event][phase].call(options, server) if includes?(event, phase) && options[@option[:name]]
46
+ end
47
+
48
+ def to_s
49
+ @blocks.inspect
50
+ end
51
+
52
+ def has_option?
53
+ not @option.nil?
54
+ end
55
+
56
+ def to_option
57
+ arguments = []
58
+
59
+ option_name = "--#{@option[:name]}"
60
+ option_name << " VALUE" if @option[:type]
61
+
62
+ arguments << option_name
63
+ arguments << @option[:type] if @option[:type]
64
+ arguments << @option[:description]
65
+ end
66
+
67
+ private
68
+
69
+ def includes?(event, phase)
70
+ @blocks.include?(event) && @blocks[event].include?(phase)
71
+ end
72
+
73
+ def store_callback(event, phase, &block)
74
+ callbacks = @blocks[event] || {}
75
+
76
+ callbacks[phase] = block
77
+
78
+ @blocks[event] = callbacks
79
+ end
80
+ end
81
+
82
+ def self.load_file(file)
83
+ load file
84
+ end
85
+
86
+ def self.trigger_event(event, phase, options, server = {})
87
+ CLI.callbacks.each do |callback|
88
+ callback.fire(event, phase, options, server)
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,16 @@
1
+ #
2
+ # Demo callback
3
+ #
4
+
5
+ callback :demo do
6
+ option :demo, nil, 'Value to print'
7
+
8
+ after :create do |options, server|
9
+ puts "Hello world"
10
+ end
11
+
12
+ after :shutdown do
13
+ puts "That's all folks!"
14
+ end
15
+ end
16
+
@@ -0,0 +1,36 @@
1
+ #
2
+ # SSH keys callback
3
+ #
4
+
5
+ require 'net/ssh'
6
+ require 'base64'
7
+ require 'openssl'
8
+
9
+ callback :ssh_copy_id do
10
+ option :identity, String, 'Identity file which is going to be copied to target machine'
11
+
12
+ before :create do |options|
13
+ # FIXME requires support for optional values
14
+ options[:identity] ||= File.join(ENV['HOME'], '.ssh/id_rsa')
15
+
16
+ abort "Specified identity file #{options[:identity]} doesn't exist!" unless File.exist?(options[:identity])
17
+ end
18
+
19
+ after :create do |options, server|
20
+ authorized_key = nil
21
+
22
+ say "Loading identity file #{options[:identity]}\n"
23
+ key = OpenSSL::PKey::RSA.new File.read options[:identity]
24
+
25
+ # build authorized key output string
26
+ authtype = key.class.to_s.split('::').last.downcase
27
+ b64pub = ::Base64.encode64(key.to_blob).strip.gsub(/[\r\n]/, '')
28
+ authorized_key = "ssh-%s %s\n" % [authtype, b64pub] # => ssh-rsa AAAAB3NzaC1...=
29
+
30
+ Net::SSH.start(server.public_addresses.first[:address], 'root', :password => options[:password]) do |ssh|
31
+ # TODO exception handling
32
+ output = ssh.exec!("mkdir ~/.ssh")
33
+ output = ssh.exec!("echo '#{authorized_key}' >>~/.ssh/authorized_keys")
34
+ end
35
+ end
36
+ end
@@ -5,6 +5,7 @@ module VirtualMaster
5
5
  class CLI
6
6
  @@api = nil
7
7
  @@config = nil
8
+ @@callbacks = []
8
9
 
9
10
  def self.run
10
11
  # load config
@@ -39,5 +40,10 @@ module VirtualMaster
39
40
  def self.config
40
41
  @@config
41
42
  end
43
+
44
+ def self.callbacks
45
+ @@callbacks
46
+ end
42
47
  end
43
48
  end
49
+
@@ -12,24 +12,32 @@ log.level = Logger::WARN
12
12
 
13
13
  command :create do |c|
14
14
  c.description = "Launch new server instance"
15
+
15
16
  c.option '--image TEMPLATE', String, 'instance template to use'
16
17
  c.option '--profile PROFILE', String, 'instance hardware profile'
17
- c.option '--copy-id', 'install public key on a machine'
18
- c.option '--identity IDENTITY', String, 'SSH identity to use (with --copy-id)'
19
18
  c.option '--zone ZONE', String, 'Availability zone to launch instance i'
20
19
  c.option '--level LEVEL', String, 'Instance level to use (personal, production)'
21
- c.option '--wait', 'wait for instance to become operational'
20
+ c.option '--detached', 'Do not wait for instance to become operational (disables callbacks)'
21
+
22
+ # generate options for callbacks
23
+ VirtualMaster::CLI.callbacks.each do |cb|
24
+ c.send :option, *(cb.to_option)
25
+ end
26
+
22
27
  c.action do |args, options|
23
28
  # default values
24
- options.default :identity => File.join(ENV['HOME'], '.ssh/id_rsa')
25
29
  options.default :zone => "prague-l1"
26
30
  options.default :level => "personal"
31
+ options.default :interactive => true
27
32
 
28
33
  name = args.shift || abort('Server name required')
29
34
 
30
35
  # verify server name
31
36
  abort("Virtual server with name #{name} already exists!") if VirtualMaster::Helpers.get_instance(name)
32
37
 
38
+ # support for non-interactive mode
39
+ options.interactive = false if options.detached
40
+
33
41
  # image
34
42
  image_name = nil
35
43
  image_id = VirtualMaster::CLI.config[:default_image] || VirtualHost::DEFAULT_IMAGE
@@ -66,11 +74,14 @@ command :create do |c|
66
74
  hwp = VirtualMaster::Helpers.get_hw_profile(profile[:memory], profile[:storage])
67
75
  abort "Internal error: hardware profile not available" unless hwp
68
76
 
77
+ # before :create callbacks
78
+ VirtualMaster::Callbacks.trigger_event(:create, :before, options.__hash__, nil)
79
+
69
80
  say "Creating '#{profile_name}' instance (#{profile[:memory]} MB memory/#{profile[:storage]/1024} GB storage)"
70
81
 
71
82
  realm = "#{options.zone}-#{options.level}"
72
83
 
73
- instance = VirtualMaster::Helpers.create_instance(name, image_id, hwp.id, realm)
84
+ instance = VirtualMaster::Helpers.create_instance(name, image_id, hwp.id, realm) if options.interactive
74
85
 
75
86
  # TODO handle exceptions (invalid image/profile, limits, etc.)
76
87
 
@@ -78,11 +89,14 @@ command :create do |c|
78
89
 
79
90
  # FIXME authentication is missrepresented within Ruby object
80
91
  password = instance.authentication[:username]
81
- say "\n"
92
+ puts
82
93
  say "Default password '#{password}'"
94
+ puts
95
+
96
+ options.password = password
83
97
 
84
98
  # copy-id implies waiting for instance to become operational
85
- if options.wait || options.copy_id
99
+ if options.interactive
86
100
  print 'Waiting for instance'
87
101
 
88
102
  while (instance = VirtualMaster::Helpers.get_instance(name)).state != "RUNNING" do
@@ -92,30 +106,12 @@ command :create do |c|
92
106
  end
93
107
 
94
108
  puts
109
+ puts "Instance ready."
110
+
111
+ # TODO consistent naming (instance vs server)
112
+ VirtualMaster::Callbacks.trigger_event(:create, :after, options.__hash__, instance)
95
113
 
96
- # copy ssh id
97
- if options.copy_id
98
- authorized_key = nil
99
-
100
- abort "Specified identity file #{options.identity} doesn't exists!" unless File.exist?(options.identity)
101
-
102
- say "Loading identity file\n"
103
- key = OpenSSL::PKey::RSA.new File.read options.identity
104
-
105
- # build authorized key output string
106
- authtype = key.class.to_s.split('::').last.downcase
107
- b64pub = ::Base64.encode64(key.to_blob).strip.gsub(/[\r\n]/, '')
108
- authorized_key = "ssh-%s %s\n" % [authtype, b64pub] # => ssh-rsa AAAAB3NzaC1...=
109
-
110
- Net::SSH.start(instance.public_addresses.first[:address], 'root', :password => password) do |ssh|
111
- # TODO exception handling
112
- output = ssh.exec!("mkdir ~/.ssh")
113
- output = ssh.exec!("echo '#{authorized_key}' >>~/.ssh/authorized_keys")
114
- end
115
- end
116
-
117
114
  puts
118
- puts "Instance ready!"
119
115
  puts "Try to login using `ssh root@#{instance.public_addresses.first[:address]}'"
120
116
  end
121
117
  end
@@ -1,3 +1,3 @@
1
1
  module VirtualMaster
2
- VERSION = "0.0.7"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -4,7 +4,7 @@ require 'vmaster'
4
4
 
5
5
  program :name, "virtualmaster"
6
6
  program :version, VirtualMaster::VERSION
7
- program :description, "VirtualMaster command line interface"
7
+ program :description, "Virtualmaster command line interface"
8
8
  program :help_formatter, :compact
9
9
 
10
10
  default_command :test
@@ -8,8 +8,8 @@ Gem::Specification.new do |s|
8
8
  s.authors = ["Radim Marek"]
9
9
  s.email = ["radim@laststation.net"]
10
10
  s.homepage = "https://github.com/virtualmaster/virtualmaster-cli"
11
- s.summary = %q{Command line interface to VirtualMaster}
12
- s.description = %q{Command line interface to VirtualMaster. Control your virtual infrastructure.}
11
+ s.summary = %q{Command line interface to Virtualmaster}
12
+ s.description = %q{Command line interface to Virtualmaster. Control your virtual infrastructure.}
13
13
 
14
14
  s.rubyforge_project = "vmaster"
15
15
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: virtualmaster
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-06 00:00:00.000000000 Z
12
+ date: 2012-04-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: commander
16
- requirement: &70327968586800 !ruby/object:Gem::Requirement
16
+ requirement: &70096844415120 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 4.1.2
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70327968586800
24
+ version_requirements: *70096844415120
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: deltacloud-client
27
- requirement: &70327968586300 !ruby/object:Gem::Requirement
27
+ requirement: &70096844414580 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 0.5.0
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70327968586300
35
+ version_requirements: *70096844414580
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: terminal-table
38
- requirement: &70327967699220 !ruby/object:Gem::Requirement
38
+ requirement: &70096844414100 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 1.4.4
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70327967699220
46
+ version_requirements: *70096844414100
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: net-ssh
49
- requirement: &70327967698520 !ruby/object:Gem::Requirement
49
+ requirement: &70096844413440 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 2.3.0
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *70327967698520
57
+ version_requirements: *70096844413440
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: rspec
60
- requirement: &70327967697440 !ruby/object:Gem::Requirement
60
+ requirement: &70096844412820 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,8 +65,8 @@ dependencies:
65
65
  version: '2'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70327967697440
69
- description: Command line interface to VirtualMaster. Control your virtual infrastructure.
68
+ version_requirements: *70096844412820
69
+ description: Command line interface to Virtualmaster. Control your virtual infrastructure.
70
70
  email:
71
71
  - radim@laststation.net
72
72
  executables:
@@ -76,10 +76,14 @@ extra_rdoc_files: []
76
76
  files:
77
77
  - .gitignore
78
78
  - Gemfile
79
+ - Gemfile.lock
79
80
  - README.mdown
80
81
  - Rakefile
81
82
  - bin/virtualmaster
82
83
  - lib/vmaster.rb
84
+ - lib/vmaster/callbacks.rb
85
+ - lib/vmaster/callbacks/demo_callback.rb
86
+ - lib/vmaster/callbacks/ssh_keys_callback.rb
83
87
  - lib/vmaster/cli.rb
84
88
  - lib/vmaster/config_command.rb
85
89
  - lib/vmaster/helpers.rb
@@ -112,7 +116,7 @@ rubyforge_project: vmaster
112
116
  rubygems_version: 1.8.11
113
117
  signing_key:
114
118
  specification_version: 3
115
- summary: Command line interface to VirtualMaster
119
+ summary: Command line interface to Virtualmaster
116
120
  test_files:
117
121
  - spec/commands_spec.rb
118
122
  - spec/spec_helper.rb