ovh-provisioner 0.1.0

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.
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