mrsk 0.1.0 → 0.3.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.
@@ -3,26 +3,42 @@ require "active_support/core_ext/string/inquiry"
3
3
  require "active_support/core_ext/module/delegation"
4
4
  require "pathname"
5
5
  require "erb"
6
+ require "mrsk/utils"
6
7
 
7
8
  class Mrsk::Configuration
8
- delegate :service, :image, :servers, :env, :labels, :registry, :builder, to: :config, allow_nil: true
9
+ delegate :service, :image, :servers, :env, :labels, :registry, :builder, to: :raw_config, allow_nil: true
10
+ delegate :argumentize, :argumentize_env_with_secrets, to: Mrsk::Utils
11
+
12
+ attr_accessor :raw_config
9
13
 
10
14
  class << self
11
- def load_file(file)
12
- if file.exist?
13
- new YAML.load(ERB.new(IO.read(file)).result).symbolize_keys
14
- else
15
- raise "Configuration file not found in #{file}"
16
- end
15
+ def create_from(base_config_file, destination: nil, version: "missing")
16
+ new(load_config_file(base_config_file).tap do |config|
17
+ if destination
18
+ config.deep_merge! \
19
+ load_config_file destination_config_file(base_config_file, destination)
20
+ end
21
+ end, version: version)
17
22
  end
18
23
 
19
- def argumentize(argument, attributes)
20
- attributes.flat_map { |k, v| [ argument, "#{k}=#{v}" ] }
21
- end
24
+ private
25
+ def load_config_file(file)
26
+ if file.exist?
27
+ YAML.load(ERB.new(IO.read(file)).result).symbolize_keys
28
+ else
29
+ raise "Configuration file not found in #{file}"
30
+ end
31
+ end
32
+
33
+ def destination_config_file(base_config_file, destination)
34
+ dir, basename = base_config_file.split
35
+ dir.join basename.to_s.remove(".yml") + ".#{destination}.yml"
36
+ end
22
37
  end
23
38
 
24
- def initialize(config, validate: true)
25
- @config = ActiveSupport::InheritableOptions.new(config)
39
+ def initialize(raw_config, version: "missing", validate: true)
40
+ @raw_config = ActiveSupport::InheritableOptions.new(raw_config)
41
+ @version = version
26
42
  ensure_required_keys_present if validate
27
43
  end
28
44
 
@@ -35,36 +51,34 @@ class Mrsk::Configuration
35
51
  roles.detect { |r| r.name == name.to_s }
36
52
  end
37
53
 
38
- def hosts
39
- hosts =
40
- case
41
- when ENV["HOSTS"]
42
- ENV["HOSTS"].split(",")
43
- when ENV["ROLES"]
44
- role_names = ENV["ROLES"].split(",")
45
- roles.select { |r| role_names.include?(r.name) }.flat_map(&:hosts)
46
- else
47
- roles.flat_map(&:hosts)
48
- end
54
+ def accessories
55
+ @accessories ||= raw_config.accessories&.keys&.collect { |name| Mrsk::Configuration::Assessory.new(name, config: self) } || []
56
+ end
49
57
 
50
- if hosts.any?
51
- hosts
52
- else
53
- raise ArgumentError, "No hosts found"
54
- end
58
+ def accessory(name)
59
+ accessories.detect { |a| a.name == name.to_s }
60
+ end
61
+
62
+
63
+ def all_hosts
64
+ roles.flat_map(&:hosts)
55
65
  end
56
66
 
57
- def primary_host
67
+ def primary_web_host
58
68
  role(:web).hosts.first
59
69
  end
60
70
 
71
+ def traefik_hosts
72
+ roles.select(&:running_traefik?).flat_map(&:hosts)
73
+ end
74
+
61
75
 
62
76
  def version
63
- @version ||= ENV["VERSION"] || `git rev-parse HEAD`.strip
77
+ @version
64
78
  end
65
79
 
66
80
  def repository
67
- [ config.registry["server"], image ].compact.join("/")
81
+ [ raw_config.registry["server"], image ].compact.join("/")
68
82
  end
69
83
 
70
84
  def absolute_image
@@ -77,15 +91,23 @@ class Mrsk::Configuration
77
91
 
78
92
 
79
93
  def env_args
80
- if config.env.present?
81
- self.class.argumentize "-e", config.env
94
+ if raw_config.env.present?
95
+ argumentize_env_with_secrets(raw_config.env)
96
+ else
97
+ []
98
+ end
99
+ end
100
+
101
+ def volume_args
102
+ if raw_config.volumes.present?
103
+ argumentize "--volume", raw_config.volumes
82
104
  else
83
105
  []
84
106
  end
85
107
  end
86
108
 
87
109
  def ssh_user
88
- config.ssh_user || "root"
110
+ raw_config.ssh_user || "root"
89
111
  end
90
112
 
91
113
  def ssh_options
@@ -93,26 +115,48 @@ class Mrsk::Configuration
93
115
  end
94
116
 
95
117
  def master_key
96
- ENV["RAILS_MASTER_KEY"] || File.read(Pathname.new(File.expand_path("config/master.key")))
118
+ unless raw_config.skip_master_key
119
+ ENV["RAILS_MASTER_KEY"] || File.read(Pathname.new(File.expand_path("config/master.key")))
120
+ end
97
121
  end
98
122
 
123
+ def to_h
124
+ {
125
+ roles: role_names,
126
+ hosts: all_hosts,
127
+ primary_host: primary_web_host,
128
+ version: version,
129
+ repository: repository,
130
+ absolute_image: absolute_image,
131
+ service_with_version: service_with_version,
132
+ env_args: env_args,
133
+ volume_args: volume_args,
134
+ ssh_options: ssh_options,
135
+ builder: raw_config.builder,
136
+ accessories: raw_config.accessories
137
+ }.compact
138
+ end
99
139
 
