kybus-cli 0.1.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 +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
|