helmsnap 0.2.0 → 0.4.0

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: d23d2a85913e1acb605cf90521fdcf9f9ce6a3c95bfcecc24b1aae69a68d33a6
4
- data.tar.gz: 927df5851020d802c04de2c2ebcb8e0c8078869087e7db735c39f02d1168b6bf
3
+ metadata.gz: ae526f7e855d2833a340fc35f9363f212fcfe687aebca504dc0e99e704c6393e
4
+ data.tar.gz: 41787e81ec544fc491d2273763a837594415a7792c823faf07fab94e72388845
5
5
  SHA512:
6
- metadata.gz: 25a1d7514779a13e738169c2119bfc9ea27b1f5f092d2ac15fc381749763a5210efc07a8b52546eb7bc4045b3a6330d0e37176c8eea8bfb756176564551dfdda
7
- data.tar.gz: 3ae5fe678eb301418175f688adc06478b7ac5b7e775d9a9e16e1bf5fcb97c8d48ed763fd7895ccddabe21d2e72e26acfb3616340ce1b4ab630d1ad0898b8baff
6
+ metadata.gz: 65fc8371b350ab030be1503d392b0a2fd5abbe9d68c64bffa1b44662d1c8339e3358b3e15d63de723498732e67540c8d1768272983213cbd7d0f0be47a6c4b1b
7
+ data.tar.gz: 3370e09e93f1a0d6b43eb2913046ce44c73905c8f61f6ce79601b627795b2aa69f67c1f7f110fa0f9ef39e0eabe6bf926633d2431d7250ac714d94ed819db3b0
data/.dockerignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/Dockerfile ADDED
@@ -0,0 +1,12 @@
1
+ FROM alpine/helm
2
+
3
+ RUN apk add --update --no-cache ruby git colordiff
4
+
5
+ WORKDIR /app
6
+
7
+ COPY . .
8
+
9
+ RUN gem install colorize && gem build && gem install helmsnap --local
10
+
11
+ ENTRYPOINT []
12
+ CMD []
data/Gemfile.lock CHANGED
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- helmsnap (0.1.0)
4
+ helmsnap (0.4.0)
5
+ colorize
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
@@ -14,6 +15,7 @@ GEM
14
15
  zeitwerk (~> 2.3)
15
16
  ast (2.4.2)
16
17
  coderay (1.1.3)
18
+ colorize (0.8.1)
17
19
  concurrent-ruby (1.1.9)
18
20
  diff-lcs (1.4.4)
19
21
  i18n (1.8.11)
@@ -84,6 +86,7 @@ GEM
84
86
 
85
87
  PLATFORMS
86
88
  x86_64-darwin-20
89
+ x86_64-linux
87
90
 
88
91
  DEPENDENCIES
89
92
  helmsnap!
@@ -94,4 +97,4 @@ DEPENDENCIES
94
97
  rubocop-config-umbrellio
95
98
 
96
99
  BUNDLED WITH
97
- 2.2.31
100
+ 2.2.32
data/README.md CHANGED
@@ -1,38 +1,69 @@
1
1
  # Helmsnap
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/helmsnap`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ ## About
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ Helmsnap is a tool for generating and checking helm chart snapshots. Example:
6
6
 
7
- ## Installation
7
+ Generate snapshots (uses `helm template` under the hood):
8
+
9
+ ```sh
10
+ helmsnap generate -c helm/mychart -s helm/snapshots -v helm/values/production.yaml
11
+ ```
12
+
13
+ Generate snapshots in some temp directory and check (diff) them against existing snapshots in `helm/snapshots` directory:
8
14
 
9
- Add this line to your application's Gemfile:
15
+ ```sh
16
+ helmsnap check -c helm/mychart -s helm/snapshots -v helm/values/production.yaml
17
+ ```
18
+
19
+ Get the full description of possible arguments:
10
20
 
11
- ```ruby
12
- gem 'helmsnap'
21
+ ```sh
22
+ helmsnap --help
13
23
  ```
14
24
 
15
- And then execute:
25
+ The typical usage flow:
16
26
 
17
- $ bundle install
27
+ 1. You generate some snapshots using `helmsnap generate` command and check them into your git repo.
28
+ 2. You add `helmsnap check` command to your CI (or run it manually on every commit).
29
+ 3. In case snapshots differ, you should carefully check the updates and either fix your chart or update the snapshots using `helmsnap generate`.
18
30
 
19
- Or install it yourself as:
31
+ This tool can also be useful when you are developing a new chart or updating an existing one: you can generate snapshots and see what is rendered without need to deploy the chart in your cluster.
20
32
 
