knife-scaleway 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.rubocop.yml +11 -0
- data/Gemfile +4 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +201 -0
- data/README.md +480 -0
- data/Rakefile +23 -0
- data/knife-scaleway.gemspec +36 -0
- data/lib/chef/knife/scaleway.rb +110 -0
- data/lib/chef/knife/scaleway_account_info.rb +45 -0
- data/lib/chef/knife/scaleway_base.rb +89 -0
- data/lib/chef/knife/scaleway_droplet_create.rb +338 -0
- data/lib/chef/knife/scaleway_droplet_destroy.rb +69 -0
- data/lib/chef/knife/scaleway_droplet_list.rb +54 -0
- data/lib/chef/knife/scaleway_droplet_power.rb +69 -0
- data/lib/chef/knife/scaleway_droplet_powercycle.rb +48 -0
- data/lib/chef/knife/scaleway_droplet_reboot.rb +48 -0
- data/lib/chef/knife/scaleway_droplet_rebuild.rb +61 -0
- data/lib/chef/knife/scaleway_droplet_rename.rb +58 -0
- data/lib/chef/knife/scaleway_droplet_resize.rb +58 -0
- data/lib/chef/knife/scaleway_droplet_snapshot.rb +58 -0
- data/lib/chef/knife/scaleway_image_destroy.rb +42 -0
- data/lib/chef/knife/scaleway_image_list.rb +58 -0
- data/lib/chef/knife/scaleway_ip_list.rb +46 -0
- data/lib/chef/knife/scaleway_region_list.rb +43 -0
- data/lib/chef/knife/scaleway_size_list.rb +41 -0
- data/lib/chef/knife/scaleway_volume_list.rb +48 -0
- data/lib/knife-scaleway.rb +8 -0
- data/lib/knife-scaleway/version.rb +5 -0
- metadata +269 -0
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler'
|
4
|
+
Bundler.setup
|
5
|
+
Bundler::GemHelper.install_tasks
|
6
|
+
|
7
|
+
require 'rspec/core/rake_task'
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new(:spec)
|
10
|
+
|
11
|
+
require 'rubocop/rake_task'
|
12
|
+
desc 'Run RuboCop on the lib directory'
|
13
|
+
RuboCop::RakeTask.new(:rubocop) do |task|
|
14
|
+
task.patterns = ['lib/**/*.rb']
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Display LOC stats'
|
18
|
+
task :loc do
|
19
|
+
puts "\n## LOC Stats"
|
20
|
+
sh 'countloc -r lib/chef/knife'
|
21
|
+
end
|
22
|
+
|
23
|
+
task default: :spec
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'knife-scaleway/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = 'knife-scaleway'
|
8
|
+
gem.version = Knife::Scaleway::VERSION
|
9
|
+
gem.authors = ['Lukas Diener']
|
10
|
+
gem.email = ['lukas.diener@hotmail.com']
|
11
|
+
gem.description = "A plugin for chef's knife to manage instances of Scaleway servers"
|
12
|
+
gem.summary = "A plugin for chef's knife to manage instances of Scaleway servers"
|
13
|
+
gem.homepage = 'https://github.com/LukasSkywalker/knife-scaleway'
|
14
|
+
gem.license = 'Apache 2.0'
|
15
|
+
|
16
|
+
gem.add_dependency 'chef', '>= 10.18'
|
17
|
+
|
18
|
+
gem.add_development_dependency 'rspec', '~> 3.1'
|
19
|
+
gem.add_development_dependency 'rubocop', '~> 0.27'
|
20
|
+
gem.add_development_dependency 'rake'
|
21
|
+
gem.add_development_dependency 'knife-solo'
|
22
|
+
gem.add_development_dependency 'knife-zero'
|
23
|
+
gem.add_development_dependency 'webmock', '~> 1.20'
|
24
|
+
gem.add_development_dependency 'vcr', '~> 2.9'
|
25
|
+
gem.add_development_dependency 'guard', '~> 2.8'
|
26
|
+
gem.add_development_dependency 'guard-rspec', '~> 4.3'
|
27
|
+
gem.add_development_dependency 'coveralls'
|
28
|
+
gem.add_development_dependency 'countloc'
|
29
|
+
gem.add_development_dependency 'simplecov'
|
30
|
+
gem.add_development_dependency 'simplecov-console'
|
31
|
+
|
32
|
+
gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
33
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
34
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
35
|
+
gem.require_paths = ['lib']
|
36
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Scaleway
|
5
|
+
class Client
|
6
|
+
attr_accessor :access_key, :token
|
7
|
+
|
8
|
+
def initialize(access_key, token)
|
9
|
+
@host1 = 'https://api.scaleway.com'
|
10
|
+
@host2 = 'https://account.scaleway.com'
|
11
|
+
@access_key = access_key
|
12
|
+
@token = token
|
13
|
+
@instance = self
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.instance
|
17
|
+
#raise StandardError, 'Create client before accessing methods' if @instance.nil?
|
18
|
+
#@instance
|
19
|
+
Scaleway::Client.new(Chef::Config[:knife][:scaleway_access_key], Chef::Config[:knife][:scaleway_token])
|
20
|
+
end
|
21
|
+
|
22
|
+
def request(path, method, payload = nil)
|
23
|
+
host = @host1 if path.index('/servers') || path.index('/images') || path.index('/volumes') || path.index('/ips')
|
24
|
+
host = @host2 if path.index('/organizations')
|
25
|
+
raise StandardError, "Add /#{path.split('/')[1]} to host map" if host.nil?
|
26
|
+
|
27
|
+
headers = {:'X-Auth-Token' => @token, :'Content-Type' => 'application/json'}
|
28
|
+
url = host + path
|
29
|
+
|
30
|
+
options = { url: url, method: method, verify_ssl: false, headers: headers }
|
31
|
+
if method == :post
|
32
|
+
options.merge!(payload: payload)
|
33
|
+
puts payload if ENV['DEBUG']
|
34
|
+
end
|
35
|
+
|
36
|
+
begin
|
37
|
+
puts "### Req #{method.upcase} #{host + path}" if ENV['DEBUG']
|
38
|
+
JSON.parse(RestClient::Request.execute(options).body, object_class: OpenStruct, array_class: Array)
|
39
|
+
rescue => e
|
40
|
+
data = JSON.parse(e.response, object_class: OpenStruct)
|
41
|
+
puts "#{data.type}: #{data.message}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def get(path)
|
46
|
+
request(path, :get)
|
47
|
+
end
|
48
|
+
|
49
|
+
def post(path, data)
|
50
|
+
request(path, :post, data)
|
51
|
+
end
|
52
|
+
|
53
|
+
def put(path, data)
|
54
|
+
request(path, :put, data)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Organization
|
59
|
+
def self.all
|
60
|
+
Client.instance.get('/organizations')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class Ip
|
65
|
+
def self.all
|
66
|
+
Client.instance.get('/ips').ips
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Image
|
71
|
+
def self.all
|
72
|
+
Client.instance.get('/images').images
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.find(query)
|
76
|
+
response = Client.instance.get('/images')
|
77
|
+
response.images.select do |image|
|
78
|
+
image.name.downcase.index(query.downcase)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class Server
|
84
|
+
def self.all
|
85
|
+
Client.instance.get('/servers').servers
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.find(id)
|
89
|
+
Client.instance.get("/servers/#{id}").server
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.create(name, image, commercial_type)
|
93
|
+
Client.instance.post('/servers', { name: name, organization: Client.instance.access_key, image: image, commercial_type: commercial_type}.to_json).server
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.actions(id)
|
97
|
+
Client.instance.get("/servers/#{id}/action")
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.action(id, act)
|
101
|
+
Client.instance.post("/servers/#{id}/action", { action: act }.to_json)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class Volume
|
106
|
+
def self.all
|
107
|
+
Client.instance.get('/volumes').volumes
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
2
|
+
# you may not use this file except in compliance with the License.
|
3
|
+
# You may obtain a copy of the License at
|
4
|
+
#
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10
|
+
# See the License for the specific language governing permissions and
|
11
|
+
# limitations under the License.
|
12
|
+
#
|
13
|
+
require 'chef/knife/scaleway_base'
|
14
|
+
|
15
|
+
class Chef
|
16
|
+
class Knife
|
17
|
+
class ScalewayAccountInfo < Knife
|
18
|
+
include Knife::ScalewayBase
|
19
|
+
|
20
|
+
banner 'knife scaleway account info (options)'
|
21
|
+
|
22
|
+
def run
|
23
|
+
$stdout.sync = true
|
24
|
+
|
25
|
+
validate!
|
26
|
+
|
27
|
+
account_info = [
|
28
|
+
ui.color('UUID', :bold),
|
29
|
+
ui.color('Email', :bold),
|
30
|
+
ui.color('Droplet Limit', :bold),
|
31
|
+
ui.color('Email Verified', :bold)
|
32
|
+
]
|
33
|
+
|
34
|
+
account = client.account.info
|
35
|
+
|
36
|
+
account_info << account.uuid.to_s
|
37
|
+
account_info << account.email.to_s
|
38
|
+
account_info << account.server_limit.to_s
|
39
|
+
account_info << account.email_verified.to_s
|
40
|
+
|
41
|
+
puts ui.list(account_info, :uneven_columns_across, 4)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# lots of awesome stoff stolen from opscode/knife-azure ;-)
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
#
|
15
|
+
class Chef
|
16
|
+
class Knife
|
17
|
+
module ScalewayBase
|
18
|
+
def self.load_deps
|
19
|
+
require_relative 'scaleway'
|
20
|
+
require 'json'
|
21
|
+
require 'chef/mixin/shell_out'
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.included(includer)
|
25
|
+
includer.class_eval do
|
26
|
+
category 'scaleway'
|
27
|
+
|
28
|
+
# Lazy load our dependencies. Later calls to `Knife#deps` override
|
29
|
+
# previous ones, so if the including class calls it, it needs to also
|
30
|
+
# call our #load_deps, i.e:
|
31
|
+
#
|
32
|
+
# Include Chef::Knife::ScalewayBase
|
33
|
+
#
|
34
|
+
# deps do
|
35
|
+
# require 'foo'
|
36
|
+
# require 'bar'
|
37
|
+
# Chef::Knife::ScalewayBase.load_deps
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
deps { Chef::Knife::ScalewayBase.load_deps }
|
41
|
+
|
42
|
+
option :scaleway_access_token,
|
43
|
+
short: '-A ACCESS_TOKEN',
|
44
|
+
long: '--scaleway_access_token ACCESS_TOKEN',
|
45
|
+
description: 'Your Scaleway ACCESS_TOKEN',
|
46
|
+
proc: proc { |access_token| Chef::Config[:knife][:scaleway_access_token] = access_token }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def client
|
51
|
+
Scaleway::Client.new(Chef::Config[:knife][:scaleway_access_key], Chef::Config[:knife][:scaleway_token])
|
52
|
+
end
|
53
|
+
|
54
|
+
def validate!(keys = [:scaleway_access_key, :scaleway_token])
|
55
|
+
errors = []
|
56
|
+
|
57
|
+
keys.each do |k|
|
58
|
+
if locate_config_value(k).nil?
|
59
|
+
errors << "You did not provide a valid '#{k}' value. " \
|
60
|
+
"Please set knife[:#{k}] in your knife.rb or pass as an option."
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
exit 1 if errors.each { |e| ui.error(e) }.any?
|
65
|
+
end
|
66
|
+
|
67
|
+
def locate_config_value(key)
|
68
|
+
key = key.to_sym
|
69
|
+
config[key] || Chef::Config[:knife][key]
|
70
|
+
end
|
71
|
+
|
72
|
+
def wait_for_status(result, status: 'in-progress', sleep: 3)
|
73
|
+
print "Waiting for state #{status}"
|
74
|
+
result = Scaleway::Server.find(locate_config_value(:id))
|
75
|
+
while result.state != status
|
76
|
+
sleep sleep
|
77
|
+
print('.')
|
78
|
+
|
79
|
+
#if status == 'starting' || status == 'stopping'
|
80
|
+
#break if client.servers.find(id: locate_config_value(:id)).status != 'in-progress'
|
81
|
+
#else
|
82
|
+
break if Scaleway::Server.find(locate_config_value(:id)).state == status
|
83
|
+
#end
|
84
|
+
end
|
85
|
+
ui.info 'OK'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,338 @@
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
2
|
+
# you may not use this file except in compliance with the License.
|
3
|
+
# You may obtain a copy of the License at
|
4
|
+
#
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10
|
+
# See the License for the specific language governing permissions and
|
11
|
+
# limitations under the License.
|
12
|
+
#
|
13
|
+
require 'chef/knife/scaleway_base'
|
14
|
+
|
15
|
+
class Chef
|
16
|
+
class Knife
|
17
|
+
class ScalewayServerCreate < Knife
|
18
|
+
include Knife::ScalewayBase
|
19
|
+
|
20
|
+
deps do
|
21
|
+
require 'socket'
|
22
|
+
require 'chef/knife/bootstrap'
|
23
|
+
Chef::Knife::Bootstrap.load_deps
|
24
|
+
Chef::Knife::ScalewayBase.load_deps
|
25
|
+
# Knife loads subcommands automatically, so we can just check if the
|
26
|
+
# class exists.
|
27
|
+
Chef::Knife::SoloBootstrap.load_deps if defined? Chef::Knife::SoloBootstrap
|
28
|
+
if defined? Chef::Knife::ZeroBootstrap
|
29
|
+
Chef::Knife::ZeroBootstrap.load_deps
|
30
|
+
self.options = Chef::Knife::ZeroBootstrap.options.merge(options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
banner 'knife scaleway server create (options)'
|
35
|
+
|
36
|
+
option :server_name,
|
37
|
+
short: '-N NAME',
|
38
|
+
long: '--server-name NAME',
|
39
|
+
description: 'The server name',
|
40
|
+
proc: proc { |server_name| Chef::Config[:knife][:server_name] = server_name }
|
41
|
+
|
42
|
+
option :image,
|
43
|
+
short: '-I IMAGE',
|
44
|
+
long: '--image IMAGE',
|
45
|
+
description: 'Your Scaleway Image',
|
46
|
+
proc: proc { |image| Chef::Config[:knife][:image] = image }
|
47
|
+
|
48
|
+
option :size,
|
49
|
+
short: '-S SIZE',
|
50
|
+
long: '--size SIZE',
|
51
|
+
description: 'Your Scaleway Size',
|
52
|
+
proc: proc { |size| Chef::Config[:knife][:size] = size }
|
53
|
+
|
54
|
+
option :location,
|
55
|
+
short: '-L REGION',
|
56
|
+
long: '--location REGION',
|
57
|
+
description: 'Scaleway Location (Region)',
|
58
|
+
proc: proc { |location| Chef::Config[:knife][:location] = location }
|
59
|
+
|
60
|
+
option :ssh_key_ids,
|
61
|
+
short: '-K KEYID',
|
62
|
+
long: '--ssh-keys KEY_ID',
|
63
|
+
description: 'Comma spearated list of your SSH key ids',
|
64
|
+
proc: ->(o) { o.split(/[\s,]+/) }
|
65
|
+
|
66
|
+
option :identity_file,
|
67
|
+
short: '-i IDENTITY_FILE',
|
68
|
+
long: '--identity-file IDENTITY_FILE',
|
69
|
+
description: 'The SSH identity file used for authentication',
|
70
|
+
proc: proc { |identity| Chef::Config[:knife][:identity_file] = identity }
|
71
|
+
|
72
|
+
option :bootstrap,
|
73
|
+
short: '-B',
|
74
|
+
long: '--bootstrap',
|
75
|
+
description: 'Do a chef-client bootstrap on the created server (for use with chef-server)'
|
76
|
+
|
77
|
+
option :solo,
|
78
|
+
long: '--[no-]solo',
|
79
|
+
description: 'Do a chef-solo bootstrap on the server using knife-solo',
|
80
|
+
proc: proc { |s| Chef::Config[:knife][:solo] = s }
|
81
|
+
|
82
|
+
option :zero,
|
83
|
+
long: '--[no-]zero',
|
84
|
+
description: 'Do a chef-zero bootstrap on the server using knife-zero',
|
85
|
+
proc: proc { |z| Chef::Config[:knife][:zero] = z }
|
86
|
+
|
87
|
+
option :ssh_user,
|
88
|
+
short: '-x USERNAME',
|
89
|
+
long: '--ssh-user USERNAME',
|
90
|
+
description: 'The ssh username; default is "root"',
|
91
|
+
default: 'root'
|
92
|
+
|
93
|
+
option :distro,
|
94
|
+
short: '-d DISTRO',
|
95
|
+
long: '--distro DISTRO',
|
96
|
+
description: 'Chef-Bootstrap a distro using a template; default is "chef-full"',
|
97
|
+
proc: proc { |d| Chef::Config[:knife][:distro] = d },
|
98
|
+
default: 'chef-full'
|
99
|
+
|
100
|
+
option :run_list,
|
101
|
+
short: '-r RUN_LIST',
|
102
|
+
long: '--run-list RUN_LIST',
|
103
|
+
description: 'Comma separated list of roles/recipes to apply',
|
104
|
+
proc: ->(o) { o.split(/[\s,]+/) },
|
105
|
+
default: []
|
106
|
+
|
107
|
+
option :template_file,
|
108
|
+
long: '--template-file TEMPLATE',
|
109
|
+
description: 'Full path to location of template to use',
|
110
|
+
proc: proc { |t| Chef::Config[:knife][:template_file] = t },
|
111
|
+
default: false
|
112
|
+
|
113
|
+
option :host_key_verify,
|
114
|
+
long: '--[no-]host-key-verify',
|
115
|
+
description: 'Verify host key, enabled by default',
|
116
|
+
default: true
|
117
|
+
|
118
|
+
option :prerelease,
|
119
|
+
long: '--prerelease',
|
120
|
+
description: 'Install the pre-release chef gems'
|
121
|
+
|
122
|
+
option :bootstrap_version,
|
123
|
+
long: '--bootstrap-version VERSION',
|
124
|
+
description: 'The version of Chef to install',
|
125
|
+
proc: proc { |v| Chef::Config[:knife][:bootstrap_version] = v }
|
126
|
+
|
127
|
+
option :environment,
|
128
|
+
short: '-E ENVIRONMENT',
|
129
|
+
long: '--environment ENVIRONMENT',
|
130
|
+
description: 'The name of the chef environment to use',
|
131
|
+
proc: proc { |e| Chef::Config[:knife][:environment] = e },
|
132
|
+
default: '_default'
|
133
|
+
|
134
|
+
option :json_attributes,
|
135
|
+
short: '-j JSON',
|
136
|
+
long: '--json-attributes JSON',
|
137
|
+
description: 'A JSON string to be added to the first run of chef-client',
|
138
|
+
proc: ->(o) { JSON.parse(o) }
|
139
|
+
|
140
|
+
option :private_networking,
|
141
|
+
long: '--private_networking',
|
142
|
+
description: 'Enables private networking if the selected region supports it',
|
143
|
+
default: false
|
144
|
+
|
145
|
+
option :secret_file,
|
146
|
+
long: '--secret-file SECRET_FILE',
|
147
|
+
description: 'A file containing the secret key to use to encrypt data bag item values',
|
148
|
+
proc: proc { |sf| Chef::Config[:knife][:secret_file] = sf }
|
149
|
+
|
150
|
+
option :ssh_port,
|
151
|
+
short: '-p PORT',
|
152
|
+
long: '--ssh-port PORT',
|
153
|
+
description: 'The ssh port',
|
154
|
+
default: '22',
|
155
|
+
proc: proc { |port| Chef::Config[:knife][:ssh_port] = port }
|
156
|
+
|
157
|
+
option :backups,
|
158
|
+
short: '-b',
|
159
|
+
long: '--backups-enabled',
|
160
|
+
description: 'Enables backups for the created server',
|
161
|
+
default: false
|
162
|
+
|
163
|
+
option :ipv6,
|
164
|
+
short: '-6',
|
165
|
+
long: '--ipv6-enabled',
|
166
|
+
description: 'Enables ipv6 for the created server',
|
167
|
+
default: false
|
168
|
+
|
169
|
+
def run
|
170
|
+
$stdout.sync = true
|
171
|
+
|
172
|
+
validate!
|
173
|
+
|
174
|
+
unless locate_config_value(:server_name)
|
175
|
+
ui.error('Server Name cannot be empty: -N <servername>')
|
176
|
+
exit 1
|
177
|
+
end
|
178
|
+
|
179
|
+
unless locate_config_value(:image)
|
180
|
+
ui.error('Image cannot be empty: -I <image>')
|
181
|
+
exit 1
|
182
|
+
end
|
183
|
+
=begin
|
184
|
+
unless locate_config_value(:size)
|
185
|
+
ui.error('Size cannot be empty: -S <size>')
|
186
|
+
exit 1
|
187
|
+
end
|
188
|
+
|
189
|
+
unless locate_config_value(:location)
|
190
|
+
ui.error('Location cannot be empty: -L <region>')
|
191
|
+
exit 1
|
192
|
+
end
|
193
|
+
|
194
|
+
unless locate_config_value(:ssh_key_ids)
|
195
|
+
ui.error('One or more Scaleway SSH key ids missing: -K <KEY1>, <KEY2> ...')
|
196
|
+
exit 1
|
197
|
+
end
|
198
|
+
=end
|
199
|
+
if solo_bootstrap? && !defined?(Chef::Knife::SoloBootstrap)
|
200
|
+
ui.error [
|
201
|
+
'Knife plugin knife-solo was not found.',
|
202
|
+
'Please add the knife-solo gem to your Gemfile or',
|
203
|
+
'install it manually with `gem install knife-solo`.'
|
204
|
+
].join(' ')
|
205
|
+
exit 1
|
206
|
+
end
|
207
|
+
|
208
|
+
if zero_bootstrap? && !defined?(Chef::Knife::ZeroBootstrap)
|
209
|
+
ui.error [
|
210
|
+
'Knife plugin knife-zero was not found.',
|
211
|
+
'Please add the knife-zero gem to your Gemfile or',
|
212
|
+
'install it manually with `gem install knife-zero`.'
|
213
|
+
].join(' ')
|
214
|
+
exit 1
|
215
|
+
end
|
216
|
+
=begin
|
217
|
+
server = DropletKit::Droplet.new(name: locate_config_value(:server_name),
|
218
|
+
size: locate_config_value(:size),
|
219
|
+
image: locate_config_value(:image),
|
220
|
+
region: locate_config_value(:location),
|
221
|
+
ssh_keys: locate_config_value(:ssh_key_ids),
|
222
|
+
private_networking: locate_config_value(:private_networking),
|
223
|
+
backups: locate_config_value(:backups),
|
224
|
+
ipv6: locate_config_value(:ipv6)
|
225
|
+
)
|
226
|
+
=end
|
227
|
+
server = Scaleway::Server.create(locate_config_value(:server_name), locate_config_value(:image), 'VC1S')
|
228
|
+
|
229
|
+
#server = client.servers.create(server)
|
230
|
+
|
231
|
+
if Scaleway::Server.find(server.id).state != 'stopped'
|
232
|
+
ui.error("Droplet could not be started #{server.inspect}")
|
233
|
+
exit 1
|
234
|
+
end
|
235
|
+
|
236
|
+
puts "Droplet creation for #{locate_config_value(:server_name)} started. Droplet-ID is #{server.id}"
|
237
|
+
|
238
|
+
unless !config.key?(:json_attributes) || config[:json_attributes].empty?
|
239
|
+
puts ui.color("JSON Attributes: #{config[:json_attributes]}", :magenta)
|
240
|
+
end
|
241
|
+
|
242
|
+
puts "Starting server #{server.id}"
|
243
|
+
|
244
|
+
Scaleway::Server.action(server.id, 'poweron')
|
245
|
+
|
246
|
+
print ui.color('Waiting for IPv4-Address', :magenta)
|
247
|
+
print('.') until ip_address = ip_address_available(server.id) do
|
248
|
+
puts 'done'
|
249
|
+
end
|
250
|
+
|
251
|
+
puts ui.color("IPv4 address is: #{ip_address.address}", :green)
|
252
|
+
|
253
|
+
print ui.color('Waiting for sshd:', :magenta)
|
254
|
+
print('.') until tcp_test_ssh(ip_address.address) do
|
255
|
+
sleep 2
|
256
|
+
puts 'done'
|
257
|
+
end
|
258
|
+
|
259
|
+
if locate_config_value(:bootstrap) || solo_bootstrap? || zero_bootstrap?
|
260
|
+
bootstrap_for_node(ip_address.address).run
|
261
|
+
else
|
262
|
+
puts ip_address.address
|
263
|
+
exit 0
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def ip_address_available(server_id)
|
268
|
+
server = Scaleway::Server.find(server_id)
|
269
|
+
if server.public_ip
|
270
|
+
yield
|
271
|
+
server.public_ip
|
272
|
+
else
|
273
|
+
sleep @initial_sleep_delay ||= 10
|
274
|
+
false
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def tcp_test_ssh(hostname)
|
279
|
+
port = Chef::Config[:knife][:ssh_port] || config[:ssh_port]
|
280
|
+
tcp_socket = TCPSocket.new(hostname, port)
|
281
|
+
readable = IO.select([tcp_socket], nil, nil, 5)
|
282
|
+
if readable
|
283
|
+
Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{tcp_socket.gets}")
|
284
|
+
yield
|
285
|
+
true
|
286
|
+
else
|
287
|
+
false
|
288
|
+
end
|
289
|
+
rescue Errno::ETIMEDOUT
|
290
|
+
false
|
291
|
+
rescue Errno::EPERM
|
292
|
+
false
|
293
|
+
rescue Errno::ECONNREFUSED
|
294
|
+
sleep 2
|
295
|
+
false
|
296
|
+
rescue Errno::EHOSTUNREACH
|
297
|
+
sleep 2
|
298
|
+
false
|
299
|
+
ensure
|
300
|
+
tcp_socket && tcp_socket.close
|
301
|
+
end
|
302
|
+
|
303
|
+
def bootstrap_for_node(ip_address)
|
304
|
+
bootstrap = bootstrap_class.new
|
305
|
+
bootstrap.name_args = [ip_address]
|
306
|
+
bootstrap.config.merge! config
|
307
|
+
bootstrap.config[:chef_node_name] = locate_config_value(:server_name)
|
308
|
+
bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
|
309
|
+
bootstrap.config[:ssh_port] = Chef::Config[:knife][:ssh_port] || config[:ssh_port]
|
310
|
+
bootstrap.config[:distro] = locate_config_value(:distro)
|
311
|
+
bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
|
312
|
+
bootstrap.config[:template_file] = locate_config_value(:template_file)
|
313
|
+
bootstrap.config[:environment] = locate_config_value(:environment)
|
314
|
+
bootstrap.config[:first_boot_attributes] = locate_config_value(:json_attributes) || {}
|
315
|
+
bootstrap.config[:secret_file] = locate_config_value(:secret_file) || {}
|
316
|
+
bootstrap
|
317
|
+
end
|
318
|
+
|
319
|
+
def bootstrap_class
|
320
|
+
if solo_bootstrap?
|
321
|
+
Chef::Knife::SoloBootstrap
|
322
|
+
elsif zero_bootstrap?
|
323
|
+
Chef::Knife::ZeroBootstrap
|
324
|
+
else
|
325
|
+
Chef::Knife::Bootstrap
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
def solo_bootstrap?
|
330
|
+
config[:solo] || (config[:solo].nil? && Chef::Config[:knife][:solo])
|
331
|
+
end
|
332
|
+
|
333
|
+
def zero_bootstrap?
|
334
|
+
config[:zero] || (config[:zero].nil? && Chef::Config[:knife][:zero])
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|