container_config 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "container_config"
4
+
5
+ module ContainerConfig
6
+ module Provider
7
+ # Base config value provider
8
+ class Base
9
+ #
10
+ # Returns name of the config value provider
11
+ #
12
+ # @return [String] provider name
13
+ #
14
+ def name
15
+ raise ContainerConfig::MissingOverride, "Must override name method in derived class #{self.class}"
16
+ end
17
+
18
+ #
19
+ # Loads a configuration setting from the provider
20
+ #
21
+ # @param [String] key Configuration key to load
22
+ # @param [Array] _dig_keys Variable keys to use to load from providers that accept a dig structure
23
+ # defaults to the lowercase key split by underscores
24
+ # "MY_PASSWORD" => ["my", "password"]
25
+ # @param [Hash] options Options Hash
26
+ # @option options [String] :default default value if the configuration setting cannot be found
27
+ # @option options [String] :secret_mount_directory directory where secret files are mounted
28
+ #
29
+ # @return [Object] configuration setting value
30
+ #
31
+ def load(key, *_dig_keys, **options)
32
+ ContainerConfig.logger.debug do
33
+ "Loading configuration value for #{key} with options #{options} from #{self.class}"
34
+ end
35
+ nil
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "container_config/provider/base"
4
+
5
+ module ContainerConfig
6
+ module Provider
7
+ # Default config value provider (handles :default option)
8
+ class Default < Base
9
+ # @see ContainerConfig::Provider::Base#name
10
+ def name
11
+ "Default Value"
12
+ end
13
+
14
+ #
15
+ # Loads a default configuration setting based on the value of options[:default]
16
+ #
17
+ # @param [String] key Configuration key to load
18
+ # @param [Array] dig_keys Variable keys to use to load from providers that accept a dig structure
19
+ # @param [Hash] options Options Hash
20
+ # @option options [String] :default default value if the configuration setting cannot be found
21
+ #
22
+ # @return [Object] configuration setting value
23
+ #
24
+ def load(key, *dig_keys, **options)
25
+ super
26
+ options[:default]
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "container_config/provider/base"
4
+
5
+ module ContainerConfig
6
+ module Provider
7
+ # Environment variable config value provider
8
+ class Env < Base
9
+ # @see ContainerConfig::Provider::Base#name
10
+ def name
11
+ "Environment Variable"
12
+ end
13
+
14
+ #
15
+ # Loads an environment value configuration setting
16
+ #
17
+ # @param [String] key Configuration key to load
18
+ # @param [Array] dig_keys Variable keys to use to load from providers that accept a dig structure
19
+ # @param [Hash] options Options Hash
20
+ #
21
+ # @return [Object] configuration setting value
22
+ #
23
+ def load(key, *dig_keys, **options)
24
+ super
25
+ ENV[key]
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "container_config/provider/base"
4
+
5
+ module ContainerConfig
6
+ module Provider
7
+ # Rails credential config value provider
8
+ class RailsCredential < Base
9
+ # @see ContainerConfig::Provider::Base#name
10
+ def name
11
+ "Rails Credential"
12
+ end
13
+
14
+ #
15
+ # Loads a Rails credential configuration setting
16
+ #
17
+ # @param [String] key Configuration key to load
18
+ # @param [Array] dig_keys Variable keys to use to load from providers that accept a dig structure
19
+ # defaults to the lowercase key split by underscores
20
+ # "MY_PASSWORD" => ["my", "password"]
21
+ # @param [Hash] options Options Hash
22
+ #
23
+ # @return [Object] configuration setting value
24
+ #
25
+ def load(key, *dig_keys, **options)
26
+ super
27
+ ::Rails.application.credentials.config.dig(*dig_keys.map(&:to_sym))
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "container_config/provider/base"
4
+
5
+ module ContainerConfig
6
+ module Provider
7
+ # Secret volume mount config value provider
8
+ class SecretVolume < Base
9
+ # Default secret volume mount path used when globbing secret files
10
+ DEFAULT_SECRET_PATH = "/etc/*-secrets"
11
+
12
+ attr_accessor :default_directory, :directory
13
+
14
+ # @see ContainerConfig::Provider::Base#name
15
+ def name
16
+ "Secret Volume"
17
+ end
18
+
19
+ #
20
+ # Initializes a new ContainerConfig::Provider::SecretVolume
21
+ #
22
+ def initialize
23
+ super
24
+ @default_directory = DEFAULT_SECRET_PATH
25
+ @directory = nil
26
+ end
27
+
28
+ #
29
+ # Loads a secret volume mount configuration setting
30
+ #
31
+ # @param [String] key Configuration key to load
32
+ # @param [Array] dig_keys Variable keys to use to load from providers that accept a dig structure
33
+ # defaults to the lowercase key split by underscores
34
+ # "MY_PASSWORD" => ["my", "password"]
35
+ # @param [Hash] options Options Hash
36
+ # @option options [String] :secret_mount_directory directory where secret files are mounted
37
+ #
38
+ # @return [Object] configuration setting value
39
+ #
40
+ def load(key, *dig_keys, **options)
41
+ super
42
+ secret_file = Dir.glob(File.join(secret_mount_directory(**options), "**", key)).first
43
+ return if secret_file.nil? || !File.exist?(secret_file)
44
+
45
+ File.read(secret_file)
46
+ end
47
+
48
+ private
49
+
50
+ def secret_mount_directory(options)
51
+ options[:secret_mount_directory] ||
52
+ directory ||
53
+ ENV["SECRET_MOUNT_DIRECTORY"] ||
54
+ default_directory
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "container_config"
4
+ require "container_config/rails/mailer"
5
+
6
+ module ContainerConfig
7
+ # Rails configuration module
8
+ module Rails
9
+ end
10
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "container_config"
4
+
5
+ module ContainerConfig
6
+ module Rails
7
+ # Rails ActionMailer config module
8
+ module Mailer
9
+ #
10
+ # loads Rails ActionMailer configuration settings from environment variables, mounted secrets, or the application credentials
11
+ #
12
+ # @param [String] key base key for the Mailer config ("MAILER")
13
+ # @param [Hash] options Options Hash
14
+ # @option options [Boolean] :required whether to raise an exception if the settings cannot be found
15
+ # @option options [String] :secret_mount_directory directory where secret files are mounted
16
+ #
17
+ # @return [Hash] Mailer configuration hash with :perform_deliveries, :perform_caching, :raise_delivery_errors,
18
+ # :delivery_method, :sendmail_settings, and :smtp_settings keys
19
+ #
20
+ def self.load(key, **options)
21
+ mail_config = {}
22
+
23
+ mail_config[:perform_deliveries] = ContainerConfig.load("#{key}_PERFORM_DELIVERIES", default: ::Rails.env.production?, type: :boolean)
24
+ mail_config[:perform_caching] = ContainerConfig.load("#{key}_PERFORM_CACHING", default: false, type: :boolean)
25
+ mail_config[:raise_delivery_errors] = ContainerConfig.load("#{key}_RAISE_DELIVERY_ERRORS", default: false, type: :boolean)
26
+ mail_config[:delivery_method] = ContainerConfig.load("#{key}_DELIVERY_METHOD", default: :sendmail, type: :symbol, enum: %i[smtp sendmail])
27
+ mail_config[:sendmail_settings] = mailer_sendmail_settings(key, **options)
28
+ mail_config[:smtp_settings] = mailer_smtp_settings(key, **options)
29
+
30
+ mail_config.compact
31
+ end
32
+
33
+ class << self
34
+ private
35
+
36
+ #
37
+ # load Mailer sendmail settings
38
+ #
39
+ # @param [String] key base key for the Mailer sendmail config
40
+ # @param [Hash] options Options Hash
41
+ # @option options [Boolean] :required whether to raise an exception if the settings cannot be found
42
+ # @option options [String] :secret_mount_directory directory where secret files are mounted
43
+ #
44
+ # @return [Hash] Sendmail settings hash with :location and :arguments keys
45
+ #
46
+ def mailer_sendmail_settings(key, **options)
47
+ {
48
+ location: ContainerConfig.load("#{key}_SENDMAIL_LOCATION", **options),
49
+ arguments: ContainerConfig.load("#{key}_SENDMAIL_ARGUMENTS", **options)
50
+ }.compact
51
+ end
52
+
53
+ #
54
+ # load Mailer SMTP settings
55
+ #
56
+ # @param [String] key base key for the Mailer SMTP config
57
+ # @param [Hash] options Options Hash
58
+ # @option options [Boolean] :required whether to raise an exception if the settings cannot be found
59
+ # @option options [String] :secret_mount_directory directory where secret files are mounted
60
+ #
61
+ # @return [Hash] SMTP settings hash with :address, :port, :domain, :user_name, :password, :authentication,
62
+ # :enable_starttls_auto, :openssl_verify_mode, :ssl, and :tls keys
63
+ #
64
+ def mailer_smtp_settings(key, **options)
65
+ {
66
+ address: ContainerConfig.load("#{key}_SMTP_ADDRESS", **options),
67
+ port: ContainerConfig.load("#{key}_SMTP_PORT", **options, default: 25, type: :integer),
68
+ domain: ContainerConfig.load("#{key}_SMTP_DOMAIN", **options),
69
+ user_name: ContainerConfig.load("#{key}_SMTP_USER_NAME", **options),
70
+ password: ContainerConfig.load("#{key}_SMTP_PASSWORD", **options),
71
+ authentication: ContainerConfig.load("#{key}_SMTP_AUTHENTICATION", **options, type: :symbol, enum: [nil, :plain, :login, :cram_md5]),
72
+ enable_starttls_auto: ContainerConfig.load("#{key}_SMTP_ENABLE_STARTTLS_AUTO", **options, coerce_nil: false, type: :boolean),
73
+ openssl_verify_mode: ContainerConfig.load("#{key}_SMTP_OPENSSL_VERIFY_MODE", **options, coerce_nil: false, type: :ssl_verify_mode),
74
+ ssl: ContainerConfig.load("#{key}_SMTP_SSL", **options, coerce_nil: false, type: :boolean),
75
+ tls: ContainerConfig.load("#{key}_SMTP_TLS", **options, coerce_nil: false, type: :boolean)
76
+ }.compact
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "container_config"
4
+
5
+ module ContainerConfig
6
+ # Redis configuration module
7
+ module Redis
8
+ #
9
+ # Loads Redis configuration settings from environment variables, mounted secrets, or the application credentials
10
+ #
11
+ # @param [String] key base key for the Redis config ("QUEUE", "CACHE", etc.)
12
+ # @param [Hash] options Options Hash
13
+ # @option options [Boolean] :required whether to raise an exception if the settings cannot be found
14
+ # @option options [String] :secret_mount_directory directory where secret files are mounted
15
+ #
16
+ # @return [Hash] Redis configuration hash with :url, :password, and :sentinels keys
17
+ # See https://github.com/redis/redis-rb for more information
18
+ #
19
+ def self.load(key, **options)
20
+ host = ContainerConfig.load("#{key}_HOST", **options, default: "localhost")
21
+ port = ContainerConfig.load("#{key}_PORT", **options, default: "6379")
22
+
23
+ url = ContainerConfig.load("#{key}_URL", **options, default: "redis://#{host}:#{port}")
24
+ sentinels = sentinel_info(key, **options)
25
+ password = ContainerConfig.load("#{key}_PASSWORD", **options)
26
+
27
+ # Ensure we never pass an empty string to Redis since it will be passed to the Redis AUTH
28
+ # command as-is and will cause an exception
29
+ password = nil if password.to_s.strip.empty?
30
+
31
+ redis_config = { url: url, password: password }
32
+ redis_config[:sentinels] = sentinels unless sentinels.empty?
33
+
34
+ # Add SSL configuration
35
+ redis_config[:ssl] = ContainerConfig.load("#{key}_SSL", **options, default: false)
36
+ redis_config[:ssl_params] = ssl_params(key, **options)
37
+
38
+ redis_config
39
+ end
40
+
41
+ class << self
42
+ private
43
+
44
+ #
45
+ # Load Redis Sentinel configuration settings
46
+ #
47
+ # @param [String] key base key for the Redis Sentinel config
48
+ # @param [Hash] options Options Hash
49
+ # @option options [Boolean] :required whether to raise an exception if the settings cannot be found
50
+ # @option options [String] :secret_mount_directory directory where secret files are mounted
51
+ #
52
+ # @return [Array] Redis sentinel configuration hashes, may be empty if no sentinel config exists
53
+ # Each hash will have :host, :port, and :password keys
54
+ #
55
+ def sentinel_info(key, **options)
56
+ sentinel_hosts = ContainerConfig.load("#{key}_SENTINELS", **options, default: [])
57
+ sentinel_hosts = sentinel_hosts.split(",").map(&:strip) if sentinel_hosts.is_a?(String)
58
+
59
+ sentinel_port = ContainerConfig.load("#{key}_SENTINEL_PORT", **options, default: "26379")
60
+ sentinel_password = ContainerConfig.load("#{key}_SENTINEL_PASSWORD", **options)
61
+
62
+ # Ensure we never pass an empty string to Redis since it will be passed to the Redis sentinel AUTH
63
+ # command as-is and will cause an exception
64
+ sentinel_password = nil if sentinel_password.to_s.strip.empty?
65
+
66
+ ssl_params = ssl_params("#{key}_SENTINEL", **options)
67
+
68
+ sentinel_hosts.map { |h| { host: h, port: sentinel_port, password: sentinel_password, ssl_params: ssl_params } }
69
+ end
70
+
71
+ #
72
+ # Load Redis SSL parameters
73
+ #
74
+ # @param [String] key base key for the Redis/Redis Sentinel config
75
+ # @param [Hash] options Options Hash
76
+ # @option options [Boolean] :required whether to raise an exception if the settings cannot be found
77
+ # @option options [String] :secret_mount_directory directory where secret files are mounted
78
+ #
79
+ # @return [Hash] SSL parameter hash (see OpenSSL::SSL::SSLContext documentation)
80
+ #
81
+ def ssl_params(key, **options)
82
+ ssl_params = {
83
+ ca_file: ContainerConfig.load("#{key}_SSL_CA_FILE", **options),
84
+ ca_path: ContainerConfig.load("#{key}_SSL_CA_PATH", **options),
85
+ cert: ContainerConfig.load("#{key}_SSL_CERT", **options, type: :ssl_certificate),
86
+ key: ContainerConfig.load("#{key}_SSL_KEY", **options, type: :ssl_key),
87
+ verify_mode: ContainerConfig.load("#{key}_SSL_VERIFY_MODE", **options, type: :ssl_verify_mode)
88
+ }
89
+
90
+ # Reject all nil key values
91
+ ssl_params.compact
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ContainerConfig
4
+ # ContainerConfig version
5
+ VERSION = "0.1.0"
6
+ end
metadata ADDED
@@ -0,0 +1,167 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: container_config
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Matthew Newell
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-03-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '13.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '13.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop-rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.2'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.2'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.9'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.9'
97
+ description: Loads container configuration values from environment variables, secrets,
98
+ and credentials.
99
+ email:
100
+ - matthewtnewell@gmail.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".github/workflows/main.yml"
106
+ - ".gitignore"
107
+ - ".rspec"
108
+ - ".rubocop.yml"
109
+ - CHANGELOG.md
110
+ - Gemfile
111
+ - Gemfile.lock
112
+ - LICENSE.txt
113
+ - README.md
114
+ - Rakefile
115
+ - bin/console
116
+ - bin/setup
117
+ - container_config.gemspec
118
+ - lib/container_config.rb
119
+ - lib/container_config/coercer.rb
120
+ - lib/container_config/coercer/base.rb
121
+ - lib/container_config/coercer/boolean.rb
122
+ - lib/container_config/coercer/float.rb
123
+ - lib/container_config/coercer/integer.rb
124
+ - lib/container_config/coercer/ssl_certificate.rb
125
+ - lib/container_config/coercer/ssl_key.rb
126
+ - lib/container_config/coercer/ssl_verify_mode.rb
127
+ - lib/container_config/coercer/string.rb
128
+ - lib/container_config/coercer/symbol.rb
129
+ - lib/container_config/logger.rb
130
+ - lib/container_config/provider.rb
131
+ - lib/container_config/provider/base.rb
132
+ - lib/container_config/provider/default.rb
133
+ - lib/container_config/provider/env.rb
134
+ - lib/container_config/provider/rails_credential.rb
135
+ - lib/container_config/provider/secret_volume.rb
136
+ - lib/container_config/rails.rb
137
+ - lib/container_config/rails/mailer.rb
138
+ - lib/container_config/redis.rb
139
+ - lib/container_config/version.rb
140
+ homepage: https://github.com/wheatevo/container_config
141
+ licenses:
142
+ - MIT
143
+ metadata:
144
+ homepage_uri: https://github.com/wheatevo/container_config
145
+ source_code_uri: https://github.com/wheatevo/container_config
146
+ changelog_uri: https://github.com/wheatevo/container_config/blob/master/CHANGELOG.md
147
+ post_install_message:
148
+ rdoc_options: []
149
+ require_paths:
150
+ - lib
151
+ required_ruby_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: 2.4.0
156
+ required_rubygems_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ requirements: []
162
+ rubygems_version: 3.1.4
163
+ signing_key:
164
+ specification_version: 4
165
+ summary: Loads container configuration values from environment variables, secrets,
166
+ and credentials.
167
+ test_files: []