21
- $ gem install helmsnap
33
+ ## Features
22
34
 
23
- ## Usage
35
+ ### Helm dependency management
24
36
 
25
- TODO: Write usage instructions here
37
+ Helmsnap will automically rebuild your chart dependencies on every snapshot generation or check. In case your dependency is using url to some local helm repo and you don't have a proper repo added, it will add it automically which is useful in CI. It also will detect local dependencies (those that start with `file://`) and rebuild their dependencies as well.
26
38
 
27
- ## Development
39
+ ### Timestamp replacement
28
40
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
41
+ Helmsnap will automically replace all occurencies of patterns that look like timestamps (format like `2022-01-01 00:00:00.000`) in your templates. This is useful in case you have some annotations like `releaseTime` that would break your snapshots checks otherwise.
42
+
43
+ ## Installation
30
44
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
45
+ Just install a gem and use the provided `helmsnap` binary.
46
+
47
+ ```sh
48
+ gem install helmsnap
49
+ ```
50
+
51
+ Alaternatively, you can use a [Docker image](https://github.com/tycooon/helmsnap/pkgs/container/helmsnap) with Ruby, helm and helmsnap gem preinstalled. This is useful for CIs or if you don't want to install Ruby locally.
52
+
53
+ ## CI example
54
+
55
+ Example job for Gitlab CI:
56
+
57
+ ```yaml
58
+ check-snapshots:
59
+ stage: test
60
+ image: ghcr.io/tycooon/helmsnap:latest
61
+ script: helmsnap check -c helm/mychart -s helm/snapshots -v helm/values/production.yaml
62
+ ```
32
63
 
33
64
  ## Contributing
34
65
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/tycooon/helmsnap.
66
+ Bug reports and pull requests are welcome.
36
67
 
37
68
  ## License
38
69
 
data/bin/setup CHANGED
File without changes
data/helmsnap.gemspec CHANGED
@@ -30,5 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
31
31
  spec.require_paths = ["lib"]
32
32
 
33
+ spec.add_dependency "colorize"
34
+
33
35
  spec.add_development_dependency "rubocop-config-umbrellio"
34
36
  end
@@ -4,6 +4,8 @@ class Helmsnap::ArgsParser
4
4
  Args = Struct.new(:chart_path, :snapshots_path, :values_path, keyword_init: true)
5
5
  MissingOption = Class.new(OptionParser::ParseError)
6
6
 
7
+ BANNER = "Usage: helmsnap CMD [options]"
8
+
7
9
  def initialize(options)
8
10
  self.options = options
9
11
  self.args = Args.new
@@ -21,8 +23,8 @@ class Helmsnap::ArgsParser
21
23
  end
22
24
 
23
25
  def print_help!(msg)
24
- puts msg, nil
25
- puts parser.help
26
+ Helmsnap::Console.error($stderr, "#{msg}\n") if msg
27
+ Helmsnap::Console.print($stdout, parser.help)
26
28
  exit 1
27
29
  end
28
30
 
@@ -30,9 +32,8 @@ class Helmsnap::ArgsParser
30
32
 
31
33
  attr_accessor :options, :parser, :args
32
34
 
33
- def build_parser
34
- OptionParser.new do |opts|
35
- opts.banner = "Usage: helmsnap CMD [options]"
35
+ def build_parser # rubocop:disable Metrics/MethodLength
36
+ OptionParser.new(BANNER, 50) do |opts|
36
37
  opts.separator("Supported commands: `generate` and `check`.")
37
38
  opts.separator("")
38
39
  opts.separator("Specific options:")
@@ -49,8 +50,13 @@ class Helmsnap::ArgsParser
49
50
  args.values_path = pn(option)
50
51
  end
51
52
 
53
+ opts.on("--version", "Show version") do
54
+ Helmsnap::Console.print($stdout, "#{Helmsnap::VERSION}\n")
55
+ exit
56
+ end
57
+
52
58
  opts.on("-h", "--help", "Show this message") do
53
- puts opts
59
+ print_help!(nil)
54
60
  exit
55
61
  end
56
62
  end
@@ -13,7 +13,7 @@ class Helmsnap::Check
13
13
 
14
14
  def call
15
15
  Dir.mktmpdir do |temp_dir|
16
- pp temp_dir_path = Pathname.new(temp_dir)
16
+ temp_dir_path = Pathname.new(temp_dir)
17
17
 
18
18
  Helmsnap::Generate.call(
19
19
  chart_path: chart_path,
@@ -21,7 +21,14 @@ class Helmsnap::Check
21
21
  values_path: values_path,
22
22
  )
23
23
 
24
- Helmsnap.run_cmd!("colordiff", "-r", temp_dir_path, snapshots_path)
24
+ result = Helmsnap.run_cmd("which", "colordiff", allow_failure: true)
25
+ util = result.success ? "colordiff" : "diff"
26
+
27
+ diff = Helmsnap.run_cmd(
28
+ util, "--unified", "--recursive", snapshots_path, temp_dir_path, allow_failure: true
29
+ ).output
30
+
31
+ diff.strip.empty?
25
32
  end
26
33
  end
27
34
 
@@ -1,38 +1,48 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Helmsnap::Command
4
+ Result = Struct.new(:success, :output)
5
+
4
6
  def self.call(...)
5
7
  new(...).call
6
8
  end
7
9
 
8
- def initialize(cmd)
10
+ def initialize(cmd, stdout: $stdout, stderr: $stderr, allow_failure: false)
9
11
  self.cmd = cmd
12
+ self.stdout = stdout
13
+ self.stderr = stderr
14
+ self.allow_failure = allow_failure
10
15
  end
11
16
 
12
17
  def call
13
- puts "\e[1m\e[33m#{cmd}\e[0m\e[22m"
18
+ Helmsnap::Console.info(stdout, "> #{cmd}")
19
+ run_command
20
+ end
21
+
22
+ private
14
23
 
15
- Open3.popen3(cmd) do |_stdin, stdout, stderr, wait_thr|
24
+ attr_accessor :cmd, :stdout, :stderr, :allow_failure
25
+
26
+ def run_command
27
+ Open3.popen3(cmd) do |_in, out, err, wait_thr|
16
28
  output = +""
17
29
 
18
- while (chunk = stdout.gets)
19
- $stdout.print(chunk)
30
+ while (chunk = out.gets)
31
+ Helmsnap::Console.print(stdout, chunk)
20
32
  output << chunk
21
33
  end
22
34
 
23
35
  exit_status = wait_thr.value
36
+ success = exit_status.success?
24
37
 
25
- unless exit_status.success?
26
- $stderr.print(stderr.read)
27
- abort "Command failed with status #{exit_status.to_i}"
38
+ if !success && !allow_failure
39
+ Helmsnap::Console.error(stderr, err.read)
40
+ Helmsnap::Console.error(stderr, "Command failed with status #{exit_status.to_i}")
41
+ abort
28
42
  end
29
43
 
30
- puts
31
- output
44
+ Helmsnap::Console.print(stdout, "\n")
45
+ Result.new(success, output)
32
46
  end
33
47
  end
34
-
35
- private
36
-
37
- attr_accessor :cmd
38
48
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Helmsnap::Console
4
+ extend self
5
+
6
+ def print(stream, msg)
7
+ stream.print(msg)
8
+ end
9
+
10
+ def info(stream, msg)
11
+ msg = ColorizedString[msg].colorize(:light_yellow)
12
+ stream.puts(msg)
13
+ end
14
+
15
+ def error(stream, msg)
16
+ msg = ColorizedString[msg].colorize(:light_red)
17
+ stream.puts(msg)
18
+ end
19
+ end
@@ -12,21 +12,21 @@ class Helmsnap::Generate
12
12
  end
13
13
 
14
14
  def call
15
- dep_list = run_cmd!("helm", "dependency", "list", "--max-col-width", 0, chart_path)
15
+ dep_list = run_cmd("helm", "dependency", "list", "--max-col-width", 0, chart_path).output
16
16
 
17
17
  dep_list.scan(%r{file://(.+?)\t}) do |dep_path|
18
- run_cmd!("helm", "dependency", "update", "--skip-refresh", chart_path.join(dep_path.first))
18
+ run_cmd("helm", "dependency", "update", "--skip-refresh", chart_path.join(dep_path.first))
19
19
  end
20
20
 
21
21
  dep_list.scan(%r{(https?://.+?)\t}) do |dep_path|
22
- run_cmd!("helm", "repo", "add", Digest::MD5.hexdigest(dep_path.first), dep_path.first)
22
+ run_cmd("helm", "repo", "add", Digest::MD5.hexdigest(dep_path.first), dep_path.first)
23
23
  end
24
24
 
25
- run_cmd!("helm", "dependency", "update", "--skip-refresh", chart_path)
25
+ run_cmd("helm", "dependency", "update", "--skip-refresh", chart_path)
26
26
 
27
27
  FileUtils.rmtree(snapshots_path)
28
28
 
29
- run_cmd!(
29
+ run_cmd(
30
30
  "helm", "template", chart_path, "--values", values_path, "--output-dir", snapshots_path
31
31
  )
32
32
 
@@ -41,7 +41,7 @@ class Helmsnap::Generate
41
41
 
42
42
  attr_accessor :chart_path, :snapshots_path, :values_path
43
43
 
44
- def run_cmd!(...)
45
- Helmsnap.run_cmd!(...)
44
+ def run_cmd(...)
45
+ Helmsnap.run_cmd(...)
46
46
  end
47
47
  end
@@ -11,7 +11,7 @@ class Helmsnap::Runner
11
11
 
12
12
  def call
13
13
  parser = Helmsnap::ArgsParser.new(args)
14
- options = parser.get_options!
14
+ self.options = parser.get_options!
15
15
 
16
16
  cmd, *rest = args
17
17
 
@@ -25,9 +25,9 @@ class Helmsnap::Runner
25
25
 
26
26
  case cmd
27
27
  when "generate"
28
- Helmsnap::Generate.call(**options.to_h)
28
+ generate!
29
29
  when "check"
30
- Helmsnap::Check.call(**options.to_h)
30
+ check!
31
31
  else
32
32
  parser.print_help!("Unknown command: #{cmd}.")
33
33
  end
@@ -35,5 +35,33 @@ class Helmsnap::Runner
35
35
 
36
36
  private
37
37
 
38
- attr_accessor :args
38
+ attr_accessor :args, :options
39
+
40
+ def generate!
41
+ Helmsnap::Generate.call(**options.to_h)
42
+ Helmsnap::Console.info($stdout, "Snapshots generated successfully.")
43
+ end
44
+
45
+ def check!
46
+ if Helmsnap::Check.call(**options.to_h)
47
+ Helmsnap::Console.info($stdout, "Snapshots are up-to-date.")
48
+ else
49
+ example_cmd = Shellwords.join(
50
+ [
51
+ "helmsnap", "generate",
52
+ "--chart-dir", options.chart_path,
53
+ "--snapshots-dir", options.snapshots_path,
54
+ "--values", options.values_path
55
+ ],
56
+ )
57
+
58
+ Helmsnap::Console.error(
59
+ $stdout,
60
+ "Snapshots are outdated. You should check the diff above and either fix your chart or " \
61
+ "update the snapshots using the following command:\n> #{example_cmd}",
62
+ )
63
+
64
+ exit 1
65
+ end
66
+ end
39
67
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Helmsnap
4
- VERSION = "0.2.0"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/helmsnap.rb CHANGED
@@ -8,9 +8,12 @@ require "pathname"
8
8
  require "shellwords"
9
9
  require "tmpdir"
10
10
 
11
+ require "colorized_string"
12
+
11
13
  module Helmsnap
12
14
  require_relative "helmsnap/args_parser"
13
15
  require_relative "helmsnap/check"
16
+ require_relative "helmsnap/console"
14
17
  require_relative "helmsnap/command"
15
18
  require_relative "helmsnap/generate"
16
19
  require_relative "helmsnap/runner"
@@ -18,8 +21,8 @@ module Helmsnap
18
21
 
19
22
  class Error < StandardError; end
20
23
 
21
- def self.run_cmd!(*cmd_parts)
24
+ def self.run_cmd(*cmd_parts, **options)
22
25
  cmd = Shellwords.join(cmd_parts)
23
- Helmsnap::Command.call(cmd)
26
+ Helmsnap::Command.call(cmd, **options)
24
27
  end
25
28
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: helmsnap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuri Smirnov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-11-27 00:00:00.000000000 Z
11
+ date: 2021-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colorize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rubocop-config-umbrellio
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -32,8 +46,10 @@ executables:
32
46
  extensions: []
33
47
  extra_rdoc_files: []
34
48
  files:
49
+ - ".dockerignore"
35
50
  - ".rspec"
36
51
  - ".rubocop.yml"
52
+ - Dockerfile
37
53
  - Gemfile
38
54
  - Gemfile.lock
39
55
  - LICENSE.txt
@@ -47,6 +63,7 @@ files:
47
63
  - lib/helmsnap/args_parser.rb
48
64
  - lib/helmsnap/check.rb
49
65
  - lib/helmsnap/command.rb
66
+ - lib/helmsnap/console.rb
50
67
  - lib/helmsnap/generate.rb
51
68
  - lib/helmsnap/runner.rb
52
69
  - lib/helmsnap/version.rb
@@ -71,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
71
88
  - !ruby/object:Gem::Version
72
89
  version: '0'
73
90
  requirements: []
74
- rubygems_version: 3.2.31
91
+ rubygems_version: 3.2.32
75
92
  signing_key:
76
93
  specification_version: 4
77
94
  summary: A tool for creating and checking helm chart snapshots.