nypl_ruby_util 0.0.5 → 0.0.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd76f2db6e4d52927503aec7dbe8a5e50c5e0fea01bafe5b7caf184e814e3ca4
4
- data.tar.gz: 2c59a7d630e18ee839780b450eab83370b32d9231e5c23c244ffe795a0323c3f
3
+ metadata.gz: 05c91cd9d8cd4ffd258f3a2c1f9b3cb41c2c6cf9b5df78d21f1483510e34c544
4
+ data.tar.gz: 55e61c0097b9721728b147b6201e0856e815c3de8631e3f41ed78b00c12d11bb
5
5
  SHA512:
6
- metadata.gz: 79f01aa5a487725cc37793f1b5b4656bf9e969a8e0e234bf2c6d9927c008107c71f161630e1539037a1974663c4df95d5f415d80089aaaaa19512257b771a3dd
7
- data.tar.gz: 3d3d398b64516e59736d254ff2c709aec0980b2682401216e945f78be77bd6f2f1b8f3162025bf177fc85c6d631caf08da5ff42560861caa00d1d08a9c37c0a4
6
+ metadata.gz: 6c2042cc492affeb2ae6d60d6c96fb6d0db3ea69c23f1ad6a31da3ff5025c52cd4d5f669112e174153e88b84dde7395f59b23b12182017c079f1ce6a024807f6
7
+ data.tar.gz: 832f11059c1267393afb6b2f5140dd5a8f98950f9e69c495e6fa1cfa31af947c78fbf4cd740016214b4fe1dc4bce939a6f57de295039a3673aa195da0ce2be2c
data/lib/deploy_helper.rb CHANGED
@@ -1,152 +1,152 @@
1
- require 'aws-sdk-lambda'
2
- require 'aws-sdk-cloudwatchevents'
3
- require 'yaml'
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
- 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?
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
- def configured?
40
- aws_access_key_id && aws_secret_access_key && region
57
+ def update_lambda_configuration
58
+ unless configured? && lambda_config
59
+ p("insufficient configuration")
60
+ return nil
41
61
  end
42
62
 
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) }
54
- end
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
- def update_lambda_configuration
58
- unless configured? && lambda_config
59
- p 'insufficient configuration'
60
- return nil
61
- end
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
- def update_event
76
- unless lambda_config['event']
77
- p 'no event config'
78
- return nil
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
- def add_event_source
90
- existing_events = lambda_client.list_event_source_mappings({
91
- function_name: function_name
92
- }).event_source_mappings
93
-
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
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
- 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
- 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
@@ -1,7 +1,7 @@
1
- require 'securerandom'
2
- require 'aws-sdk-kinesis'
3
- require_relative 'nypl_avro'
4
- require_relative 'errors'
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
@@ -9,40 +9,108 @@ class KinesisClient
9
9
 
10
10
  def initialize(config)
11
11
  @config = config
12
+ @stream_name = @config[:stream_name]
12
13
  @avro = nil
14
+ @batch_size = @config[:batch_size] || 1
15
+ @client_options = set_config(config)
16
+ @batch_count = 0
17
+ @records = []
18
+ @automatically_push = !(@config[:automatically_push] == false)
19
+ @client = Aws::Kinesis::Client.new(@client_options)
13
20
 
14
- if config[:schema_string]
15
- @avro = NYPLAvro.by_name(config[:schema_string])
16
- end
21
+ @avro = NYPLAvro.by_name(config[:schema_string]) if config[:schema_string]
17
22
 
23
+ @shovel_method = @batch_size > 1 ? :push_to_records : :push_record
18
24
  end
19
25
 
20
- def <<(json_message)
26
+ def set_config(config)
27
+ if config[:profile]
28
+ { profile: config[:profile] }
29
+ elsif config[:custom_aws_config]
30
+ config[:custom_aws_config]
31
+ else
32
+ {}
33
+ end
34
+ end
35
+
36
+ def convert_to_record(json_message)
21
37
  if config[:schema_string]
22
38
  message = avro.encode(json_message, false)
23
39
  else
24
40
  message = json_message
25
41
  end
26
42
 
27
- client = Aws::Kinesis::Client.new
28
43
  partition_key = (config[:partition_key] ? json_message[config[:partition_key]] : SecureRandom.hex(20)).hash.to_s
29
-
30
- resp = client.put_record({
31
- stream_name: config[:stream_name],
44
+ {
32
45
  data: message,
33
46
  partition_key: partition_key
34
- })
47
+ }
48
+ end
49
+
50
+ def <<(json_message)
51
+ send(@shovel_method, json_message)
52
+ end
53
+
54
+ #This method is broken
55
+ def push_record(json_message)
56
+ record = convert_to_record(json_message)
57
+ record[:stream_name] = @stream_name
58
+
59
+ @client.put_record(record)
60
+
61
+ return_hash = {}
35
62
 
