easy_manager 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a183ff2d7a5d75f1f6f75bb9aecae632bd834cf85e42f5eb54ccca4a5b1fc05c
4
+ data.tar.gz: 7329205dad75e774fdffe1c09d83969a953540062888ac392824e36add60e7fc
5
+ SHA512:
6
+ metadata.gz: 9875faf48edbdeac8d47bfdcd7f2d46980cfb78b6493b513fce019135ad100748551662c4a8b13b376639bd5bb25585db3d21e74d73a8c2dc08e1549f7cd085a
7
+ data.tar.gz: e47ef66b3b3bab4a3e4f76b09397c163483678d958fc2089333b7239607dcbde83c0b27307126089cf36aa14ed4dc5a277d5206e30cf4ccdd74536a2c89f350d
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'typhoeus'
4
+
5
+ require_relative 'easymanager/scaleway/main'
6
+ require_relative 'easymanager/ssh'
7
+ require_relative 'easymanager/utilities'
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EasyManager
4
+ class Scaleway
5
+ # Methods to retrieve information specific to Scaleway
6
+ class Config
7
+ def self.srv_infos(srv_type)
8
+ srv_infos = {
9
+ 'DEV1-S' => { volume: 20_000_000_000, volume_type: 'l_ssd' },
10
+ 'DEV1-M' => { volume: 40_000_000_000, volume_type: 'l_ssd' },
11
+ 'DEV1-L' => { volume: 80_000_000_000, volume_type: 'l_ssd' },
12
+ 'DEV1-XL' => { volume: 120_000_000_000, volume_type: 'l_ssd' }
13
+ }
14
+ srv_infos[srv_type]
15
+ end
16
+
17
+ def self.image_id(image)
18
+ image_id = {
19
+ 'ubuntu-jammy' => '2289fad9-2694-48ab-bb41-f19e4a9a8584',
20
+ 'debian-buster' => '6d124a42-de28-493f-933b-85a0df5552eb'
21
+ }
22
+ image_id[image]
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EasyManager
4
+ class Scaleway
5
+ # Specific method for ips management
6
+ # https://developers.scaleway.com/en/products/instance/api/#ips-268151
7
+ class Ips
8
+ def self.reserve(scw)
9
+ data = { project: scw.project }
10
+
11
+ response = Typhoeus.post(
12
+ File.join(scw.api_url, "instance/v1/zones/#{scw.zone}/ips"),
13
+ headers: scw.headers,
14
+ body: data.to_json
15
+ )
16
+ return unless response&.code == 201
17
+
18
+ Utilities.parse_json(response.body)
19
+ end
20
+
21
+ def self.delete(scw, ip_id)
22
+ Typhoeus.delete(
23
+ File.join(scw.api_url, "/instance/v1/zones/#{scw.zone}/ips/#{ip_id}"),
24
+ headers: scw.headers
25
+ )
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+ require_relative 'servers'
5
+ require_relative '../ssh'
6
+
7
+ class EasyManager
8
+ # Scaleway Class with global methods
9
+ class Scaleway
10
+ attr_reader :provider, :zone, :project, :api_url, :headers, :secret_token
11
+
12
+ def initialize(options)
13
+ @secret_token = options[:secret_token]
14
+ @zone = options[:zone]
15
+ @project = options[:project]
16
+ @api_url = 'https://api.scaleway.com/'
17
+ @headers = { 'X-Auth-Token' => @secret_token, 'Content-Type' => 'application/json' }
18
+ end
19
+
20
+ def list
21
+ Servers.list(self)
22
+ end
23
+
24
+ def create(options)
25
+ srv_type = options[:srv_type] || 'DEV1-S'
26
+ image = options[:image] || 'ubuntu-jammy'
27
+ name_pattern = options[:name_pattern] || 'scw-easymanager-__RANDOM__'
28
+ cloud_init = options[:cloud_init] || false
29
+
30
+ Servers.create(self, srv_type, image, name_pattern, cloud_init)
31
+ end
32
+
33
+ def delete(srv)
34
+ Servers.delete(self, srv['id'], srv['public_ip']['id'])
35
+ end
36
+
37
+ def delete_by_id(id)
38
+ servers = list
39
+
40
+ servers.each { |server| delete(server) if server['id'] == id }
41
+ end
42
+
43
+ def status(srv)
44
+ Servers.status(self, srv['id'])
45
+ end
46
+
47
+ def srv_ready?(srv, ssh)
48
+ Servers.ready?(self, srv, ssh, srv_ready_cmds)
49
+ end
50
+
51
+ def wait_until_ready!(srv, ssh, timeout = 300)
52
+ ready = false
53
+ start = Time.now
54
+ loop do
55
+ ready = srv_ready?(srv, ssh)
56
+ break if ready || Utilities.elapsed_times(Time.now.to_s, start.to_s) >= timeout
57
+
58
+ sleep(30)
59
+ end
60
+
61
+ ready
62
+ end
63
+
64
+ private
65
+
66
+ def srv_ready_cmds
67
+ check_cloud_init_cmd = "test -f '/var/log/cloud-init.log' && echo true"
68
+ cloud_init_ready_cmd = 'tail -1 /var/log/cloud-init-output.log'
69
+
70
+ [check_cloud_init_cmd, cloud_init_ready_cmd, 'hostname']
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'config'
4
+ require_relative 'ips'
5
+
6
+ class EasyManager
7
+ class Scaleway
8
+ # Specific method for server management
9
+ # https://developers.scaleway.com/en/products/instance/api/#servers-8bf7d7
10
+ class Servers
11
+ def self.list(scw)
12
+ response = Typhoeus.get(
13
+ File.join(scw.api_url, "instance/v1/zones/#{scw.zone}/servers"),
14
+ headers: scw.headers
15
+ )
16
+ return unless response&.code == 200
17
+
18
+ json_body = Utilities.parse_json(response.body)
19
+ return unless json_body
20
+
21
+ json_body['servers']
22
+ end
23
+
24
+ def self.srv_up?(scw, srv)
25
+ status(scw, srv['id']) == 'running' && Utilities.elapsed_times(Time.now.to_s, srv['creation_date']) > 90
26
+ end
27
+
28
+ def self.ready?(scw, srv, ssh, cmds)
29
+ return unless srv_up?(scw, srv)
30
+
31
+ cmd_values = SSH.cmd_exec(ssh, srv, cmds)
32
+ if cmd_values[cmds[0]].empty?
33
+ cmd_values['hostname'] == srv['hostname']
34
+ else
35
+ cmd_values[cmds[1]].include?('The system is finally up')
36
+ end
37
+ end
38
+
39
+ def self.status(scw, srv_id)
40
+ response = Typhoeus.get(
41
+ File.join(scw.api_url, "/instance/v1/zones/#{scw.zone}/servers/#{srv_id}"),
42
+ headers: scw.headers
43
+ )
44
+ return unless response&.code == 200
45
+
46
+ json_body = Utilities.parse_json(response.body)
47
+ return unless json_body
48
+
49
+ json_body['server']['state']
50
+ end
51
+
52
+ def self.create(scw, srv_type, image, name_pattern, cloud_init)
53
+ data = srv_data(scw, srv_type, image, name_pattern)
54
+ return if data.nil?
55
+
56
+ response = Typhoeus.post(File.join(scw.api_url, "/instance/v1/zones/#{scw.zone}/servers/"),
57
+ headers: scw.headers, body: data.to_json)
58
+ return unless response&.code == 201
59
+
60
+ body_json = Utilities.parse_json(response.body)
61
+ return unless body_json
62
+
63
+ srv_id = body_json['server']['id']
64
+ launch(scw, srv_id, cloud_init)
65
+
66
+ body_json['server']
67
+ end
68
+
69
+ def self.delete(scw, srv_id, srv_ip_id)
70
+ Ips.delete(scw, srv_ip_id)
71
+ action(scw, srv_id, 'terminate')
72
+ end
73
+
74
+ def self.launch(scw, srv_id, cloud_init)
75
+ add_cloud_init(scw, srv_id, cloud_init) if cloud_init
76
+ action(scw, srv_id, 'poweron')
77
+ end
78
+
79
+ def self.add_cloud_init(scw, srv_id, cloud_init)
80
+ data = Utilities.file_read(cloud_init)
81
+ return if data.nil?
82
+
83
+ Typhoeus.patch(
84
+ File.join(scw.api_url, "/instance/v1/zones/#{scw.zone}/servers/#{srv_id}/user_data/cloud-init"),
85
+ headers: { 'X-Auth-Token' => scw.secret_token, 'Content-Type' => 'text/plain' },
86
+ body: data
87
+ )
88
+ end
89
+
90
+ def self.action(scw, srv_id, action)
91
+ data = { action: action }
92
+ Typhoeus.post(
93
+ File.join(scw.api_url, "/instance/v1/zones/#{scw.zone}/servers/#{srv_id}/action"),
94
+ headers: scw.headers,
95
+ body: data.to_json
96
+ )
97
+ end
98
+
99
+ def self.srv_data(scw, srv_type, image, name_pattern)
100
+ srv_infos = Config.srv_infos(srv_type)
101
+ image_id = Config.image_id(image)
102
+ new_ip = Ips.reserve(scw)
103
+ return if image_id.nil? || srv_infos.nil? || new_ip.nil?
104
+
105
+ {
106
+ name: name_pattern.gsub('__RANDOM__', Utilities.random_string), commercial_type: srv_type,
107
+ public_ip: new_ip['ip']['id'], project: scw.project,
108
+ image: image_id,
109
+ volumes: { '0' => { size: srv_infos[:volume], volume_type: srv_infos[:volume_type] } }
110
+ }
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/ssh'
4
+ require 'net/scp'
5
+
6
+ class EasyManager
7
+ # SSH Class
8
+ class SSH
9
+ attr_reader :username, :ssh_key
10
+
11
+ def initialize(options = {})
12
+ @username = options[:username] || 'root'
13
+ @ssh_key = options[:ssh_key] || '~/.ssh/id_rsa'
14
+ end
15
+
16
+ def self.cmd_exec(ssh, srv, cmds)
17
+ cmd_values = {}
18
+
19
+ Net::SSH.start(srv['public_ip']['address'], ssh.username, keys: ssh.ssh_key) do |shell|
20
+ cmds.each do |cmd|
21
+ cmd_values[cmd] = shell.exec!(cmd).chomp
22
+ end
23
+ end
24
+
25
+ cmd_values
26
+ rescue Net::SSH::AuthenticationFailed
27
+ nil
28
+ end
29
+
30
+ def self.scp(ssh, srv, files)
31
+ Net::SCP.start(srv['public_ip']['address'], ssh.username, keys: ssh.ssh_key) do |shell|
32
+ files.each do |name, infos|
33
+ shell.upload! name, infos[:remote], recursive: infos[:recursive] || false
34
+ end
35
+ end
36
+ rescue Net::SSH::AuthenticationFailed
37
+ nil
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ # Utility method that can be reused in the code
6
+ class Utilities
7
+ def self.parse_json(content)
8
+ JSON.parse(content)
9
+ rescue JSON::ParseError
10
+ nil
11
+ end
12
+
13
+ def self.random_string
14
+ (0...8).map { rand(97..122).chr }.join
15
+ end
16
+
17
+ def self.file_read(file)
18
+ File.read(file)
19
+ rescue (Errno::ENOENT)
20
+ nil
21
+ end
22
+
23
+ def self.elapsed_times(first, second)
24
+ ((DateTime.parse(first) - DateTime.parse(second)) * 24 * 60 * 60).to_i
25
+ end
26
+ end
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: easy_manager
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.1
5
+ platform: ruby
6
+ authors:
7
+ - Joshua MARTINELLE
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-08-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bcrypt_pbkdf
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.1.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.1'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.1.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: ed25519
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.3'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 1.3.0
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '1.3'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.3.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: net-scp
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: 4.0.0.rc1
60
+ type: :runtime
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: 4.0.0.rc1
67
+ - !ruby/object:Gem::Dependency
68
+ name: net-ssh
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: 7.0.0beta1
74
+ type: :runtime
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: 7.0.0beta1
81
+ - !ruby/object:Gem::Dependency
82
+ name: typhoeus
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '1.4'
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 1.4.0
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '1.4'
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: 1.4.0
101
+ - !ruby/object:Gem::Dependency
102
+ name: x25519
103
+ requirement: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - '='
106
+ - !ruby/object:Gem::Version
107
+ version: 1.0.9
108
+ type: :runtime
109
+ prerelease: false
110
+ version_requirements: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - '='
113
+ - !ruby/object:Gem::Version
114
+ version: 1.0.9
115
+ description:
116
+ email:
117
+ - contact@jomar.fr
118
+ executables: []
119
+ extensions: []
120
+ extra_rdoc_files: []
121
+ files:
122
+ - lib/easy_manager.rb
123
+ - lib/easymanager/scaleway/config.rb
124
+ - lib/easymanager/scaleway/ips.rb
125
+ - lib/easymanager/scaleway/main.rb
126
+ - lib/easymanager/scaleway/servers.rb
127
+ - lib/easymanager/ssh.rb
128
+ - lib/easymanager/utilities.rb
129
+ homepage: https://rubygems.org/gems/easymanager
130
+ licenses:
131
+ - MIT
132
+ metadata: {}
133
+ post_install_message:
134
+ rdoc_options: []
135
+ require_paths:
136
+ - lib
137
+ required_ruby_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: 2.7.1
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ requirements: []
148
+ rubygems_version: 3.1.2
149
+ signing_key:
150
+ specification_version: 4
151
+ summary: Cloud Server Manager Library
152
+ test_files: []