ovh-provisioner 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.gitlab-ci.yml +23 -0
  4. data/.rspec +2 -0
  5. data/.ruby-version +1 -0
  6. data/CHANGELOG +7 -0
  7. data/CONTRIBUTING.md +62 -0
  8. data/Gemfile +6 -0
  9. data/LICENSE +202 -0
  10. data/README.md +107 -0
  11. data/Rakefile +24 -0
  12. data/bin/console +32 -0
  13. data/bin/ovh_provisioner +27 -0
  14. data/bin/setup +23 -0
  15. data/lib/ovh/provisioner.rb +43 -0
  16. data/lib/ovh/provisioner/api_list.rb +158 -0
  17. data/lib/ovh/provisioner/api_object/api_object.rb +125 -0
  18. data/lib/ovh/provisioner/api_object/dedicated_server.rb +225 -0
  19. data/lib/ovh/provisioner/api_object/domain_zone.rb +115 -0
  20. data/lib/ovh/provisioner/api_object/ip.rb +83 -0
  21. data/lib/ovh/provisioner/api_object/record.rb +48 -0
  22. data/lib/ovh/provisioner/api_object/vrack.rb +92 -0
  23. data/lib/ovh/provisioner/cli.rb +173 -0
  24. data/lib/ovh/provisioner/cli_domain.rb +138 -0
  25. data/lib/ovh/provisioner/cli_ip.rb +64 -0
  26. data/lib/ovh/provisioner/cli_vrack.rb +71 -0
  27. data/lib/ovh/provisioner/init.rb +77 -0
  28. data/lib/ovh/provisioner/self_cli.rb +81 -0
  29. data/lib/ovh/provisioner/spawner.rb +63 -0
  30. data/lib/ovh/provisioner/version.rb +24 -0
  31. data/ovh-provisioner.gemspec +53 -0
  32. data/spec/config.yml +53 -0
  33. data/spec/helpers/highline_helper.rb +36 -0
  34. data/spec/ovh/provisioner/cli_domain_spec.rb +140 -0
  35. data/spec/ovh/provisioner/cli_ip_spec.rb +90 -0
  36. data/spec/ovh/provisioner/cli_spec.rb +186 -0
  37. data/spec/ovh/provisioner/cli_vrack_spec.rb +83 -0
  38. data/spec/ovh/provisioner/stubs/domain_stubs.rb +204 -0
  39. data/spec/ovh/provisioner/stubs/ip_stubs.rb +152 -0
  40. data/spec/ovh/provisioner/stubs/server_stubs.rb +146 -0
  41. data/spec/ovh/provisioner/stubs/vrack_stubs.rb +87 -0
  42. data/spec/ovh/provisioner_spec.rb +25 -0
  43. data/spec/spec_helper.rb +47 -0
  44. metadata +350 -0
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2015-2016 Sam4Mobile, 2017-2018 Make.org
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'thor'
20
+ require 'pp'
21
+ require 'ruby-progressbar'
22
+ require 'highline/import'
23
+
24
+ module OVH
25
+ module Provisioner
26
+ # Command line for domain (actually domain/zone)
27
+ class CliDomain < Thor
28
+ # Exit 1 on failure
29
+ def self.exit_on_failure?
30
+ true
31
+ end
32
+
33
+ # options
34
+ SUBDOMAIN = [
35
+ :subdomain,
36
+ default: '',
37
+ desc: 'Record subdomain',
38
+ aliases: ['-d']
39
+ ].freeze
40
+
41
+ TYPE = [
42
+ :type,
43
+ default: '',
44
+ desc: 'Record type',
45
+ aliases: ['-y']
46
+ ].freeze
47
+
48
+ TARGET = [
49
+ :target,
50
+ default: '',
51
+ desc: 'Record target',
52
+ aliases: ['-t']
53
+ ].freeze
54
+
55
+ TTL = [
56
+ :ttl,
57
+ default: '0',
58
+ desc: 'Record TTL',
59
+ aliases: ['-l']
60
+ ].freeze
61
+
62
+ desc 'list', 'Print the list of your domains'
63
+ def list(*targets)
64
+ spawner = Provisioner.init(options)
65
+ puts spawner.get('DomainZone', *Cli.all(targets)).format
66
+ end
67
+
68
+ desc 'show domain', 'Show records in a domain'
69
+ def show(domain)
70
+ spawner = Provisioner.init(options)
71
+ zones = spawner.get('DomainZone', domain).list
72
+ zones.each { |z| puts z.details } if check_zone_input(domain, zones)
73
+ end
74
+
75
+ desc 'add domain', 'Add a record in a domain'
76
+ [SUBDOMAIN, TYPE, TARGET, TTL].map { |o| option(*o) }
77
+ def add(domain)
78
+ spawner = Provisioner.init(options)
79
+ zones = spawner.get('DomainZone', domain).list
80
+ return unless check_zone_input(domain, zones, false)
81
+
82
+ zone = zones.first
83
+ add_record(zone, options)
84
+ end
85
+
86
+ desc 'rm domain', 'Remove records in domain'
87
+ [SUBDOMAIN, TYPE, TARGET, TTL].map { |o| option(*o) }
88
+ def rm(domain)
89
+ spawner = Provisioner.init(options)
90
+ zones = spawner.get('DomainZone', domain).list
91
+ return unless check_zone_input(domain, zones, false)
92
+
93
+ zone = zones.first
94
+ matches = zone.filter_records(Provisioner.config)
95
+ rm_records(zone, matches)
96
+ end
97
+
98
+ no_commands do # rubocop:disable Metrics/BlockLength
99
+ def check_zone_input(search, zones, allow_many = true)
100
+ ok = true
101
+ if zones.empty?
102
+ puts "No registered services of your account match #{search}"
103
+ ok = false
104
+ end
105
+ if !allow_many && zones.size > 1
106
+ puts "Need one zone, got many: #{zones.map(&:id)}"
107
+ ok = false
108
+ end
109
+ ok
110
+ end
111
+
112
+ def add_record(zone, options)
113
+ sub = options['subdomain']
114
+ type = options['type'].upcase
115
+ target = options['target']
116
+ ttl = options['ttl']
117
+ Cli.ask_validation(
118
+ "You are going to add a record to #{zone.id}:",
119
+ " #{APIObject::Record.print(zone, sub, ttl, type, target)}"
120
+ )
121
+ puts zone.add_record(sub, type, target, ttl)
122
+ end
123
+
124
+ def rm_records(zone, matches)
125
+ if matches.list.empty?
126
+ puts 'Nothing to do…'
127
+ else
128
+ Cli.ask_validation(
129
+ "You are going to remove theses zones from #{zone.id}:",
130
+ zone.details(matches).lines[1..-1].join('')
131
+ )
132
+ puts zone.rm_records(matches)
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2015-2016 Sam4Mobile, 2017-2018 Make.org
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'thor'
20
+ require 'pp'
21
+ require 'ruby-progressbar'
22
+ require 'highline/import'
23
+
24
+ module OVH
25
+ module Provisioner
26
+ # The command line runner
27
+ class CliIP < Thor
28
+ # Exit 1 on failure
29
+ def self.exit_on_failure?
30
+ true
31
+ end
32
+
33
+ desc 'list', 'print the list of your OVH IPs'
34
+ def list(*targets)
35
+ spawner = Provisioner.init(options)
36
+ puts spawner.get('IP', *Cli.all(targets)).format('routed_to', 'kind')
37
+ end
38
+
39
+ desc 'set_reverse ip reverse', 'Set the reverse of the IP'
40
+ def set_reverse(ip, reverse)
41
+ spawner = Provisioner.init(options)
42
+ ips = spawner.get('IP', ip).list
43
+ return unless Cli.check_service_input(ip, ips, false)
44
+
45
+ ip = ips.first
46
+ ask = "You are going to set the reverse of #{ip.id} to #{reverse}"
47
+ Cli.ask_validation(ask)
48
+ puts ip.add_reverse(reverse)
49
+ end
50
+
51
+ desc 'rm_reverse ip', 'Remove the reverse of the IP'
52
+ def rm_reverse(ip)
53
+ spawner = Provisioner.init(options)
54
+ ips = spawner.get('IP', ip).list
55
+ return unless Cli.check_service_input(ip, ips, false)
56
+
57
+ ip = ips.first
58
+ ask = "You are going to remove the reverse of #{ip.id}"
59
+ Cli.ask_validation(ask)
60
+ puts ip.rm_reverse
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2015-2016 Sam4Mobile, 2017-2018 Make.org
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'thor'
20
+ require 'pp'
21
+ require 'ruby-progressbar'
22
+ require 'highline/import'
23
+
24
+ module OVH
25
+ module Provisioner
26
+ # The command line runner
27
+ class CliVrack < Thor
28
+ # Exit 1 on failure
29
+ def self.exit_on_failure?
30
+ true
31
+ end
32
+
33
+ desc 'list', 'print the list of your dedicated servers'
34
+ def list(*targets)
35
+ spawner = Provisioner.init(options)
36
+ puts spawner.get('Vrack', *Cli.all(targets)).format
37
+ end
38
+
39
+ desc 'add vrack_id targets…', 'Add one/multiple servers in a vrack'
40
+ def add(vrack_id, *targets)
41
+ msg = 'You are going to add those servers to vrack'
42
+ execute_on_vrack(vrack_id, targets, :add, msg)
43
+ end
44
+
45
+ desc 'rm vrack_id targets_', 'Remove one/multiple servers from a vrack'
46
+ def rm(vrack_id, *targets)
47
+ msg = 'You are going to remove those servers from vrack'
48
+ execute_on_vrack(vrack_id, targets, :remove, msg)
49
+ end
50
+
51
+ no_commands do
52
+ def init_vrack(vrack_id, targets)
53
+ spawner = Provisioner.init(options)
54
+ servers = spawner.get('DedicatedServer', *Cli.all(targets))
55
+ vracks = spawner.get('Vrack', vrack_id)
56
+ [servers, vracks]
57
+ end
58
+
59
+ def execute_on_vrack(vrack_id, targets, method, msg)
60
+ servers, vracks = init_vrack(vrack_id, targets)
61
+ return unless vracks.list.size == 1
62
+
63
+ vrack = vracks.list.first
64
+ msg = "#{msg} #{vrack.id}(#{vrack.name}):"
65
+ Cli.ask_validation(msg, servers.format('vrack'))
66
+ servers.puts_each(method, [], vrack)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2015-2016 Sam4Mobile, 2017-2018 Make.org
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'yaml'
20
+ require 'ovh/rest'
21
+
22
+ module OVH
23
+ # Load configuration and initialize client
24
+ module Provisioner
25
+ class << self
26
+ attr_reader :config
27
+ attr_reader :client
28
+ attr_reader :spawner
29
+
30
+ def init(options)
31
+ @config = load_config(options)
32
+ @client = create_client
33
+ @spawner = create_spawner
34
+ end
35
+
36
+ def load_config(options)
37
+ config_file = options['config_file']
38
+ begin
39
+ config = YAML.load_file config_file if File.exist? config_file
40
+ rescue StandardError => e
41
+ puts "#{e}\nCould not load configuration file: #{config_file}"
42
+ exit 1
43
+ end
44
+ check_missing((config || {}).merge(options))
45
+ end
46
+
47
+ def check_missing(config)
48
+ missing = %w[
49
+ app_key app_secret consumer_key api_url
50
+ ].map { |key| config[key].nil? ? key : nil }.compact
51
+
52
+ return config if missing.empty?
53
+
54
+ puts "Please provide valid #{missing.join(', ')}"
55
+ exit 1
56
+ end
57
+
58
+ def create_client
59
+ return @client unless @client.nil?
60
+
61
+ OVH::REST.new(
62
+ config['app_key'],
63
+ config['app_secret'],
64
+ config['consumer_key'],
65
+ config['api_url']
66
+ )
67
+ end
68
+
69
+ def create_spawner
70
+ return @spawner unless @spawner.nil?
71
+
72
+ spawner = Spawner.new
73
+ Celluloid::Actor[Spawner::NAME] = spawner
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2015-2016 Sam4Mobile, 2017-2018 Make.org
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'thor'
20
+
21
+ module OVH
22
+ module Provisioner
23
+ # The command line runner
24
+ class Cli < Thor
25
+ # Exit 1 on failure
26
+ def self.exit_on_failure?
27
+ true
28
+ end
29
+
30
+ def self.ask_validation(question, what = nil)
31
+ say question
32
+ say what unless what.nil?
33
+ exit unless HighLine.agree('Do you want to proceed?')
34
+ end
35
+
36
+ def self.check_service_input(search, services, allow_many = true)
37
+ ok = true
38
+ if services.empty?
39
+ puts "No registered services of your account match #{search}"
40
+ ok = false
41
+ end
42
+ if !allow_many && services.size > 1
43
+ puts "Need one service, got many: #{services.map(&:id)}"
44
+ ok = false
45
+ end
46
+ ok
47
+ end
48
+
49
+ def self.all(targets)
50
+ targets.empty? ? [''] : targets
51
+ end
52
+
53
+ class_option(
54
+ :config_file,
55
+ desc: 'Configuration file to use',
56
+ default: File.join(Dir.home, '.config', 'ovh-provisioner.yml'),
57
+ aliases: ['-c']
58
+ )
59
+ class_option(
60
+ :app_key,
61
+ desc: 'Define/Override the Application Key',
62
+ aliases: ['-a']
63
+ )
64
+ class_option(
65
+ :app_secret,
66
+ desc: 'Define/Override the Application Secret',
67
+ aliases: ['-s']
68
+ )
69
+ class_option(
70
+ :consumer_key,
71
+ desc: 'Define/Override the Consumer Key',
72
+ aliases: ['-k']
73
+ )
74
+ class_option(
75
+ :api_url,
76
+ desc: 'Override the API url',
77
+ aliases: ['-u']
78
+ )
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2015-2016 Sam4Mobile, 2017-2018 Make.org
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'celluloid/current'
20
+ require 'ovh/provisioner/api_object/api_object'
21
+
22
+ module OVH
23
+ module Provisioner
24
+ # Is responsible for spawning other cells
25
+ class Spawner
26
+ include Celluloid
27
+
28
+ NAME = 'Spawner'
29
+
30
+ # Return and create on demand a given api_object or api_list
31
+ #
32
+ # - class_name is an api_object class name
33
+ # - args is an array of arguments used to object creation
34
+ # - parent is the requester (like a vrack asking for its tasks)
35
+ # - id is the id of the api_object (nil for api_list)
36
+ #
37
+ # Example:
38
+ # - get('Vrack', id: 'pn-123'): Vrack.new('pn-123')
39
+ # - get('Task', parent: 'vrack/pn-12', id: '98'):
40
+ # Task('98', 'vrack/pn-12')
41
+ # - get('DedicatedServer', '03'): APIList.new(DedicatedServer, '03')
42
+ def get(class_name, *args, parent: nil, id: nil)
43
+ cell_name = "#{parent}:#{class_name}@#{id}##{args}"
44
+ cell = Actor[cell_name.to_sym] ||= create(class_name, parent, id, args)
45
+ cell.init_properties
46
+ cell
47
+ end
48
+
49
+ private
50
+
51
+ def create(class_name, parent, id, args)
52
+ exclusive do
53
+ cell_class = APIObject.const_get(class_name.to_sym)
54
+ if id.nil?
55
+ APIList.new(cell_class, parent, *args)
56
+ else
57
+ cell_class.new(id, parent, *args)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end