hetzner-cli 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ pkg/*
data/.rvmrc ADDED
@@ -0,0 +1,6 @@
1
+ #this uses rvm
2
+ rvm use 1.8.7
3
+ rvm_gemset_create_on_use_flag=1
4
+ rvm gemset use hetzner-cli
5
+ alias rake="bundle exec rake"
6
+ alias hetzner-cli="bin/hetzner-cli"
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "hetzner-cli", :path => '.'
4
+ gem "faraday"
5
+ gem "rake"
6
+
7
+ gemspec
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # Hetnzer CLI
2
+ ## Purpose
3
+ [Hetzner](http://www.hetzner.de) is a hoster that allows you to get 'real' physical machines instead of all the 'virtualized' cloud stuff.
4
+ This is especially handy for testing virtualization stuff.
5
+
6
+ They provide a 'REST' like api that you can use to call separate tasks , like reset a server, install linux etc..
7
+ For most of the simple tasks,one could get away with using 'curl', but some tasks (like re-installing a server) require more coordination
8
+
9
+ 1. purpose #1 : of this is to automate these 'combined' hetnzer tasks via CLI instead of using their webinterface.
10
+ 2. purpose #2 : me to learn better their api and potentially put this into [fog](http://fog.io)
11
+ 3. purpose #3 : extend [mccloud](http://github.com/jedi4ever/mccloud) to allow for Hetnzer support
12
+
13
+ ## Tasks implemented
14
+
15
+ ### Distributions
16
+
17
+ Usage:
18
+ hetzner-cli distributions IP --password=PASSWORD --user=USER
19
+
20
+ Options:
21
+ --user=USER # Hetzner Admin Username
22
+ --password=PASSWORD # Hetzner Admin Password
23
+ [--robot-url=ROBOT_URL] # URL to connect to hetzner robo service
24
+ # Default: https://robot-ws.your-server.de/
25
+
26
+ List availble distributions for IP
27
+
28
+ ### Kickstart
29
+ The tasks of re-installing a server from scratch and putting an initial ssh key on it
30
+
31
+ Usage:
32
+ hetzner-cli kickstart IP --dist=DIST --password=PASSWORD --user=USER
33
+
34
+ Options:
35
+ [--lang=LANG] # Architecture to use
36
+ # Default: en
37
+ [--arch=ARCH] # Architecture to use (32|64)
38
+ # Default: 64
39
+ --user=USER # Hetzner Admin Username
40
+ --password=PASSWORD # Hetzner Admin Password
41
+ [--robot-url=ROBOT_URL] # URL to connect to hetzner robo service
42
+ # Default: https://robot-ws.your-server.de/
43
+ [--key-file=KEY_FILE] # SSH key to install as root user
44
+ # Default: /Users/patrick/.ssh/id_dsa.pub
45
+ --dist=DIST # Distribution to use
46
+
47
+ Re-install server with IP
48
+
49
+ ## Todo
50
+
51
+ - obviously make it catch errors more and write tests
52
+ - potentially integrate the functionality into fog with a hetzner provider
53
+ - look into using the hetzner-api plugin to leverage all the API calls
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ require 'bundler/setup'
4
+
5
+ require 'rake/testtask'
6
+ Bundler::GemHelper.install_tasks
data/bin/hetzner-cli ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ require 'hetzner-cli'
3
+
4
+ # Disable color if the proper argument was passed
5
+ shell = ARGV.include?("--no-color") ? Thor::Shell::Basic.new : Thor::Base.shell.new
6
+
7
+ # Start the CLI
8
+ ::HetznerCli::CLI.start(ARGV)
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/hetzner-cli/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "hetzner-cli"
6
+ s.version = HetznerCli::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["Patrick Debois"]
9
+ s.email = ["patrick.debois@jedi.be"]
10
+ s.homepage = "http://github.com/jedi4ever/hetzner-cli/"
11
+ s.summary = %q{Manage a Hetzner machine}
12
+ s.description = %q{Manage a Hetzner machine}
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+ s.rubyforge_project = "hetzner-cli"
16
+
17
+ s.add_dependency "thor"
18
+ s.add_dependency "yajl-ruby"
19
+ s.add_dependency "json"
20
+ s.add_dependency "excon"
21
+ s.add_dependency "net-ssh"
22
+ s.add_dependency "system_timer"
23
+
24
+ s.add_development_dependency "bundler", ">= 1.0.0"
25
+
26
+ s.files = `git ls-files`.split("\n")
27
+ s.executables = `git ls-files`.split("\n").map{ |f| f =~ /^bin\/(.*)/ ? $1 : nil }.compact
28
+ s.require_path = 'lib'
29
+ end
30
+
@@ -0,0 +1,3 @@
1
+ require 'thor'
2
+
3
+ require 'hetzner-cli/cli'
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+
3
+ require 'hetzner-cli/command'
4
+
5
+ module HetznerCli
6
+ class CLI < Thor
7
+
8
+ include HetznerCli::Command
9
+
10
+ desc "kickstart IP", "Re-install server with IP"
11
+ method_option :robot_url , :default => 'https://robot-ws.your-server.de/', :desc => "URL to connect to hetzner robo service"
12
+ method_option :user, :desc => 'Hetzner Admin Username', :required => true
13
+ method_option :password, :desc => 'Hetzner Admin Password', :required => true
14
+ method_option :dist, :desc => "Distribution to use", :required => true
15
+ method_option :arch, :default => '64', :desc => "Architecture to use (32|64)"
16
+ method_option :key_file, :default => File.join(ENV['HOME'],'.ssh','id_dsa.pub'), :desc => "SSH key to install as root user"
17
+ method_option :lang, :default => 'en', :desc => "Architecture to use"
18
+
19
+ def kickstart(ip)
20
+ _kickstart(ip,options)
21
+ end
22
+
23
+ desc "distributions IP", "List availble distributions for IP"
24
+ method_option :robot_url , :default => 'https://robot-ws.your-server.de/', :desc => "URL to connect to hetzner robo service"
25
+ method_option :user, :desc => 'Hetzner Admin Username', :required => true
26
+ method_option :password, :desc => 'Hetzner Admin Password', :required => true
27
+
28
+ def distributions(ip)
29
+ _distributions(ip,options)
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,9 @@
1
+ require 'hetzner-cli/command/kickstart'
2
+ require 'hetzner-cli/command/distributions'
3
+
4
+ module HetznerCli
5
+ module Command
6
+ include Kickstart
7
+ include Distributions
8
+ end
9
+ end
@@ -0,0 +1,40 @@
1
+ module HetznerCli
2
+ module Distributions
3
+
4
+ require 'pp'
5
+ require 'faraday'
6
+ require 'json'
7
+ require 'net/ssh'
8
+
9
+ def _distributions(ip,options)
10
+ user = options['user']
11
+ password = options['password']
12
+ robot_url = options['robot_url']
13
+
14
+ # Create connection
15
+ conn = Faraday.new(:url => robot_url) do |faraday|
16
+ faraday.request :url_encoded # form-encode POST params
17
+ #faraday.response :logger # log requests to STDOUT
18
+ faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
19
+ end
20
+
21
+ # Set credentials
22
+ conn.basic_auth(user,password)
23
+
24
+ begin
25
+ # Get a list of available distributions
26
+ puts "[#{ip}] Available distributions:"
27
+ response = conn.get("/boot/#{ip}")
28
+ boot_info = JSON.parse(response.body)
29
+ distributions = boot_info['boot']['linux']['dist']
30
+ distributions.each do |distro|
31
+ puts "[#{ip}] - #{distro}"
32
+ end
33
+ rescue Faraday::Error::ConnectionFailed => ex
34
+ $stderr.puts "Error logging in #{ex}"
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,116 @@
1
+ module HetznerCli
2
+ module Kickstart
3
+
4
+ require 'pp'
5
+ require 'faraday'
6
+ require 'json'
7
+ require 'net/ssh'
8
+
9
+ def _kickstart(ip,options)
10
+ user = options['user']
11
+ password = options['password']
12
+ robot_url = options['robot_url']
13
+ dist = options['dist']
14
+ lang = options['lang']
15
+ arch = options['arch']
16
+ key_file = options['key_file']
17
+ key = ''
18
+
19
+ # Reading keyfile
20
+ begin
21
+ key = File.read(key_file)
22
+ rescue Error => ex
23
+ $stderr.puts "[#{ip}] Error reading key_file"
24
+ exit -1
25
+ end
26
+
27
+ # Create connection
28
+ conn = Faraday.new(:url => robot_url) do |faraday|
29
+ faraday.request :url_encoded # form-encode POST params
30
+ #faraday.response :logger # log requests to STDOUT
31
+ faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
32
+ end
33
+
34
+ # Set credentials
35
+ conn.basic_auth(user,password)
36
+
37
+ # Check if new installation was already requested
38
+ unless installing?(conn,ip)
39
+
40
+ # Get a list of available distributions
41
+ puts "[#{ip}] Available distributions:"
42
+ response = conn.get("/boot/#{ip}")
43
+ boot_info = JSON.parse(response.body)
44
+ distributions = boot_info['boot']['linux']['dist']
45
+ distributions.each do |distro|
46
+ puts "[#{ip}] - #{distro}"
47
+ end
48
+
49
+ # Bail out if the distribution specified is not listed
50
+ unless distributions.include?(dist)
51
+ $stderr.puts "[#{ip}] The specified distribution '#{dist}' is not available for server with"
52
+ exit -1
53
+ else
54
+ puts "[#{ip}] Distribution selected: #{dist}"
55
+ end
56
+
57
+ # Trigger a new linux install on reboot
58
+ puts "[#{ip}] Activating new linux install: distribution '#{dist}', arch '#{arch}', lang '#{lang}'"
59
+ response = conn.post("/boot/#{ip}/linux", { :dist => dist , :arch => arch , :lang => lang})
60
+ linux_info = JSON.parse(response.body)
61
+ new_password = linux_info['linux']['password']
62
+
63
+ puts "[#{ip}] Sending hw reset"
64
+ # Hardware reboot the system
65
+ response = conn.post("/reset/#{ip}", { :type => 'hw'})
66
+
67
+ # Allowing the system to go down, saves us from checking bad authentication errors
68
+ puts "[#{ip}] New Password : #{new_password}"
69
+
70
+ begin
71
+ fully_booted = false
72
+ print "[#{ip}] Waiting for the linux install to finish and reboot: "
73
+ STDOUT.flush
74
+ 4.times {
75
+ sleep 5
76
+ print '.'
77
+ STDOUT.flush
78
+ }
79
+ while !fully_booted do
80
+ sleep 5
81
+ print '.'
82
+ STDOUT.flush
83
+ begin
84
+ # Ignoring new key
85
+ Net::SSH.start(ip,'root',:password => new_password , :paranoid => false, :timeout => 5 ) do |ssh|
86
+ output = ssh.exec!("cat /root/.ssh/authorized_keys2")
87
+ if output.include?("No such file")
88
+ puts
89
+ puts "[#{ip}] Install is finished and system is available"
90
+ puts "[#{ip}] Installing root ssh key"
91
+ output = ssh.exec!("echo '#{key}' > /root/.ssh/authorized_keys")
92
+ fully_booted = true
93
+ end
94
+ end
95
+ rescue Net::SSH::Disconnect,Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ECONNABORTED, Errno::ECONNRESET, Errno::ENETUNREACH,Errno::ETIMEDOUT,IOError,Timeout::Error,Net::SSH::AuthenticationFailed
96
+ #Ignoring these errors
97
+ end
98
+ end
99
+ end
100
+ puts "[#{ip}] System is ready for login"
101
+ puts "[#{ip}] You might want to cleanup your old key:"
102
+ puts "[#{ip}] ssh-keygen -R #{ip}"
103
+ puts "[#{ip}] ssh-keyscan #{ip} >> $HOME/.ssh/known_hosts"
104
+ else
105
+ $stderr.puts "[#{ip}] Installation already in progress, aborting"
106
+ exit -1
107
+ end
108
+ end
109
+
110
+ def installing?(conn,ip)
111
+ response = conn.get("/boot/#{ip}/linux")
112
+ linux_info = JSON.parse(response.body)
113
+ return linux_info['linux']['active'] == true
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,3 @@
1
+ module HetznerCli
2
+ VERSION = "0.0.2"
3
+ end
metadata ADDED
@@ -0,0 +1,179 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hetzner-cli
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
11
+ platform: ruby
12
+ authors:
13
+ - Patrick Debois
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-08-15 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ requirement: &id001 !ruby/object:Gem::Requirement
22
+ none: false
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ hash: 3
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ name: thor
32
+ version_requirements: *id001
33
+ prerelease: false
34
+ - !ruby/object:Gem::Dependency
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ hash: 3
41
+ segments:
42
+ - 0
43
+ version: "0"
44
+ type: :runtime
45
+ name: yajl-ruby
46
+ version_requirements: *id002
47
+ prerelease: false
48
+ - !ruby/object:Gem::Dependency
49
+ requirement: &id003 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ hash: 3
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ type: :runtime
59
+ name: json
60
+ version_requirements: *id003
61
+ prerelease: false
62
+ - !ruby/object:Gem::Dependency
63
+ requirement: &id004 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ hash: 3
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ type: :runtime
73
+ name: excon
74
+ version_requirements: *id004
75
+ prerelease: false
76
+ - !ruby/object:Gem::Dependency
77
+ requirement: &id005 !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ hash: 3
83
+ segments:
84
+ - 0
85
+ version: "0"
86
+ type: :runtime
87
+ name: net-ssh
88
+ version_requirements: *id005
89
+ prerelease: false
90
+ - !ruby/object:Gem::Dependency
91
+ requirement: &id006 !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ hash: 3
97
+ segments:
98
+ - 0
99
+ version: "0"
100
+ type: :runtime
101
+ name: system_timer
102
+ version_requirements: *id006
103
+ prerelease: false
104
+ - !ruby/object:Gem::Dependency
105
+ requirement: &id007 !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ hash: 23
111
+ segments:
112
+ - 1
113
+ - 0
114
+ - 0
115
+ version: 1.0.0
116
+ type: :development
117
+ name: bundler
118
+ version_requirements: *id007
119
+ prerelease: false
120
+ description: Manage a Hetzner machine
121
+ email:
122
+ - patrick.debois@jedi.be
123
+ executables:
124
+ - hetzner-cli
125
+ extensions: []
126
+
127
+ extra_rdoc_files: []
128
+
129
+ files:
130
+ - .gitignore
131
+ - .rvmrc
132
+ - Gemfile
133
+ - README.md
134
+ - Rakefile
135
+ - bin/hetzner-cli
136
+ - hetzner-cli.gemspec
137
+ - lib/hetzner-cli.rb
138
+ - lib/hetzner-cli/cli.rb
139
+ - lib/hetzner-cli/command.rb
140
+ - lib/hetzner-cli/command/distributions.rb
141
+ - lib/hetzner-cli/command/kickstart.rb
142
+ - lib/hetzner-cli/version.rb
143
+ homepage: http://github.com/jedi4ever/hetzner-cli/
144
+ licenses: []
145
+
146
+ post_install_message:
147
+ rdoc_options: []
148
+
149
+ require_paths:
150
+ - lib
151
+ required_ruby_version: !ruby/object:Gem::Requirement
152
+ none: false
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ hash: 3
157
+ segments:
158
+ - 0
159
+ version: "0"
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ hash: 23
166
+ segments:
167
+ - 1
168
+ - 3
169
+ - 6
170
+ version: 1.3.6
171
+ requirements: []
172
+
173
+ rubyforge_project: hetzner-cli
174
+ rubygems_version: 1.8.12
175
+ signing_key:
176
+ specification_version: 3
177
+ summary: Manage a Hetzner machine
178
+ test_files: []
179
+