helmsnap 0.5.1 → 0.7.0
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.
- checksums.yaml +4 -4
- data/Dockerfile +2 -1
- data/Gemfile.lock +4 -4
- data/README.md +31 -8
- data/lib/helmsnap/args_parser.rb +17 -23
- data/lib/helmsnap/check.rb +5 -11
- data/lib/helmsnap/config.rb +32 -0
- data/lib/helmsnap/env.rb +20 -0
- data/lib/helmsnap/generate.rb +17 -10
- data/lib/helmsnap/runner.rb +11 -11
- data/lib/helmsnap/setup_dependencies.rb +12 -6
- data/lib/helmsnap/version.rb +1 -1
- data/lib/helmsnap.rb +3 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 82cc0b42f25f49c54cb4ec921c092f7a3e6cc9de565bc3e39e357e4129b5c3ff
|
4
|
+
data.tar.gz: 0fc97b6105f8a9ab5fb8360bf390be3cfaea21bc24b21edf42960b88658c54a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f514cd9633fab8f88efac3ae96dda8579c9b8dbd2a90da8822979958c334681e0b882c498e1b4acfeda279814beeaf181fd942366b036da04100b62cbe1d9dc8
|
7
|
+
data.tar.gz: 5b9046c477241a66acbe1a2bc8b7f982205b49bbcf3f53edbeb1f4f516b4008f0ab4c52aeb5f829538796a1f717931588bea7abce9d33ecd9726031fe43c24dd
|
data/Dockerfile
CHANGED
@@ -2,8 +2,9 @@ FROM alpine/helm
|
|
2
2
|
|
3
3
|
RUN apk add --update --no-cache ruby git colordiff
|
4
4
|
|
5
|
-
WORKDIR /
|
5
|
+
WORKDIR /wd
|
6
6
|
|
7
|
+
COPY --from=quay.io/roboll/helmfile:v0.142.0 /usr/local/bin/helmfile /usr/local/bin/helmfile
|
7
8
|
COPY . .
|
8
9
|
|
9
10
|
RUN gem install colorize && gem build && gem install helmsnap --local
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
helmsnap (0.
|
4
|
+
helmsnap (0.7.0)
|
5
5
|
colorize
|
6
6
|
|
7
7
|
GEM
|
@@ -23,7 +23,7 @@ GEM
|
|
23
23
|
method_source (1.0.0)
|
24
24
|
minitest (5.14.4)
|
25
25
|
parallel (1.21.0)
|
26
|
-
parser (3.0.3.
|
26
|
+
parser (3.0.3.1)
|
27
27
|
ast (~> 2.4.1)
|
28
28
|
pry (0.14.1)
|
29
29
|
coderay (~> 1.1)
|
@@ -31,7 +31,7 @@ GEM
|
|
31
31
|
rack (2.2.3)
|
32
32
|
rainbow (3.0.0)
|
33
33
|
rake (13.0.6)
|
34
|
-
regexp_parser (2.
|
34
|
+
regexp_parser (2.2.0)
|
35
35
|
rexml (3.2.5)
|
36
36
|
rspec (3.10.0)
|
37
37
|
rspec-core (~> 3.10.0)
|
@@ -55,7 +55,7 @@ GEM
|
|
55
55
|
rubocop-ast (>= 1.7.0, < 2.0)
|
56
56
|
ruby-progressbar (~> 1.7)
|
57
57
|
unicode-display_width (>= 1.4.0, < 3.0)
|
58
|
-
rubocop-ast (1.
|
58
|
+
rubocop-ast (1.14.0)
|
59
59
|
parser (>= 3.0.1.1)
|
60
60
|
rubocop-config-umbrellio (1.17.0.53)
|
61
61
|
rubocop (= 1.17.0)
|
data/README.md
CHANGED
@@ -2,18 +2,24 @@
|
|
2
2
|
|
3
3
|
## About
|
4
4
|
|
5
|
-
Helmsnap is a tool for generating and checking
|
5
|
+
Helmsnap is a tool for generating and checking helmfile snapshots. Example:
|
6
6
|
|
7
|
-
Generate snapshots (uses `
|
7
|
+
Generate snapshots (uses `helmfile template` under the hood):
|
8
8
|
|
9
9
|
```sh
|
10
|
-
helmsnap generate
|
10
|
+
helmsnap generate
|
11
11
|
```
|
12
12
|
|
13
|
-
Generate snapshots in
|
13
|
+
Generate snapshots in a temporary directory and check (diff) them against existing snapshots in `helm/snapshots` directory:
|
14
14
|
|
15
15
|
```sh
|
16
|
-
helmsnap check
|
16
|
+
helmsnap check
|
17
|
+
```
|
18
|
+
|
19
|
+
Just build dependencies for each release in a helmfile:
|
20
|
+
|
21
|
+
```sh
|
22
|
+
helmsnap dependencies # or `helmsnap deps`
|
17
23
|
```
|
18
24
|
|
19
25
|
Get the full description of possible arguments:
|
@@ -30,6 +36,23 @@ The typical usage flow:
|
|
30
36
|
|
31
37
|
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.
|
32
38
|
|
39
|
+
## Configuration
|
40
|
+
|
41
|
+
By default, helmsnap will render your helmfile using `default` environment and will place snapshots in `helm/snapshots` directory. If you want to configure that, create a `.helmsnap.yaml` file and put there configuration that looks like this:
|
42
|
+
|
43
|
+
```yaml
|
44
|
+
envs: [staging, production] # `[default]` by default
|
45
|
+
snapshotsPath: somedir/snapshots # `helm/snapshots` by default
|
46
|
+
```
|
47
|
+
|
48
|
+
You can also override configuration file location using `--config` option.
|
49
|
+
|
50
|
+
## Dependencies
|
51
|
+
|
52
|
+
- Ruby 2.7+.
|
53
|
+
- [Helmfile](https://github.com/roboll/helmfile), which in turn relies on [Helm](https://github.com/helm/helm).
|
54
|
+
- Colordiff or diff utility.
|
55
|
+
|
33
56
|
## Features
|
34
57
|
|
35
58
|
### Helm dependency management
|
@@ -42,13 +65,13 @@ Helmsnap will automically replace all occurencies of patterns that look like tim
|
|
42
65
|
|
43
66
|
## Installation
|
44
67
|
|
45
|
-
Just install
|
68
|
+
Just install the gem and use the provided `helmsnap` binary.
|
46
69
|
|
47
70
|
```sh
|
48
71
|
gem install helmsnap
|
49
72
|
```
|
50
73
|
|
51
|
-
Alaternatively, you can use
|
74
|
+
Alaternatively, you can use the [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 and Helmfile on your machine.
|
52
75
|
|
53
76
|
## CI example
|
54
77
|
|
@@ -58,7 +81,7 @@ Example job for Gitlab CI:
|
|
58
81
|
check-snapshots:
|
59
82
|
stage: test
|
60
83
|
image: ghcr.io/tycooon/helmsnap:latest
|
61
|
-
script: helmsnap check
|
84
|
+
script: helmsnap check
|
62
85
|
```
|
63
86
|
|
64
87
|
## Contributing
|
data/lib/helmsnap/args_parser.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Helmsnap::ArgsParser
|
4
|
-
|
5
|
-
MissingOption = Class.new(OptionParser::ParseError)
|
4
|
+
InvalidConfigPath = Class.new(RuntimeError)
|
6
5
|
|
6
|
+
Args = Struct.new(:config_path)
|
7
|
+
|
8
|
+
DEFAULT_CONFIG_PATH = Pathname.new(".helmsnap.yaml")
|
9
|
+
CONFIG_PATH_HELP = %{Path to config (default: "#{DEFAULT_CONFIG_PATH}")}
|
7
10
|
BANNER = "Usage: helmsnap CMD [options]"
|
8
11
|
|
9
12
|
def initialize(options)
|
10
13
|
self.options = options
|
11
|
-
self.args = Args.new
|
14
|
+
self.args = Args.new(DEFAULT_CONFIG_PATH)
|
12
15
|
self.parser = build_parser
|
13
16
|
end
|
14
17
|
|
15
18
|
def get_options!
|
16
19
|
parser.parse!(options)
|
17
|
-
raise MissingOption, "Missing option: CHARTDIR" unless args.chart_path
|
18
|
-
raise MissingOption, "Missing option: SNAPDIR" unless args.snapshots_path
|
19
|
-
raise MissingOption, "Missing option: VALUES" unless args.values_path
|
20
20
|
args
|
21
|
-
rescue OptionParser::ParseError => error
|
22
|
-
print_help!(error)
|
21
|
+
rescue OptionParser::ParseError, InvalidConfigPath => error
|
22
|
+
print_help!(error.message)
|
23
23
|
end
|
24
24
|
|
25
25
|
def print_help!(msg)
|
@@ -32,25 +32,23 @@ class Helmsnap::ArgsParser
|
|
32
32
|
|
33
33
|
attr_accessor :options, :parser, :args
|
34
34
|
|
35
|
-
def build_parser
|
35
|
+
def build_parser
|
36
36
|
OptionParser.new(BANNER, 50) do |opts|
|
37
|
-
opts.separator("Supported commands: `generate` and `
|
37
|
+
opts.separator("Supported commands: `generate`, `check` and `dependencies`.")
|
38
38
|
opts.separator("")
|
39
39
|
opts.separator("Specific options:")
|
40
40
|
|
41
|
-
opts.on("-c", "--
|
42
|
-
|
43
|
-
end
|
41
|
+
opts.on("-c", "--config CONFIG_PATH", CONFIG_PATH_HELP) do |val|
|
42
|
+
path = Pathname.new(val)
|
44
43
|
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
unless path.file? && path.readable?
|
45
|
+
raise InvalidConfigPath, "Not a readable file: #{val}"
|
46
|
+
end
|
48
47
|
|
49
|
-
|
50
|
-
args.values_path = pn(option)
|
48
|
+
args.config_path = path
|
51
49
|
end
|
52
50
|
|
53
|
-
opts.on("--version", "Show version") do
|
51
|
+
opts.on("-v", "--version", "Show version") do
|
54
52
|
Helmsnap::Console.print($stdout, "#{Helmsnap::VERSION}\n")
|
55
53
|
exit
|
56
54
|
end
|
@@ -61,8 +59,4 @@ class Helmsnap::ArgsParser
|
|
61
59
|
end
|
62
60
|
end
|
63
61
|
end
|
64
|
-
|
65
|
-
def pn(...)
|
66
|
-
Pathname.new(...)
|
67
|
-
end
|
68
62
|
end
|
data/lib/helmsnap/check.rb
CHANGED
@@ -1,26 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Helmsnap::Check < Helmsnap::Service
|
4
|
-
def initialize(
|
4
|
+
def initialize(config)
|
5
5
|
super()
|
6
|
-
self.
|
7
|
-
self.snapshots_path = snapshots_path
|
8
|
-
self.values_path = values_path
|
6
|
+
self.config = config
|
9
7
|
end
|
10
8
|
|
11
9
|
def call
|
12
10
|
temp_dir_path = Pathname.new(Dir.mktmpdir)
|
13
11
|
|
14
|
-
Helmsnap::Generate.call(
|
15
|
-
chart_path: chart_path,
|
16
|
-
snapshots_path: temp_dir_path,
|
17
|
-
values_path: values_path,
|
18
|
-
)
|
12
|
+
Helmsnap::Generate.call(config, snapshots_path: temp_dir_path)
|
19
13
|
|
20
14
|
result = run_cmd("which", "colordiff", allow_failure: true)
|
21
15
|
util = result.success ? "colordiff" : "diff"
|
22
16
|
|
23
|
-
cmd_parts = [util, "--unified", "--recursive", snapshots_path, temp_dir_path]
|
17
|
+
cmd_parts = [util, "--unified", "--recursive", config.snapshots_path, temp_dir_path]
|
24
18
|
diff = run_cmd(*cmd_parts, allow_failure: true).output
|
25
19
|
|
26
20
|
diff.strip.empty?
|
@@ -30,5 +24,5 @@ class Helmsnap::Check < Helmsnap::Service
|
|
30
24
|
|
31
25
|
private
|
32
26
|
|
33
|
-
attr_accessor :
|
27
|
+
attr_accessor :config
|
34
28
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Helmsnap::Config
|
4
|
+
attr_reader :envs, :snapshots_path
|
5
|
+
|
6
|
+
DEFAULT_ENV = "default"
|
7
|
+
DEFAULT_SNAPSHOTS_PATH = "helm/snapshots"
|
8
|
+
|
9
|
+
def initialize(config_path)
|
10
|
+
if config_path.exist?
|
11
|
+
yaml = YAML.load_file(config_path.to_s)
|
12
|
+
else
|
13
|
+
yaml = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
self.envs = parse_envs(yaml)
|
17
|
+
self.snapshots_path = parse_snaphots_path(yaml)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_writer :envs, :snapshots_path
|
23
|
+
|
24
|
+
def parse_envs(yaml)
|
25
|
+
value = yaml.fetch("envs", [DEFAULT_ENV])
|
26
|
+
value.map { |x| Helmsnap::Env.new(x) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse_snaphots_path(yaml)
|
30
|
+
Pathname.new(yaml.fetch("snapshotsPath", DEFAULT_SNAPSHOTS_PATH))
|
31
|
+
end
|
32
|
+
end
|
data/lib/helmsnap/env.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Helmsnap::Env
|
4
|
+
attr_reader :name
|
5
|
+
|
6
|
+
def initialize(name)
|
7
|
+
self.name = name
|
8
|
+
end
|
9
|
+
|
10
|
+
def release_paths
|
11
|
+
@release_paths ||= begin
|
12
|
+
json = Helmsnap.run_cmd("helmfile", "--environment", name, "list", "--output", "json").output
|
13
|
+
YAML.load(json).map { |x| x.fetch("chart") }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_writer :name
|
20
|
+
end
|
data/lib/helmsnap/generate.rb
CHANGED
@@ -1,21 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Helmsnap::Generate < Helmsnap::Service
|
4
|
-
def initialize(
|
4
|
+
def initialize(config, snapshots_path: nil)
|
5
5
|
super()
|
6
|
-
self.
|
7
|
-
self.snapshots_path = snapshots_path
|
8
|
-
self.values_path = values_path
|
6
|
+
self.config = config
|
7
|
+
self.snapshots_path = snapshots_path || config.snapshots_path
|
9
8
|
end
|
10
9
|
|
11
10
|
def call
|
12
|
-
Helmsnap::SetupDependencies.call(chart_path)
|
13
|
-
|
14
11
|
FileUtils.rmtree(snapshots_path)
|
15
12
|
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
Helmsnap::SetupDependencies.call(config)
|
14
|
+
|
15
|
+
config.envs.each do |env|
|
16
|
+
run_cmd(
|
17
|
+
"helmfile",
|
18
|
+
"--environment",
|
19
|
+
env.name,
|
20
|
+
"template",
|
21
|
+
"--output-dir-template",
|
22
|
+
snapshots_path.join(env.name).join("{{ .Release.Name }}"),
|
23
|
+
"--skip-deps",
|
24
|
+
)
|
25
|
+
end
|
19
26
|
|
20
27
|
snapshots_path.glob(["**/*yaml", "**/*.yml"]).each do |path|
|
21
28
|
content = path.read
|
@@ -26,5 +33,5 @@ class Helmsnap::Generate < Helmsnap::Service
|
|
26
33
|
|
27
34
|
private
|
28
35
|
|
29
|
-
attr_accessor :
|
36
|
+
attr_accessor :config, :snapshots_path
|
30
37
|
end
|
data/lib/helmsnap/runner.rb
CHANGED
@@ -9,6 +9,7 @@ class Helmsnap::Runner < Helmsnap::Service
|
|
9
9
|
def call
|
10
10
|
parser = Helmsnap::ArgsParser.new(args)
|
11
11
|
self.options = parser.get_options!
|
12
|
+
self.config = Helmsnap::Config.new(options.config_path)
|
12
13
|
|
13
14
|
cmd, *rest = args
|
14
15
|
|
@@ -25,6 +26,8 @@ class Helmsnap::Runner < Helmsnap::Service
|
|
25
26
|
generate!
|
26
27
|
when "check"
|
27
28
|
check!
|
29
|
+
when "dependencies", "deps"
|
30
|
+
setup_deps!
|
28
31
|
else
|
29
32
|
parser.print_help!("Unknown command: #{cmd}.")
|
30
33
|
end
|
@@ -32,25 +35,18 @@ class Helmsnap::Runner < Helmsnap::Service
|
|
32
35
|
|
33
36
|
private
|
34
37
|
|
35
|
-
attr_accessor :args, :options
|
38
|
+
attr_accessor :args, :options, :config
|
36
39
|
|
37
40
|
def generate!
|
38
|
-
Helmsnap::Generate.call(
|
41
|
+
Helmsnap::Generate.call(config)
|
39
42
|
Helmsnap::Console.info($stdout, "Snapshots generated successfully.")
|
40
43
|
end
|
41
44
|
|
42
45
|
def check!
|
43
|
-
if Helmsnap::Check.call(
|
46
|
+
if Helmsnap::Check.call(config)
|
44
47
|
Helmsnap::Console.info($stdout, "Snapshots are up-to-date.")
|
45
48
|
else
|
46
|
-
example_cmd = Shellwords.join(
|
47
|
-
[
|
48
|
-
"helmsnap", "generate",
|
49
|
-
"--chart-dir", options.chart_path,
|
50
|
-
"--snapshots-dir", options.snapshots_path,
|
51
|
-
"--values", options.values_path
|
52
|
-
],
|
53
|
-
)
|
49
|
+
example_cmd = Shellwords.join(["helmsnap", "generate", "--config", options.config_path])
|
54
50
|
|
55
51
|
Helmsnap::Console.error(
|
56
52
|
$stdout,
|
@@ -61,4 +57,8 @@ class Helmsnap::Runner < Helmsnap::Service
|
|
61
57
|
exit 1
|
62
58
|
end
|
63
59
|
end
|
60
|
+
|
61
|
+
def setup_deps!
|
62
|
+
Helmsnap::SetupDependencies.call(config)
|
63
|
+
end
|
64
64
|
end
|
@@ -1,12 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Helmsnap::SetupDependencies < Helmsnap::Service
|
4
|
-
def initialize(
|
4
|
+
def initialize(config)
|
5
5
|
super()
|
6
|
-
self.
|
6
|
+
self.config = config
|
7
7
|
end
|
8
8
|
|
9
9
|
def call
|
10
|
+
config.envs.flat_map(&:release_paths).uniq.each do |chart_path|
|
11
|
+
setup!(chart_path)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_accessor :config
|
18
|
+
|
19
|
+
def setup!(chart_path)
|
10
20
|
dep_list = run_cmd("helm", "dependency", "list", "--max-col-width", 0, chart_path).output
|
11
21
|
|
12
22
|
dep_list.scan(%r{file://(.+?)\t}) do |dep_path|
|
@@ -19,8 +29,4 @@ class Helmsnap::SetupDependencies < Helmsnap::Service
|
|
19
29
|
|
20
30
|
run_cmd("helm", "dependency", "update", "--skip-refresh", chart_path)
|
21
31
|
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
attr_accessor :chart_path
|
26
32
|
end
|
data/lib/helmsnap/version.rb
CHANGED
data/lib/helmsnap.rb
CHANGED
@@ -7,10 +7,13 @@ require "optparse"
|
|
7
7
|
require "pathname"
|
8
8
|
require "shellwords"
|
9
9
|
require "tmpdir"
|
10
|
+
require "yaml"
|
10
11
|
|
11
12
|
require "colorized_string"
|
12
13
|
|
13
14
|
module Helmsnap
|
15
|
+
require_relative "helmsnap/config"
|
16
|
+
require_relative "helmsnap/env"
|
14
17
|
require_relative "helmsnap/service"
|
15
18
|
require_relative "helmsnap/version"
|
16
19
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: helmsnap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.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-12-
|
11
|
+
date: 2021-12-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -63,7 +63,9 @@ files:
|
|
63
63
|
- lib/helmsnap/args_parser.rb
|
64
64
|
- lib/helmsnap/check.rb
|
65
65
|
- lib/helmsnap/command.rb
|
66
|
+
- lib/helmsnap/config.rb
|
66
67
|
- lib/helmsnap/console.rb
|
68
|
+
- lib/helmsnap/env.rb
|
67
69
|
- lib/helmsnap/generate.rb
|
68
70
|
- lib/helmsnap/runner.rb
|
69
71
|
- lib/helmsnap/service.rb
|