opsup 0.0.2 → 0.0.3
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 +4 -4
- data/README.md +1 -1
- data/lib/opsup.rb +3 -0
- data/lib/opsup/app.rb +38 -9
- data/lib/opsup/cli.rb +12 -49
- data/lib/opsup/cli/option_builder.rb +77 -0
- data/lib/opsup/config.rb +21 -0
- data/lib/opsup/cookbook_uploader.rb +80 -0
- data/lib/opsup/stack_operator.rb +16 -78
- data/lib/opsup/stack_operator/command_deployer.rb +97 -0
- data/lib/opsup/version.rb +1 -1
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8030ca85d3e6f53108fdda9754e8aca25918716218f0bb72d0bb3c48dcbbeb8b
|
4
|
+
data.tar.gz: 62a00a6858cb1cb044d0f4f78d7f1fc28f1915819253c28aab6306d4e0026b42
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7566237b3ed9fea0b6458f2691632e4ebea7739f128c0cdade3d726a218a86dbebe34d69cc5c224ea20de781599d1c6bfc03565cc22ba6f7f39cf204db4d1abb
|
7
|
+
data.tar.gz: 11cf8034044990549f00d1153983366c0c9dd67acc98b7814480e64e71fa51bff54a9fe474165e59eb20bc47a61dc2cda4be0192ecb3abe258f51c5baa1fad5d
|
data/README.md
CHANGED
@@ -16,6 +16,7 @@ gem install opsup
|
|
16
16
|
|
17
17
|
Currently Opsup can run these commands:
|
18
18
|
|
19
|
+
- `upload_cookbooks` (to S3)
|
19
20
|
- `update_cookbooks`
|
20
21
|
- `setup`
|
21
22
|
- `configure`
|
@@ -31,7 +32,6 @@ Opsup waits until the command completes.
|
|
31
32
|
|
32
33
|
### TODO
|
33
34
|
|
34
|
-
- Add a command to build cookbooks and upload them to S3
|
35
35
|
- Write tests
|
36
36
|
- (maybe) Load options from environment varibles or a configuration file
|
37
37
|
- (maybe) Add commands to create, start, stop, and delete instances
|
data/lib/opsup.rb
CHANGED
@@ -2,12 +2,15 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'sorbet-runtime'
|
5
|
+
require 'aws-sdk-opsworks'
|
6
|
+
require 'aws-sdk-s3'
|
5
7
|
|
6
8
|
require_relative 'opsup/version'
|
7
9
|
require_relative 'opsup/error'
|
8
10
|
require_relative 'opsup/config'
|
9
11
|
require_relative 'opsup/logger'
|
10
12
|
require_relative 'opsup/stack_operator'
|
13
|
+
require_relative 'opsup/cookbook_uploader'
|
11
14
|
require_relative 'opsup/app'
|
12
15
|
require_relative 'opsup/cli'
|
13
16
|
|
data/lib/opsup/app.rb
CHANGED
@@ -1,17 +1,13 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require 'aws-sdk-opsworks'
|
5
|
-
|
6
4
|
module Opsup
|
7
5
|
class App
|
8
6
|
extend T::Sig
|
9
7
|
|
10
8
|
sig { returns(Opsup::App) }
|
11
9
|
def self.create
|
12
|
-
new(
|
13
|
-
logger: Opsup::Logger.instance,
|
14
|
-
)
|
10
|
+
new(logger: Opsup::Logger.instance)
|
15
11
|
end
|
16
12
|
|
17
13
|
sig { params(logger: ::Logger).void }
|
@@ -21,6 +17,7 @@ module Opsup
|
|
21
17
|
|
22
18
|
AVAILABLE_COMMANDS = T.let(
|
23
19
|
%w[
|
20
|
+
upload_cookbooks
|
24
21
|
update_cookbooks
|
25
22
|
setup
|
26
23
|
configure
|
@@ -41,15 +38,20 @@ module Opsup
|
|
41
38
|
@logger.debug("Running #{commands} with #{config.to_h}")
|
42
39
|
|
43
40
|
opsworks = new_opsworks_client(config)
|
44
|
-
opsworks_commands = commands.map { |c| command_to_opsworks_command(c) }
|
45
|
-
|
46
41
|
stack_operator = Opsup::StackOperator.create(opsworks: opsworks)
|
47
|
-
stack_operator.
|
48
|
-
opsworks_commands,
|
42
|
+
deployer = stack_operator.new_deployer(
|
49
43
|
stack_name: config.stack_name,
|
50
44
|
mode: config.running_mode,
|
51
45
|
dryrun: config.dryrun,
|
52
46
|
)
|
47
|
+
|
48
|
+
commands.each do |command|
|
49
|
+
if command == 'upload_cookbooks'
|
50
|
+
upload_cookbooks(config)
|
51
|
+
next
|
52
|
+
end
|
53
|
+
deployer.run_command(command_to_opsworks_command(command))
|
54
|
+
end
|
53
55
|
ensure
|
54
56
|
@logger.warn('Finished in DRYRUN MODE') if config.dryrun
|
55
57
|
end
|
@@ -73,5 +75,32 @@ module Opsup
|
|
73
75
|
private def command_to_opsworks_command(command)
|
74
76
|
command == 'update_cookbooks' ? 'update_custom_cookbooks' : command
|
75
77
|
end
|
78
|
+
|
79
|
+
sig { params(config: Opsup::Config).void }
|
80
|
+
def upload_cookbooks(config)
|
81
|
+
if config.cookbook_url.nil?
|
82
|
+
raise Opsup::Error, 'cookbook URL is required to run upload_cookbooks'
|
83
|
+
end
|
84
|
+
if config.s3_bucket_name.nil?
|
85
|
+
raise Opsup::Error, 'S3 Bucket name is required to run upload_cookbooks'
|
86
|
+
end
|
87
|
+
|
88
|
+
s3_object_config = CookbookUploader::S3ObjectConfig.new(
|
89
|
+
bucket_name: T.must(config.s3_bucket_name),
|
90
|
+
key: "cookbook_#{config.stack_name}.tar.gz",
|
91
|
+
)
|
92
|
+
|
93
|
+
cookbook_uploader = CookbookUploader.create(s3: new_s3_client(config), config: config)
|
94
|
+
cookbook_uploader.build_and_upload(
|
95
|
+
cookbook_url: T.must(config.cookbook_url),
|
96
|
+
s3_object_config: s3_object_config,
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
sig { params(config: Opsup::Config).returns(Aws::S3::Client) }
|
101
|
+
private def new_s3_client(config)
|
102
|
+
creds = Aws::Credentials.new(config.aws_access_key_id, config.aws_secret_access_key)
|
103
|
+
Aws::S3::Client.new(region: config.s3_region, credentials: creds)
|
104
|
+
end
|
76
105
|
end
|
77
106
|
end
|
data/lib/opsup/cli.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'optparse'
|
5
|
+
require_relative 'cli/option_builder'
|
5
6
|
|
6
7
|
module Opsup
|
7
8
|
class CLI
|
@@ -12,13 +13,21 @@ module Opsup
|
|
12
13
|
new(
|
13
14
|
app: Opsup::App.create,
|
14
15
|
option_builder: Opsup::CLI::OptionBuilder.create,
|
16
|
+
env_vars: ENV.to_h,
|
15
17
|
)
|
16
18
|
end
|
17
19
|
|
18
|
-
sig
|
19
|
-
|
20
|
+
sig do
|
21
|
+
params(
|
22
|
+
app: Opsup::App,
|
23
|
+
option_builder: Opsup::CLI::OptionBuilder,
|
24
|
+
env_vars: T::Hash[String, T.nilable(String)],
|
25
|
+
).void
|
26
|
+
end
|
27
|
+
def initialize(app:, option_builder:, env_vars:)
|
20
28
|
@app = T.let(app, Opsup::App)
|
21
29
|
@option_builder = T.let(option_builder, Opsup::CLI::OptionBuilder)
|
30
|
+
@env_vars = T.let(env_vars, T::Hash[String, T.nilable(String)])
|
22
31
|
end
|
23
32
|
|
24
33
|
sig { params(argv: T::Array[String]).returns(T::Boolean) }
|
@@ -26,7 +35,7 @@ module Opsup
|
|
26
35
|
parser = create_parser
|
27
36
|
@option_builder.define_options(parser)
|
28
37
|
|
29
|
-
options =
|
38
|
+
options = @option_builder.options_from_env_vars(@env_vars)
|
30
39
|
begin
|
31
40
|
# It automatically exits with a help message if necessary.
|
32
41
|
commands = parser.parse(argv, into: options)
|
@@ -64,51 +73,5 @@ module Opsup
|
|
64
73
|
BANNER
|
65
74
|
end
|
66
75
|
end
|
67
|
-
|
68
|
-
class OptionBuilder
|
69
|
-
extend T::Sig
|
70
|
-
|
71
|
-
sig { returns(Opsup::CLI::OptionBuilder) }
|
72
|
-
def self.create
|
73
|
-
new
|
74
|
-
end
|
75
|
-
|
76
|
-
DEFAULT_OPSWORKS_REGION = 'ap-northeast-1'
|
77
|
-
|
78
|
-
sig { params(parser: OptionParser).returns(OptionParser) }
|
79
|
-
def define_options(parser)
|
80
|
-
parser.tap do |p|
|
81
|
-
p.on('-s', '--stack STACK_NAME', 'target stack name')
|
82
|
-
p.on('-m', '--mode MODE', Opsup::Config::MODES.join(' | ').to_s)
|
83
|
-
p.on('--aws-cred KEY_ID,SECRET_KEY', 'AWS credentials')
|
84
|
-
p.on('--opsworks-region REGION', "default: #{DEFAULT_OPSWORKS_REGION}")
|
85
|
-
p.on('-d', '--dryrun')
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
sig { params(options: T::Hash[Symbol, T.untyped]).returns(Opsup::Config) }
|
90
|
-
def generate_config(options)
|
91
|
-
%w[stack aws-cred].each do |key|
|
92
|
-
raise Opsup::Error, "missing required option: --#{key}" unless options[key.to_sym]
|
93
|
-
end
|
94
|
-
|
95
|
-
aws_key_id, aws_secret = options[:"aws-cred"].split(',')
|
96
|
-
if aws_key_id.nil? || aws_secret.nil?
|
97
|
-
raise Opsup::Error, "aws-cred must be 'key_id,secret_key' format"
|
98
|
-
end
|
99
|
-
|
100
|
-
mode = options[:mode]&.to_sym
|
101
|
-
raise Opsup::Error, "invalid mode: #{mode}" if mode && !Opsup::Config::MODES.include?(mode)
|
102
|
-
|
103
|
-
Opsup::Config.new(
|
104
|
-
stack_name: options[:stack],
|
105
|
-
aws_access_key_id: aws_key_id,
|
106
|
-
aws_secret_access_key: aws_secret,
|
107
|
-
opsworks_region: options[:"opsworks-region"] || DEFAULT_OPSWORKS_REGION,
|
108
|
-
running_mode: mode,
|
109
|
-
dryrun: options[:dryrun] || false,
|
110
|
-
)
|
111
|
-
end
|
112
|
-
end
|
113
76
|
end
|
114
77
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Opsup
|
5
|
+
class CLI
|
6
|
+
class OptionBuilder
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { returns(Opsup::CLI::OptionBuilder) }
|
10
|
+
def self.create
|
11
|
+
new
|
12
|
+
end
|
13
|
+
|
14
|
+
DEFAULT_AWS_REGION = 'ap-northeast-1'
|
15
|
+
|
16
|
+
sig { params(parser: OptionParser).returns(OptionParser) }
|
17
|
+
def define_options(parser)
|
18
|
+
parser.tap do |p|
|
19
|
+
p.on('-s', '--stack STACK_NAME', 'target stack name')
|
20
|
+
p.on('-m', '--mode MODE', Opsup::Config::MODES.join(' | ').to_s)
|
21
|
+
p.on('--aws-cred KEY_ID,SECRET_KEY', 'AWS credentials')
|
22
|
+
p.on('--opsworks-region REGION', "default: #{DEFAULT_AWS_REGION}")
|
23
|
+
p.on('--cookbook-url URL', 'URL of cookbook to upload')
|
24
|
+
p.on('--s3-bucket NAME', 'S3 bucket name for cookbooks')
|
25
|
+
p.on('-d', '--dryrun')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
sig do
|
30
|
+
params(
|
31
|
+
env_vars: T::Hash[String, T.nilable(String)],
|
32
|
+
).returns(T::Hash[Symbol, T.untyped])
|
33
|
+
end
|
34
|
+
def options_from_env_vars(env_vars)
|
35
|
+
[
|
36
|
+
%w[stack STACK],
|
37
|
+
%w[mode MODE],
|
38
|
+
%w[aws-cred AWS_CRED],
|
39
|
+
%w[opsworks-region OPSWORKS_REGION],
|
40
|
+
%w[cookbook-url COOKBOOK_URL],
|
41
|
+
%w[s3-bucket S3_BUCKET],
|
42
|
+
%w[dryrun DRYRUN],
|
43
|
+
].each_with_object({}) do |(key, env_key), obj|
|
44
|
+
value = env_vars["OPSUP_#{env_key}"]
|
45
|
+
obj[key.to_sym] = value if value
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
sig { params(options: T::Hash[Symbol, T.untyped]).returns(Opsup::Config) }
|
50
|
+
def generate_config(options)
|
51
|
+
%w[stack aws-cred].each do |key|
|
52
|
+
raise Opsup::Error, "missing required option: --#{key}" unless options[key.to_sym]
|
53
|
+
end
|
54
|
+
|
55
|
+
aws_key_id, aws_secret = options[:"aws-cred"].split(',')
|
56
|
+
if aws_key_id.nil? || aws_secret.nil?
|
57
|
+
raise Opsup::Error, "aws-cred must be 'key_id,secret_key' format"
|
58
|
+
end
|
59
|
+
|
60
|
+
mode = options[:mode]&.to_sym
|
61
|
+
raise Opsup::Error, "invalid mode: #{mode}" if mode && !Opsup::Config::MODES.include?(mode)
|
62
|
+
|
63
|
+
Opsup::Config.new(
|
64
|
+
stack_name: options[:stack],
|
65
|
+
aws_access_key_id: aws_key_id,
|
66
|
+
aws_secret_access_key: aws_secret,
|
67
|
+
opsworks_region: options[:"opsworks-region"] || DEFAULT_AWS_REGION,
|
68
|
+
cookbook_url: options[:"cookbook-url"],
|
69
|
+
s3_bucket_name: options[:"s3-bucket"],
|
70
|
+
s3_region: options[:"s3-region"] || DEFAULT_AWS_REGION,
|
71
|
+
running_mode: mode,
|
72
|
+
dryrun: options[:dryrun] || false,
|
73
|
+
)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/opsup/config.rb
CHANGED
@@ -17,6 +17,15 @@ module Opsup
|
|
17
17
|
sig { returns(String) }
|
18
18
|
attr_reader :opsworks_region
|
19
19
|
|
20
|
+
sig { returns(T.nilable(String)) }
|
21
|
+
attr_reader :cookbook_url
|
22
|
+
|
23
|
+
sig { returns(T.nilable(String)) }
|
24
|
+
attr_reader :s3_bucket_name
|
25
|
+
|
26
|
+
sig { returns(T.nilable(String)) }
|
27
|
+
attr_reader :s3_region
|
28
|
+
|
20
29
|
sig { returns(Symbol) }
|
21
30
|
attr_reader :running_mode
|
22
31
|
|
@@ -31,6 +40,9 @@ module Opsup
|
|
31
40
|
aws_access_key_id: String,
|
32
41
|
aws_secret_access_key: String,
|
33
42
|
opsworks_region: String,
|
43
|
+
cookbook_url: T.nilable(String),
|
44
|
+
s3_bucket_name: T.nilable(String),
|
45
|
+
s3_region: T.nilable(String),
|
34
46
|
running_mode: T.nilable(Symbol),
|
35
47
|
dryrun: T::Boolean,
|
36
48
|
).void
|
@@ -40,6 +52,9 @@ module Opsup
|
|
40
52
|
aws_access_key_id:,
|
41
53
|
aws_secret_access_key:,
|
42
54
|
opsworks_region:,
|
55
|
+
cookbook_url:,
|
56
|
+
s3_bucket_name: nil,
|
57
|
+
s3_region: nil,
|
43
58
|
running_mode: nil,
|
44
59
|
dryrun: false
|
45
60
|
)
|
@@ -47,6 +62,9 @@ module Opsup
|
|
47
62
|
@aws_access_key_id = T.let(aws_access_key_id, String)
|
48
63
|
@aws_secret_access_key = T.let(aws_secret_access_key, String)
|
49
64
|
@opsworks_region = T.let(opsworks_region, String)
|
65
|
+
@cookbook_url = T.let(cookbook_url, T.nilable(String))
|
66
|
+
@s3_bucket_name = T.let(s3_bucket_name, T.nilable(String))
|
67
|
+
@s3_region = T.let(s3_region, T.nilable(String))
|
50
68
|
@running_mode = T.let(running_mode || MODES.fetch(0), Symbol)
|
51
69
|
@dryrun = T.let(dryrun, T::Boolean)
|
52
70
|
end
|
@@ -58,6 +76,9 @@ module Opsup
|
|
58
76
|
aws_access_key_id: aws_access_key_id,
|
59
77
|
aws_secret_access_key: aws_secret_access_key,
|
60
78
|
opsworks_region: opsworks_region,
|
79
|
+
cookbook_url: cookbook_url,
|
80
|
+
s3_bucket_name: s3_bucket_name,
|
81
|
+
s3_region: s3_region,
|
61
82
|
running_mode: running_mode,
|
62
83
|
dryrun: dryrun,
|
63
84
|
}
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Opsup
|
5
|
+
class CookbookUploader
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
class S3ObjectConfig < T::Struct
|
9
|
+
const :bucket_name, String
|
10
|
+
const :key, String
|
11
|
+
end
|
12
|
+
|
13
|
+
sig do
|
14
|
+
params(
|
15
|
+
s3: Aws::S3::Client,
|
16
|
+
config: Opsup::Config,
|
17
|
+
).returns(Opsup::CookbookUploader)
|
18
|
+
end
|
19
|
+
def self.create(s3:, config:)
|
20
|
+
new(s3: s3, config: config, logger: Opsup::Logger.instance)
|
21
|
+
end
|
22
|
+
|
23
|
+
sig do
|
24
|
+
params(
|
25
|
+
s3: Aws::S3::Client,
|
26
|
+
config: Opsup::Config,
|
27
|
+
logger: ::Logger,
|
28
|
+
).void
|
29
|
+
end
|
30
|
+
def initialize(s3:, config:, logger:)
|
31
|
+
@s3 = T.let(s3, Aws::S3::Client)
|
32
|
+
@config = T.let(config, Opsup::Config)
|
33
|
+
@logger = T.let(logger, ::Logger)
|
34
|
+
end
|
35
|
+
|
36
|
+
sig { params(cookbook_url: String, s3_object_config: S3ObjectConfig).void }
|
37
|
+
def build_and_upload(cookbook_url:, s3_object_config:)
|
38
|
+
Dir.mktmpdir do |work_dir|
|
39
|
+
clone(work_dir, cookbook_url)
|
40
|
+
package_path = build(work_dir)
|
41
|
+
upload(package_path, s3_object_config)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
sig { params(cmd: String).void }
|
46
|
+
private def system(cmd)
|
47
|
+
if @config.dryrun
|
48
|
+
@logger.info("(dryrun) #{cmd}")
|
49
|
+
else
|
50
|
+
super(cmd)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
sig { params(dir: String, url: String).void }
|
55
|
+
private def clone(dir, url)
|
56
|
+
system("git clone --depth=1 #{url} #{dir}")
|
57
|
+
end
|
58
|
+
|
59
|
+
sig { params(dir: String).returns(String) }
|
60
|
+
private def build(dir)
|
61
|
+
Dir.chdir(dir) do
|
62
|
+
system('bundle')
|
63
|
+
system('bundle exec berks package built.tar.gz')
|
64
|
+
end
|
65
|
+
"#{dir}/built.tar.gz"
|
66
|
+
end
|
67
|
+
|
68
|
+
sig { params(package_path: String, object_config: S3ObjectConfig).void }
|
69
|
+
private def upload(package_path, object_config)
|
70
|
+
unless @config.dryrun
|
71
|
+
@s3.put_object(
|
72
|
+
bucket: object_config.bucket_name,
|
73
|
+
body: File.open(package_path),
|
74
|
+
key: object_config.key,
|
75
|
+
)
|
76
|
+
end
|
77
|
+
@logger.info("cookbook #{object_config.key} uploaded")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/opsup/stack_operator.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require_relative 'stack_operator/command_deployer'
|
5
|
+
|
4
6
|
module Opsup
|
5
7
|
class StackOperator
|
6
8
|
extend T::Sig
|
@@ -21,13 +23,12 @@ module Opsup
|
|
21
23
|
|
22
24
|
sig do
|
23
25
|
params(
|
24
|
-
commands: T::Array[String],
|
25
26
|
stack_name: String,
|
26
27
|
mode: Symbol,
|
27
28
|
dryrun: T::Boolean,
|
28
|
-
).
|
29
|
+
).returns(StackOperator::CommandDeployer)
|
29
30
|
end
|
30
|
-
def
|
31
|
+
def new_deployer(stack_name:, mode:, dryrun: false)
|
31
32
|
# Find the target stack.
|
32
33
|
@logger.debug('Verifying the specified stack exists...')
|
33
34
|
stacks = @opsworks.describe_stacks.stacks
|
@@ -50,82 +51,19 @@ module Opsup
|
|
50
51
|
"#{instances.size} #{instances.size == 1 ? 'instance is' : 'instances are'} found",
|
51
52
|
)
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
app: app,
|
66
|
-
instance_ids: instance_ids,
|
67
|
-
)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
sig do
|
72
|
-
params(
|
73
|
-
command: String,
|
74
|
-
dryrun: T::Boolean,
|
75
|
-
mode: Symbol,
|
76
|
-
stack: Aws::OpsWorks::Types::Stack,
|
77
|
-
app: Aws::OpsWorks::Types::App,
|
78
|
-
instance_ids: T::Array[String],
|
79
|
-
).void
|
80
|
-
end
|
81
|
-
private def run_command(command, dryrun:, mode:, stack:, app:, instance_ids:)
|
82
|
-
case mode
|
83
|
-
when :parallel
|
84
|
-
@logger.info("Creating single deployment for the #{instance_ids.size} instances...")
|
85
|
-
create_deployment(command, stack, app, instance_ids) unless dryrun
|
86
|
-
when :serial
|
87
|
-
instance_ids.each.with_index do |id, i|
|
88
|
-
@logger.info("Creating deployment for instances[#{i}] (#{id})...")
|
89
|
-
create_deployment(command, stack, app, [id]) unless dryrun
|
90
|
-
end
|
91
|
-
when :one_then_all
|
92
|
-
@logger.info("Creating deployment for the first instance (#{instance_ids[0]})...")
|
93
|
-
create_deployment(command, stack, app, [T.must(instance_ids[0])]) unless dryrun
|
94
|
-
|
95
|
-
rest = T.must(instance_ids[1..-1])
|
96
|
-
if !rest.empty?
|
97
|
-
@logger.info("Creating deployment for the other #{rest.size} instances...")
|
98
|
-
create_deployment(command, stack, app, rest) unless dryrun
|
99
|
-
else
|
100
|
-
@logger.info('No other instances exist.')
|
101
|
-
end
|
102
|
-
else
|
103
|
-
raise "Unknown running mode: #{mode}"
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
sig do
|
108
|
-
params(
|
109
|
-
command: String,
|
110
|
-
stack: Aws::OpsWorks::Types::Stack,
|
111
|
-
app: Aws::OpsWorks::Types::App,
|
112
|
-
instance_ids: T::Array[String],
|
113
|
-
).void
|
114
|
-
end
|
115
|
-
private def create_deployment(command, stack, app, instance_ids)
|
116
|
-
res = @opsworks.create_deployment(
|
117
|
-
stack_id: stack.stack_id,
|
118
|
-
app_id: app.app_id,
|
119
|
-
instance_ids: instance_ids,
|
120
|
-
command: { name: command, args: {} },
|
54
|
+
config = StackOperator::CommandDeployer::Config.new(
|
55
|
+
stack: stack,
|
56
|
+
mode: mode,
|
57
|
+
# Currently Opsup deploys only the first app by default.
|
58
|
+
app: apps.first,
|
59
|
+
instance_ids: instances.map(&:instance_id),
|
60
|
+
dryrun: dryrun,
|
61
|
+
)
|
62
|
+
StackOperator::CommandDeployer.create(
|
63
|
+
config: config,
|
64
|
+
opsworks: @opsworks,
|
65
|
+
logger: @logger,
|
121
66
|
)
|
122
|
-
|
123
|
-
@logger.info("Waiting deployment #{res.deployment_id}...")
|
124
|
-
@opsworks.wait_until(:deployment_successful, {
|
125
|
-
deployment_ids: [res.deployment_id],
|
126
|
-
})
|
127
|
-
|
128
|
-
nil
|
129
67
|
end
|
130
68
|
end
|
131
69
|
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Opsup
|
5
|
+
class StackOperator
|
6
|
+
class CommandDeployer
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
class Config < T::Struct
|
10
|
+
const :stack, Aws::OpsWorks::Types::Stack
|
11
|
+
const :mode, Symbol
|
12
|
+
const :app, Aws::OpsWorks::Types::App
|
13
|
+
const :instance_ids, T::Array[String]
|
14
|
+
const :dryrun, T::Boolean
|
15
|
+
end
|
16
|
+
|
17
|
+
sig do
|
18
|
+
params(
|
19
|
+
config: Config,
|
20
|
+
opsworks: Aws::OpsWorks::Client,
|
21
|
+
logger: ::Logger,
|
22
|
+
).returns(Opsup::StackOperator::CommandDeployer)
|
23
|
+
end
|
24
|
+
def self.create(config:, opsworks:, logger:)
|
25
|
+
new(config: config, opsworks: opsworks, logger: logger)
|
26
|
+
end
|
27
|
+
|
28
|
+
sig do
|
29
|
+
params(
|
30
|
+
config: Config,
|
31
|
+
opsworks: Aws::OpsWorks::Client,
|
32
|
+
logger: ::Logger,
|
33
|
+
).void
|
34
|
+
end
|
35
|
+
def initialize(config:, opsworks:, logger:)
|
36
|
+
@config = T.let(config, Config)
|
37
|
+
@opsworks = T.let(opsworks, Aws::OpsWorks::Client)
|
38
|
+
@logger = T.let(logger, ::Logger)
|
39
|
+
end
|
40
|
+
|
41
|
+
sig { params(command: String).void }
|
42
|
+
def run_command(command)
|
43
|
+
mode = @config.mode
|
44
|
+
instance_ids = @config.instance_ids
|
45
|
+
dryrun = @config.dryrun
|
46
|
+
|
47
|
+
@logger.info("Running #{command} command in #{mode} mode...")
|
48
|
+
|
49
|
+
case mode
|
50
|
+
when :parallel
|
51
|
+
@logger.info("Creating single deployment for the #{instance_ids.size} instances...")
|
52
|
+
create_deployment(command, instance_ids) unless dryrun
|
53
|
+
when :serial
|
54
|
+
instance_ids.each.with_index do |id, i|
|
55
|
+
@logger.info("Creating deployment for instances[#{i}] (#{id})...")
|
56
|
+
create_deployment(command, [id]) unless dryrun
|
57
|
+
end
|
58
|
+
when :one_then_all
|
59
|
+
@logger.info("Creating deployment for the first instance (#{instance_ids[0]})...")
|
60
|
+
create_deployment(command, [T.must(instance_ids[0])]) unless dryrun
|
61
|
+
|
62
|
+
rest = T.must(instance_ids[1..-1])
|
63
|
+
if !rest.empty?
|
64
|
+
@logger.info("Creating deployment for the other #{rest.size} instances...")
|
65
|
+
create_deployment(command, rest) unless dryrun
|
66
|
+
else
|
67
|
+
@logger.info('No other instances exist.')
|
68
|
+
end
|
69
|
+
else
|
70
|
+
raise "Unknown running mode: #{mode}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
sig do
|
75
|
+
params(
|
76
|
+
command: String,
|
77
|
+
instance_ids: T::Array[String],
|
78
|
+
).void
|
79
|
+
end
|
80
|
+
private def create_deployment(command, instance_ids)
|
81
|
+
res = @opsworks.create_deployment(
|
82
|
+
stack_id: @config.stack.stack_id,
|
83
|
+
app_id: @config.app.app_id,
|
84
|
+
instance_ids: instance_ids,
|
85
|
+
command: { name: command, args: {} },
|
86
|
+
)
|
87
|
+
|
88
|
+
@logger.info("Waiting deployment #{res.deployment_id}...")
|
89
|
+
@opsworks.wait_until(:deployment_successful, {
|
90
|
+
deployment_ids: [res.deployment_id],
|
91
|
+
})
|
92
|
+
|
93
|
+
nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/lib/opsup/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opsup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ryym
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-08
|
11
|
+
date: 2019-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-opsworks
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: aws-sdk-s3
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rubocop
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,10 +94,13 @@ files:
|
|
80
94
|
- lib/opsup.rb
|
81
95
|
- lib/opsup/app.rb
|
82
96
|
- lib/opsup/cli.rb
|
97
|
+
- lib/opsup/cli/option_builder.rb
|
83
98
|
- lib/opsup/config.rb
|
99
|
+
- lib/opsup/cookbook_uploader.rb
|
84
100
|
- lib/opsup/error.rb
|
85
101
|
- lib/opsup/logger.rb
|
86
102
|
- lib/opsup/stack_operator.rb
|
103
|
+
- lib/opsup/stack_operator/command_deployer.rb
|
87
104
|
- lib/opsup/version.rb
|
88
105
|
homepage: https://github.com/ryym/opsup
|
89
106
|
licenses:
|