exercism-config 0.33.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.
@@ -0,0 +1,26 @@
1
+ # Exercism Config
2
+
3
+ ![Tests](https://github.com/exercism/config/workflows/Tests/badge.svg)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/513edbd6599a2de3218d/maintainability)](https://codeclimate.com/github/exercism/config/maintainability)
5
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/513edbd6599a2de3218d/test_coverage)](https://codeclimate.com/github/exercism/config/test_coverage)
6
+ [![Gem Version](https://badge.fury.io/rb/exercism_config.svg)](https://badge.fury.io/rb/exercism_config)
7
+
8
+ When terraform creates Exercism's infrastructure, it writes endpoints and DNS entries to DynamoDB.
9
+ This gem allows you to trivially retrieve that data.
10
+
11
+ When running on AWS, simply ensure the machine has read-access to the relevant table (currently hardcoded to `config`).
12
+ On a local machine specify the AWS_PROFILE environment variable with the relevant profile stored in your `.aws/credentials`.
13
+
14
+ ## Usage
15
+
16
+ Simply include this gem in your Gemfile, require it, and then refer to the object:
17
+
18
+ ```ruby
19
+ # Gemfile
20
+ gem 'exercism_config'
21
+
22
+ # Your code
23
+ require 'exercism_config'
24
+ ExercismConfig.config.webservers_alb_dns_name
25
+ ```
26
+
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << 'test'
6
+ t.libs << 'lib'
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task default: :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'exercism_config'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start(__FILE__)
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ gem bump -c -v minor -t -p -r
@@ -0,0 +1,4 @@
1
+ #!/bin/bash
2
+
3
+ git diff --name-status --staged | grep '^[MA]' | grep -o '\\s\\+.*rb' | xargs bundle exec rubocop --except Metrics --auto-correct --format quiet --force-exclusion lib/exercism_config.rb && \
4
+ git diff --name-status --staged | grep '^[MA]' | grep -o '\\s\\+.*rb' | xargs git add
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'exercism_config'
5
+ require 'erb'
6
+ require 'yaml'
7
+
8
+ env = ExercismConfig::DetermineEnvironment.call
9
+ if env == :production
10
+ puts 'Aborted! This script should not be run in production.'
11
+ exit
12
+ end
13
+
14
+ def create_table(client)
15
+ client.create_table(
16
+ table_name: :config,
17
+ attribute_definitions: [
18
+ {
19
+ attribute_name: 'id',
20
+ attribute_type: 'S'
21
+ }
22
+ ],
23
+ key_schema: [
24
+ {
25
+ attribute_name: 'id',
26
+ key_type: 'HASH'
27
+ }
28
+ ],
29
+ provisioned_throughput: {
30
+ read_capacity_units: 1,
31
+ write_capacity_units: 1
32
+ }
33
+ )
34
+ end
35
+
36
+ def delete_table(client)
37
+ client.delete_table(
38
+ table_name: :config
39
+ )
40
+ end
41
+
42
+ def set_config_value(client, id, value)
43
+ client.put_item(
44
+ table_name: :config,
45
+ item: {
46
+ id: id,
47
+ value: value
48
+ }
49
+ )
50
+ end
51
+
52
+ client = ExercismConfig::SetupDynamoDBClient.call
53
+
54
+ begin
55
+ create_table(client)
56
+ rescue Aws::DynamoDB::Errors::ResourceInUseException => e
57
+ if ARGV.include?('--force')
58
+ puts 'Table exists. Recreating...'
59
+ delete_table(client)
60
+ create_table(client)
61
+ puts 'Table recreated.'
62
+ else
63
+ puts 'Table exists. Not recreating.'
64
+ end
65
+ end
66
+
67
+ settings_file_arg = ARGV.select { |arg| arg.start_with?('--settings-file=') }.first
68
+ settings_file =
69
+ if settings_file_arg
70
+ settings_file_arg.split('=').last
71
+ elsif ENV['EXERCISM_DOCKER']
72
+ File.expand_path('../settings/docker.yml', __dir__)
73
+ elsif ENV['EXERCISM_CI']
74
+ File.expand_path('../settings/test-ci.yml', __dir__)
75
+ else
76
+ File.expand_path('../settings/development.yml', __dir__)
77
+ end
78
+
79
+ puts "Using settings file: #{settings_file}"
80
+ settings = YAML.load(ERB.new(File.read(settings_file)).result)
81
+
82
+ settings.each do |key, value|
83
+ set_config_value(client, key, value)
84
+ end
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env ruby
2
+ require "bundler/setup"
3
+ require "exercism_config"
4
+
5
+ # Only allow this to run in development
6
+ return unless Exercism.environment == :development
7
+
8
+ puts "Create AWS/DynamoDB tables..."
9
+
10
+ Exercism.config.dynamodb_client = ExercismConfig::SetupDynamoDBClient.()
11
+
12
+ ##################
13
+ # Setup local s3 #
14
+ ##################
15
+
16
+ # We don't need this running for CI atm as none of our
17
+ # tests actually hit s3. Although we might choose to change this
18
+ unless ENV["EXERCISM_CI"]
19
+ begin
20
+ ExercismConfig::SetupS3Client.().create_bucket(bucket: Exercism.config.aws_iterations_bucket)
21
+ rescue Seahorse::Client::NetworkingError => e
22
+ puts "local S3 not up yet..."
23
+ sleep 2 # slighty retry delaty
24
+ retry
25
+ end
26
+ end
27
+
28
+ ########################
29
+ # Setup local dynamodb #
30
+ ########################
31
+ %w[tooling_jobs tooling_jobs-test].each do |table_name|
32
+ begin
33
+ Exercism.config.dynamodb_client.delete_table(
34
+ table_name: table_name
35
+ )
36
+ rescue Aws::DynamoDB::Errors::ResourceNotFoundException
37
+ end
38
+ puts "[x] #{table_name}"
39
+
40
+ Exercism.config.dynamodb_client.create_table(
41
+ table_name: table_name,
42
+ attribute_definitions: [
43
+ {
44
+ attribute_name: "id",
45
+ attribute_type: "S"
46
+ }
47
+ ],
48
+ key_schema: [
49
+ {
50
+ attribute_name: "id",
51
+ key_type: "HASH"
52
+ }
53
+ ],
54
+ provisioned_throughput: {
55
+ read_capacity_units: 1,
56
+ write_capacity_units: 1
57
+ }
58
+ )
59
+
60
+ Exercism.config.dynamodb_client.update_table(
61
+ table_name: table_name,
62
+ attribute_definitions: [
63
+ {
64
+ attribute_name: "job_status",
65
+ attribute_type: "S"
66
+ },
67
+ {
68
+ attribute_name: "created_at",
69
+ attribute_type: "N"
70
+ }
71
+ ],
72
+ global_secondary_index_updates: [
73
+ {
74
+ create: {
75
+ index_name: "job_status", # required
76
+ key_schema: [ # required
77
+ {
78
+ attribute_name: "job_status", # required
79
+ key_type: "HASH" # required, accepts HASH, RANGE
80
+ },
81
+ {
82
+ attribute_name: "created_at", # required
83
+ key_type: "RANGE" # required, accepts HASH, RANGE
84
+ }
85
+ ],
86
+ projection: { # required
87
+ projection_type: "KEYS_ONLY"
88
+ },
89
+ provisioned_throughput: {
90
+ read_capacity_units: 1, # required
91
+ write_capacity_units: 1 # required
92
+ }
93
+ }
94
+ }
95
+ ]
96
+ )
97
+ end
@@ -0,0 +1,41 @@
1
+ require_relative 'lib/exercism_config/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'exercism-config'
5
+ spec.version = ExercismConfig::VERSION
6
+ spec.authors = ['Jeremy Walker']
7
+ spec.email = ['jez.walker@gmail.com']
8
+
9
+ spec.summary = 'Retrieves stored config for Exercism'
10
+ spec.homepage = 'https://exercism.io'
11
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
12
+
13
+ spec.metadata['homepage_uri'] = spec.homepage
14
+ spec.metadata['source_code_uri'] = 'https://github.com/exercism/config'
15
+
16
+ # Specify which files should be added to the gem when it is released.
17
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
19
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ end
21
+ spec.bindir = "bin"
22
+ spec.executables = %w[
23
+ setup_exercism_config
24
+ setup_exercism_local_aws
25
+ ]
26
+ spec.require_paths = ["lib"]
27
+
28
+ spec.add_dependency 'aws-sdk-dynamodb', '~> 1.0'
29
+ spec.add_dependency 'mandate'
30
+ spec.add_dependency 'zeitwerk'
31
+
32
+ spec.add_development_dependency 'bundler', '~> 2.1'
33
+ spec.add_development_dependency 'minitest', '~> 5.0'
34
+ spec.add_development_dependency 'mocha'
35
+ spec.add_development_dependency 'rake', '~> 12.3'
36
+
37
+ # This isn't a compulsary dependency
38
+ # but can be used if someone puts it in their
39
+ # own gemfile
40
+ spec.add_development_dependency 'aws-sdk-s3'
41
+ end
@@ -0,0 +1,12 @@
1
+ module Exercism
2
+ class Config < OpenStruct
3
+ def initialize(data, aws_settings)
4
+ super(data)
5
+ self.aws_settings = aws_settings
6
+ end
7
+
8
+ def to_json(*_args)
9
+ to_h.to_json
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,27 @@
1
+ require 'aws-sdk-dynamodb'
2
+ require 'mandate'
3
+ require 'ostruct'
4
+ require 'json'
5
+
6
+ require_relative 'exercism_config/determine_environment'
7
+ require_relative 'exercism_config/generate_aws_settings'
8
+ require_relative 'exercism_config/setup_dynamodb_client'
9
+ require_relative 'exercism_config/setup_s3_client'
10
+ require_relative 'exercism_config/retrieve'
11
+ require_relative 'exercism_config/generate_aws_settings'
12
+ require_relative 'exercism_config/version'
13
+
14
+ require_relative 'exercism/config'
15
+
16
+ module Exercism
17
+ class ConfigError < RuntimeError
18
+ end
19
+
20
+ def self.environment
21
+ @environment ||= ExercismConfig::DetermineEnvironment.()
22
+ end
23
+
24
+ def self.config
25
+ @config ||= ExercismConfig::Retrieve.()
26
+ end
27
+ end
@@ -0,0 +1,16 @@
1
+ module ExercismConfig
2
+ class DetermineEnvironment
3
+ include Mandate
4
+
5
+ def call
6
+ env = ENV['EXERCISM_ENV'] || ENV['RAILS_ENV'] || ENV['APP_ENV']
7
+ raise Exercism::ConfigError, 'No environment set - set one of EXERCISM_ENV, RAILS_ENV or APP_ENV' unless env
8
+
9
+ unless %w[development test production].include?(env)
10
+ raise Exercism::ConfigError, "environment must be one of development, test or production. Got #{env}."
11
+ end
12
+
13
+ env.to_sym
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,38 @@
1
+ module ExercismConfig
2
+ class GenerateAwsSettings
3
+ include Mandate
4
+
5
+ def call
6
+ {
7
+ region: 'eu-west-2',
8
+ profile: profile,
9
+ access_key_id: aws_access_key_id,
10
+ secret_access_key: aws_secret_access_key
11
+ }.select { |_k, v| v }
12
+ end
13
+
14
+ memoize
15
+ def aws_access_key_id
16
+ case Exercism.environment
17
+ when :development, :test
18
+ 'FAKE'
19
+ end
20
+ end
21
+
22
+ memoize
23
+ def aws_secret_access_key
24
+ case Exercism.environment
25
+ when :development, :test
26
+ 'FAKE'
27
+ end
28
+ end
29
+
30
+ memoize
31
+ def profile
32
+ case Exercism.environment
33
+ when :production
34
+ 'exercism_staging'
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,67 @@
1
+ module ExercismConfig
2
+ class Retrieve
3
+ include Mandate
4
+
5
+ def call
6
+ return generate_mock if Exercism.environment == :test
7
+
8
+ retrieve_from_dynamodb
9
+ end
10
+
11
+ private
12
+ def generate_mock
13
+ require 'erb'
14
+ require 'yaml'
15
+
16
+ filename = if ENV["EXERCISM_CI"]
17
+ 'test-ci'
18
+ elsif ENV["EXERCISM_DOCKER"]
19
+ 'test-docker'
20
+ else
21
+ 'test-local'
22
+ end
23
+
24
+ settings_file = File.expand_path("../../../settings/#{filename}.yml", __FILE__)
25
+ settings = YAML.safe_load(ERB.new(File.read(settings_file)).result)
26
+
27
+ Exercism::Config.new(settings, {})
28
+ end
29
+
30
+ def retrieve_from_dynamodb
31
+ client = SetupDynamoDBClient.()
32
+
33
+ resp = client.scan({ table_name: 'config' })
34
+ items = resp.to_h[:items]
35
+ data = items.each_with_object({}) do |item, h|
36
+ h[item['id']] = item['value']
37
+ end
38
+
39
+ # Tweak things for dynamodb when we're running in test mode
40
+ if Exercism.environment == :test
41
+ %w[dynamodb_tooling_jobs_table].each do |key|
42
+ data[key] = "#{data[key]}-test"
43
+ end
44
+ end
45
+
46
+ aws_settings = GenerateAwsSettings.()
47
+ Exercism::Config.new(data, aws_settings)
48
+ rescue Exercism::ConfigError
49
+ raise
50
+ rescue StandardError => e
51
+ raise Exercism::ConfigError, "Exercism Config could not be loaded: #{e.message}"
52
+ end
53
+
54
+ memoize
55
+ def aws_client
56
+ config = {
57
+ region: 'eu-west-2',
58
+ profile: profile,
59
+ endpoint: endpoint,
60
+ access_key_id: aws_access_key_id,
61
+ secret_access_key: aws_secret_access_key
62
+ }.select { |_k, v| v }
63
+
64
+ Aws::DynamoDB::Client.new(config)
65
+ end
66
+ end
67
+ end