simplygenius-atmos 0.7.1 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -4
- data/exe/atmos +2 -2
- data/lib/{atmos.rb → simplygenius/atmos.rb} +9 -7
- data/lib/simplygenius/atmos/cli.rb +116 -0
- data/lib/simplygenius/atmos/commands/account.rb +69 -0
- data/lib/simplygenius/atmos/commands/apply.rb +24 -0
- data/lib/simplygenius/atmos/commands/auth_exec.rb +34 -0
- data/lib/simplygenius/atmos/commands/base_command.rb +16 -0
- data/lib/simplygenius/atmos/commands/bootstrap.rb +76 -0
- data/lib/simplygenius/atmos/commands/container.rb +62 -0
- data/lib/simplygenius/atmos/commands/destroy.rb +22 -0
- data/lib/simplygenius/atmos/commands/generate.rb +187 -0
- data/lib/simplygenius/atmos/commands/init.rb +22 -0
- data/lib/simplygenius/atmos/commands/new.rb +22 -0
- data/lib/simplygenius/atmos/commands/otp.rb +58 -0
- data/lib/simplygenius/atmos/commands/plan.rb +24 -0
- data/lib/simplygenius/atmos/commands/secret.rb +91 -0
- data/lib/simplygenius/atmos/commands/terraform.rb +56 -0
- data/lib/simplygenius/atmos/commands/user.rb +78 -0
- data/lib/simplygenius/atmos/config.rb +279 -0
- data/lib/simplygenius/atmos/exceptions.rb +13 -0
- data/lib/simplygenius/atmos/generator.rb +232 -0
- data/lib/simplygenius/atmos/ipc.rb +136 -0
- data/lib/simplygenius/atmos/ipc_actions/notify.rb +31 -0
- data/lib/simplygenius/atmos/ipc_actions/ping.rb +23 -0
- data/lib/simplygenius/atmos/logging.rb +164 -0
- data/lib/simplygenius/atmos/otp.rb +62 -0
- data/lib/simplygenius/atmos/plugin.rb +27 -0
- data/lib/simplygenius/atmos/plugin_manager.rb +120 -0
- data/lib/simplygenius/atmos/plugins/output_filter.rb +29 -0
- data/lib/simplygenius/atmos/plugins/prompt_notify.rb +21 -0
- data/lib/simplygenius/atmos/provider_factory.rb +23 -0
- data/lib/simplygenius/atmos/providers/aws/account_manager.rb +83 -0
- data/lib/simplygenius/atmos/providers/aws/auth_manager.rb +220 -0
- data/lib/simplygenius/atmos/providers/aws/container_manager.rb +118 -0
- data/lib/simplygenius/atmos/providers/aws/provider.rb +53 -0
- data/lib/simplygenius/atmos/providers/aws/s3_secret_manager.rb +51 -0
- data/lib/simplygenius/atmos/providers/aws/user_manager.rb +213 -0
- data/lib/simplygenius/atmos/settings_hash.rb +93 -0
- data/lib/simplygenius/atmos/source_path.rb +186 -0
- data/lib/simplygenius/atmos/template.rb +117 -0
- data/lib/simplygenius/atmos/terraform_executor.rb +297 -0
- data/lib/simplygenius/atmos/ui.rb +173 -0
- data/lib/simplygenius/atmos/utils.rb +54 -0
- data/lib/simplygenius/atmos/version.rb +5 -0
- data/templates/new/config/atmos.yml +21 -13
- data/templates/new/config/atmos/recipes.yml +16 -0
- data/templates/new/config/atmos/runtime.yml +9 -0
- metadata +46 -40
- data/lib/atmos/cli.rb +0 -105
- data/lib/atmos/commands/account.rb +0 -65
- data/lib/atmos/commands/apply.rb +0 -20
- data/lib/atmos/commands/auth_exec.rb +0 -29
- data/lib/atmos/commands/base_command.rb +0 -12
- data/lib/atmos/commands/bootstrap.rb +0 -72
- data/lib/atmos/commands/container.rb +0 -58
- data/lib/atmos/commands/destroy.rb +0 -18
- data/lib/atmos/commands/generate.rb +0 -90
- data/lib/atmos/commands/init.rb +0 -18
- data/lib/atmos/commands/new.rb +0 -18
- data/lib/atmos/commands/otp.rb +0 -54
- data/lib/atmos/commands/plan.rb +0 -20
- data/lib/atmos/commands/secret.rb +0 -87
- data/lib/atmos/commands/terraform.rb +0 -52
- data/lib/atmos/commands/user.rb +0 -74
- data/lib/atmos/config.rb +0 -208
- data/lib/atmos/exceptions.rb +0 -9
- data/lib/atmos/generator.rb +0 -199
- data/lib/atmos/generator_factory.rb +0 -93
- data/lib/atmos/ipc.rb +0 -132
- data/lib/atmos/ipc_actions/notify.rb +0 -27
- data/lib/atmos/ipc_actions/ping.rb +0 -19
- data/lib/atmos/logging.rb +0 -160
- data/lib/atmos/otp.rb +0 -61
- data/lib/atmos/provider_factory.rb +0 -19
- data/lib/atmos/providers/aws/account_manager.rb +0 -82
- data/lib/atmos/providers/aws/auth_manager.rb +0 -208
- data/lib/atmos/providers/aws/container_manager.rb +0 -116
- data/lib/atmos/providers/aws/provider.rb +0 -51
- data/lib/atmos/providers/aws/s3_secret_manager.rb +0 -49
- data/lib/atmos/providers/aws/user_manager.rb +0 -211
- data/lib/atmos/settings_hash.rb +0 -90
- data/lib/atmos/terraform_executor.rb +0 -267
- data/lib/atmos/ui.rb +0 -159
- data/lib/atmos/utils.rb +0 -50
- data/lib/atmos/version.rb +0 -3
@@ -0,0 +1,118 @@
|
|
1
|
+
require_relative '../../../atmos'
|
2
|
+
require 'aws-sdk-ecs'
|
3
|
+
require 'aws-sdk-ecr'
|
4
|
+
require 'open3'
|
5
|
+
|
6
|
+
module SimplyGenius
|
7
|
+
module Atmos
|
8
|
+
module Providers
|
9
|
+
module Aws
|
10
|
+
|
11
|
+
class ContainerManager
|
12
|
+
include GemLogger::LoggerSupport
|
13
|
+
|
14
|
+
def initialize(provider)
|
15
|
+
@provider = provider
|
16
|
+
end
|
17
|
+
|
18
|
+
def push(ecs_name, local_image,
|
19
|
+
ecr_repo: ecs_name, revision: nil)
|
20
|
+
|
21
|
+
revision = Time.now.strftime('%Y%m%d%H%M%S') unless revision.present?
|
22
|
+
result = {}
|
23
|
+
|
24
|
+
ecr = ::Aws::ECR::Client.new
|
25
|
+
resp = nil
|
26
|
+
|
27
|
+
resp = ecr.get_authorization_token
|
28
|
+
auth_data = resp.authorization_data.first
|
29
|
+
token = auth_data.authorization_token
|
30
|
+
endpoint = auth_data.proxy_endpoint
|
31
|
+
user, password = Base64.decode64(token).split(':')
|
32
|
+
|
33
|
+
# docker login into the ECR repo for the current account so that we can pull/push to it
|
34
|
+
run("docker", "login", "-u", user, "-p", password, endpoint)#, stdin_data: token)
|
35
|
+
|
36
|
+
image="#{ecs_name}:latest"
|
37
|
+
ecs_image="#{endpoint.sub(/https?:\/\//, '')}/#{ecr_repo}"
|
38
|
+
|
39
|
+
tags = ['latest', revision]
|
40
|
+
logger.info "Tagging local image '#{local_image}' with #{tags}"
|
41
|
+
tags.each {|t| run("docker", "tag", local_image, "#{ecs_image}:#{t}") }
|
42
|
+
|
43
|
+
logger.info "Pushing tagged image to ECR repo"
|
44
|
+
tags.each {|t| run("docker", "push", "#{ecs_image}:#{t}") }
|
45
|
+
|
46
|
+
result[:remote_image] = "#{ecs_image}:#{revision}"
|
47
|
+
return result
|
48
|
+
end
|
49
|
+
|
50
|
+
def deploy_task(task, remote_image)
|
51
|
+
result = {}
|
52
|
+
|
53
|
+
ecs = ::Aws::ECS::Client.new
|
54
|
+
resp = nil
|
55
|
+
|
56
|
+
resp = ecs.list_task_definitions(family_prefix: task, sort: 'DESC')
|
57
|
+
latest_defn_arn = resp.task_definition_arns.first
|
58
|
+
|
59
|
+
logger.info "Latest task definition: #{latest_defn_arn}"
|
60
|
+
|
61
|
+
resp = ecs.describe_task_definition(task_definition: latest_defn_arn)
|
62
|
+
latest_defn = resp.task_definition
|
63
|
+
|
64
|
+
new_defn = latest_defn.to_h
|
65
|
+
[:revision, :status, :task_definition_arn,
|
66
|
+
:requires_attributes, :compatibilities].each do |attr|
|
67
|
+
new_defn.delete(attr)
|
68
|
+
end
|
69
|
+
new_defn[:container_definitions].each {|c| c[:image] = remote_image}
|
70
|
+
|
71
|
+
resp = ecs.register_task_definition(**new_defn)
|
72
|
+
result[:task_definition] = resp.task_definition.task_definition_arn
|
73
|
+
|
74
|
+
logger.info "Updated task=#{task} to #{result[:task_definition]} with image #{remote_image}"
|
75
|
+
|
76
|
+
return result
|
77
|
+
end
|
78
|
+
|
79
|
+
def deploy_service(cluster, service, remote_image)
|
80
|
+
result = {}
|
81
|
+
|
82
|
+
ecs = ::Aws::ECS::Client.new
|
83
|
+
resp = nil
|
84
|
+
|
85
|
+
# Get current task definition name from service
|
86
|
+
resp = ecs.describe_services(cluster: cluster, services: [service])
|
87
|
+
current_defn_arn = resp.services.first.task_definition
|
88
|
+
defn_name = current_defn_arn.split("/").last.split(":").first
|
89
|
+
|
90
|
+
logger.info "Current task definition (name=#{defn_name}): #{current_defn_arn}"
|
91
|
+
result = deploy_task(defn_name, remote_image)
|
92
|
+
new_taskdef = result[:task_definition]
|
93
|
+
|
94
|
+
logger.info "Updating service with new task definition: #{new_taskdef}"
|
95
|
+
|
96
|
+
resp = ecs.update_service(cluster: cluster, service: service, task_definition: new_taskdef)
|
97
|
+
|
98
|
+
logger.info "Updated service=#{service} on cluster=#{cluster} to #{new_taskdef} with image #{remote_image}"
|
99
|
+
|
100
|
+
return result
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def run(*args, **opts)
|
106
|
+
logger.debug("Running: #{args}")
|
107
|
+
stdout, status = Open3.capture2e(ENV, *args, **opts)
|
108
|
+
logger.debug(stdout)
|
109
|
+
raise "Failed to run #{args}: #{stdout}" unless status.success?
|
110
|
+
return stdout
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require_relative '../../../atmos'
|
2
|
+
|
3
|
+
Dir.glob(File.join(__dir__, '*.rb')) do |f|
|
4
|
+
require_relative "#{File.basename(f).sub(/\.rb$/, "")}"
|
5
|
+
end
|
6
|
+
|
7
|
+
module SimplyGenius
|
8
|
+
module Atmos
|
9
|
+
module Providers
|
10
|
+
module Aws
|
11
|
+
|
12
|
+
class Provider
|
13
|
+
include GemLogger::LoggerSupport
|
14
|
+
|
15
|
+
def initialize(name)
|
16
|
+
@name = name
|
17
|
+
end
|
18
|
+
|
19
|
+
def auth_manager
|
20
|
+
@auth_manager ||= begin
|
21
|
+
AuthManager.new(self)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def user_manager
|
26
|
+
@user_manager ||= begin
|
27
|
+
UserManager.new(self)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def account_manager
|
32
|
+
@account_manager ||= begin
|
33
|
+
AccountManager.new(self)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def secret_manager
|
38
|
+
@secret_manager ||= begin
|
39
|
+
S3SecretManager.new(self)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def container_manager
|
44
|
+
@container_manager ||= begin
|
45
|
+
ContainerManager.new(self)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require_relative '../../../atmos'
|
2
|
+
require 'aws-sdk-s3'
|
3
|
+
|
4
|
+
module SimplyGenius
|
5
|
+
module Atmos
|
6
|
+
module Providers
|
7
|
+
module Aws
|
8
|
+
|
9
|
+
class S3SecretManager
|
10
|
+
include GemLogger::LoggerSupport
|
11
|
+
|
12
|
+
def initialize(provider)
|
13
|
+
@provider = provider
|
14
|
+
logger.debug("Secrets config is: #{Atmos.config[:secret]}")
|
15
|
+
@bucket_name = Atmos.config[:secret][:bucket]
|
16
|
+
@encrypt = Atmos.config[:secret][:encrypt]
|
17
|
+
end
|
18
|
+
|
19
|
+
def set(key, value)
|
20
|
+
opts = {}
|
21
|
+
opts[:server_side_encryption] = "AES256" if @encrypt
|
22
|
+
bucket.object(key).put(body: value, **opts)
|
23
|
+
end
|
24
|
+
|
25
|
+
def get(key)
|
26
|
+
bucket.object(key).get.body.read
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete(key)
|
30
|
+
bucket.object(key).delete
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_h
|
34
|
+
Hash[bucket.objects.collect {|o|
|
35
|
+
[o.key, o.get.body.read]
|
36
|
+
}]
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def bucket
|
42
|
+
raise ArgumentError.new("The s3 secret bucket is not set") unless @bucket_name
|
43
|
+
@bucket ||= ::Aws::S3::Bucket.new(@bucket_name)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
require_relative '../../../atmos'
|
2
|
+
require_relative '../../../atmos/otp'
|
3
|
+
require 'aws-sdk-iam'
|
4
|
+
require 'securerandom'
|
5
|
+
|
6
|
+
module SimplyGenius
|
7
|
+
module Atmos
|
8
|
+
module Providers
|
9
|
+
module Aws
|
10
|
+
|
11
|
+
class UserManager
|
12
|
+
include GemLogger::LoggerSupport
|
13
|
+
include FileUtils
|
14
|
+
|
15
|
+
def initialize(provider)
|
16
|
+
@provider = provider
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_user(user_name)
|
20
|
+
result = {}
|
21
|
+
client = ::Aws::IAM::Client.new
|
22
|
+
resource = ::Aws::IAM::Resource.new
|
23
|
+
|
24
|
+
user = resource.user(user_name)
|
25
|
+
|
26
|
+
if user.exists?
|
27
|
+
logger.info "User '#{user_name}' already exists"
|
28
|
+
else
|
29
|
+
logger.info "Creating new user '#{user_name}'"
|
30
|
+
user = resource.create_user(user_name: user_name)
|
31
|
+
client.wait_until(:user_exists, user_name: user_name)
|
32
|
+
logger.debug "User created, user_name=#{user_name}"
|
33
|
+
end
|
34
|
+
|
35
|
+
result[:user_name] = user_name
|
36
|
+
|
37
|
+
return result
|
38
|
+
end
|
39
|
+
|
40
|
+
def set_groups(user_name, groups, force: false)
|
41
|
+
result = {}
|
42
|
+
resource = ::Aws::IAM::Resource.new
|
43
|
+
|
44
|
+
user = resource.user(user_name)
|
45
|
+
|
46
|
+
existing_groups = user.groups.collect(&:name)
|
47
|
+
groups_to_add = groups - existing_groups
|
48
|
+
groups_to_remove = existing_groups - groups
|
49
|
+
|
50
|
+
result[:groups] = existing_groups
|
51
|
+
|
52
|
+
groups_to_add.each do |group|
|
53
|
+
logger.debug "Adding group: #{group}"
|
54
|
+
user.add_group(group_name: group)
|
55
|
+
result[:groups] << group
|
56
|
+
end
|
57
|
+
|
58
|
+
if force
|
59
|
+
groups_to_remove.each do |group|
|
60
|
+
logger.debug "Removing group: #{group}"
|
61
|
+
user.remove_group(group_name: group)
|
62
|
+
result[:groups].delete(group)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
logger.info "User associated with groups=#{result[:groups]}"
|
67
|
+
|
68
|
+
return result
|
69
|
+
end
|
70
|
+
|
71
|
+
def enable_login(user_name, force: false)
|
72
|
+
result = {}
|
73
|
+
resource = ::Aws::IAM::Resource.new
|
74
|
+
|
75
|
+
user = resource.user(user_name)
|
76
|
+
|
77
|
+
password = ""
|
78
|
+
classes = [/[a-z]/, /[A-Z]/, /[0-9]/, /[!@#$%^&*()_+\-=\[\]{}|']/]
|
79
|
+
while ! classes.all? {|c| password =~ c }
|
80
|
+
password = SecureRandom.base64(15)
|
81
|
+
end
|
82
|
+
|
83
|
+
exists = false
|
84
|
+
begin
|
85
|
+
user.login_profile.create_date
|
86
|
+
exists = true
|
87
|
+
rescue ::Aws::IAM::Errors::NoSuchEntity
|
88
|
+
exists = false
|
89
|
+
end
|
90
|
+
|
91
|
+
if exists
|
92
|
+
logger.info "User login already exists"
|
93
|
+
if force
|
94
|
+
user.login_profile.update(password: password, password_reset_required: true)
|
95
|
+
result[:password] = password
|
96
|
+
logger.info "Updated user login with password=#{password}"
|
97
|
+
end
|
98
|
+
else
|
99
|
+
user.create_login_profile(password: password, password_reset_required: true)
|
100
|
+
result[:password] = password
|
101
|
+
logger.info "User login enabled with password=#{password}"
|
102
|
+
end
|
103
|
+
|
104
|
+
return result
|
105
|
+
end
|
106
|
+
|
107
|
+
def enable_mfa(user_name, force: false)
|
108
|
+
result = {}
|
109
|
+
client = ::Aws::IAM::Client.new
|
110
|
+
resource = ::Aws::IAM::Resource.new
|
111
|
+
|
112
|
+
user = resource.user(user_name)
|
113
|
+
|
114
|
+
if user.mfa_devices.first
|
115
|
+
logger.info "User mfa devices already exist"
|
116
|
+
if force
|
117
|
+
logger.info "Deleting old mfa devices"
|
118
|
+
user.mfa_devices.each do |dev|
|
119
|
+
dev.disassociate
|
120
|
+
client.delete_virtual_mfa_device(serial_number: dev.serial_number)
|
121
|
+
Otp.instance.remove(user_name)
|
122
|
+
end
|
123
|
+
else
|
124
|
+
return result
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
resp = client.create_virtual_mfa_device(
|
129
|
+
virtual_mfa_device_name: user_name
|
130
|
+
)
|
131
|
+
|
132
|
+
serial = resp.virtual_mfa_device.serial_number
|
133
|
+
seed = resp.virtual_mfa_device.base_32_string_seed
|
134
|
+
|
135
|
+
Otp.instance.add(user_name, seed)
|
136
|
+
code1 = Otp.instance.generate(user_name)
|
137
|
+
interval = (30 - (Time.now.to_i % 30)) + 1
|
138
|
+
logger.info "Waiting for #{interval}s to generate second otp key for enablement"
|
139
|
+
sleep interval
|
140
|
+
code2 = Otp.instance.generate(user_name)
|
141
|
+
raise "MFA codes should not be the same" if code1 == code2
|
142
|
+
|
143
|
+
resp = client.enable_mfa_device({
|
144
|
+
user_name: user_name,
|
145
|
+
serial_number: serial,
|
146
|
+
authentication_code_1: code1,
|
147
|
+
authentication_code_2: code2,
|
148
|
+
})
|
149
|
+
|
150
|
+
result[:mfa_secret] = seed
|
151
|
+
|
152
|
+
return result
|
153
|
+
end
|
154
|
+
|
155
|
+
def enable_access_keys(user_name, force: false)
|
156
|
+
result = {}
|
157
|
+
resource = ::Aws::IAM::Resource.new
|
158
|
+
|
159
|
+
user = resource.user(user_name)
|
160
|
+
|
161
|
+
if user.access_keys.first
|
162
|
+
logger.info "User access keys already exist"
|
163
|
+
if force
|
164
|
+
logger.info "Deleting old access keys"
|
165
|
+
user.access_keys.each do |key|
|
166
|
+
key.delete
|
167
|
+
end
|
168
|
+
else
|
169
|
+
return result
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# TODO: auto add to ~/.aws/credentials and config
|
174
|
+
key_pair = user.create_access_key_pair
|
175
|
+
result[:key] = key_pair.access_key_id
|
176
|
+
result[:secret] = key_pair.secret
|
177
|
+
logger.debug "User keys generated key=#{key_pair.access_key_id}, secret=#{key_pair.secret}"
|
178
|
+
|
179
|
+
return result
|
180
|
+
end
|
181
|
+
|
182
|
+
def set_public_key(user_name, public_key, force: false)
|
183
|
+
result = {}
|
184
|
+
client = ::Aws::IAM::Client.new
|
185
|
+
resource = ::Aws::IAM::Resource.new
|
186
|
+
|
187
|
+
user = resource.user(user_name)
|
188
|
+
keys = client.list_ssh_public_keys(user_name: user_name).ssh_public_keys
|
189
|
+
if keys.size > 0
|
190
|
+
logger.info "User ssh public keys already exist"
|
191
|
+
if force
|
192
|
+
logger.info "Deleting old ssh public keys"
|
193
|
+
keys.each do |key|
|
194
|
+
client.delete_ssh_public_key(user_name: user_name,
|
195
|
+
ssh_public_key_id: key.ssh_public_key_id)
|
196
|
+
end
|
197
|
+
else
|
198
|
+
return result
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
client.upload_ssh_public_key(user_name: user_name, ssh_public_key_body: public_key)
|
203
|
+
logger.debug "User public key assigned: #{public_key}"
|
204
|
+
|
205
|
+
return result
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
module SimplyGenius
|
4
|
+
module Atmos
|
5
|
+
|
6
|
+
class SettingsHash < Hashie::Mash
|
7
|
+
include GemLogger::LoggerSupport
|
8
|
+
include Hashie::Extensions::DeepMerge
|
9
|
+
include Hashie::Extensions::DeepFetch
|
10
|
+
disable_warnings
|
11
|
+
|
12
|
+
PATH_PATTERN = /[\.\[\]]/
|
13
|
+
|
14
|
+
def notation_get(key)
|
15
|
+
path = key.to_s.split(PATH_PATTERN).compact
|
16
|
+
path = path.collect {|p| p =~ /^\d+$/ ? p.to_i : p }
|
17
|
+
result = nil
|
18
|
+
|
19
|
+
begin
|
20
|
+
result = deep_fetch(*path)
|
21
|
+
rescue Hashie::Extensions::DeepFetch::UndefinedPathError => e
|
22
|
+
logger.debug("Settings missing value for key='#{key}'")
|
23
|
+
end
|
24
|
+
|
25
|
+
return result
|
26
|
+
end
|
27
|
+
|
28
|
+
def notation_put(key, value, additive: true)
|
29
|
+
path = key.to_s.split(PATH_PATTERN).compact
|
30
|
+
path = path.collect {|p| p =~ /^\d+$/ ? p.to_i : p }
|
31
|
+
current_level = self
|
32
|
+
path.each_with_index do |p, i|
|
33
|
+
|
34
|
+
if i == path.size - 1
|
35
|
+
if additive && current_level[p].is_a?(Array)
|
36
|
+
current_level[p] = current_level[p] | Array(value)
|
37
|
+
else
|
38
|
+
current_level[p] = value
|
39
|
+
end
|
40
|
+
else
|
41
|
+
if current_level[p].nil?
|
42
|
+
if path[i+1].is_a?(Integer)
|
43
|
+
current_level[p] = []
|
44
|
+
else
|
45
|
+
current_level[p] = {}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
current_level = current_level[p]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.add_config(yml_file, key, value, additive: true)
|
55
|
+
orig_config_with_comments = File.read(yml_file)
|
56
|
+
|
57
|
+
comment_places = {}
|
58
|
+
comment_lines = []
|
59
|
+
orig_config_with_comments.each_line do |line|
|
60
|
+
line.gsub!(/\s+$/, "\n")
|
61
|
+
if line =~ /^\s*(#.*)?$/
|
62
|
+
comment_lines << line
|
63
|
+
else
|
64
|
+
if comment_lines.present?
|
65
|
+
comment_places[line.chomp] = comment_lines
|
66
|
+
comment_lines = []
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
comment_places["<EOF>"] = comment_lines
|
71
|
+
|
72
|
+
orig_config = SettingsHash.new((YAML.load_file(yml_file) rescue {}))
|
73
|
+
orig_config.notation_put(key, value, additive: additive)
|
74
|
+
new_config_no_comments = YAML.dump(orig_config.to_hash)
|
75
|
+
new_config_no_comments.sub!(/\A---\n/, "")
|
76
|
+
|
77
|
+
new_yml = ""
|
78
|
+
new_config_no_comments.each_line do |line|
|
79
|
+
line.gsub!(/\s+$/, "\n")
|
80
|
+
cline = comment_places.keys.find {|k| line =~ /^#{k}/ }
|
81
|
+
comments = comment_places[cline]
|
82
|
+
comments.each {|comment| new_yml << comment } if comments
|
83
|
+
new_yml << line
|
84
|
+
end
|
85
|
+
comment_places["<EOF>"].each {|comment| new_yml << comment }
|
86
|
+
|
87
|
+
return new_yml
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|