opsup 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0698811d56d95cb68cd1e4bc1575bacaff650c46ed2e22e4ccb84a5d2196a769'
4
- data.tar.gz: 4e65ac9d6515e35a4c5f71ed72bacda9bb17c7b188b3dd0412a76344bc4403bc
3
+ metadata.gz: 8030ca85d3e6f53108fdda9754e8aca25918716218f0bb72d0bb3c48dcbbeb8b
4
+ data.tar.gz: 62a00a6858cb1cb044d0f4f78d7f1fc28f1915819253c28aab6306d4e0026b42
5
5
  SHA512:
6
- metadata.gz: 6453421cf1c5ef732f00e21c243eb8f324ca8e486564663166fa5a892402ec041cecf1b7f160bc3e207da5c4d64267fa6d3232d7012e8020b008f6a8eb8ec2a7
7
- data.tar.gz: 76af537846ec040151228949946efdd8e3b5c93dff6dee1eedaa127d01a5e8919a8ca45c2aaa62b4f27939a157313afc7be23b37348e41d1b42990dbec939cea
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
@@ -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
 
@@ -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.run_commands(
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
@@ -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 { params(app: Opsup::App, option_builder: Opsup::CLI::OptionBuilder).void }
19
- def initialize(app:, option_builder:)
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
@@ -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
@@ -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
- ).void
29
+ ).returns(StackOperator::CommandDeployer)
29
30
  end
30
- def run_commands(commands, stack_name:, mode:, dryrun: false)
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
- # Currently Opsup deploys only the first app by default.
54
- app = apps.first
55
- instance_ids = instances.map(&:instance_id)
56
-
57
- # Run the commands sequentially.
58
- commands.each do |command|
59
- @logger.info("Running #{command} command in #{mode} mode...")
60
- run_command(
61
- command,
62
- dryrun: dryrun,
63
- mode: mode,
64
- stack: stack,
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
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Opsup
5
- VERSION = '0.0.2'
5
+ VERSION = '0.0.3'
6
6
  end
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.2
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-25 00:00:00.000000000 Z
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: