nypl_ruby_util 0.0.7 → 0.0.8
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/lib/deploy_helper.rb +136 -136
- data/lib/kinesis_client.rb +40 -35
- data/lib/kms_client.rb +6 -6
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d2086b8b4b5bef52f3459a1245af09a7364cb35190ec40c0541a7bd72681a8f6
|
4
|
+
data.tar.gz: 4e1c6a34f318b81c6659cca2f57f1930bfbf0e406040e950f681c4631177bec3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68670e8961ed473bb3854b2320cafc0c960b9418b37d336fdaac6f9cf41ae14acca33d131565986fad25f2d8eb6cb57f225828c93e5539c685bf497b80e6bab6
|
7
|
+
data.tar.gz: 433b9dea26fa844a28a6711eff1d510a8f21df2f3f71e011d76e497f77dd2988714162973345084b681bb068771bc95433957f513e26553243cefbad521c70b3
|
data/lib/deploy_helper.rb
CHANGED
@@ -1,152 +1,152 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require "aws-sdk-lambda"
|
2
|
+
require "aws-sdk-cloudwatchevents"
|
3
|
+
require "yaml"
|
4
4
|
|
5
5
|
# Utility class for running rake methods
|
6
6
|
class DeployHelper
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
7
|
+
attr_reader(
|
8
|
+
:travis_branch,
|
9
|
+
:aws_access_key_id,
|
10
|
+
:aws_secret_access_key,
|
11
|
+
:aws_configuration,
|
12
|
+
:region,
|
13
|
+
:lambda_client,
|
14
|
+
:yaml,
|
15
|
+
:lambda_config,
|
16
|
+
:function_name,
|
17
|
+
:event
|
18
|
+
)
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@travis_branch = ENV["TRAVIS_BRANCH"].upcase
|
22
|
+
@travis_branch = ["MAIN", "MASTER"].include?(@travis_branch) ? "PRODUCTION" : @travis_branch
|
23
|
+
@aws_access_key_id = ENV["AWS_ACCESS_KEY_ID_#{travis_branch}"]
|
24
|
+
@aws_secret_access_key = ENV["AWS_SECRET_ACCESS_KEY_#{travis_branch}"]
|
25
|
+
@yaml = YAML.safe_load(File.read(".travis.yml"))
|
26
|
+
@lambda_config = yaml["deploy"].find { |conf| name_matches_branch?(conf["function_name"], travis_branch) }
|
27
|
+
@region = @lambda_config["region"]
|
28
|
+
@function_name = @lambda_config["function_name"]
|
29
|
+
@aws_configuration = {
|
30
|
+
region: region,
|
31
|
+
access_key_id: aws_access_key_id,
|
32
|
+
secret_access_key: aws_secret_access_key
|
33
|
+
}
|
34
|
+
p("using configuration: ", aws_configuration)
|
35
|
+
p("lambda config: ", lambda_config)
|
36
|
+
@lambda_client = Aws::Lambda::Client.new(aws_configuration) if configured?
|
37
|
+
end
|
38
|
+
|
39
|
+
def configured?
|
40
|
+
aws_access_key_id && aws_secret_access_key && region
|
41
|
+
end
|
42
|
+
|
43
|
+
def name_matches_branch?(name, branch)
|
44
|
+
downcase_name = name.downcase
|
45
|
+
downcase_branch = branch.downcase
|
46
|
+
variants = [
|
47
|
+
["dev", "development"],
|
48
|
+
["qa"],
|
49
|
+
["main", "master", "production", "prod"],
|
50
|
+
]
|
51
|
+
variants.any? do |group|
|
52
|
+
group.any? { |variant| downcase_name.include?(variant) }\
|
53
|
+
&& group.any? { |variant| downcase_branch.include?(variant) }
|
37
54
|
end
|
55
|
+
end
|
38
56
|
|
39
|
-
|
40
|
-
|
57
|
+
def update_lambda_configuration
|
58
|
+
unless configured? && lambda_config
|
59
|
+
p("insufficient configuration")
|
60
|
+
return nil
|
41
61
|
end
|
42
62
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
63
|
+
updated_lambda_configuration = {
|
64
|
+
function_name: function_name,
|
65
|
+
vpc_config: lambda_config["vpc_config"],
|
66
|
+
environment: lambda_config["environment"],
|
67
|
+
layers: lambda_config["layers"]
|
68
|
+
}
|
69
|
+
updated_lambda_configuration[:function_name] = function_name
|
70
|
+
p("updating_function_configuration with: ", updated_lambda_configuration)
|
71
|
+
update_configuration_resp = lambda_client.update_function_configuration(updated_lambda_configuration)
|
72
|
+
p("update_configuration_resp: ", update_configuration_resp)
|
73
|
+
end
|
74
|
+
|
75
|
+
def update_event
|
76
|
+
unless lambda_config["event"]
|
77
|
+
p("no event config")
|
78
|
+
return nil
|
55
79
|
end
|
56
80
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
updated_lambda_configuration = {
|
64
|
-
function_name: function_name,
|
65
|
-
vpc_config: lambda_config['vpc_config'],
|
66
|
-
environment: lambda_config['environment'],
|
67
|
-
layers: lambda_config['layers']
|
68
|
-
}
|
69
|
-
updated_lambda_configuration[:function_name] = function_name
|
70
|
-
p 'updating_function_configuration with: ', updated_lambda_configuration
|
71
|
-
update_configuration_resp = lambda_client.update_function_configuration(updated_lambda_configuration)
|
72
|
-
p 'update_configuration_resp: ', update_configuration_resp
|
81
|
+
@event = lambda_config["event"]
|
82
|
+
if event["event_source_arn"]
|
83
|
+
add_event_source
|
84
|
+
elsif event["schedule_expression"]
|
85
|
+
add_cron
|
73
86
|
end
|
87
|
+
end
|
74
88
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
@event = lambda_config['event']
|
82
|
-
if event['event_source_arn']
|
83
|
-
add_event_source
|
84
|
-
elsif event['schedule_expression']
|
85
|
-
add_cron
|
86
|
-
end
|
87
|
-
end
|
89
|
+
def add_event_source
|
90
|
+
existing_events = lambda_client.list_event_source_mappings({
|
91
|
+
function_name: function_name
|
92
|
+
}).event_source_mappings
|
88
93
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
94
|
+
existing_events.each do |existing_event|
|
95
|
+
p("deleting event with uuid: ", existing_event.uuid, "and arn: ", existing_event.event_source_arn)
|
96
|
+
lambda_client.delete_event_source_mapping({ uuid: existing_event.uuid })
|
97
|
+
end
|
98
|
+
event_to_create = event.map { |k, v| [k.to_sym, v] }.to_h
|
99
|
+
event_to_create[:function_name] = function_name
|
100
|
+
p("creating event: ", event_to_create)
|
101
|
+
create_resp = lambda_client.create_event_source_mapping(event_to_create)
|
102
|
+
p("created: ", create_resp)
|
103
|
+
end
|
104
|
+
|
105
|
+
def add_cron
|
106
|
+
## create the event
|
107
|
+
events_client = Aws::CloudWatchEvents::Client.new(aws_configuration)
|
108
|
+
schedule_expression = event["schedule_expression"]
|
109
|
+
rule_name = "#{function_name}-rule"
|
110
|
+
p("rule_name: ", rule_name, "schedule_expression: ", schedule_expression)
|
111
|
+
events_client.put_rule(name: rule_name, schedule_expression: schedule_expression)
|
112
|
+
|
113
|
+
## next we have to connect the event to the lambda
|
114
|
+
## the first step is to get the lambda
|
115
|
+
|
116
|
+
return "missing function_name" unless function_name
|
117
|
+
|
118
|
+
p("getting lambda with function name", function_name)
|
119
|
+
lambda_resp = lambda_client.get_function(function_name: function_name).configuration
|
120
|
+
arn = lambda_resp.function_arn
|
121
|
+
|
122
|
+
## next figure out if the lambda already has granted cloudwatch
|
123
|
+
## permission to invoke
|
124
|
+
begin
|
125
|
+
policy_resp = lambda_client.get_policy(function_name: function_name)
|
126
|
+
if policy_resp.policy.include?("#{function_name}-permission")
|
127
|
+
p("lambda already has permission")
|
128
|
+
else
|
129
|
+
add_policy = true
|
130
|
+
end
|
131
|
+
rescue Aws::Lambda::Errors::ResourceNotFoundException
|
132
|
+
add_policy = true
|
133
|
+
p("no policy")
|
103
134
|
end
|
104
135
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
## the first step is to get the lambda
|
115
|
-
|
116
|
-
return 'missing function_name' unless function_name
|
117
|
-
|
118
|
-
p 'getting lambda with function name', function_name
|
119
|
-
lambda_resp = lambda_client.get_function(function_name: function_name).configuration
|
120
|
-
arn = lambda_resp.function_arn
|
121
|
-
|
122
|
-
## next figure out if the lambda already has granted cloudwatch
|
123
|
-
## permission to invoke
|
124
|
-
begin
|
125
|
-
policy_resp = lambda_client.get_policy(function_name: function_name)
|
126
|
-
unless policy_resp.policy.include?("#{function_name}-permission")
|
127
|
-
add_policy = true
|
128
|
-
else
|
129
|
-
p 'lambda already has permission'
|
130
|
-
end
|
131
|
-
rescue Aws::Lambda::Errors::ResourceNotFoundException
|
132
|
-
add_policy = true
|
133
|
-
p 'no policy'
|
134
|
-
end
|
135
|
-
|
136
|
-
## if not, add permission to invoke
|
137
|
-
if add_policy
|
138
|
-
permission = lambda_client.add_permission({
|
139
|
-
function_name: function_name,
|
140
|
-
principal: 'events.amazonaws.com',
|
141
|
-
statement_id: "#{function_name}-permission",
|
142
|
-
action: 'lambda:InvokeFunction'
|
143
|
-
})
|
144
|
-
p 'permission: ', permission
|
145
|
-
end
|
146
|
-
|
147
|
-
## finally we can tell the event to invoke the lambda
|
148
|
-
target_id = "#{function_name}-lambda"
|
149
|
-
p 'putting targets ', 'rule: ', rule_name, 'target_id: ', target_id, 'arn: ', arn
|
150
|
-
events_client.put_targets(rule: rule_name, targets: [{ id: target_id, arn: arn }])
|
136
|
+
## if not, add permission to invoke
|
137
|
+
if add_policy
|
138
|
+
permission = lambda_client.add_permission({
|
139
|
+
function_name: function_name,
|
140
|
+
principal: "events.amazonaws.com",
|
141
|
+
statement_id: "#{function_name}-permission",
|
142
|
+
action: "lambda:InvokeFunction"
|
143
|
+
})
|
144
|
+
p("permission: ", permission)
|
151
145
|
end
|
146
|
+
|
147
|
+
## finally we can tell the event to invoke the lambda
|
148
|
+
target_id = "#{function_name}-lambda"
|
149
|
+
p("putting targets ", "rule: ", rule_name, "target_id: ", target_id, "arn: ", arn)
|
150
|
+
events_client.put_targets(rule: rule_name, targets: [{ id: target_id, arn: arn }])
|
151
|
+
end
|
152
152
|
end
|
data/lib/kinesis_client.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require_relative
|
4
|
-
require_relative
|
1
|
+
require "securerandom"
|
2
|
+
require "aws-sdk-kinesis"
|
3
|
+
require_relative "nypl_avro"
|
4
|
+
require_relative "errors"
|
5
5
|
# Model representing the result message posted to Kinesis stream about everything that has gone on here -- good, bad, or otherwise.
|
6
6
|
|
7
7
|
class KinesisClient
|
@@ -12,17 +12,15 @@ class KinesisClient
|
|
12
12
|
@stream_name = @config[:stream_name]
|
13
13
|
@avro = nil
|
14
14
|
@batch_size = @config[:batch_size] || 1
|
15
|
-
@
|
16
|
-
@
|
15
|
+
@batch_count = 0
|
16
|
+
@records = []
|
17
|
+
@automatically_push = !(@config[:automatically_push] == false)
|
17
18
|
@client_options = config[:profile] ? { profile: config[:profile] } : {}
|
18
|
-
@client = Aws::Kinesis::Client.new
|
19
|
+
@client = Aws::Kinesis::Client.new(@client_options)
|
19
20
|
|
20
|
-
if config[:schema_string]
|
21
|
-
@avro = NYPLAvro.by_name(config[:schema_string])
|
22
|
-
end
|
23
|
-
|
24
|
-
@shovel_method = @batch_size > 1 ? :push_to_batch : :push_record
|
21
|
+
@avro = NYPLAvro.by_name(config[:schema_string]) if config[:schema_string]
|
25
22
|
|
23
|
+
@shovel_method = @batch_size > 1 ? :push_to_records : :push_record
|
26
24
|
end
|
27
25
|
|
28
26
|
def convert_to_record(json_message)
|
@@ -37,7 +35,6 @@ class KinesisClient
|
|
37
35
|
data: message,
|
38
36
|
partition_key: partition_key
|
39
37
|
}
|
40
|
-
|
41
38
|
end
|
42
39
|
|
43
40
|
def <<(json_message)
|
@@ -45,10 +42,10 @@ class KinesisClient
|
|
45
42
|
end
|
46
43
|
|
47
44
|
def push_record(json_message)
|
48
|
-
record = convert_to_record
|
45
|
+
record = convert_to_record(json_message)
|
49
46
|
record[:stream_name] = @stream_name
|
50
47
|
|
51
|
-
@client.put_record
|
48
|
+
@client.put_record(record)
|
52
49
|
|
53
50
|
return_hash = {}
|
54
51
|
|
@@ -57,21 +54,19 @@ class KinesisClient
|
|
57
54
|
return_hash["message"] = json_message, resp
|
58
55
|
$logger.info("Message sent to #{config[:stream_name]} #{json_message}, #{resp}") if $logger
|
59
56
|
else
|
60
|
-
$logger.error("message" => "FAILED to send message to
|
61
|
-
raise
|
57
|
+
$logger.error("message" => "FAILED to send message to #{@stream_name} #{json_message}, #{resp}.") if $logger
|
58
|
+
raise(NYPLError.new(json_message, resp))
|
62
59
|
end
|
63
60
|
return_hash
|
64
61
|
end
|
65
62
|
|
66
|
-
def
|
63
|
+
def push_to_records(json_message)
|
67
64
|
begin
|
68
|
-
@
|
65
|
+
@records << convert_to_record(json_message)
|
69
66
|
rescue AvroError => e
|
70
67
|
$logger.error("message" => "Avro encoding error #{e.message} for #{json_message}")
|
71
68
|
end
|
72
|
-
if @automatically_push && @
|
73
|
-
push_records
|
74
|
-
end
|
69
|
+
push_records if @automatically_push && @records.length >= @batch_size
|
75
70
|
end
|
76
71
|
|
77
72
|
def push_batch(batch)
|
@@ -82,21 +77,31 @@ class KinesisClient
|
|
82
77
|
|
83
78
|
$logger.debug("Received #{resp} from #{@stream_name}")
|
84
79
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
"message": return_message.to_json
|
95
|
-
}
|
80
|
+
if resp.failed_record_count > 0
|
81
|
+
return_message = {
|
82
|
+
failures: resp.failed_record_count,
|
83
|
+
failures_data: filter_failures(resp)
|
84
|
+
}
|
85
|
+
$logger.warn("Message sent to #{config[:stream_name]} #{return_message}") if $logger
|
86
|
+
else
|
87
|
+
$logger.info("Message sent to #{config[:stream_name]} successfully") if $logger
|
88
|
+
end
|
96
89
|
end
|
97
90
|
|
98
91
|
def push_records
|
99
|
-
@
|
100
|
-
|
92
|
+
if @records.length > 0
|
93
|
+
@records.each_slice(@batch_size) do |slice|
|
94
|
+
push_batch(slice)
|
95
|
+
@batch_count += 1
|
96
|
+
end
|
97
|
+
@records = []
|
98
|
+
@batch_count = 0
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def filter_failures(resp)
|
103
|
+
resp.records.filter_map.with_index do |record, i|
|
104
|
+
avro.decode(@records[i + @batch_size * @batch_count]) if record.responds_to?(:error_message)
|
105
|
+
end
|
101
106
|
end
|
102
107
|
end
|
data/lib/kms_client.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "aws-sdk-kms"
|
2
|
+
require "base64"
|
3
3
|
|
4
4
|
class KmsClient
|
5
5
|
@@kms = nil
|
@@ -11,15 +11,15 @@ class KmsClient
|
|
11
11
|
|
12
12
|
def decrypt(cipher)
|
13
13
|
# Assume value is base64 encoded:
|
14
|
-
decoded = Base64.decode64
|
15
|
-
decrypted = @kms.decrypt
|
14
|
+
decoded = Base64.decode64(cipher)
|
15
|
+
decrypted = @kms.decrypt(ciphertext_blob: decoded)
|
16
16
|
decrypted[:plaintext]
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.aws_kms_client(options)
|
20
20
|
params = {
|
21
|
-
region:
|
22
|
-
stub_responses: ENV[
|
21
|
+
region: "us-east-1",
|
22
|
+
stub_responses: ENV["APP_ENV"] == "test"
|
23
23
|
}.merge(options)
|
24
24
|
@@kms = Aws::KMS::Client.new(params) if @@kms.nil?
|
25
25
|
@@kms
|
metadata
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nypl_ruby_util
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Appel
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 2020-09-08 00:00:00.000000000 Z
|
@@ -109,7 +109,7 @@ dependencies:
|
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: 1.0.3
|
111
111
|
description: A repository of common utilities for NYPL Ruby application
|
112
|
-
email:
|
112
|
+
email:
|
113
113
|
executables: []
|
114
114
|
extensions: []
|
115
115
|
extra_rdoc_files: []
|
@@ -126,7 +126,7 @@ homepage: https://github.com/NYPL/NYPLRubyUtil
|
|
126
126
|
licenses:
|
127
127
|
- MIT
|
128
128
|
metadata: {}
|
129
|
-
post_install_message:
|
129
|
+
post_install_message:
|
130
130
|
rdoc_options: []
|
131
131
|
require_paths:
|
132
132
|
- lib
|
@@ -141,8 +141,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
141
141
|
- !ruby/object:Gem::Version
|
142
142
|
version: '0'
|
143
143
|
requirements: []
|
144
|
-
rubygems_version: 3.2
|
145
|
-
signing_key:
|
144
|
+
rubygems_version: 3.1.2
|
145
|
+
signing_key:
|
146
146
|
specification_version: 4
|
147
147
|
summary: A repository of common utilities for NYPL Ruby application
|
148
148
|
test_files: []
|