36
- return_hash = {}
63
+ if resp.successful?
64
+ return_hash["code"] = "200"
65
+ return_hash["message"] = json_message, resp
66
+ $logger.info("Message sent to #{config[:stream_name]} #{json_message}, #{resp}") if $logger
67
+ else
68
+ $logger.error("message" => "FAILED to send message to #{@stream_name} #{json_message}, #{resp}.") if $logger
69
+ raise(NYPLError.new(json_message, resp))
70
+ end
71
+ return_hash
72
+ end
73
+
74
+ def push_to_records(json_message)
75
+ begin
76
+ @records << convert_to_record(json_message)
77
+ rescue AvroError => e
78
+ $logger.error("message" => "Avro encoding error #{e.message} for #{json_message}")
79
+ end
80
+ push_records if @automatically_push && @records.length >= @batch_size
81
+ end
37
82
 
38
- if resp.successful?
39
- return_hash["code"] = "200"
40
- return_hash["message"] = json_message, resp
41
- $logger.info("Message sent to #{config[:stream_name]} #{json_message}, #{resp}") if $logger
42
- else
43
- $logger.error("message" => "FAILED to send message to HoldRequestResult #{json_message}, #{resp}.") if $logger
44
- raise NYPLError.new json_message, resp
83
+ def push_batch(batch)
84
+ resp = @client.put_records({
85
+ records: batch.to_a,
86
+ stream_name: @stream_name
87
+ })
88
+
89
+ if resp.failed_record_count > 0
90
+ failure_message = {
91
+ failures: resp.failed_record_count,
92
+ failures_data: filter_failures(resp)
93
+ }
94
+ $logger.warn("Batch sent to #{config[:stream_name]} with failures: #{failure_message}")
95
+ else
96
+ $logger.info("Batch sent to #{config[:stream_name]} successfully")
97
+ end
98
+ end
99
+
100
+ def push_records
101
+ if @records.length > 0
102
+ @records.each_slice(@batch_size) do |slice|
103
+ push_batch(slice)
104
+ @batch_count += 1
45
105
  end
46
- return_hash
106
+ @records = []
107
+ @batch_count = 0
108
+ end
109
+ end
110
+
111
+ def filter_failures(resp)
112
+ resp.records.filter_map.with_index do |record, i|
113
+ avro.decode(@records[i + @batch_size * @batch_count]) if record.responds_to?(:error_message)
114
+ end
47
115
  end
48
116
  end
data/lib/kms_client.rb CHANGED
@@ -1,5 +1,5 @@
1
- require 'aws-sdk-kms'
2
- require 'base64'
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 cipher
15
- decrypted = @kms.decrypt ciphertext_blob: decoded
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: 'us-east-1',
22
- stub_responses: ENV['APP_ENV'] == 'test'
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
data/lib/nypl_avro.rb CHANGED
@@ -38,7 +38,7 @@ class NYPLAvro
38
38
  begin
39
39
  bin_encoder.write(decoded_data, encoder)
40
40
  rescue Avro::IO::AvroTypeError => e
41
- raise AvroError.new(e), "Error encoding data using #{@schema.name} schema due to #{e.message}"
41
+ raise AvroError.new(e), "Error encoding data #{decoded_data} using #{@schema.name} schema due to #{e.message}"
42
42
  end
43
43
 
44
44
  buffer.rewind
@@ -61,6 +61,7 @@ class PlatformApiClient
61
61
 
62
62
  # Authorizes the request.
63
63
  def authenticate!
64
+ $logger.debug('authenticating')
64
65
  # NOOP if we've already authenticated
65
66
  return nil if ! access_token.nil?
66
67
 
@@ -81,8 +82,10 @@ class PlatformApiClient
81
82
  end
82
83
 
83
84
  if response.code == '200'
84
- access_token = JSON.parse(response.body)["access_token"]
85
+ self.access_token = JSON.parse(response.body)["access_token"]
86
+ $logger.debug('got token')
85
87
  else
88
+ $logger.debug('no token')
86
89
  nil
87
90
  end
88
91
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nypl_ruby_util
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Appel
@@ -141,7 +141,7 @@ 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.3
144
+ rubygems_version: 3.1.4
145
145
  signing_key:
146
146
  specification_version: 4
147
147
  summary: A repository of common utilities for NYPL Ruby application