on_container 0.0.7 → 0.0.12
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 +5 -5
- data/README.md +52 -5
- data/lib/on_container/common/performable.rb +17 -0
- data/lib/on_container/common/safe_performable.rb +26 -0
- data/lib/on_container/load_env_secrets.rb +7 -45
- data/lib/on_container/secrets/env_loader.rb +43 -0
- data/lib/on_container/secrets/google_cloud/env_loader.rb +41 -0
- data/lib/on_container/secrets/google_cloud/fetcher.rb +62 -0
- data/lib/on_container/secrets/google_cloud/service_base.rb +19 -0
- data/lib/on_container/secrets/mounted_files/env_loader.rb +58 -0
- data/lib/on_container/secrets/url_variable_processor.rb +71 -0
- data/lib/on_container/version.rb +1 -1
- data/on_container.gemspec +3 -2
- metadata +29 -15
- data/lib/on_container/step_down_from_root.rb +0 -55
- data/spec/on_container_spec.rb +0 -5
- data/spec/spec_helper.rb +0 -14
- data/spec/step_down_from_root_spec.rb +0 -63
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: cc7ffb417b7d418f2415df1cd5c6cbd0325d78e576e71a701010bd44ead098f6
|
4
|
+
data.tar.gz: 260e06a5fa81789bc977cac9b02c8e092100a0896d9eccc4c4f88423592a1f9d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e54869d4ec1c390a555731ac199ece8b804098eece937321123e48884d0b7fa0dff418147f97e811b4e05fb23517f433bf622b08fc4f4941d04801fa31f8570
|
7
|
+
data.tar.gz: 410eb4b5c14a632600ecccade83dcca9764965085dfa95177976365bc762516ac36e8d6ec70cdd51925d9204c19652afe80c50887774b67ffbe0990c9edeb18b
|
data/README.md
CHANGED
@@ -52,7 +52,7 @@ end if command_requires_setup?
|
|
52
52
|
execute_given_or_default_command
|
53
53
|
```
|
54
54
|
|
55
|
-
### Loading secrets into environment variables
|
55
|
+
### Loading secrets into environment variables
|
56
56
|
|
57
57
|
When using Docker Swarm, the secrets are loaded as files mounted into the container's filesystem.
|
58
58
|
|
@@ -65,11 +65,58 @@ For our Rails example app, we added the following line to the `config/boot.rb` f
|
|
65
65
|
|
66
66
|
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
67
67
|
|
68
|
-
require 'on_container/load_env_secrets' # Load secrets injected by Kubernetes/Swarm
|
69
|
-
|
70
68
|
require 'bundler/setup' # Set up gems listed in the Gemfile.
|
71
69
|
require 'bootsnap/setup' # Speed up boot time by caching expensive operations.
|
70
|
+
|
71
|
+
# Load swarm/gcp secrets to ENV:
|
72
|
+
require 'on_container/load_env_secrets'
|
73
|
+
```
|
74
|
+
|
75
|
+
#### Loading Google Cloud Secret Manager secrets into ENV
|
76
|
+
|
77
|
+
If you require loading YAML data stored at [Google Cloud Secret Manager](https://cloud.google.com/secret-manager),
|
78
|
+
you will require the following steps:
|
79
|
+
|
80
|
+
1. Install the `google-cloud-secret_manager` gem
|
81
|
+
|
82
|
+
On your gemfile:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
# Read secrets from Google Cloud Secret Manager
|
86
|
+
gem 'google-cloud-secret_manager', '~> 1.0'
|
87
|
+
```
|
88
|
+
|
89
|
+
2. Require it at `config/boot.rb`
|
90
|
+
|
91
|
+
On `config/boot`, right before requiring `on_container/load_env_secrets`:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
# Require Google Cloud Secret Manager to enable 'on_container/load_env_secrets'
|
95
|
+
# loading secrets from GCP to ENV:
|
96
|
+
require 'google/cloud/secret_manager'
|
97
|
+
|
98
|
+
# Load swarm/gcp secrets to ENV:
|
99
|
+
require 'on_container/load_env_secrets'
|
100
|
+
```
|
101
|
+
|
102
|
+
3. Make sure your app has google cloud credentials configured, and have access
|
103
|
+
to the secrets your'e planning to use.
|
104
|
+
|
105
|
+
4. Configure any number of environment variables ending with
|
106
|
+
`_GOOGLE_CLOUD_SECRET`, each containing the secret you want to load. In the
|
107
|
+
following example, the command to deploy an image into Google Cloud Run:
|
108
|
+
|
109
|
+
```bash
|
110
|
+
gcloud run deploy my-demo-app \
|
111
|
+
--platform "managed" \
|
112
|
+
--region us-central1 \
|
113
|
+
--allow-unauthenticated \
|
114
|
+
--set-env-vars A_GOOGLE_CLOUD_SECRET=my-super-secret \
|
115
|
+
--set-env-vars ANOTHER_GOOGLE_CLOUD_SECRET=project/another-project/secrets/another-secret/versions/1 \
|
116
|
+
--service-account=my-demo-service-account@google-cloud \
|
117
|
+
--image gcr.io/my-demo-project/my-demo-app:latest
|
72
118
|
```
|
119
|
+
#### Inserting credentials into URL environment variables
|
73
120
|
|
74
121
|
The `on_container/load_env_secrets` also merges any credential available in environment variables into any matching
|
75
122
|
`_URL` environment variable. For example, consider the following environment variables:
|
@@ -96,7 +143,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
96
143
|
|
97
144
|
## Contributing
|
98
145
|
|
99
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/on_container. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/on_container/blob/
|
146
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/on_container. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/on_container/blob/main/CODE_OF_CONDUCT.md).
|
100
147
|
|
101
148
|
|
102
149
|
## License
|
@@ -105,4 +152,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
105
152
|
|
106
153
|
## Code of Conduct
|
107
154
|
|
108
|
-
Everyone interacting in the OnContainer project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/on_container/blob/
|
155
|
+
Everyone interacting in the OnContainer project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/on_container/blob/main/CODE_OF_CONDUCT.md).
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OnContainer
|
4
|
+
module Common
|
5
|
+
module Performable
|
6
|
+
def self.included(base)
|
7
|
+
base.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def perform!(*args, **kargs)
|
12
|
+
new(*args, **kargs).perform!
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'on_container/common/performable'
|
4
|
+
|
5
|
+
module OnContainer
|
6
|
+
module Common
|
7
|
+
module SafePerformable
|
8
|
+
def self.included(base)
|
9
|
+
base.include OnContainer::Common::Performable
|
10
|
+
base.extend ClassMethods
|
11
|
+
end
|
12
|
+
|
13
|
+
def perform(*args, **kargs)
|
14
|
+
perform!(*args, **kargs)
|
15
|
+
rescue
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
def perform(*args, **kargs)
|
21
|
+
new(*args, **kargs).perform
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,50 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
#
|
4
|
-
# variables:
|
5
|
-
|
6
|
-
require 'active_support'
|
7
|
-
require 'active_support/core_ext/object'
|
8
|
-
|
9
|
-
# Process only a known list of env vars that can filled by reading a file (i.e.
|
10
|
-
# a docker secret):
|
11
|
-
Dir["#{ENV.fetch('SECRETS_PATH', '/run/secrets/')}*"].each do |secret_filepath|
|
12
|
-
next unless File.file?(secret_filepath)
|
13
|
-
|
14
|
-
secret_envvarname = File.basename(secret_filepath, '.*').upcase
|
15
|
-
|
16
|
-
# Skip if variable is already set - already-set variables have precedence over
|
17
|
-
# the secret files:
|
18
|
-
next if ENV.key?(secret_envvarname) && ENV[secret_envvarname].present?
|
19
|
-
|
20
|
-
ENV[secret_envvarname] = File.read(secret_filepath).strip
|
21
|
-
end
|
22
|
-
|
23
|
-
# For each *_URL environment variable where there's also a *_(USER|USERNAME) or
|
24
|
-
# *_(PASS|PASSWORD), update the URL environment variable with the given
|
25
|
-
# credentials. For example:
|
3
|
+
# This script achieves a list of secret loading & processing:
|
26
4
|
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
5
|
+
# 1. Loads secrets from Google Cloud Secret Manager to ENV, if configured.
|
6
|
+
# 2. Reads files in a configured Folder, and loads them into ENV variables.
|
7
|
+
# 3. Processes "*_URL" env vars, adding their respective "*_USER" and "*_PASS".
|
30
8
|
#
|
31
|
-
#
|
32
|
-
# DATABASE_URL = postgres://lalito:lepass@postgres:5432/demo_production
|
33
|
-
require 'uri' if (url_keys = ENV.keys.select { |key| key =~ /_URL/ }).any?
|
34
|
-
|
35
|
-
url_keys.each do |url_key|
|
36
|
-
credential_pattern_string = url_key.gsub('_URL', '_(USER(NAME)?|PASS(WORD)?)')
|
37
|
-
credential_pattern = Regexp.new "\\A#{credential_pattern_string}\\z"
|
38
|
-
credential_keys = ENV.keys.select { |key| key =~ credential_pattern }
|
39
|
-
next unless credential_keys.any?
|
40
|
-
|
41
|
-
uri = URI(ENV[url_key])
|
42
|
-
username = URI.encode_www_form_component ENV[credential_keys.detect { |key| key =~ /USER/ }]
|
43
|
-
password = URI.encode_www_form_component ENV[credential_keys.detect { |key| key =~ /PASS/ }]
|
44
|
-
|
45
|
-
uri.user = username if username
|
46
|
-
uri.password = password if password
|
47
|
-
ENV[url_key] = uri.to_s
|
48
|
-
end
|
9
|
+
# - See https://github.com/IcaliaLabs/on-container-for-ruby#loading-secrets-into-environment-variables
|
49
10
|
|
50
|
-
|
11
|
+
require 'on_container/secrets/env_loader'
|
12
|
+
OnContainer::Secrets::EnvLoader.perform!
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'on_container/common/safe_performable'
|
4
|
+
require 'on_container/secrets/google_cloud/env_loader'
|
5
|
+
require 'on_container/secrets/mounted_files/env_loader'
|
6
|
+
require 'on_container/secrets/url_variable_processor'
|
7
|
+
|
8
|
+
module OnContainer
|
9
|
+
module Secrets
|
10
|
+
#= EnvLoader
|
11
|
+
#
|
12
|
+
# Reads the specified secret paths (i.e. Docker Secrets) into environment
|
13
|
+
# variables:
|
14
|
+
class EnvLoader
|
15
|
+
include OnContainer::Common::SafePerformable
|
16
|
+
|
17
|
+
def perform!
|
18
|
+
load_secrets_from_google_cloud if google_cloud_secrets?
|
19
|
+
load_secrets_from_mounted_files
|
20
|
+
process_url_variables
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def google_cloud_secrets?
|
27
|
+
OnContainer::Secrets::GoogleCloud::EnvLoader.secret_manager?
|
28
|
+
end
|
29
|
+
|
30
|
+
def load_secrets_from_google_cloud
|
31
|
+
OnContainer::Secrets::GoogleCloud::EnvLoader.perform!
|
32
|
+
end
|
33
|
+
|
34
|
+
def load_secrets_from_mounted_files
|
35
|
+
OnContainer::Secrets::MountedFiles::EnvLoader.perform!
|
36
|
+
end
|
37
|
+
|
38
|
+
def process_url_variables
|
39
|
+
OnContainer::Secrets::UrlVariableProcessor.perform!
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "on_container/secrets/google_cloud/fetcher"
|
4
|
+
|
5
|
+
module OnContainer
|
6
|
+
module Secrets
|
7
|
+
module GoogleCloud
|
8
|
+
class EnvLoader < ServiceBase
|
9
|
+
ENV_KEY_SUFIX = '_GOOGLE_CLOUD_SECRET'
|
10
|
+
|
11
|
+
def env_keys
|
12
|
+
@env_keys ||= ENV.keys.select do |key|
|
13
|
+
key.end_with?(ENV_KEY_SUFIX)
|
14
|
+
end.sort
|
15
|
+
end
|
16
|
+
|
17
|
+
def env_keys?
|
18
|
+
env_keys.any?
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.secret_manager?
|
22
|
+
defined?(Google::Cloud::SecretManager) == 'constant'
|
23
|
+
end
|
24
|
+
|
25
|
+
def secret_manager?
|
26
|
+
self.class.secret_manager?
|
27
|
+
end
|
28
|
+
|
29
|
+
def perform!
|
30
|
+
return unless env_keys? && secret_manager?
|
31
|
+
|
32
|
+
env_keys.each do |key|
|
33
|
+
ENV.merge! Fetcher.perform! ENV[key], client: client
|
34
|
+
end
|
35
|
+
|
36
|
+
true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'on_container/secrets/google_cloud/service_base'
|
5
|
+
|
6
|
+
module OnContainer
|
7
|
+
module Secrets
|
8
|
+
module GoogleCloud
|
9
|
+
class Fetcher < ServiceBase
|
10
|
+
PROJECT_PATTERN = %r{\Aprojects\/(\w+)\/.*}.freeze
|
11
|
+
SECRET_NAME_PATTERN = %r{secrets\/([\w-]+)\/?}.freeze
|
12
|
+
SECRET_VERSION_PATTERN = %r{versions\/(\d+|latest)\z}.freeze
|
13
|
+
|
14
|
+
attr_reader :project, :secret_name, :secret_version
|
15
|
+
|
16
|
+
def initialize(given_key, client: nil)
|
17
|
+
@client = client
|
18
|
+
@project = extract_project given_key
|
19
|
+
@secret_version = extract_secret_version given_key
|
20
|
+
@secret_name = extract_secret_name given_key
|
21
|
+
end
|
22
|
+
|
23
|
+
def perform!
|
24
|
+
# Build the resource name of the secret version.
|
25
|
+
name = client.secret_version_path project: @project,
|
26
|
+
secret: @secret_name,
|
27
|
+
secret_version: @secret_version
|
28
|
+
|
29
|
+
version = client.access_secret_version name: name
|
30
|
+
|
31
|
+
YAML.load version.payload.data
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def default_project
|
37
|
+
ENV['GOOGLE_CLOUD_PROJECT']
|
38
|
+
end
|
39
|
+
|
40
|
+
def extract_project(given_key)
|
41
|
+
match = given_key.match(PROJECT_PATTERN)
|
42
|
+
return default_project unless match
|
43
|
+
|
44
|
+
match.captures.first
|
45
|
+
end
|
46
|
+
|
47
|
+
def extract_secret_version(given_key)
|
48
|
+
match = given_key.match(SECRET_VERSION_PATTERN)
|
49
|
+
return 'latest' unless match
|
50
|
+
|
51
|
+
match.captures.first
|
52
|
+
end
|
53
|
+
|
54
|
+
def extract_secret_name(given_key)
|
55
|
+
given_key
|
56
|
+
.sub("projects/#{@project}/secrets/", '')
|
57
|
+
.sub("/versions/#{@secret_version}", '')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "on_container/common/safe_performable"
|
4
|
+
|
5
|
+
module OnContainer
|
6
|
+
module Secrets
|
7
|
+
module GoogleCloud
|
8
|
+
class ServiceBase
|
9
|
+
include OnContainer::Common::SafePerformable
|
10
|
+
|
11
|
+
attr_reader :client
|
12
|
+
|
13
|
+
def client
|
14
|
+
@client ||= Google::Cloud::SecretManager.secret_manager_service
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/core_ext/object'
|
5
|
+
require 'on_container/common/safe_performable'
|
6
|
+
|
7
|
+
module OnContainer
|
8
|
+
module Secrets
|
9
|
+
module MountedFiles
|
10
|
+
class EnvLoader
|
11
|
+
include OnContainer::Common::SafePerformable
|
12
|
+
|
13
|
+
def perform!
|
14
|
+
setup_secrets_path
|
15
|
+
scan_secrets_path_for_files
|
16
|
+
load_secret_files_to_env_vars
|
17
|
+
end
|
18
|
+
|
19
|
+
def secrets_path
|
20
|
+
@secrets_path ||= ENV.fetch('SECRETS_PATH', '/run/secrets')
|
21
|
+
end
|
22
|
+
|
23
|
+
def secret_mounted_file_paths
|
24
|
+
@secret_mounted_file_paths ||= Dir["#{secrets_path}/**/*"]
|
25
|
+
.map { |path| Pathname.new(path) }
|
26
|
+
.select(&:file?)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
alias setup_secrets_path secrets_path
|
32
|
+
alias scan_secrets_path_for_files secret_mounted_file_paths
|
33
|
+
|
34
|
+
def load_secret_files_to_env_vars
|
35
|
+
return if @already_loaded
|
36
|
+
|
37
|
+
secret_mounted_file_paths
|
38
|
+
.each { |file_path| load_secret_file_to_env_var(file_path) }
|
39
|
+
|
40
|
+
@already_loaded = true
|
41
|
+
end
|
42
|
+
|
43
|
+
def load_secret_file_to_env_var(file_path)
|
44
|
+
env_var_name = file_path.basename('.*').to_s.upcase
|
45
|
+
|
46
|
+
# Skip if variable is already set - already-set variables have
|
47
|
+
# precedence over the secret files:
|
48
|
+
return if ENV.key?(env_var_name) && ENV[env_var_name].present?
|
49
|
+
|
50
|
+
contents = file_path.read.strip
|
51
|
+
|
52
|
+
# TODO: Do not load if content has null bytes
|
53
|
+
ENV[env_var_name] = file_path.read.strip
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'on_container/common/safe_performable'
|
4
|
+
|
5
|
+
module OnContainer
|
6
|
+
module Secrets
|
7
|
+
#= UrlVariableProcessor
|
8
|
+
#
|
9
|
+
# For each *_URL environment variable where there's also a *_(USER|USERNAME)
|
10
|
+
# or *_(PASS|PASSWORD), updates the URL environment variable with the given
|
11
|
+
# credentials. For example:
|
12
|
+
#
|
13
|
+
# DATABASE_URL: postgres://postgres:5432/demo_production
|
14
|
+
# DATABASE_USERNAME: lalito
|
15
|
+
# DATABASE_PASSWORD: lepass
|
16
|
+
#
|
17
|
+
# Results in the following updated DATABASE_URL:
|
18
|
+
# DATABASE_URL = postgres://lalito:lepass@postgres:5432/demo_production
|
19
|
+
class UrlVariableProcessor
|
20
|
+
include OnContainer::Common::SafePerformable
|
21
|
+
|
22
|
+
def perform!
|
23
|
+
require_uri_module if url_keys?
|
24
|
+
process_credential_keys
|
25
|
+
end
|
26
|
+
|
27
|
+
def url_keys
|
28
|
+
@url_keys ||= ENV.keys.select { |key| key =~ /_URL/ }
|
29
|
+
end
|
30
|
+
|
31
|
+
def url_keys?
|
32
|
+
url_keys.any?
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def process_credential_keys
|
38
|
+
url_keys.each { |url_key| process_credential_keys_for(url_key) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def require_uri_module
|
42
|
+
require 'uri'
|
43
|
+
end
|
44
|
+
|
45
|
+
def credential_keys_for(url_key)
|
46
|
+
credential_pattern_string = url_key
|
47
|
+
.gsub('_URL', '_(USER(NAME)?|PASS(WORD)?)')
|
48
|
+
|
49
|
+
credential_pattern = Regexp.new "\\A#{credential_pattern_string}\\z"
|
50
|
+
ENV.keys.select { |key| key =~ credential_pattern }
|
51
|
+
end
|
52
|
+
|
53
|
+
def process_credential_keys_for(url_key)
|
54
|
+
return unless (credential_keys = credential_keys_for(url_key)).any?
|
55
|
+
|
56
|
+
uri = URI(ENV[url_key])
|
57
|
+
|
58
|
+
# Reverse sorting will place "*_USER" before "*_PASS":
|
59
|
+
credential_keys.sort.reverse.each do |credential_key|
|
60
|
+
credential_value = URI.encode_www_form_component ENV[credential_key]
|
61
|
+
case credential_key
|
62
|
+
when /USER/ then uri.user = credential_value
|
63
|
+
when /PASS/ then uri.password = credential_value
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
ENV[url_key] = uri.to_s
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/on_container/version.rb
CHANGED
data/on_container.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
|
|
16
16
|
|
17
17
|
spec.metadata['homepage_uri'] = spec.homepage
|
18
18
|
spec.metadata['source_code_uri'] = spec.homepage
|
19
|
-
spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/
|
19
|
+
spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
20
20
|
|
21
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
22
|
spec.files = `git ls-files -- lib/* *.md *.gemspec *.txt Rakefile`.split("\n")
|
@@ -24,5 +24,6 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.bindir = 'exe'
|
25
25
|
spec.require_paths = ['lib']
|
26
26
|
|
27
|
-
spec.
|
27
|
+
spec.add_runtime_dependency 'activesupport', '>= 4'
|
28
|
+
spec.add_development_dependency 'bundler', '~> 2.1'
|
28
29
|
end
|
metadata
CHANGED
@@ -1,29 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: on_container
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roberto Quintanilla
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
16
30
|
requirements:
|
17
31
|
- - "~>"
|
18
32
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1
|
33
|
+
version: '2.1'
|
20
34
|
type: :development
|
21
35
|
prerelease: false
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
23
37
|
requirements:
|
24
38
|
- - "~>"
|
25
39
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1
|
40
|
+
version: '2.1'
|
27
41
|
description: A small collection of scripts and routines to help ruby development within
|
28
42
|
containers
|
29
43
|
email:
|
@@ -38,6 +52,8 @@ files:
|
|
38
52
|
- README.md
|
39
53
|
- Rakefile
|
40
54
|
- lib/on_container.rb
|
55
|
+
- lib/on_container/common/performable.rb
|
56
|
+
- lib/on_container/common/safe_performable.rb
|
41
57
|
- lib/on_container/dev/active_record_ops.rb
|
42
58
|
- lib/on_container/dev/bundler_ops.rb
|
43
59
|
- lib/on_container/dev/container_command_ops.rb
|
@@ -47,12 +63,14 @@ files:
|
|
47
63
|
- lib/on_container/dev/setup_ops.rb
|
48
64
|
- lib/on_container/load_env_secrets.rb
|
49
65
|
- lib/on_container/ops/service_connection_checks.rb
|
50
|
-
- lib/on_container/
|
66
|
+
- lib/on_container/secrets/env_loader.rb
|
67
|
+
- lib/on_container/secrets/google_cloud/env_loader.rb
|
68
|
+
- lib/on_container/secrets/google_cloud/fetcher.rb
|
69
|
+
- lib/on_container/secrets/google_cloud/service_base.rb
|
70
|
+
- lib/on_container/secrets/mounted_files/env_loader.rb
|
71
|
+
- lib/on_container/secrets/url_variable_processor.rb
|
51
72
|
- lib/on_container/version.rb
|
52
73
|
- on_container.gemspec
|
53
|
-
- spec/on_container_spec.rb
|
54
|
-
- spec/spec_helper.rb
|
55
|
-
- spec/step_down_from_root_spec.rb
|
56
74
|
homepage: https://github.com/IcaliaLabs/on-container-for-ruby
|
57
75
|
licenses:
|
58
76
|
- MIT
|
@@ -60,7 +78,7 @@ metadata:
|
|
60
78
|
allowed_push_host: https://rubygems.org
|
61
79
|
homepage_uri: https://github.com/IcaliaLabs/on-container-for-ruby
|
62
80
|
source_code_uri: https://github.com/IcaliaLabs/on-container-for-ruby
|
63
|
-
changelog_uri: https://github.com/IcaliaLabs/on-container-for-ruby/blob/
|
81
|
+
changelog_uri: https://github.com/IcaliaLabs/on-container-for-ruby/blob/main/CHANGELOG.md
|
64
82
|
post_install_message:
|
65
83
|
rdoc_options: []
|
66
84
|
require_paths:
|
@@ -76,13 +94,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
94
|
- !ruby/object:Gem::Version
|
77
95
|
version: '0'
|
78
96
|
requirements: []
|
79
|
-
|
80
|
-
rubygems_version: 2.5.2.3
|
97
|
+
rubygems_version: 3.1.4
|
81
98
|
signing_key:
|
82
99
|
specification_version: 4
|
83
100
|
summary: A small collection of scripts and routines to help ruby development within
|
84
101
|
containers
|
85
|
-
test_files:
|
86
|
-
- spec/on_container_spec.rb
|
87
|
-
- spec/spec_helper.rb
|
88
|
-
- spec/step_down_from_root_spec.rb
|
102
|
+
test_files: []
|
@@ -1,55 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'etc'
|
4
|
-
|
5
|
-
module OnContainer
|
6
|
-
class StepDownFromRoot
|
7
|
-
attr_reader :curent_user, :target_user
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
@curent_user = Etc.getpwuid
|
11
|
-
end
|
12
|
-
|
13
|
-
def target_user
|
14
|
-
@target_user ||= Etc.getpwuid(developer_uid)
|
15
|
-
end
|
16
|
-
|
17
|
-
def perform
|
18
|
-
return unless root_user?
|
19
|
-
return warn_no_developer_uid unless developer_uid?
|
20
|
-
|
21
|
-
switch_to_developer_user
|
22
|
-
end
|
23
|
-
|
24
|
-
def root_user?
|
25
|
-
curent_user.name == 'root'
|
26
|
-
end
|
27
|
-
|
28
|
-
def developer_uid?
|
29
|
-
developer_uid > 0
|
30
|
-
end
|
31
|
-
|
32
|
-
def developer_uid
|
33
|
-
@developer_uid ||= ENV.fetch('DEVELOPER_UID', '').to_i
|
34
|
-
end
|
35
|
-
|
36
|
-
protected
|
37
|
-
|
38
|
-
def switch_to_developer_user
|
39
|
-
target_user_name = target_user.name
|
40
|
-
puts "Switching from 'root' user to '#{target_user_name}'..."
|
41
|
-
Kernel.exec 'su-exec', target_user_name, $0, *$*
|
42
|
-
end
|
43
|
-
|
44
|
-
def warn_no_developer_uid
|
45
|
-
puts "The 'DEVELOPER_UID' environment variable is not set... " \
|
46
|
-
'still running as root!'
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.perform
|
50
|
-
new.perform
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
OnContainer::StepDownFromRoot.perform
|
data/spec/on_container_spec.rb
DELETED
data/spec/spec_helper.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
require "bundler/setup"
|
2
|
-
require "on_container"
|
3
|
-
|
4
|
-
RSpec.configure do |config|
|
5
|
-
# Enable flags like --only-failures and --next-failure
|
6
|
-
config.example_status_persistence_file_path = ".rspec_status"
|
7
|
-
|
8
|
-
# Disable RSpec exposing methods globally on `Module` and `main`
|
9
|
-
config.disable_monkey_patching!
|
10
|
-
|
11
|
-
config.expect_with :rspec do |c|
|
12
|
-
c.syntax = :expect
|
13
|
-
end
|
14
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'on_container/step_down_from_root'
|
4
|
-
|
5
|
-
RSpec.describe OnContainer::StepDownFromRoot do
|
6
|
-
let(:root_user) do
|
7
|
-
instance_double "struct Etc::Passwd",
|
8
|
-
name: 'root',
|
9
|
-
passwd: 'x',
|
10
|
-
uid: 0,
|
11
|
-
gid: 0,
|
12
|
-
gecos: 'root',
|
13
|
-
dir: '/root',
|
14
|
-
shell: '/bin/bash'
|
15
|
-
end
|
16
|
-
|
17
|
-
let(:developer_user) do
|
18
|
-
instance_double "struct Etc::Passwd",
|
19
|
-
name: 'developer',
|
20
|
-
passwd: 'x',
|
21
|
-
uid: 1000,
|
22
|
-
gid: 1000,
|
23
|
-
gecos: 'Developer User,,,',
|
24
|
-
dir: '/usr/src',
|
25
|
-
shell: '/bin/bash'
|
26
|
-
end
|
27
|
-
|
28
|
-
let(:example_current_user) { root_user }
|
29
|
-
let(:example_target_user) { developer_user }
|
30
|
-
let(:example_developer_uid) { '1000' }
|
31
|
-
|
32
|
-
before do
|
33
|
-
allow(ENV).to receive(:fetch).with('DEVELOPER_UID', '') { example_developer_uid }
|
34
|
-
allow(Etc).to receive(:getpwuid) { example_current_user }
|
35
|
-
allow(Etc).to receive(:getpwuid).with(example_target_user.uid) { example_target_user }
|
36
|
-
allow(Kernel).to receive(:exec).with('su-exec', example_target_user.name, any_args)
|
37
|
-
end
|
38
|
-
|
39
|
-
describe '#perform' do
|
40
|
-
it 'changes to the target user' do
|
41
|
-
subject.perform
|
42
|
-
expect(Kernel).to have_received(:exec).with 'su-exec', example_target_user.name, any_args
|
43
|
-
end
|
44
|
-
|
45
|
-
context 'without a developer uid' do
|
46
|
-
let(:example_developer_uid) { '' }
|
47
|
-
|
48
|
-
example 'does not change the current user' do
|
49
|
-
subject.perform
|
50
|
-
expect(Kernel).not_to have_received(:exec)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
context 'when not as root' do
|
55
|
-
let(:example_current_user) { developer_user }
|
56
|
-
|
57
|
-
example 'does not change the current user' do
|
58
|
-
subject.perform
|
59
|
-
expect(Kernel).not_to have_received(:exec)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|