100
- private
101
- attr_accessor :config
102
140
 
141
+ private
103
142
  def ensure_required_keys_present
104
- %i[ service image registry ].each do |key|
105
- raise ArgumentError, "Missing required configuration for #{key}" unless config[key].present?
143
+ %i[ service image registry servers ].each do |key|
144
+ raise ArgumentError, "Missing required configuration for #{key}" unless raw_config[key].present?
106
145
  end
107
146
 
108
- %w[ username password ].each do |key|
109
- raise ArgumentError, "Missing required configuration for registry/#{key}" unless config.registry[key].present?
110
- end
147
+ if raw_config.registry["username"].blank?
148
+ raise ArgumentError, "You must specify a username for the registry in config/deploy.yml"
149
+ end
150
+
151
+ if raw_config.registry["password"].blank?
152
+ raise ArgumentError, "You must specify a password for the registry in config/deploy.yml (or set the ENV variable if that's used)"
153
+ end
111
154
  end
112
155
 
113
156
  def role_names
114
- config.servers.is_a?(Array) ? [ "web" ] : config.servers.keys.sort
157
+ raw_config.servers.is_a?(Array) ? [ "web" ] : raw_config.servers.keys.sort
115
158
  end
116
159
  end
117
160
 
118
161
  require "mrsk/configuration/role"
162
+ require "mrsk/configuration/accessory"
@@ -0,0 +1,12 @@
1
+ require "sshkit"
2
+ require "sshkit/dsl"
3
+
4
+ class SSHKit::Backend::Abstract
5
+ def capture_with_info(*args)
6
+ capture(*args, verbosity: Logger::INFO)
7
+ end
8
+
9
+ def puts_by_host(host, output, type: "App")
10
+ puts "#{type} Host: #{host}\n#{output}\n\n"
11
+ end
12
+ end
data/lib/mrsk/utils.rb ADDED
@@ -0,0 +1,29 @@
1
+ module Mrsk::Utils
2
+ extend self
3
+
4
+ # Return a list of shell arguments using the same named argument against the passed attributes (hash or array).
5
+ def argumentize(argument, attributes, redacted: false)
6
+ Array(attributes).flat_map do |k, v|
7
+ if v.present?
8
+ [ argument, redacted ? redact("#{k}=#{v}") : "#{k}=#{v}" ]
9
+ else
10
+ [ argument, k ]
11
+ end
12
+ end
13
+ end
14
+
15
+ # Return a list of shell arguments using the same named argument against the passed attributes,
16
+ # but redacts and expands secrets.
17
+ def argumentize_env_with_secrets(env)
18
+ if (secrets = env["secret"]).present?
19
+ argumentize("-e", secrets.to_h { |key| [ key, ENV.fetch(key) ] }, redacted: true) + argumentize("-e", env["clear"])
20
+ else
21
+ argumentize "-e", env
22
+ end
23
+ end
24
+
25
+ # Copied from SSHKit::Backend::Abstract#redact to be available inside Commands classes
26
+ def redact(arg) # Used in execute_command to hide redact() args a user passes in
27
+ arg.to_s.extend(SSHKit::Redaction) # to_s due to our inability to extend Integer, etc
28
+ end
29
+ end
data/lib/mrsk/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mrsk
2
- VERSION = "0.1.0"
2
+ VERSION = "0.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mrsk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-14 00:00:00.000000000 Z
11
+ date: 2023-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -64,6 +64,7 @@ files:
64
64
  - bin/mrsk
65
65
  - lib/mrsk.rb
66
66
  - lib/mrsk/cli.rb
67
+ - lib/mrsk/cli/accessory.rb
67
68
  - lib/mrsk/cli/app.rb
68
69
  - lib/mrsk/cli/base.rb
69
70
  - lib/mrsk/cli/build.rb
@@ -75,17 +76,23 @@ files:
75
76
  - lib/mrsk/cli/traefik.rb
76
77
  - lib/mrsk/commander.rb
77
78
  - lib/mrsk/commands.rb
79
+ - lib/mrsk/commands/accessory.rb
78
80
  - lib/mrsk/commands/app.rb
79
81
  - lib/mrsk/commands/base.rb
80
82
  - lib/mrsk/commands/builder.rb
83
+ - lib/mrsk/commands/builder/base.rb
81
84
  - lib/mrsk/commands/builder/multiarch.rb
82
85
  - lib/mrsk/commands/builder/multiarch/remote.rb
83
86
  - lib/mrsk/commands/builder/native.rb
87
+ - lib/mrsk/commands/builder/native/remote.rb
84
88
  - lib/mrsk/commands/prune.rb
85
89
  - lib/mrsk/commands/registry.rb
86
90
  - lib/mrsk/commands/traefik.rb
87
91
  - lib/mrsk/configuration.rb
92
+ - lib/mrsk/configuration/accessory.rb
88
93
  - lib/mrsk/configuration/role.rb
94
+ - lib/mrsk/sshkit_with_ext.rb
95
+ - lib/mrsk/utils.rb
89
96
  - lib/mrsk/version.rb
90
97
  homepage: https://github.com/rails/mrsk
91
98
  licenses:
@@ -106,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
113
  - !ruby/object:Gem::Version
107
114
  version: '0'
108
115
  requirements: []
109
- rubygems_version: 3.4.1
116
+ rubygems_version: 3.4.5
110
117
  signing_key:
111
118
  specification_version: 4
112
119
  summary: Deploy Rails apps in containers to servers running Docker with zero downtime.