simplygenius-atmos 0.7.1 → 0.8.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 +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
|