lex-onboard 0.1.1 → 0.2.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.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 945d2252b6dec50ef82d5ff441ef39099c146b0a1dbd7300e1eb9a10238f9bc3
|
|
4
|
+
data.tar.gz: 4213f845c3c1211175427ab0f98381c11832640da13556fbc30dcf7264723867
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 966c8d189ef9f44a6c77c44b65eedcf582f4ac9dfca8dd2a6fc61223d48d29c7057aad5633095e08ad5d1bd94d5a3657dc225b5c13110ef36ae247eca3937946
|
|
7
|
+
data.tar.gz: 63bece2311db70ff269547be69007c130d302f5c415e1792aeea6e55ef905d77108bd1a2af880ae4b5bda5e67c7b92e1595c5662a0e64ea0eb6c17facd0b46f6
|
|
@@ -5,55 +5,125 @@ module Legion
|
|
|
5
5
|
module Onboard
|
|
6
6
|
module Runners
|
|
7
7
|
module Provision
|
|
8
|
+
include Validator
|
|
9
|
+
|
|
10
|
+
ROLLBACK_ACTIONS = {
|
|
11
|
+
vault_namespace: ->(client, askid) { client.delete_namespace(name: askid) },
|
|
12
|
+
consul_partition: ->(client, askid) { client.delete_partition(name: askid) },
|
|
13
|
+
tfe_project: ->(client, askid) { client.delete_project(name: askid) }
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
8
16
|
def provision(askid:, tfe_organization: 'terraform.uhg.com', requester_slack_webhook: nil, **)
|
|
17
|
+
validation = validate_askid(askid: askid)
|
|
18
|
+
return { status: 'rejected', askid: askid, reason: validation[:reason] } unless validation[:valid]
|
|
19
|
+
|
|
20
|
+
conflicts = check_conflicts(askid: askid)
|
|
21
|
+
unless conflicts[:conflicts].empty?
|
|
22
|
+
return { status: 'rejected', askid: askid, reason: "conflict in: #{conflicts[:conflicts].join(', ')}" }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
completed_steps = []
|
|
9
26
|
steps = []
|
|
10
27
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
28
|
+
%i[vault_namespace consul_partition tfe_project].each do |step_name|
|
|
29
|
+
result = run_step(step_name, askid: askid, tfe_organization: tfe_organization)
|
|
30
|
+
steps << result
|
|
31
|
+
|
|
32
|
+
if result[:status] == 'error'
|
|
33
|
+
rollback_results = rollback(completed_steps, askid: askid)
|
|
34
|
+
return { status: 'failed', askid: askid, steps: steps, rollback: rollback_results }
|
|
35
|
+
end
|
|
15
36
|
|
|
16
|
-
|
|
37
|
+
completed_steps << step_name if result[:status] == 'completed'
|
|
38
|
+
end
|
|
17
39
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
askid: askid,
|
|
21
|
-
steps: steps
|
|
22
|
-
}
|
|
40
|
+
notify_requester(askid: askid, webhook: requester_slack_webhook) if requester_slack_webhook
|
|
41
|
+
{ status: 'completed', askid: askid, steps: steps, rollback: [] }
|
|
23
42
|
end
|
|
24
43
|
|
|
25
44
|
private
|
|
26
45
|
|
|
27
|
-
def run_step(
|
|
28
|
-
|
|
29
|
-
|
|
46
|
+
def run_step(step_name, askid:, tfe_organization:)
|
|
47
|
+
case step_name
|
|
48
|
+
when :vault_namespace then vault_namespace(askid: askid)
|
|
49
|
+
when :consul_partition then consul_partition(askid: askid)
|
|
50
|
+
when :tfe_project then tfe_project(askid: askid, organization: tfe_organization)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def rollback(completed_steps, askid:)
|
|
55
|
+
completed_steps.reverse.map do |step_name|
|
|
56
|
+
action = ROLLBACK_ACTIONS[step_name]
|
|
57
|
+
next unless action
|
|
58
|
+
|
|
59
|
+
action.call(client_for_step(step_name), askid)
|
|
60
|
+
{ step: step_name, status: 'rolled_back' }
|
|
61
|
+
rescue StandardError => e
|
|
62
|
+
{ step: step_name, status: 'rollback_failed', error: e.message }
|
|
63
|
+
end.compact
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def client_for_step(step_name)
|
|
67
|
+
case step_name
|
|
68
|
+
when :vault_namespace then vault_client
|
|
69
|
+
when :consul_partition then consul_client
|
|
70
|
+
when :tfe_project then tfe_client
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def vault_namespace(askid:)
|
|
75
|
+
unless defined?(Legion::Extensions::Vault::Client)
|
|
76
|
+
return { step: :vault_namespace, status: 'skipped', reason: 'vault unavailable' }
|
|
77
|
+
end
|
|
78
|
+
return { step: :vault_namespace, status: 'skipped', reason: 'already exists' } if vault_exists?(askid)
|
|
79
|
+
|
|
80
|
+
vault_client.create_namespace(name: askid)
|
|
81
|
+
{ step: :vault_namespace, status: 'completed', askid: askid }
|
|
30
82
|
rescue StandardError => e
|
|
31
|
-
{
|
|
83
|
+
{ step: :vault_namespace, status: 'error', error: e.message }
|
|
32
84
|
end
|
|
33
85
|
|
|
34
|
-
def
|
|
35
|
-
|
|
86
|
+
def consul_partition(askid:)
|
|
87
|
+
unless defined?(Legion::Extensions::Consul::Client)
|
|
88
|
+
return { step: :consul_partition, status: 'skipped', reason: 'consul unavailable' }
|
|
89
|
+
end
|
|
90
|
+
return { step: :consul_partition, status: 'skipped', reason: 'already exists' } if consul_exists?(askid)
|
|
36
91
|
|
|
37
|
-
|
|
92
|
+
consul_client.create_partition(name: askid)
|
|
93
|
+
{ step: :consul_partition, status: 'completed', askid: askid }
|
|
94
|
+
rescue StandardError => e
|
|
95
|
+
{ step: :consul_partition, status: 'error', error: e.message }
|
|
38
96
|
end
|
|
39
97
|
|
|
40
|
-
def
|
|
41
|
-
|
|
98
|
+
def tfe_project(askid:, organization:)
|
|
99
|
+
unless defined?(Legion::Extensions::Tfe::Client)
|
|
100
|
+
return { step: :tfe_project, status: 'skipped', reason: 'tfe unavailable' }
|
|
101
|
+
end
|
|
102
|
+
return { step: :tfe_project, status: 'skipped', reason: 'already exists' } if tfe_exists?(askid)
|
|
42
103
|
|
|
43
|
-
|
|
104
|
+
tfe_client.create_project(organization: organization, name: askid)
|
|
105
|
+
{ step: :tfe_project, status: 'completed', askid: askid }
|
|
106
|
+
rescue StandardError => e
|
|
107
|
+
{ step: :tfe_project, status: 'error', error: e.message }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def vault_client
|
|
111
|
+
Legion::Extensions::Vault::Client.new
|
|
44
112
|
end
|
|
45
113
|
|
|
46
|
-
def
|
|
47
|
-
|
|
114
|
+
def consul_client
|
|
115
|
+
Legion::Extensions::Consul::Client.new
|
|
116
|
+
end
|
|
48
117
|
|
|
49
|
-
|
|
118
|
+
def tfe_client
|
|
119
|
+
Legion::Extensions::Tfe::Client.new
|
|
50
120
|
end
|
|
51
121
|
|
|
52
122
|
def notify_requester(askid:, webhook: nil)
|
|
53
123
|
return true unless webhook && defined?(Legion::Extensions::Slack::Client)
|
|
54
124
|
|
|
55
125
|
Legion::Extensions::Slack::Client.new.send_webhook(
|
|
56
|
-
webhook: webhook,
|
|
126
|
+
webhook: webhook, message: "Onboarding complete for #{askid}"
|
|
57
127
|
)
|
|
58
128
|
rescue StandardError
|
|
59
129
|
true
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Onboard
|
|
6
|
+
module Runners
|
|
7
|
+
module Validator
|
|
8
|
+
ASKID_PATTERN = /\A[a-z0-9]([a-z0-9-]*[a-z0-9])?\z/
|
|
9
|
+
MAX_ASKID_LENGTH = 63
|
|
10
|
+
|
|
11
|
+
def validate_askid(askid:)
|
|
12
|
+
return { valid: false, reason: 'askid is empty' } if askid.nil? || askid.empty?
|
|
13
|
+
return { valid: false, reason: "askid exceeds #{MAX_ASKID_LENGTH} characters" } if askid.length > MAX_ASKID_LENGTH
|
|
14
|
+
|
|
15
|
+
unless askid.match?(ASKID_PATTERN)
|
|
16
|
+
return { valid: false,
|
|
17
|
+
reason: 'askid format invalid — must be lowercase alphanumeric with hyphens' }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
{ valid: true }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def check_conflicts(askid:)
|
|
24
|
+
conflicts = []
|
|
25
|
+
conflicts << :vault if vault_exists?(askid)
|
|
26
|
+
conflicts << :consul if consul_exists?(askid)
|
|
27
|
+
conflicts << :tfe if tfe_exists?(askid)
|
|
28
|
+
|
|
29
|
+
{ conflicts: conflicts, askid: askid }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def vault_exists?(askid)
|
|
35
|
+
return false unless defined?(Legion::Extensions::Vault::Client)
|
|
36
|
+
|
|
37
|
+
Legion::Extensions::Vault::Client.new.list_namespaces[:namespaces]&.include?(askid)
|
|
38
|
+
rescue StandardError
|
|
39
|
+
false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def consul_exists?(askid)
|
|
43
|
+
return false unless defined?(Legion::Extensions::Consul::Client)
|
|
44
|
+
|
|
45
|
+
Legion::Extensions::Consul::Client.new.list_partitions[:partitions]&.any? { |p| p[:name] == askid }
|
|
46
|
+
rescue StandardError
|
|
47
|
+
false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def tfe_exists?(askid)
|
|
51
|
+
return false unless defined?(Legion::Extensions::Tfe::Client)
|
|
52
|
+
|
|
53
|
+
Legion::Extensions::Tfe::Client.new.list_projects[:projects]&.any? { |p| p[:name] == askid }
|
|
54
|
+
rescue StandardError
|
|
55
|
+
false
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lex-onboard
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -118,6 +118,7 @@ files:
|
|
|
118
118
|
- lib/legion/extensions/onboard.rb
|
|
119
119
|
- lib/legion/extensions/onboard/actors/provision.rb
|
|
120
120
|
- lib/legion/extensions/onboard/runners/provision.rb
|
|
121
|
+
- lib/legion/extensions/onboard/runners/validator.rb
|
|
121
122
|
- lib/legion/extensions/onboard/version.rb
|
|
122
123
|
homepage: https://github.com/LegionIO
|
|
123
124
|
licenses:
|