aws-rails-provisioner 0.0.0.rc1

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.
@@ -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