kybus-cli 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/kybus +6 -0
- data/lib/kybus/aws/code_packager.rb +47 -0
- data/lib/kybus/aws/lambda.rb +166 -0
- data/lib/kybus/aws/log_group.rb +34 -0
- data/lib/kybus/aws/policy.rb +34 -0
- data/lib/kybus/aws/resource.rb +33 -0
- data/lib/kybus/aws/role.rb +76 -0
- data/lib/kybus/aws.rb +8 -0
- data/lib/kybus/cli/bot/controller_generator.rb +38 -0
- data/lib/kybus/cli/bot/deploy_init_generator.rb +33 -0
- data/lib/kybus/cli/bot/deployer.rb +70 -0
- data/lib/kybus/cli/bot/deployers/aws_bot_deployer.rb +95 -0
- data/lib/kybus/cli/bot/deployers/deployer_base.rb +23 -0
- data/lib/kybus/cli/bot/deployers/telegram_configurator.rb +33 -0
- data/lib/kybus/cli/bot/file_provider.rb +50 -0
- data/lib/kybus/cli/bot/file_providers/autoconfig_generator.rb +28 -0
- data/lib/kybus/cli/bot/file_providers/autoconfig_loader_generator.rb +37 -0
- data/lib/kybus/cli/bot/file_providers/bot_builder_generator.rb +25 -0
- data/lib/kybus/cli/bot/file_providers/bot_generator.rb +29 -0
- data/lib/kybus/cli/bot/file_providers/composefile_generator.rb +54 -0
- data/lib/kybus/cli/bot/file_providers/config_default_generator.rb +50 -0
- data/lib/kybus/cli/bot/file_providers/config_generator.rb +45 -0
- data/lib/kybus/cli/bot/file_providers/db_generator.rb +53 -0
- data/lib/kybus/cli/bot/file_providers/deployment_file_provider.rb +39 -0
- data/lib/kybus/cli/bot/file_providers/dockerfile_generator.rb +33 -0
- data/lib/kybus/cli/bot/file_providers/gemfile_generator.rb +41 -0
- data/lib/kybus/cli/bot/file_providers/lambda_handler_generator.rb +38 -0
- data/lib/kybus/cli/bot/file_providers/rakefile_generator.rb +40 -0
- data/lib/kybus/cli/bot/file_providers/test_helper_generator.rb +32 -0
- data/lib/kybus/cli/bot/project_generator.rb +76 -0
- data/lib/kybus/cli/bot.rb +60 -0
- data/lib/kybus/cli/file_writer.rb +17 -0
- data/lib/kybus/cli.rb +15 -0
- metadata +193 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2787f36a297b9c14e6c9fb0d4b28284721e4065c0c2a7c4f64db8246da1767fc
|
4
|
+
data.tar.gz: 619dcf54613ed6d18aecec46e84ae1b1fecad32c05882ec5e2b6d1d4ca4bc8aa
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4e2cd597770a3f06ef11a3077752d35608f90883def9e89be0b78cc10c4256d1039fa5877dd38d217c1702fbe9b7b82d1026c3cf9a0d79ac90e41429b2b06ed9
|
7
|
+
data.tar.gz: f2fc53893eb88358018a1a3251c0b78d24120735d8ec9e09ce81ef26ab985768800bb9e83de51810b7b66f18931041a6d6eaf67a5cb4a0797ae31cdfdcfb6008
|
data/bin/kybus
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kybus
|
4
|
+
module AWS
|
5
|
+
class CodePackager < Resource
|
6
|
+
def create_or_update!
|
7
|
+
ruby_version = RUBY_VERSION.split('.')[0..1].join('.') + '.0'
|
8
|
+
create_zip('.deps.zip', "vendor/bundle/ruby/#{ruby_version}", zip_root: "ruby/gems/#{ruby_version}")
|
9
|
+
create_zip('.kybuscode.zip', @config['repo_path'], exclude_files: [
|
10
|
+
'test', 'Gemfile*', 'Rakefile', '.gitignore', '.bundle', '.git', '.deps.zip', '.kybuscode.zip', 'kybusbot.yaml', 'vendor', '.ruby-version'
|
11
|
+
], extra_files: { '.bundle/config' => 'BUNDLE_PATH: "/opt/vendor/bundle"' }, zip_root: '.')
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_zip(zip_name, directory, exclude_files: [], extra_files: {}, zip_root: '')
|
15
|
+
require 'zip'
|
16
|
+
FileUtils.rm(zip_name, force: true)
|
17
|
+
entries = Dir.entries(directory) - %w[. ..]
|
18
|
+
|
19
|
+
Zip::File.open(zip_name, Zip::File::CREATE) do |zipfile|
|
20
|
+
extra_files.each do |entry, contents|
|
21
|
+
zipfile.get_output_stream(entry) { |f| f.puts(contents) }
|
22
|
+
end
|
23
|
+
|
24
|
+
entries.each do |entry|
|
25
|
+
entry_path = File.join(directory, entry)
|
26
|
+
next if exclude_files.any? { |pattern| File.fnmatch(pattern, entry) || File.fnmatch(pattern, entry_path) }
|
27
|
+
|
28
|
+
puts "Adding #{entry} to #{zip_name}"
|
29
|
+
|
30
|
+
if File.directory?(entry_path)
|
31
|
+
zipfile.mkdir("#{zip_root}#{entry_path.sub(directory, '')}")
|
32
|
+
Dir[File.join(entry_path, '**', '**')].each do |file|
|
33
|
+
next if exclude_files.any? { |pattern| File.fnmatch(pattern, file) }
|
34
|
+
|
35
|
+
zipfile.add("#{zip_root}#{file.sub(directory, '')}", file)
|
36
|
+
end
|
37
|
+
else
|
38
|
+
zipfile.add("#{zip_root}#{entry_path.sub(directory, '')}", entry_path)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def destroy!; end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
def calculate_md5(file_path)
|
6
|
+
md5 = Digest::MD5.new
|
7
|
+
File.open(file_path, 'rb') do |file|
|
8
|
+
buffer = String.new
|
9
|
+
md5.update(buffer) while file.read(4096, buffer)
|
10
|
+
end
|
11
|
+
md5.hexdigest
|
12
|
+
end
|
13
|
+
|
14
|
+
module Kybus
|
15
|
+
module AWS
|
16
|
+
class Lambda < Resource
|
17
|
+
attr_reader :url, :name
|
18
|
+
|
19
|
+
def initialize(configs, name)
|
20
|
+
super(configs)
|
21
|
+
@name = name
|
22
|
+
end
|
23
|
+
|
24
|
+
def lambda_client
|
25
|
+
@lambda_client ||= Aws::Lambda::Client.new(region: @region)
|
26
|
+
end
|
27
|
+
|
28
|
+
def function_name
|
29
|
+
@name
|
30
|
+
end
|
31
|
+
|
32
|
+
def deploy_lambda!
|
33
|
+
layer_arn_deps = create_or_update_layer('.deps.zip', "#{function_name}-deps")
|
34
|
+
|
35
|
+
function_exists = begin
|
36
|
+
lambda_client.get_function(function_name:)
|
37
|
+
rescue StandardError
|
38
|
+
false
|
39
|
+
end
|
40
|
+
|
41
|
+
if function_exists
|
42
|
+
update_lambda!(layer_arn_deps)
|
43
|
+
else
|
44
|
+
create_lambda!(layer_arn_deps)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_layer(zip_file, layer_name, zipfile_hash)
|
49
|
+
response = lambda_client.publish_layer_version({
|
50
|
+
layer_name:,
|
51
|
+
content: {
|
52
|
+
zip_file: File.read(zip_file)
|
53
|
+
},
|
54
|
+
description: zipfile_hash,
|
55
|
+
compatible_runtimes: ["ruby3.3"]
|
56
|
+
})
|
57
|
+
puts "Layer '#{layer_name}' created: #{response.layer_version_arn}"
|
58
|
+
response.layer_version_arn
|
59
|
+
end
|
60
|
+
|
61
|
+
def create_or_update_layer(zip_file, layer_name)
|
62
|
+
layer_exists = begin
|
63
|
+
response = lambda_client.list_layer_versions({
|
64
|
+
layer_name: layer_name,
|
65
|
+
max_items: 1
|
66
|
+
})
|
67
|
+
|
68
|
+
response.layer_versions.first
|
69
|
+
end
|
70
|
+
|
71
|
+
zipfile_hash = calculate_md5('Gemfile.lock')
|
72
|
+
|
73
|
+
if !layer_exists || layer_exists.description != zipfile_hash
|
74
|
+
create_layer(zip_file, layer_name, zipfile_hash)
|
75
|
+
else
|
76
|
+
puts "Layer unmodified: #{layer_name}"
|
77
|
+
layer_exists.layer_version_arn
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def update_lambda!(layer_arn_deps)
|
82
|
+
with_retries(Aws::Lambda::Errors::ResourceConflictException) do
|
83
|
+
lambda_client.update_function_configuration({
|
84
|
+
function_name:,
|
85
|
+
layers: [layer_arn_deps],
|
86
|
+
timeout: @config['timeout'] || 3,
|
87
|
+
environment: {
|
88
|
+
variables: {
|
89
|
+
'SECRET_TOKEN' => @config['secret_token']
|
90
|
+
}
|
91
|
+
}
|
92
|
+
})
|
93
|
+
end
|
94
|
+
|
95
|
+
with_retries(Aws::Lambda::Errors::ResourceConflictException) do
|
96
|
+
lambda_client.update_function_code(function_name:, zip_file: File.read('.kybuscode.zip'))
|
97
|
+
end
|
98
|
+
puts "Lambda function '#{function_name}' updated."
|
99
|
+
end
|
100
|
+
|
101
|
+
def create_lambda!(layer_arn_deps)
|
102
|
+
with_retries(Aws::Lambda::Errors::ResourceConflictException) do
|
103
|
+
lambda_client.create_function({
|
104
|
+
function_name:,
|
105
|
+
runtime: 'ruby3.3',
|
106
|
+
role: "arn:aws:iam::#{account_id}:role/#{function_name}",
|
107
|
+
handler: 'handler.lambda_handler',
|
108
|
+
layers: [layer_arn_deps],
|
109
|
+
code: {
|
110
|
+
zip_file: File.read('.kybuscode.zip')
|
111
|
+
},
|
112
|
+
timeout: @config['timeout'] || 3,
|
113
|
+
environment: {
|
114
|
+
variables: {
|
115
|
+
'SECRET_TOKEN' => @config['secret_token']
|
116
|
+
}
|
117
|
+
}
|
118
|
+
})
|
119
|
+
puts "Lambda function '#{function_name}' created."
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def make_public!
|
124
|
+
with_retries(Aws::Lambda::Errors::ResourceConflictException) do
|
125
|
+
response = lambda_client.create_function_url_config({
|
126
|
+
function_name:,
|
127
|
+
auth_type: 'NONE'
|
128
|
+
})
|
129
|
+
puts "Function URL created: #{response.function_url}"
|
130
|
+
@url = response.function_url
|
131
|
+
rescue Aws::Lambda::Errors::ResourceConflictException
|
132
|
+
response = lambda_client.get_function_url_config({
|
133
|
+
function_name:
|
134
|
+
})
|
135
|
+
puts "Function URL exists: #{response.function_url}"
|
136
|
+
@url = response.function_url
|
137
|
+
end
|
138
|
+
|
139
|
+
begin
|
140
|
+
response = lambda_client.add_permission({
|
141
|
+
function_name:,
|
142
|
+
statement_id: 'AllowPublicInvoke',
|
143
|
+
action: 'lambda:InvokeFunctionUrl',
|
144
|
+
principal: '*',
|
145
|
+
function_url_auth_type: 'NONE'
|
146
|
+
})
|
147
|
+
puts "Permission added successfully: #{response}"
|
148
|
+
rescue Aws::Lambda::Errors::ServiceError => e
|
149
|
+
puts "Error adding permission: #{e.message}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def create_or_update!
|
154
|
+
deploy_lambda!
|
155
|
+
make_public!
|
156
|
+
end
|
157
|
+
|
158
|
+
def destroy!
|
159
|
+
lambda_client.delete_function(function_name:)
|
160
|
+
puts "Lambda function '#{function_name}' deleted."
|
161
|
+
rescue Aws::Lambda::Errors::ResourceNotFoundException
|
162
|
+
puts "Lambda function '#{function_name}' not found."
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kybus
|
4
|
+
module AWS
|
5
|
+
class LogGroup < Resource
|
6
|
+
def initialize(config, name)
|
7
|
+
super(config)
|
8
|
+
@name = name
|
9
|
+
end
|
10
|
+
|
11
|
+
def logs_client
|
12
|
+
@logs_client ||= Aws::CloudWatchLogs::Client.new(region: @region)
|
13
|
+
end
|
14
|
+
|
15
|
+
def log_group_name
|
16
|
+
"/aws/lambda/#{@name}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_or_update!
|
20
|
+
logs_client.create_log_group(log_group_name:)
|
21
|
+
puts "Log group '#{log_group_name}' created."
|
22
|
+
rescue Aws::CloudWatchLogs::Errors::ResourceAlreadyExistsException
|
23
|
+
puts "Log group '#{log_group_name}' already exists."
|
24
|
+
end
|
25
|
+
|
26
|
+
def destroy!
|
27
|
+
logs_client.delete_log_group(log_group_name:)
|
28
|
+
puts "Log group '#{log_group_name}' deleted."
|
29
|
+
rescue Aws::CloudWatchLogs::Errors::ResourceNotFoundException
|
30
|
+
puts "Log group '#{log_group_name}' not found."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kybus
|
4
|
+
module AWS
|
5
|
+
class Policy < Resource
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
def initialize(config, name, body)
|
9
|
+
super(config)
|
10
|
+
@name = name
|
11
|
+
@body = body
|
12
|
+
@iam_client = Aws::IAM::Client.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def arn
|
16
|
+
"arn:aws:iam::#{account_id}:policy/#{name}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_or_update!
|
20
|
+
@iam_client.create_policy(policy_name: @name, policy_document: @body.to_json)
|
21
|
+
puts "Policy '#{@name}' created."
|
22
|
+
rescue Aws::IAM::Errors::EntityAlreadyExists
|
23
|
+
puts "Policy '#{@name}' already exists."
|
24
|
+
end
|
25
|
+
|
26
|
+
def destroy!
|
27
|
+
@iam_client.delete_policy(policy_arn: arn)
|
28
|
+
puts "Policy '#{@name}' deleted."
|
29
|
+
rescue Aws::IAM::Errors::NoSuchEntity
|
30
|
+
puts "Policy '#{@name}' not found."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kybus
|
4
|
+
module AWS
|
5
|
+
class Resource
|
6
|
+
def initialize(config)
|
7
|
+
@config = config
|
8
|
+
@region = @config['region'] || 'us-east-1'
|
9
|
+
end
|
10
|
+
|
11
|
+
def account_id
|
12
|
+
@config['account_id']
|
13
|
+
end
|
14
|
+
|
15
|
+
def with_retries(exception, max_retries = 5)
|
16
|
+
retry_count = 0
|
17
|
+
begin
|
18
|
+
yield
|
19
|
+
rescue exception
|
20
|
+
retry_count += 1
|
21
|
+
unless retry_count <= max_retries
|
22
|
+
raise "Failed to apply #{self.class} after #{max_retries} attempts due to ongoing updates."
|
23
|
+
end
|
24
|
+
|
25
|
+
sleep_time = 2**retry_count
|
26
|
+
puts "Update in progress, retrying in #{sleep_time} seconds..."
|
27
|
+
sleep(sleep_time)
|
28
|
+
retry
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kybus
|
4
|
+
module AWS
|
5
|
+
class Role < Resource
|
6
|
+
LAMBDA_ASSUME_ROLE_POLICY = {
|
7
|
+
Version: '2012-10-17',
|
8
|
+
Statement: [
|
9
|
+
{
|
10
|
+
Effect: 'Allow',
|
11
|
+
Principal: {
|
12
|
+
Service: 'lambda.amazonaws.com'
|
13
|
+
},
|
14
|
+
Action: 'sts:AssumeRole'
|
15
|
+
}
|
16
|
+
]
|
17
|
+
}.to_json.freeze
|
18
|
+
|
19
|
+
def initialize(config, name, type)
|
20
|
+
super(config)
|
21
|
+
@type = type
|
22
|
+
@name = name
|
23
|
+
@iam_client = Aws::IAM::Client.new
|
24
|
+
@policies = []
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_policy(policy)
|
28
|
+
@policies << policy
|
29
|
+
end
|
30
|
+
|
31
|
+
def assume_role_policy
|
32
|
+
case @type
|
33
|
+
when :lambda
|
34
|
+
LAMBDA_ASSUME_ROLE_POLICY
|
35
|
+
else
|
36
|
+
raise 'Invalid Role Type'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def create_or_update!
|
41
|
+
begin
|
42
|
+
@iam_client.create_role({
|
43
|
+
role_name: @name,
|
44
|
+
assume_role_policy_document: assume_role_policy
|
45
|
+
})
|
46
|
+
puts "Role '#{@name}' created."
|
47
|
+
rescue Aws::IAM::Errors::EntityAlreadyExists
|
48
|
+
puts "Role '#{@name}' already exists."
|
49
|
+
end
|
50
|
+
|
51
|
+
@policies.each do |policy|
|
52
|
+
@iam_client.attach_role_policy(role_name: @name, policy_arn: policy.arn)
|
53
|
+
puts "Policy '#{policy.name}' attached to role '#{@name}'."
|
54
|
+
rescue Aws::IAM::Errors::EntityAlreadyExists
|
55
|
+
puts "Policy '#{policy.name}' already attached to role '#{@name}'."
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def destroy!
|
60
|
+
@policies.each do |policy|
|
61
|
+
@iam_client.detach_role_policy({ role_name: @name, policy_arn: policy.arn })
|
62
|
+
puts "Policy '#{policy.name}' deleted."
|
63
|
+
rescue Aws::IAM::Errors::NoSuchEntity
|
64
|
+
puts "Policy '#{policy.name}' not found."
|
65
|
+
end
|
66
|
+
|
67
|
+
begin
|
68
|
+
@iam_client.delete_role(role_name: @name)
|
69
|
+
puts "Role '#{@name}' deleted."
|
70
|
+
rescue Aws::IAM::Errors::NoSuchEntity
|
71
|
+
puts "Role '#{@name}' not found."
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/kybus/aws.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kybus
|
4
|
+
class CLI < Thor
|
5
|
+
class Bot < Thor
|
6
|
+
class ControllerGenerator
|
7
|
+
def initialize(name)
|
8
|
+
@name = name
|
9
|
+
@file_writer = Kybus::CLI::FileWriter.new('routes')
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate
|
13
|
+
@file_writer.write("#{@name}_controller.rb", controller_content)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def controller_content
|
19
|
+
<<~RUBY
|
20
|
+
# frozen_string_literal: true
|
21
|
+
|
22
|
+
module #{@name.capitalize}Controller
|
23
|
+
def self.included(base)
|
24
|
+
base.instance_eval do
|
25
|
+
include Routes
|
26
|
+
|
27
|
+
def #{@name}_routes
|
28
|
+
# Define your routes here
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
RUBY
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kybus
|
4
|
+
module CLI
|
5
|
+
module Bot
|
6
|
+
class DeployInitGenerator
|
7
|
+
def initialize(name, options)
|
8
|
+
@file_writer = Kybus::CLI::FileWriter.new(name)
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate
|
13
|
+
@file_writer.write('.kybusbot.yaml', config_yaml_content)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def config_yaml_content
|
19
|
+
<<~YAML
|
20
|
+
bot_name: #{bot_name_snake_case}
|
21
|
+
cloud_provider: #{@options[:cloud_provider] || 'aws'}
|
22
|
+
dynamo:
|
23
|
+
capacity: #{@options[:dynamo_capacity] || 'on_demand'}
|
24
|
+
table_name: #{@options[:dynamo_table] || 'bot_sessions'}
|
25
|
+
chat_provider: #{@options[:chat_provider] || 'telegram'}
|
26
|
+
telegram:
|
27
|
+
token: 'REPLACE_ME'
|
28
|
+
YAML
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'uri'
|
5
|
+
require 'json'
|
6
|
+
require 'rake'
|
7
|
+
require 'securerandom'
|
8
|
+
require 'fileutils'
|
9
|
+
require 'yaml'
|
10
|
+
require 'aws-sdk-iam'
|
11
|
+
require 'aws-sdk-lambda'
|
12
|
+
require 'aws-sdk-cloudwatchlogs'
|
13
|
+
require 'zip'
|
14
|
+
|
15
|
+
require 'kybus/aws'
|
16
|
+
require_relative 'deployers/telegram_configurator'
|
17
|
+
require_relative 'deployers/aws_bot_deployer'
|
18
|
+
|
19
|
+
module Kybus
|
20
|
+
class CLI < Thor
|
21
|
+
class Deployer
|
22
|
+
DEFAULT_CONFIGS = {
|
23
|
+
'repo_path' => '.',
|
24
|
+
'output_path' => './.kybusbotcode.zip'
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
def initialize(options)
|
28
|
+
@params = options
|
29
|
+
load_kybusdeploy_file!
|
30
|
+
@telegram = ::Kybus::CLI::BotDeployerTelegramConfigurator.new(@url, config_with_options)
|
31
|
+
@lambda = ::Kybus::CLI::AWSBotDeployer.new(config_with_options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def run_migrations!
|
35
|
+
Rake::Task.clear
|
36
|
+
load 'Rakefile'
|
37
|
+
Rake::Task['db:migrate'].invoke
|
38
|
+
end
|
39
|
+
|
40
|
+
def load_kybusdeploy_file!
|
41
|
+
@config = YAML.load_file('./kybusbot.yaml')
|
42
|
+
end
|
43
|
+
|
44
|
+
def config_with_options
|
45
|
+
@config_with_options ||= DEFAULT_CONFIGS.merge(@config.merge(@params))
|
46
|
+
end
|
47
|
+
|
48
|
+
def compress_repo!
|
49
|
+
code = ::Kybus::AWS::CodePackager.new(config_with_options)
|
50
|
+
code.create_or_update!
|
51
|
+
end
|
52
|
+
|
53
|
+
def deploy_lambda!
|
54
|
+
@lambda.create_or_update!
|
55
|
+
@telegram.url = @lambda.url
|
56
|
+
end
|
57
|
+
|
58
|
+
def run
|
59
|
+
compress_repo!
|
60
|
+
deploy_lambda!
|
61
|
+
@telegram.create_or_update!
|
62
|
+
end
|
63
|
+
|
64
|
+
def destroy
|
65
|
+
@lambda.destroy!
|
66
|
+
@telegram.destroy!
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kybus
|
4
|
+
class CLI < Thor
|
5
|
+
class AWSBotDeployer < BotDeployerBase
|
6
|
+
def make_dynamo_policy_document
|
7
|
+
{
|
8
|
+
Version: '2012-10-17',
|
9
|
+
Statement: [
|
10
|
+
{
|
11
|
+
Effect: 'Allow',
|
12
|
+
Action: [
|
13
|
+
'dynamodb:BatchGetItem',
|
14
|
+
'dynamodb:BatchWriteItem',
|
15
|
+
'dynamodb:Describe*',
|
16
|
+
'dynamodb:Get*',
|
17
|
+
'dynamodb:List*',
|
18
|
+
'dynamodb:PutItem',
|
19
|
+
'dynamodb:Query',
|
20
|
+
'dynamodb:Scan',
|
21
|
+
'dynamodb:UpdateItem',
|
22
|
+
'dynamodb:DeleteItem'
|
23
|
+
],
|
24
|
+
Resource: "arn:aws:dynamodb:#{@region}:#{account_id}:table/#{function_name}*"
|
25
|
+
}, {
|
26
|
+
Effect: :Allow,
|
27
|
+
Action: [
|
28
|
+
'dynamodb:Describe*',
|
29
|
+
'dynamodb:Get*',
|
30
|
+
'dynamodb:List*'
|
31
|
+
],
|
32
|
+
Resource: '*'
|
33
|
+
}
|
34
|
+
]
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def make_log_groupo_policy_document
|
39
|
+
{
|
40
|
+
Version: '2012-10-17',
|
41
|
+
Statement: [
|
42
|
+
{
|
43
|
+
Effect: 'Allow',
|
44
|
+
Action: 'logs:CreateLogGroup',
|
45
|
+
Resource: "arn:aws:logs:#{@region}:#{account_id}:*"
|
46
|
+
},
|
47
|
+
{
|
48
|
+
Effect: 'Allow',
|
49
|
+
Action: [
|
50
|
+
'logs:CreateLogStream',
|
51
|
+
'logs:PutLogEvents'
|
52
|
+
],
|
53
|
+
Resource: [
|
54
|
+
"arn:aws:logs:#{@region}:#{account_id}:log-group:/aws/lambda/#{function_name}:*"
|
55
|
+
]
|
56
|
+
}
|
57
|
+
]
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def initialize(configs)
|
62
|
+
configs['account_id'] = account_id
|
63
|
+
super
|
64
|
+
@role = ::Kybus::AWS::Role.new(configs, function_name, :lambda)
|
65
|
+
@dynamo_policy = ::Kybus::AWS::Policy.new(configs, "#{function_name}-dynamo", make_dynamo_policy_document)
|
66
|
+
@cloudwatch_policy = ::Kybus::AWS::Policy.new(configs, "#{function_name}-cloudwatch",
|
67
|
+
make_log_groupo_policy_document)
|
68
|
+
@role.add_policy(@dynamo_policy)
|
69
|
+
@role.add_policy(@cloudwatch_policy)
|
70
|
+
@log_group = ::Kybus::AWS::LogGroup.new(configs, function_name)
|
71
|
+
@lambda = ::Kybus::AWS::Lambda.new(configs, function_name)
|
72
|
+
end
|
73
|
+
|
74
|
+
def destroy!
|
75
|
+
@lambda.destroy!
|
76
|
+
@role.destroy!
|
77
|
+
@dynamo_policy.destroy!
|
78
|
+
@cloudwatch_policy.destroy!
|
79
|
+
@log_group.destroy!
|
80
|
+
end
|
81
|
+
|
82
|
+
def url
|
83
|
+
@lambda.url
|
84
|
+
end
|
85
|
+
|
86
|
+
def create_or_update!
|
87
|
+
@log_group.create_or_update!
|
88
|
+
@dynamo_policy.create_or_update!
|
89
|
+
@cloudwatch_policy.create_or_update!
|
90
|
+
@role.create_or_update!
|
91
|
+
@lambda.create_or_update!
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kybus
|
4
|
+
class CLI < Thor
|
5
|
+
class BotDeployerBase
|
6
|
+
def initialize(configs)
|
7
|
+
@config = configs
|
8
|
+
end
|
9
|
+
|
10
|
+
def function_name
|
11
|
+
"#{@config[:env] || 'production'}-#{@config['name']}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def account_id
|
15
|
+
@account_id ||= begin
|
16
|
+
sts_client = Aws::STS::Client.new
|
17
|
+
response = sts_client.get_caller_identity
|
18
|
+
response.account
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|