Skip to content

Commit

Permalink
Allow to inject custom labels via --label option (#91)
Browse files Browse the repository at this point in the history
* allow to inject custom labels via --label option

* allow to set labels via config

* docs

* rubocop
  • Loading branch information
jakolehm authored and kke committed Sep 30, 2019
1 parent 9d11dc0 commit 2dbe47f
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 10 deletions.
27 changes: 22 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ See examples at [examples/templates](examples/templates).

As for any process, environment variables are also available for Mortar during template processing.

```
```yaml
kind: Pod
apiVersion: v1
metadata:
Expand All @@ -168,7 +168,7 @@ Another option to use variables is via command-line options. Use `mortar --var f

Each of the variables defined will be available in the template via `var.<variable name>`.

```
```yaml
kind: Pod
apiVersion: v1
metadata:
Expand All @@ -189,8 +189,9 @@ You could shoot this resource with `mortar --var port.name=some-port --var port.

### Shot configuration file

It is also possible to pass both [variables](#variables) and [overlays](#overlays) through a configuration file. As your templates complexity and the amount of variables used grows, it might be easier to manage the variables with an yaml configuration file. The config file has the following syntax:
```
It is also possible to pass [variables](#variables), [overlays](#overlays) and [labels](#labels) through a configuration file. As your templates complexity and the amount of variables used grows, it might be easier to manage the variables with an yaml configuration file. The config file has the following syntax:

```yaml
variables:
ports:
- name: http
Expand All @@ -202,12 +203,28 @@ overlays:
- bar
```

Both `variables` and `overlays` are optional.
`variables`, `overlays` and `labels` are optional.

For variables the hash is translated into a `RecursiveOpenStruct` object. What that means is that you can access each element with dotted path notation, just like the vars given through `--var` option. And of course arrays can be looped etc.. Check examples folder how to use variables effectively.

The configuration file can be given using `-c` option to `mortar fire` command. By default Mortar will look for `shot.yml` or `shot.yaml` files present in current working directory.

### Labels

It's possible to set global labels for all resources in a shot via options or configuration file.

#### Labels via options

Use `mortar --label foo=bar my-app resource` to set label to all resources in a shot.

#### Labels via configuration file

```yaml
labels:
foo: bar
bar: baz
```

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/kontena/mortar.
Expand Down
25 changes: 23 additions & 2 deletions lib/mortar/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,46 @@ def self.load(path)
raise ConfigError, "Failed to load config, check config file syntax" unless cfg.is_a? Hash
raise ConfigError, "Failed to load config, overlays needs to be an array" if cfg.key?('overlays') && !cfg['overlays'].is_a?(Array)

new(variables: cfg['variables'] || {}, overlays: cfg['overlays'] || [])
if cfg.key?('labels')
raise ConfigError, "Failed to load config, labels needs to be a hash" if !cfg['labels'].is_a?(Hash)
raise ConfigError, "Failed to load config, label values need to be strings" if cfg['labels'].values.any? { |value| !value.is_a?(String) }
end

new(
variables: cfg['variables'] || {},
overlays: cfg['overlays'] || [],
labels: cfg['labels'] || {}
)
end

def initialize(variables: {}, overlays: [])
def initialize(variables: {}, overlays: [], labels: {})
@variables = variables
@overlays = overlays
@labels = labels
end

# @param other [Hash]
# @return [RecursiveOpenStruct]
def variables(other = {})
hash = @variables.dup
hash.deep_merge!(other)
RecursiveOpenStruct.new(hash, recurse_over_arrays: true)
end

# @param other [Array<Object>]
# @return [Array<Object>]
def overlays(other = [])
return @overlays unless other

(@overlays + other).uniq.compact
end

# @param other [Hash]
# @return [RecursiveOpenStruct]
def labels(other = {})
hash = @labels.dup
hash.merge!(other)
RecursiveOpenStruct.new(hash)
end
end
end
33 changes: 33 additions & 0 deletions lib/mortar/fire_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class FireCommand < Mortar::Command
parameter "NAME", "deployment name"

option ["--var"], "VAR", "set template variables", multivalued: true
option ["--label"], "LABEL", "extra labels that are set to all resources", multivalued: true
option ["--output"], :flag, "only output generated yaml"
option ["--[no-]prune"], :flag, "automatically delete removed resources", default: true
option ["--overlay"], "OVERLAY", "overlay dirs", multivalued: true
Expand Down Expand Up @@ -45,6 +46,7 @@ def execute
load_config

resources = process_overlays
resources = inject_extra_labels(resources, process_extra_labels)

if output?
puts resources_output(resources)
Expand Down Expand Up @@ -89,6 +91,37 @@ def process_overlays
resources
end

# @return [Hash]
def extra_labels
return @extra_labels if @extra_labels

@extra_labels = {}
label_list.each do |label|
key, value = label.split('=')
@extra_labels[key] = value
end

@extra_labels
end

# @return [Hash]
def process_extra_labels
@config.labels(extra_labels)
end

# @param resources [Array<K8s::Resource>]
# @param labels [Hash]
# @return [Array<K8s::Resource>]
def inject_extra_labels(resources, labels)
resources.map { |resource|
resource.merge(
metadata: {
labels: labels
}
)
}
end

# @return [RecursiveOpenStruct]
def variables_struct
@variables_struct ||= @configuration.variables(variables_hash)
Expand Down
16 changes: 13 additions & 3 deletions spec/config_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
RSpec.describe Mortar::Config do

describe '#self.load' do
it 'raises on empty config' do
expect {
Expand All @@ -13,18 +12,29 @@
expect(vars.foo).to eq('bar')
expect(vars.some.deeper).to eq('variable')
expect(cfg.overlays).to eq([])
expect(cfg.labels.to_h).to eq({})
end

it 'loads overlays from file' do
cfg = described_class.load(fixture_path('config/config_overlays.yaml'))
expect(cfg.overlays).to eq(['foo', 'bar'])
end

it 'loads labels from file' do
cfg = described_class.load(fixture_path('config/config_labels.yaml'))
expect(cfg.labels.foo).to eq('bar')
end

it 'raises on non array overlays' do
expect {
described_class.load(fixture_path('config/config_overlays_error.yaml'))
}.to raise_error(Mortar::Config::ConfigError, 'Failed to load config, overlays needs to be an array')
end
end

end
it 'raises on non hash labels' do
expect {
described_class.load(fixture_path('config/config_labels_error.yaml'))
}.to raise_error(Mortar::Config::ConfigError, 'Failed to load config, labels needs to be a hash')
end
end
end
45 changes: 45 additions & 0 deletions spec/fire_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,49 @@
expect(subject.variables_hash).to eq({})
end
end

describe "#extra_labels" do
let(:subject) { described_class.new('') }

it 'returns empty hash by default' do
expect(subject.extra_labels).to eq({})
end

it 'returns label has if label options are given' do
subject.parse(["--label", "foo=bar", "--label", "bar=baz", "foobar", "foobar"])
expect(subject.extra_labels).to eq({
"foo" => "bar", "bar" => "baz"
})
end
end

describe "#inject_extra_labels" do
let(:subject) { described_class.new('') }
let(:resources) do
[
K8s::Resource.new({
metadata: {
labels: {
userlabel: 'test'
}
}
}),
K8s::Resource.new({
metadata: {
name: 'foo'
}
})
]
end

it 'injects labels to resources' do
extra_labels = { "foo" => "bar", "bar" => "baz" }
result = subject.inject_extra_labels(resources, extra_labels)
expect(result.first.metadata.labels.to_h).to eq({
bar: "baz",
foo: "bar",
userlabel: "test"
})
end
end
end
2 changes: 2 additions & 0 deletions spec/fixtures/config/config_labels.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
labels:
foo: bar
2 changes: 2 additions & 0 deletions spec/fixtures/config/config_labels_error.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
labels:
- foo=bar

0 comments on commit 2dbe47f

Please sign in to comment.