awshark 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1893ca9f5ab0defd36515f70c621d987d2981c01b8c70bab0735ff422ec7c588
4
+ data.tar.gz: dd4c8992a1c8c5aec6e978f60e5d8f879f46e4013fe1fe5ea45e274d426f3b81
5
+ SHA512:
6
+ metadata.gz: 31f09cd910a4b782c4ba1e142d89446fb92259eafbb51cbbdbbc2d0108aa9bce50bd24c9dd5f01740bc5f5ec560fc7703554df347b7b4279bce8a6a38c2ce666
7
+ data.tar.gz: fd69d7cd781331e6a25d2d1c5615b66bfae57796a8f16bd441888a348951a3e9cb157b5def2df3f4214517c7b5f71b85b2affcb76af78771b995843fbe360236
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ gems.locked
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --color
2
+ --require spec_helper
3
+ --format progress
4
+ --profile
data/.rubocop.yml ADDED
@@ -0,0 +1,130 @@
1
+ # RuboCop will start looking for the configuration file in the directory
2
+ # where the inspected file is and continue its way up to the root directory.
3
+ #
4
+ # See https://docs.rubocop.org/rubocop/configuration
5
+
6
+ AllCops:
7
+ TargetRubyVersion: 2.6
8
+ Exclude:
9
+ - 'bin/*'
10
+ - 'node_modules/**/*'
11
+ - 'vendor/**/*'
12
+ - 'spec/**/*'
13
+ - 'tmp/*'
14
+
15
+
16
+ Layout/BeginEndAlignment:
17
+ Enabled: true
18
+ EnforcedStyleAlignWith: begin
19
+ Layout/EmptyLinesAroundAttributeAccessor:
20
+ Enabled: true
21
+ Layout/SpaceAroundMethodCallOperator:
22
+ Enabled: true
23
+
24
+ Lint/DeprecatedOpenSSLConstant:
25
+ Enabled: true
26
+ Lint/MixedRegexpCaptureTypes:
27
+ Enabled: true
28
+ Lint/RaiseException:
29
+ Enabled: true
30
+ Lint/StructNewOverride:
31
+ Enabled: true
32
+ Lint/BinaryOperatorWithIdenticalOperands:
33
+ Enabled: true
34
+ Lint/ConstantDefinitionInBlock:
35
+ Enabled: true
36
+ Lint/DuplicateElsifCondition:
37
+ Enabled: true
38
+ Lint/DuplicateRequire:
39
+ Enabled: true
40
+ Lint/DuplicateRescueException:
41
+ Enabled: true
42
+ Lint/EmptyConditionalBody:
43
+ Enabled: true
44
+ Lint/EmptyFile:
45
+ Enabled: true
46
+ Lint/FloatComparison:
47
+ Enabled: true
48
+ Lint/HashCompareByIdentity:
49
+ Enabled: true
50
+ Lint/IdentityComparison:
51
+ Enabled: true
52
+ Lint/MissingSuper:
53
+ Enabled: true
54
+ Lint/OutOfRangeRegexpRef:
55
+ Enabled: true
56
+ Lint/RedundantSafeNavigation:
57
+ Enabled: true
58
+ Lint/SelfAssignment:
59
+ Enabled: true
60
+ Lint/TopLevelReturnWithArgument:
61
+ Enabled: true
62
+ Lint/TrailingCommaInAttributeDeclaration:
63
+ Enabled: true
64
+ Lint/UnreachableLoop:
65
+ Enabled: true
66
+ Lint/UselessMethodDefinition:
67
+ Enabled: true
68
+ Lint/UselessTimes:
69
+ Enabled: true
70
+
71
+ Metrics/AbcSize:
72
+ Enabled: true
73
+ Max: 20
74
+ Metrics/MethodLength:
75
+ Enabled: false
76
+ Metrics/ModuleLength:
77
+ Enabled: false
78
+
79
+ Style/Documentation:
80
+ Enabled: false
81
+ Style/ExponentialNotation:
82
+ Enabled: true
83
+ Style/HashEachMethods:
84
+ Enabled: true
85
+ Style/HashTransformKeys:
86
+ Enabled: true
87
+ Style/HashTransformValues:
88
+ Enabled: true
89
+ Style/RedundantFetchBlock:
90
+ Enabled: true
91
+ Style/RedundantRegexpCharacterClass:
92
+ Enabled: true
93
+ Style/RedundantRegexpEscape:
94
+ Enabled: true
95
+ Style/SlicingWithRange:
96
+ Enabled: true
97
+ Style/AccessorGrouping:
98
+ Enabled: false
99
+ Style/BisectedAttrAccessor:
100
+ Enabled: true
101
+ Style/CaseLikeIf:
102
+ Enabled: true
103
+ Style/ClassEqualityComparison:
104
+ Enabled: true
105
+ Style/CombinableLoops:
106
+ Enabled: true
107
+ Style/ExplicitBlockArgument:
108
+ Enabled: true
109
+ Style/GlobalStdStream:
110
+ Enabled: true
111
+ Style/HashAsLastArrayItem:
112
+ Enabled: true
113
+ Style/HashLikeCase:
114
+ Enabled: true
115
+ Style/KeywordParametersOrder:
116
+ Enabled: true
117
+ Style/OptionalBooleanParameter:
118
+ Enabled: true
119
+ Style/RedundantAssignment:
120
+ Enabled: true
121
+ Style/RedundantFileExtensionInRequire:
122
+ Enabled: true
123
+ Style/RedundantSelfAssignment:
124
+ Enabled: true
125
+ Style/SingleArgumentDig:
126
+ Enabled: true
127
+ Style/SoleNestedConditional:
128
+ Enabled: true
129
+ Style/StringConcatenation:
130
+ Enabled: true
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ ## Changelog
2
+
3
+ #### 1.0.0
4
+ - initial version
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # Awshark
2
+
3
+ [![GitHub Actions Test Status](https://github.com/jdahlke/awshark/workflows/Tests/badge.svg?branch=develop)](https://github.com/jdahlke/awshark/actions)
4
+
5
+ Simple command-line tool for some useful tasks for AWS *EC2*, *S3* and *CloudFormation*.
6
+
7
+
8
+ ### Installation
9
+
10
+ ```
11
+ gem install awshark
12
+ ```
13
+
14
+
15
+ ### Usage
16
+
17
+ #### S3 commands
18
+
19
+ List all S3 buckets
20
+ ```
21
+ awshark s3 list --profile=AWS_PROFILE
22
+ ```
23
+
24
+ List all objects in a specific S3 bucket
25
+ ```
26
+ awshark s3 objects BUCKET_NAME fonts/ --profile=AWS_PROFILE
27
+ ```
28
+
29
+ #### EC2 commands
30
+
31
+ List all EC2 instances in a region
32
+ ```
33
+ awshark ec2 list --profile=AWS_PROFILE
34
+ ```
35
+
36
+ #### Cloud Formation commands
37
+
38
+ Update (diff) Cloud Formation stack
39
+ ```
40
+ awshark cf deploy TEMPLATE_PATH --stage=STAGE --bucket=S3_BUCKET.bundesimmo.de --profile=AWS_PROFILE
41
+
42
+ awshark cf diff TEMPLATE_PATH --stage=STAGE --bucket=S3_BUCKET.bundesimmo.de --profile=AWS_PROFILE
43
+ ```
44
+
45
+ For a further information visit the [Wiki](https://github.com/jdahlke/awshark/wiki).
46
+
47
+
48
+ ### Development
49
+
50
+ After checking out the repo, run `bin/setup` to install dependencies.
51
+ You can also run `bin/console` for an interactive prompt that will allow you to experiment.
52
+
53
+ To install this gem onto your local machine, run
54
+
55
+ gem build awshark.gemspec
56
+ gem install --local awshark-0.1.0.gem
57
+ rm awshark-0.1.0.gem
58
+
59
+
60
+ ### Contributing
61
+
62
+ Bug reports and pull requests are welcome on GitHub at https://github.com/jdahlke/awshark.
data/awshark.gemspec ADDED
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'awshark/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'awshark'
9
+ spec.version = Awshark::VERSION
10
+ spec.authors = ['Joergen Dahlke']
11
+ spec.email = ['joergen.dahlke@infopark.de']
12
+
13
+ spec.summary = 'Custom CLI for for AWS related tasks'
14
+ spec.description = 'Custom CLI for for AWS to simplify common tasks with EC2, S3 and Cloud Formation'
15
+ spec.homepage = 'https://github.com/jdahlke/awshark'
16
+ spec.license = 'MIT'
17
+
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = 'https://github.com/jdahlke/awshark'
20
+ spec.metadata['changelog_uri'] = 'https://github.com/jdahlke/awshark/blob/develop/CHANGELOG.md'
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(.github|bin|spec)/}) }
26
+ end
27
+ spec.bindir = 'exe'
28
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ['lib']
30
+ spec.required_ruby_version = '>= 2.6'
31
+
32
+ spec.add_dependency 'activesupport'
33
+ spec.add_dependency 'aws-sdk-cloudformation'
34
+ spec.add_dependency 'aws-sdk-cloudwatch'
35
+ spec.add_dependency 'aws-sdk-ec2'
36
+ spec.add_dependency 'aws-sdk-ecr'
37
+ spec.add_dependency 'aws-sdk-rds'
38
+ spec.add_dependency 'aws-sdk-s3'
39
+ spec.add_dependency 'diffy'
40
+ spec.add_dependency 'mini_mime'
41
+ spec.add_dependency 'recursive-open-struct'
42
+ spec.add_dependency 'thor', '~> 1.0'
43
+
44
+ spec.add_development_dependency 'bundler', '~> 2.0'
45
+ spec.add_development_dependency 'rspec', '~> 3.9.0'
46
+ spec.add_development_dependency 'rubocop', '0.93.1'
47
+ end
data/exe/awshark ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'awshark'
6
+
7
+ Awshark::Cli.start(ARGV)
data/gems.rb ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in awshark.gemspec
6
+ gemspec
data/lib/awshark.rb ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/all'
4
+ require 'logger'
5
+ require 'thor'
6
+ require 'yaml'
7
+
8
+ require 'awshark/version'
9
+ require 'awshark/configuration'
10
+
11
+ module Awshark
12
+ class GracefulFail < StandardError; end
13
+
14
+ def self.config
15
+ @config ||= Configuration.new
16
+ end
17
+
18
+ def self.configure
19
+ yield config
20
+ end
21
+
22
+ def self.logger
23
+ return @logger if @logger
24
+
25
+ @logger = ::Logger.new($stdout)
26
+ @logger.level = Logger::INFO
27
+ @logger.formatter = proc do |_severity, _datetime, _progname, msg|
28
+ "[awshark] #{msg}\n"
29
+ end
30
+
31
+ @logger
32
+ end
33
+ end
34
+
35
+ require 'awshark/cli'
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'awshark/profile_resolver'
4
+
5
+ require 'awshark/subcommands/class_options'
6
+ require 'awshark/subcommands/cloud_formation'
7
+ require 'awshark/subcommands/ec2'
8
+ require 'awshark/subcommands/ecs'
9
+ require 'awshark/subcommands/rds'
10
+ require 'awshark/subcommands/s3'
11
+
12
+ module Awshark
13
+ class Cli < Thor
14
+ package_name 'Awshark'
15
+
16
+ map '-v' => :version
17
+
18
+ class_option :help, type: :boolean, desc: 'Prints this help'
19
+ class_option :profile, type: :string, desc: 'Load the AWS credentials profile named PROFILE.'
20
+ class_option :region, type: :string, desc: 'Sets the AWS region name REGION.'
21
+
22
+ desc 'cf COMMAND', 'Run CloudFormation command'
23
+ subcommand 'cf', Awshark::Subcommands::CloudFormation
24
+
25
+ desc 'ec2 COMMAND', 'Run EC2 command'
26
+ subcommand 'ec2', Awshark::Subcommands::EC2
27
+
28
+ desc 'ecs COMMAND', 'Run ECS command'
29
+ subcommand 'ecs', Awshark::Subcommands::Ecs
30
+
31
+ desc 'rds COMMAND', 'Run RDS command'
32
+ subcommand 'rds', Awshark::Subcommands::Rds
33
+
34
+ desc 's3 COMMAND', 'Run CloudFormation command'
35
+ subcommand 's3', Awshark::Subcommands::S3
36
+
37
+ desc 'version', 'Displays current version of AwsShark'
38
+ long_desc <<-LONGDESC
39
+ Displays current version of AwsShark.
40
+ LONGDESC
41
+ def version
42
+ puts "AwShark version: #{Awshark::VERSION}"
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Awshark
4
+ module CloudFormation
5
+ module FileLoading
6
+ def load_file(filepath, context = nil)
7
+ return nil if filepath.blank?
8
+
9
+ content = File.read(filepath)
10
+ content = ERB.new(content).result_with_hash(context) if context.present?
11
+
12
+ case File.extname(filepath)
13
+ when '.json'
14
+ JSON.parse(content)
15
+ when '.yml', '.yaml'
16
+ YAML.safe_load(content, permitted_classes: [Date, Time], aliases: true)
17
+ else
18
+ raise ArgumentError, "Unsupported file extension for #{filepath}"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/CloudFormation/Client.html
5
+ #
6
+ module Awshark
7
+ module CloudFormation
8
+ class Inferrer
9
+ attr_reader :path, :stage
10
+
11
+ def initialize(path, stage = nil)
12
+ @path = path
13
+ @stage = stage
14
+ end
15
+
16
+ def stack_name
17
+ file_extension = File.extname(path)
18
+ name = File.basename(path, file_extension)
19
+
20
+ [name, stage].compact.join('-').gsub('_', '-')
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/CloudFormation/Client.html
5
+ #
6
+ module Awshark
7
+ module CloudFormation
8
+ class Manager
9
+ attr_reader :path, :options
10
+ attr_reader :stack, :stage, :capabilities
11
+
12
+ def initialize(path, options = {})
13
+ @path = path
14
+ @options = options
15
+ @stage = options[:stage]
16
+
17
+ @capabilities = if options[:iam]
18
+ %w[CAPABILITY_IAM CAPABILITY_NAMED_IAM]
19
+ else
20
+ []
21
+ end
22
+
23
+ @stack = Stack.new(name: inferrer.stack_name)
24
+ end
25
+
26
+ def diff_stack_template
27
+ new_template = template.body
28
+ old_template = stack.template
29
+ options = { context: 2 }
30
+
31
+ diff = Diffy::Diff.new(old_template, new_template, options)
32
+ diff.to_s(:color)
33
+ end
34
+
35
+ def update_stack
36
+ stack.create_or_update(
37
+ capabilities: capabilities,
38
+ stack_name: stack.name,
39
+ template_url: template.url,
40
+ parameters: parameters.stack_parameters
41
+ )
42
+ rescue Aws::CloudFormation::Errors::ValidationError => e
43
+ raise GracefulFail, e.message if e.message.match(/No updates are to be performed/)
44
+ raise GracefulFail, e.message if e.message.match(/ROLLBACK_COMPLETE state and can not be updated/)
45
+
46
+ raise e
47
+ end
48
+
49
+ def tail_stack_events
50
+ stack.reload
51
+ stack_events = StackEvents.new(stack)
52
+
53
+ loop do
54
+ events = stack_events.new_events
55
+ print_stack_events(events)
56
+
57
+ break if stack_events.done?
58
+
59
+ sleep(event_polling)
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def event_polling
66
+ Awshark.config.cloud_formation.event_polling || 3
67
+ end
68
+
69
+ def inferrer
70
+ @inferrer ||= Inferrer.new(path, stage)
71
+ end
72
+
73
+ def parameters
74
+ @parameters ||= Parameters.new(path: path, stage: stage)
75
+ end
76
+
77
+ def template
78
+ @template ||= Template.new(path, options.merge(name: stack.name))
79
+ end
80
+
81
+ def print_stack_events(events)
82
+ $stdout.sync
83
+ events.each do |event|
84
+ printf "%-50<type>s %-30<logical_id>s %-20<status>s %<reason>s\n",
85
+ type: event.resource_type,
86
+ logical_id: event.logical_resource_id,
87
+ status: event.resource_status,
88
+ reason: event.resource_status_reason
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end