aws-rails-provisioner 0.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c9de388b6ca4097bd72598adc6fc56011f777731
4
+ data.tar.gz: 9a63b6307132b657f956eeac9e809b481694bfd4
5
+ SHA512:
6
+ metadata.gz: 82b592d28b3182f7801f103a148b4b321312b6eb7f2a273fcec03cce23f4aa081a3f518e07904fef48e1266a7812bb3cf52cd4924088eedb0bd87c87eed404b7
7
+ data.tar.gz: 78b9222a2d4448a89994d1fb47ad52df52603c2f3abfcfe7b1c6737fbd3876004d31ae282f4ebeabee8bf71724092e6b965025305244d795ab40221c086f0b0d
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'optparse'
5
+ require 'fileutils'
6
+
7
+ # load the `aws-rails-provisioner` gem
8
+ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
9
+ require 'aws-rails-provisioner'
10
+
11
+ # setup default options
12
+ options = {
13
+ file_path: 'aws-rails-provisioner.yml'
14
+ }
15
+ options[ARGV[0].to_sym] = {}
16
+
17
+ OptionParser.new do |opts|
18
+
19
+ opts.banner = "Usage: aws-rails-provisioner [command] [options]"
20
+ opts.separator("")
21
+ opts.separator("Commands:")
22
+ opts.separator(" build: generate CDK stacks from aws-rails-provisioner.yml")
23
+ opts.separator(" deploy: deploy generated CDK stacks")
24
+ opts.separator("")
25
+ opts.separator("Options:")
26
+
27
+ svc_name_msg = 'Service name. Changes apply to all services '\
28
+ 'configured under :services if not provided'
29
+ opts.on('-s', '--service SERVICE_NAME', svc_name_msg) do |name|
30
+ options[:service_name] = name.to_sym
31
+ end
32
+
33
+ opts.on('-p', '--profile AWS_PROFILE_NAME', 'Used the indicated AWS profile') do |profile|
34
+ options[:profile] = profile
35
+ end
36
+
37
+ file_path_msg = 'File path to `aws-rails-provisioner.yml`, default to `aws-rails-provisioner.yml`'\
38
+ ' at current directory'
39
+ opts.on('-f', '--file YAML_FILE_PATH', file_path_msg) do |path|
40
+ options[:file_path] = path
41
+ end
42
+
43
+ dir_msg = "Directory where generated code lives, defaults to `cdk-sample`"
44
+ opts.on('-d', '--cdk-directory DIR_PATH', dir_msg) do |dir|
45
+ options[:cdk_dir] = dir
46
+ end
47
+
48
+ # Build
49
+ cicd_msg = "Enable CICD stack generation, use with `build`"
50
+ opts.on('--with-cicd', cicd_msg) do
51
+ if options[:build].nil?
52
+ raise Aws::RailsProvisioner::Errors::InvalidCommandOption.new('build', '--with-cicd')
53
+ end
54
+ options[:build][:enable_cicd] = true
55
+ end
56
+
57
+ # need to consider scenarios for dryrun
58
+ #opts.on('--dryrun') do
59
+ #options[:dryrun] = true
60
+ #end
61
+
62
+ # Deploy
63
+ init_msg = "Deploy the stack that creates VPC and ECS cluster"
64
+ opts.on('--init', init_msg) do
65
+ if options[:deploy].nil?
66
+ raise Aws::RailsProvisioner::Errors::InvalidCommandOption.new('deploy', '--init')
67
+ end
68
+ options[:deploy][:init] = true
69
+ end
70
+
71
+ fargate_msg = "Deploy the stack that creates Fargate service and DBCluster (if specificed)"
72
+ opts.on('--fargate', fargate_msg) do
73
+ if options[:deploy].nil?
74
+ raise Aws::RailsProvisioner::Errors::InvalidCommandOption.new('deploy', '--fargate')
75
+ end
76
+ options[:deploy][:fargate] = true
77
+ end
78
+
79
+ cicd_deploy_msg = "Deploy the stack that builds CICD flow"
80
+ opts.on('--cicd', cicd_deploy_msg) do
81
+ if options[:deploy].nil?
82
+ raise Aws::RailsProvisioner::Errors::InvalidCommandOption.new('deploy', '--cicd')
83
+ end
84
+ options[:deploy][:cicd] = true
85
+ end
86
+
87
+ opts.on('-h', '--help', 'Show help') do
88
+ puts opts
89
+ exit
90
+ end
91
+
92
+ end.parse!
93
+
94
+ config = Aws::RailsProvisioner::Utils.parse(options[:file_path])
95
+ cdk_dir = options[:cdk_dir] || config[:cdk_dir] || 'cdk-sample'
96
+ config[:cdk_dir] = cdk_dir
97
+
98
+ # prepare codebuilder params
99
+ if options[:build]
100
+ options[:build][:profile] = options[:profile]
101
+ options[:build][:file_path] = options[:file_path]
102
+ if svc_name = options[:service_name]
103
+ config[:services][svc_name].merge!(options[:build])
104
+ else
105
+ config[:services].each do |_, svc|
106
+ svc.merge!(options[:build])
107
+ end
108
+ end
109
+ end
110
+ code_builder = Aws::RailsProvisioner::CDKCodeBuilder.new(config)
111
+
112
+ if options[:build]
113
+ Aws::RailsProvisioner::CDKBuilder.new(
114
+ cdk_dir: cdk_dir,
115
+ source_files: code_builder.source_files,
116
+ default_stack: code_builder.default_stack,
117
+ services: code_builder.services
118
+ ).run
119
+ elsif options[:deploy]
120
+ deploy_params = {
121
+ cdk_dir: cdk_dir,
122
+ services: code_builder.services,
123
+ stack_prefix: code_builder.stack_prefix,
124
+ service_name: options[:service_name]
125
+ }
126
+ if options[:profile]
127
+ deploy_params[:profile] = profile
128
+ end
129
+ deploy_params.merge!(options[:deploy])
130
+ deployer = Aws::RailsProvisioner::CDKDeployer.new(deploy_params)
131
+ deployer.run
132
+ end
@@ -0,0 +1,42 @@
1
+ # services
2
+ require 'aws-sdk-rds'
3
+
4
+ # code gen helpers
5
+ require_relative 'aws-rails-provisioner/cdk_builder'
6
+ require_relative 'aws-rails-provisioner/cdk_deployer'
7
+ require_relative 'aws-rails-provisioner/cdk_code_builder'
8
+
9
+ # utils
10
+ require_relative 'aws-rails-provisioner/services'
11
+ require_relative 'aws-rails-provisioner/service'
12
+ require_relative 'aws-rails-provisioner/utils'
13
+ require_relative 'aws-rails-provisioner/errors'
14
+
15
+ # init stack
16
+ require_relative 'aws-rails-provisioner/vpc'
17
+ require_relative 'aws-rails-provisioner/subnet_selection'
18
+
19
+ # CICD stack
20
+ require_relative 'aws-rails-provisioner/code_build'
21
+ require_relative 'aws-rails-provisioner/build'
22
+ require_relative 'aws-rails-provisioner/migration'
23
+
24
+ # fargate stack
25
+ require_relative 'aws-rails-provisioner/db_cluster'
26
+ require_relative 'aws-rails-provisioner/fargate'
27
+ require_relative 'aws-rails-provisioner/scaling'
28
+
29
+ # views
30
+ require_relative 'aws-rails-provisioner/view'
31
+ require_relative 'aws-rails-provisioner/views/init_stack'
32
+ require_relative 'aws-rails-provisioner/views/fargate_stack'
33
+ require_relative 'aws-rails-provisioner/views/pipeline_stack'
34
+ require_relative 'aws-rails-provisioner/views/project'
35
+
36
+ # version
37
+ require_relative 'aws-rails-provisioner/version'
38
+
39
+ module Aws
40
+ module RailsProvisioner
41
+ end
42
+ end
@@ -0,0 +1,43 @@
1
+ module Aws::RailsProvisioner
2
+ class Build < Aws::RailsProvisioner::CodeBuild
3
+
4
+ # An AWS CodeBuild Project that build, tag and
5
+ # push image to AWS ECR Repo
6
+ #
7
+ # configuration for :build
8
+ #
9
+ # @param [Hash] options
10
+ #
11
+ # @option options [String] :project_name name for
12
+ # the CodeBuild project, default to 'RailsProvisionerImageBuild'
13
+ #
14
+ # @option options [String] :description description for this
15
+ # CodeBuild project, default to 'build, tag and push image to ECR'
16
+ #
17
+ # @option options [String] :buildspec buildspec.yml file path, default
18
+ # to `buildspec-ecr.yml` under root directory, using template
19
+ # under `buildspecs/`
20
+ #
21
+ # @option options [String] :build_image default to codebuild `ubuntu_14_04_docker_18_09_0`
22
+ # full list of supported images see:
23
+ # https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-codebuild.LinuxBuildImage.html
24
+ #
25
+ # @option options [Integer] :timeout number of minutes after which
26
+ # CodeBuild stops the build if it’s not complete
27
+ #
28
+ def initialize(options = {})
29
+ unless options[:description]
30
+ options[:description] = 'build, tag and push image to ECR'
31
+ end
32
+ unless options[:buildspec]
33
+ options[:buildspec] = 'buildspec-ecr.yml'
34
+ end
35
+ unless options[:build_image]
36
+ options[:build_image] = 'ubuntu_14_04_docker_18_09_0'
37
+ end
38
+ super(options)
39
+ end
40
+
41
+ end
42
+ end
43
+
@@ -0,0 +1,68 @@
1
+ module Aws::RailsProvisioner
2
+ class CDKBuilder
3
+
4
+ def initialize(options = {})
5
+ @source_files = options[:source_files]
6
+ @default_stack = options[:default_stack]
7
+ @cdk_dir = options[:cdk_dir]
8
+ @services = options[:services]
9
+ end
10
+
11
+ def run
12
+ _init_cdk
13
+ # code to files
14
+ files = @source_files
15
+ files.each do |path, code|
16
+ if File.exists?(path)
17
+ puts "replacing #{path}"
18
+ else
19
+ puts "creating #{path}"
20
+ end
21
+ FileUtils.mkdir_p(File.dirname(path))
22
+ File.open(path, 'w') do |f|
23
+ f.write(code)
24
+ end
25
+ end
26
+ _install_dependencies
27
+ _npm_build
28
+ end
29
+
30
+ private
31
+
32
+ def _init_cdk
33
+ unless Dir.exist?(@cdk_dir)
34
+ FileUtils.mkdir_p(@cdk_dir)
35
+ unless @dryrun
36
+ Dir.chdir(@cdk_dir) do
37
+ `npm i -g aws-cdk`
38
+ `cdk init app --language=typescript`
39
+ end
40
+
41
+ if File.exists?(@default_stack)
42
+ FileUtils.rm_f(@default_stack)
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def _install_dependencies
49
+ pkgs = @services.inject(Set.new) do |set, svc|
50
+ set.merge(svc.packages)
51
+ set
52
+ end
53
+ Dir.chdir(@cdk_dir) do
54
+ pkgs.each do |pkg|
55
+ `npm install #{pkg}`
56
+ end
57
+ end
58
+ end
59
+
60
+ def _npm_build
61
+ Dir.chdir(@cdk_dir) do
62
+ puts "running npm run build ..."
63
+ `npm run build`
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,140 @@
1
+ require 'set'
2
+
3
+ module Aws::RailsProvisioner
4
+ class CDKCodeBuilder
5
+
6
+ # Generates CDK projects with separate stacks:
7
+ #
8
+ # * InitStack - VPC and Fargate Cluster
9
+ # * FargetStack - Fargate Service, AutoScaling, ALB
10
+ # * PipelineStack - CICD CodePipline (when enabled) that:
11
+ # * Source: CodeCommit Repository when host rails applications
12
+ # * Build : CodeBuild Project that takes care of image build, tag(commit id) and push to ECR
13
+ # * Migration: CodeBuild Project that run migration inside DB subnet (optional)
14
+ # * Deploy: CodeDeploy that deploys tagged(commit id) image from ECR to Fargate service
15
+ #
16
+ # @param [Hash] options
17
+ #
18
+ # @option options [String] :cdk_dir Directory path for
19
+ # generated CDK code, defaults to "cdk-sample"
20
+ #
21
+ # @option options [Hash] :vpc VPC configurations
22
+ # @see {Aws::RailsProvisioner::Vpc}
23
+ #
24
+ # @option options [Hash] :services
25
+ #
26
+ # @option options [Hash] :db_cluster DB cluster configurations
27
+ # @see {Aws::RailsProvisioner::DBCluster}
28
+ #
29
+ # @option options [required, String] :source_path path to the directory containing
30
+ # Rails Application source
31
+ #
32
+ # @option options [Hash] :fargate Fargate service configurations
33
+ # @example: at `aws-rails-provisioner.yml`
34
+ # fargate:
35
+ # desired_count: 5
36
+ # task:
37
+ # environment:
38
+ # RAILS_LOG_TO_STDOUT: true
39
+ # @see {Aws::RailsProvisioner::Fargate}
40
+ #
41
+ # @option options [Hash] :scaling Scaling definitions for the Fargate service
42
+ # @example: at `aws-rails-provisioner.yml`
43
+ # scaling:
44
+ # max_capacity: 7
45
+ # on_cpu:
46
+ # target_util_percent: 40
47
+ # on_request:
48
+ # requests_per_target: 20000
49
+ # @see {Aws::RailsProvisioner::Scaling}
50
+ #
51
+ # @option options [Hash] :loadbalancer Application Loadbalancer that front
52
+ # the Fargate service
53
+ # @example: at `aws-rails-provisioner.yml`
54
+ # loadbalancer:
55
+ # internet_facing: true
56
+ # @see {Aws::RailsProvisioner::LoadBalancer}
57
+ #
58
+ # @option options [Hash] :cicd configurations for CICD that covers:
59
+ # source, build, database migration with code pipline
60
+ # @see {Aws::RailsProvisioner::Pipeline}
61
+ #
62
+ def initialize(options = {})
63
+ @cdk_dir = options[:cdk_dir] || "cdk-sample"
64
+ @stack_prefix = _stack_prefix
65
+
66
+ # init, vpc & fargate cluster
67
+ @vpc = options[:vpc] || {}
68
+
69
+ # fargate services defs
70
+ # including related db, alb, scaling, cicd etc.
71
+ @services = Aws::RailsProvisioner::ServiceEnumerator.new(options[:services] || {})
72
+
73
+ # npm cdk service packages need to be installed
74
+ @packages = Set.new
75
+ end
76
+
77
+ # @return [Enumerable]
78
+ attr_reader :services
79
+
80
+ # @return [Set]
81
+ attr_reader :packages
82
+
83
+ # @return [String]
84
+ attr_reader :cdk_dir
85
+
86
+ # @api private
87
+ # @return [String]
88
+ attr_reader :stack_prefix
89
+
90
+ def source_files
91
+ Enumerator.new do |y|
92
+ y.yield("#{@cdk_dir}/lib/#{@cdk_dir}-init-stack.ts", init_stack)
93
+ @services.each do |svc|
94
+ y.yield("#{@cdk_dir}/lib/#{svc.path_prefix}-fargate-stack.ts",
95
+ svc.fargate_stack)
96
+ y.yield("#{@cdk_dir}/lib/#{svc.path_prefix}-pipeline-stack.ts",
97
+ svc.pipeline_stack) if svc.enable_cicd
98
+ @packages.merge(svc.packages)
99
+ end
100
+
101
+ y.yield("#{@cdk_dir}/bin/#{@cdk_dir}.ts", project)
102
+ end
103
+ end
104
+
105
+ def default_stack
106
+ # CDK init cmd default empty stack
107
+ "#{@cdk_dir}/lib/#{@cdk_dir}-stack.ts"
108
+ end
109
+
110
+ # @api private
111
+ def project
112
+ Aws::RailsProvisioner::Views::Project.new(
113
+ stack_prefix: @stack_prefix,
114
+ path_prefix: @cdk_dir,
115
+ services: @services
116
+ ).render
117
+ end
118
+
119
+ # @api private
120
+ def init_stack
121
+ init = Aws::RailsProvisioner::Views::InitStack.new(
122
+ vpc: @vpc,
123
+ stack_prefix: @stack_prefix
124
+ )
125
+ @packages.merge(init.packages)
126
+ init.render
127
+ end
128
+
129
+ private
130
+
131
+ def _stack_prefix
132
+ dir = @cdk_dir.dup
133
+ dir.split('-').map do |part|
134
+ part[0] = part[0].upcase
135
+ part
136
+ end.join
137
+ end
138
+
139
+ end
140
+ end
@@ -0,0 +1,62 @@
1
+ require 'open3'
2
+
3
+ module Aws::RailsProvisioner
4
+ class CDKDeployer
5
+
6
+ def initialize(options = {})
7
+ @cdk_dir = options[:cdk_dir]
8
+ @services = options[:services]
9
+ @stack_prefix = options[:stack_prefix]
10
+
11
+ both = options[:fargate].nil? && options[:cicd].nil?
12
+ @fargate_stack = both || !!options[:fargate]
13
+ @pipeline_stack = both || !!options[:cicd]
14
+
15
+ @init_stack_only = !!options[:init]
16
+
17
+ @svc_name = options[:service_name]
18
+ @profile = options[:profile]
19
+ end
20
+
21
+ def run
22
+ Dir.chdir(@cdk_dir) do
23
+ deploy_init_stack
24
+ unless @init_stack_only
25
+ if @svc_name
26
+ deploy_svc(@services[@svc_name].stack_prefix)
27
+ else
28
+ @services.each do |svc|
29
+ deploy_svc(svc.stack_prefix)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def deploy_init_stack
39
+ opts = @profile ? " --profile #{@profile}" : ''
40
+ # disable prompts in deploy command
41
+ opts += " --require-approval never"
42
+ cmd = "cdk deploy #{@stack_prefix}InitStack#{opts}"
43
+ Open3.popen3(cmd) do |_, stdout, stderr, _|
44
+ puts stdout.read
45
+ puts stderr.read
46
+ end
47
+ end
48
+
49
+ def deploy_svc(stack_prefix)
50
+ opts = @profile ? " --profile #{@profile}" : ''
51
+ # disable prompts in deploy command
52
+ opts += " --require-approval never"
53
+ if @fargate_stack
54
+ `cdk deploy #{stack_prefix}FargateStack#{opts}`
55
+ end
56
+ if @pipeline_stack
57
+ `cdk deploy #{stack_prefix}PipelineStack#{opts}`
58
+ end
59
+ end
60
+
61
+ end
62
+ end