Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement strip_component source feature #1128

Merged
merged 2 commits into from
Mar 9, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.mkd
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ CHANGELOG
Unreleased
----------

- Add 'strip\_component' environment source configuration setting, to allow deploying Git branches named like "env/production" as Puppet environments named like "production". [#1128](https://github.com/puppetlabs/r10k/pull/1128)

3.8.0
-----

Expand Down
17 changes: 17 additions & 0 deletions doc/dynamic-environments/configuration.mkd
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,23 @@ sources:
* if `false` (default) environment folder will not be prefixed
* if `String` environment folder will be prefixed with the `prefix` value.

### strip\_component

The 'strip\_component' setting allows parts of environment names from a source to have a transformation applied, removing a part of the name before turning them into Puppet environments. This is primarily useful for VCS sources (e.g. Git), because it allows branch names to use prefixes or organizing name components such as "env/production", "env/development", but deploy Puppet environments from these branches named without the leading "env/" component. E.g. "production", "development".

```yaml
---
sources:
mysource:
basedir: '/etc/puppet/environments'
strip_component: 'env/'
```

#### strip\_component behaviour

* if `string` environment names will have this prefix removed, if the prefix is present. Note that when string values are used, names can only have prefix components removed.
* if `/regex/` the regex will be matched against environment names and if a match is found, the matching name component will be removed.

### ignore_branch_prefixes

The 'ignore_branch_prefixes' setting causes environments to be ignored which match in part or whole
Expand Down
24 changes: 20 additions & 4 deletions lib/r10k/environment/name.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ class Name
INVALID_CHARACTERS = %r[\W]

def initialize(name, opts)
@name = name
@opts = opts

@source = opts[:source]
@prefix = opts[:prefix]
@invalid = opts[:invalid]

@name = derive_name(name, opts[:strip_component])
@opts = opts

case @invalid
when 'correct_and_warn'
@validate = true
Expand Down Expand Up @@ -71,8 +71,24 @@ def dirname

private

def derive_prefix(source,prefix)
def derive_name(name, strip_component)
if strip_component == nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is false a value you could see users put in here? Should we instead handle "falsey" values?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that would make sense. Updated to handle "falsey" instead of nil.

name
elsif %r{^/.*/$}.match(strip_component)
regex = Regexp.new(strip_component[1..-2])
name.gsub(regex, '')
elsif name.start_with?(strip_component)
name[strip_component.size..-1]
else
name
end
rescue TypeError, NoMethodError
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, it's simpler to test the class of the input then doing a rescue afterwards.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Funny how code structure sometimes reveals developer (my) chronological thought process, and highlights afterthoughts 😅 . Pushing improvement.

raise _('Improper configuration value given for strip_component setting in %{src} source. ' \
'Value must be a string, a /regex/, or omitted. Got "%{val}" (%{type})' \
% {src: @source, val: strip_component, type: strip_component.class})
end

def derive_prefix(source,prefix)
if prefix == true
"#{source}_"
elsif prefix.is_a? String
Expand Down
5 changes: 5 additions & 0 deletions lib/r10k/source/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,15 @@ class R10K::Source::Base
# @option options [Boolean, String] :prefix If a String this becomes the prefix.
# If true, will use the source name as the prefix. All sources should respect this option.
# Defaults to false for no environment prefix.
# @option options [String] :strip_component If a string, this value will be
# removed from the beginning of each generated environment's name, if
# present. If the string is contained within two "/" characters, it will
# be treated as a regular expression.
def initialize(name, basedir, options = {})
@name = name
@basedir = Pathname.new(basedir).cleanpath.to_s
@prefix = options.delete(:prefix)
@strip_component = options.delete(:strip_component)
@puppetfile_name = options.delete(:puppetfile_name)
@options = options
end
Expand Down
5 changes: 4 additions & 1 deletion lib/r10k/source/git.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,10 @@ def filter_branches_by_command(branches, command)
private

def branch_names
opts = {:prefix => @prefix, :invalid => @invalid_branches, :source => @name}
opts = {prefix: @prefix,
invalid: @invalid_branches,
source: @name,
strip_component: @strip_component}
branches = @cache.branches
if @ignore_branch_prefixes && !@ignore_branch_prefixes.empty?
branches = filter_branches_by_regexp(branches, @ignore_branch_prefixes)
Expand Down
6 changes: 4 additions & 2 deletions lib/r10k/source/hash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,10 @@ def set_environments_hash(hash)
R10K::Util::SymbolizeKeys.symbolize_keys!(opts)
memo.merge({
name => opts.merge({
:basedir => @basedir,
:dirname => R10K::Environment::Name.new(name, {prefix: @prefix, source: @name}).dirname
basedir: @basedir,
dirname: R10K::Environment::Name.new(name, {prefix: @prefix,
source: @name,
strip_component: @strip_component}).dirname
})
})
end
Expand Down
6 changes: 5 additions & 1 deletion lib/r10k/source/svn.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,11 @@ def filter_branches(branches, ignore_prefixes)

def names_and_paths
branches = []
opts = {:prefix => @prefix, :correct => false, :validate => false, :source => @name}
opts = {prefix: @prefix,
correct: false,
validate: false,
source: @name,
strip_component: @strip_component}
branches << [R10K::Environment::Name.new('production', opts), "#{@remote}/trunk"]
additional_branch_names = @svn_remote.branches
if @ignore_branch_prefixes && !@ignore_branch_prefixes.empty?
Expand Down
28 changes: 28 additions & 0 deletions spec/unit/environment/name_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,34 @@
require 'r10k/environment/name'

describe R10K::Environment::Name do
describe "strip_component" do
it "does not modify the given name when no strip_component is given" do
bn = described_class.new('myenv', source: 'source', prefix: false)
expect(bn.dirname).to eq 'myenv'
end

it "removes the first occurance of a regex match when a regex is given" do
bn = described_class.new('myenv', source: 'source', prefix: false, strip_component: '/env/')
expect(bn.dirname).to eq 'my'
end

it "does not modify the given name when there is no regex match" do
bn = described_class.new('myenv', source: 'source', prefix: false, strip_component: '/bar/')
expect(bn.dirname).to eq 'myenv'
end

it "removes the given name's prefix when it matches strip_component" do
bn = described_class.new('env/prod', source: 'source', prefix: false, strip_component: 'env/')
expect(bn.dirname).to eq 'prod'
end

it "raises an error when given an integer" do
expect {
described_class.new('env/prod', source: 'source', prefix: false, strip_component: 4)
}.to raise_error(%r{Improper.*"4"})
end
end

describe "prefixing" do
it "uses the branch name as the dirname when prefixing is off" do
bn = described_class.new('mybranch', :source => 'source', :prefix => false)
Expand Down