r10k 3.7.0 → 3.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8b9e50ee8c5d0887c46aaf294074c57626229cb19218fc08dd87f5ae1812888
4
- data.tar.gz: 96c59ea0332a86792c6922f354fdf5bed5c9cb31a37b608e09692fbce00cb1bb
3
+ metadata.gz: dddce45b116f972f8efe10ad99c330b60186cfbcdb8747fbab2edc37d8634efb
4
+ data.tar.gz: 26ac288406c3e4fb3862aa5ca64aba4b7ed102dcc407ba59d364f094b1491480
5
5
  SHA512:
6
- metadata.gz: 45e41d47b78d5951e533f6b2dece2f2f127610b6ac6a9d3d95927699eb11369159add33e467198e0b7869442c1803b5e183e02a44c00ccc8eadf43aabf263b8e
7
- data.tar.gz: 4790f4189c4e15502cfc633d52605e000a9607acc1b59ec2b011d14736eed3dbcb05333e3bb2817d6bad3e08603c8472267f3a19dca0d64c3f1df10c99eb4200
6
+ metadata.gz: dbe3bd1fbc2ca1f1fee87fb5ce9e613baf9783cd1a728f50e0de65718a50f7321609458a1e60817fee94ec32464b95ebf3ecf32dde5c065ccabe369fdc2c6b0d
7
+ data.tar.gz: 1d33d1f5772a71c0e6dae7b36730a810b8d2bc59e8ec71a4639b3e4f38a5394396dd62270a63032619c2a3cadeb1e1066a0242e8d9b4b1436933660aec806eab
data/CHANGELOG.mkd CHANGED
@@ -4,6 +4,13 @@ CHANGELOG
4
4
  Unreleased
5
5
  ----------
6
6
 
7
+ 3.8.0
8
+ -----
9
+
10
+ - When a forge module fails name validation the offending name will now be printed in the error message. [#1126](https://github.com/puppetlabs/r10k/pull/1126)
11
+ - Module ref resolution will now fall back to the normal default branch if the default branch override cannot be resolved. [#1122](https://github.com/puppetlabs/r10k/pull/1122)
12
+ - Experimental feature change: conflicts between environment-defined modules and Puppetfile-defined modules now default to logging a warning and deploying the environment module version, overriding the Puppetfile. Previously, conflicts would result in an error. The behavior is now configurable via the `module_conflicts` environment setting [#1107](https://github.com/puppetlabs/r10k/pull/1107)
13
+
7
14
  3.7.0
8
15
  -----
9
16
 
data/CODEOWNERS CHANGED
@@ -1,2 +1,2 @@
1
- * @puppetlabs/puppetserver-maintainers @adrienthebo @dhollinger
1
+ * @puppetlabs/puppetserver-maintainers
2
2
  /docker/ @puppetlabs/pupperware
@@ -673,6 +673,31 @@ modules:
673
673
  ref: 62d07f2
674
674
  ```
675
675
 
676
+ #### Puppetfile module conflicts
677
+
678
+ When a module is defined in an environment and also in a Puppetfile, the default behavior is for the environment definition of the module to take precedence, a warning to be logged, and the Puppetfile definition to be ignored. The behavior is configurable to optionally skip the warning, or allow a hard failure instead. Use the `module_conflicts` option in an environment definition to control this.
679
+
680
+ Available `module_conflicts` options:
681
+
682
+ * `override_and_warn` (default): the version of the module defined by the environment will be used, and the version defined in the Puppetfile will be ignored. A warning will be printed.
683
+ * `override`: the version of the module defined by the environment will be used, and the version defined in the Puppetfile will be ignored.
684
+ * `error`: an error will be raised alerting the user to the conflict. The environment will not be deployed.
685
+
686
+ ```yaml
687
+ # production.yaml
688
+ ---
689
+ type: git
690
+ remote: git@github.com:puppetlabs/control-repo.git
691
+ ref: 8820892
692
+ module_conflicts: override_puppetfile_and_warn
693
+ modules:
694
+ puppetlabs-stdlib: 6.0.0
695
+ puppetlabs-concat: 6.1.0
696
+ reidmv-xampl:
697
+ git: https://github.com/reidmv/reidmv-xampl.git
698
+ ref: 62d07f2
699
+ ```
700
+
676
701
  ### Bare Environment Type
677
702
 
678
703
  A "control repository" typically contains a hiera.yaml, an environment.conf, a manifests/site.pp file, and a few other things. However, none of these are strictly necessary for an environment to be functional if modules can be deployed to it.
@@ -66,11 +66,12 @@ Update a single environment and specify a default branch override:
66
66
 
67
67
  r10k deploy environment my_working_environment --puppetfile --default-branch-override default_branch_override
68
68
 
69
- This will update the given environment and update all contained modules, overrideing
70
- the :default_branch entry in the Puppetfile of each module. This is used primarily to allow
69
+ This will update the given environment and update all contained modules, overriding
70
+ the :default_branch entry in the Puppetfile of each module. If the specified override branch is not
71
+ found, it will fall back to the normal default branch and attempt to use that. This is used primarily to allow
71
72
  automated r10k solutions using the control_branch pattern with a temporary branch deployment to
72
- ensure the deployment is pushed to the correct module repository branch. Note that the :default_branch
73
- is only ever utilized if the desired ref cannot be located.
73
+ ensure the deployment is pushed to the correct module repository branch. Note that the :default_branch and its
74
+ override are only ever used if the specific desired ref cannot be located.
74
75
 
75
76
  ### Deploying modules
76
77
 
@@ -189,6 +189,8 @@ module R10K
189
189
  'generate-types': :self,
190
190
  'puppet-path': :self,
191
191
  'puppet-conf': :self,
192
+ 'private-key': :self,
193
+ 'oauth-token': :self,
192
194
  'default-branch-override': :self)
193
195
  end
194
196
  end
@@ -77,7 +77,9 @@ module R10K
77
77
  'no-force': :self,
78
78
  'generate-types': :self,
79
79
  'puppet-path': :self,
80
- 'puppet-conf': :self)
80
+ 'puppet-conf': :self,
81
+ 'private-key': :self,
82
+ 'oauth-token': :self)
81
83
  end
82
84
  end
83
85
  end
@@ -55,6 +55,10 @@ module R10K
55
55
  newval
56
56
  end
57
57
 
58
+ # Credentials from the CLI override both the global and per-repo
59
+ # credentials from the config, and so need to be handled specially
60
+ with_overrides = add_credential_overrides(with_overrides)
61
+
58
62
  @settings = R10K::Settings.global_settings.evaluate(with_overrides)
59
63
 
60
64
  R10K::Initializers::GlobalInitializer.new(@settings).call
@@ -92,6 +96,35 @@ module R10K
92
96
 
93
97
  results
94
98
  end
99
+
100
+ def add_credential_overrides(overrides)
101
+ sshkey_path = @opts[:'private-key']
102
+ token_path = @opts[:'oauth-token']
103
+
104
+ if sshkey_path && token_path
105
+ raise R10K::Error, "Cannot specify both an SSH key and a token to use with this deploy."
106
+ end
107
+
108
+ if sshkey_path
109
+ overrides[:git] ||= {}
110
+ overrides[:git][:private_key] = sshkey_path
111
+ if repo_settings = overrides[:git][:repositories]
112
+ repo_settings.each do |repo|
113
+ repo[:private_key] = sshkey_path
114
+ end
115
+ end
116
+ elsif token_path
117
+ overrides[:git] ||= {}
118
+ overrides[:git][:oauth_token] = token_path
119
+ if repo_settings = overrides[:git][:repositories]
120
+ repo_settings.each do |repo|
121
+ repo[:oauth_token] = token_path
122
+ end
123
+ end
124
+ end
125
+
126
+ overrides
127
+ end
95
128
  end
96
129
  end
97
130
  end
@@ -21,7 +21,7 @@ module R10K::CLI
21
21
  (https://puppet.com/docs/puppet/latest/environments_about.html).
22
22
  DESCRIPTION
23
23
 
24
- required nil, :cachedir, 'Specify a cachedir, overriding the value in config'
24
+ option nil, :cachedir, 'Specify a cachedir, overriding the value in config', argument: :required
25
25
  flag nil, :'no-force', 'Prevent the overwriting of local module modifications'
26
26
  flag nil, :'generate-types', 'Run `puppet generate types` after updating an environment'
27
27
  option nil, :'puppet-path', 'Path to puppet executable', argument: :required do |value, cmd|
@@ -32,6 +32,8 @@ module R10K::CLI
32
32
  end
33
33
  end
34
34
  option nil, :'puppet-conf', 'Path to puppet.conf', argument: :required
35
+ option nil, :'private-key', 'Path to SSH key to use when cloning. Only valid with rugged provider', argument: :required
36
+ option nil, :'oauth-token', 'Path to OAuth token to use when cloning. Only valid with rugged provider', argument: :required
35
37
 
36
38
  run do |opts, args, cmd|
37
39
  puts cmd.help(:verbose => opts[:verbose])
@@ -62,7 +64,8 @@ scheduled. On subsequent deployments, Puppetfile deployment will default to off.
62
64
  DESCRIPTION
63
65
 
64
66
  flag :p, :puppetfile, 'Deploy modules from a puppetfile'
65
- required nil, :'default-branch-override', 'Specify a branchname to override the default branch in the puppetfile'
67
+ option nil, :'default-branch-override', 'Specify a branchname to override the default branch in the puppetfile',
68
+ argument: :required
66
69
 
67
70
  runner R10K::Action::CriRunner.wrap(R10K::Action::Deploy::Environment)
68
71
  end
@@ -82,7 +85,7 @@ It will load the Puppetfile configurations out of all environments, and will
82
85
  try to deploy the given module names in all environments.
83
86
  DESCRIPTION
84
87
 
85
- required :e, :environment, 'Update the modules in the given environment'
88
+ option :e, :environment, 'Update the modules in the given environment', argument: :required
86
89
 
87
90
  runner R10K::Action::CriRunner.wrap(R10K::Action::Deploy::Module)
88
91
  end
@@ -100,7 +103,8 @@ try to deploy the given module names in all environments.
100
103
  flag :p, :puppetfile, 'Display Puppetfile modules'
101
104
  flag nil, :detail, 'Display detailed information'
102
105
  flag nil, :fetch, 'Update available environment lists from all remote sources'
103
- required nil, :format, 'Display output in a specific format. Valid values: json, yaml. Default: yaml'
106
+ option nil, :format, 'Display output in a specific format. Valid values: json, yaml. Default: yaml',
107
+ argument: :required
104
108
 
105
109
  runner R10K::Action::CriRunner.wrap(R10K::Action::Deploy::Display)
106
110
  end
@@ -30,8 +30,8 @@ Puppetfile (http://bombasticmonkey.com/librarian-puppet/).
30
30
  name 'install'
31
31
  usage 'install'
32
32
  summary 'Install all modules from a Puppetfile'
33
- required nil, :moduledir, 'Path to install modules to'
34
- required nil, :puppetfile, 'Path to puppetfile'
33
+ option nil, :moduledir, 'Path to install modules to', argument: :required
34
+ option nil, :puppetfile, 'Path to puppetfile', argument: :required
35
35
  flag nil, :force, 'Force locally changed files to be overwritten'
36
36
  runner R10K::Action::Puppetfile::CriRunner.wrap(R10K::Action::Puppetfile::Install)
37
37
  end
@@ -45,7 +45,7 @@ Puppetfile (http://bombasticmonkey.com/librarian-puppet/).
45
45
  usage 'check'
46
46
  summary 'Try and load the Puppetfile to verify the syntax is correct.'
47
47
 
48
- required nil, :puppetfile, 'Path to Puppetfile'
48
+ option nil, :puppetfile, 'Path to Puppetfile', argument: :required
49
49
  runner R10K::Action::Puppetfile::CriRunner.wrap(R10K::Action::Puppetfile::Check)
50
50
  end
51
51
  end
@@ -58,8 +58,8 @@ Puppetfile (http://bombasticmonkey.com/librarian-puppet/).
58
58
  usage 'purge'
59
59
  summary 'Purge unmanaged modules from a Puppetfile managed directory'
60
60
 
61
- required nil, :moduledir, 'Path to install modules to'
62
- required nil, :puppetfile, 'Path to Puppetfile'
61
+ option nil, :moduledir, 'Path to install modules to', argument: :required
62
+ option nil, :puppetfile, 'Path to Puppetfile', argument: :required
63
63
  runner R10K::Action::Puppetfile::CriRunner.wrap(R10K::Action::Puppetfile::Purge)
64
64
  end
65
65
  end
@@ -103,6 +103,13 @@ class R10K::Environment::Base
103
103
  @puppetfile.modules
104
104
  end
105
105
 
106
+ # @return [Array<R10K::Module::Base>] Whether or not the given module
107
+ # conflicts with any modules already defined in the r10k environment
108
+ # object.
109
+ def module_conflicts?(mod)
110
+ false
111
+ end
112
+
106
113
  def accept(visitor)
107
114
  visitor.visit(:environment, self) do
108
115
  puppetfile.accept(visitor)
@@ -46,10 +46,33 @@ class R10K::Environment::WithModules < R10K::Environment::Base
46
46
  # - The r10k environment object
47
47
  # - A Puppetfile in the environment's content
48
48
  def modules
49
- return @modules if @puppetfile.nil?
49
+ return @modules if puppetfile.nil?
50
50
 
51
- @puppetfile.load unless @puppetfile.loaded?
52
- @modules + @puppetfile.modules
51
+ puppetfile.load unless puppetfile.loaded?
52
+ @modules + puppetfile.modules
53
+ end
54
+
55
+ def module_conflicts?(mod_b)
56
+ conflict = @modules.any? { |mod_a| mod_a.name == mod_b.name }
57
+ return false unless conflict
58
+
59
+ msg_vars = {src: mod_b.origin, name: mod_b.name}
60
+ msg_error = _('Environment and %{src} both define the "%{name}" module' % msg_vars)
61
+ msg_continue = _("#{msg_error}. The %{src} definition will be ignored" % msg_vars)
62
+
63
+ case conflict_opt = @options[:module_conflicts]
64
+ when 'override_and_warn', nil
65
+ logger.warn msg_continue
66
+ when 'override'
67
+ logger.debug msg_continue
68
+ when 'error'
69
+ raise R10K::Error, msg_error
70
+ else
71
+ raise R10K::Error, _('Unexpected value for `module_conflicts` setting in %{env} ' \
72
+ 'environment: %{val}' % {env: self.name, val: conflict_opt})
73
+ end
74
+
75
+ true
53
76
  end
54
77
 
55
78
  def accept(visitor)
@@ -59,7 +82,6 @@ class R10K::Environment::WithModules < R10K::Environment::Base
59
82
  end
60
83
 
61
84
  puppetfile.accept(visitor)
62
- validate_no_module_conflicts
63
85
  end
64
86
  end
65
87
 
@@ -88,26 +110,12 @@ class R10K::Environment::WithModules < R10K::Environment::Base
88
110
  @managed_content[install_path] = Array.new unless @managed_content.has_key?(install_path)
89
111
 
90
112
  mod = R10K::Module.new(name, install_path, args, self.name)
91
- mod.origin = 'Environment'
113
+ mod.origin = :environment
92
114
 
93
115
  @managed_content[install_path] << mod.name
94
116
  @modules << mod
95
117
  end
96
118
 
97
- def validate_no_module_conflicts
98
- @puppetfile.load unless @puppetfile.loaded?
99
- conflicts = (@modules + @puppetfile.modules)
100
- .group_by { |mod| mod.name }
101
- .select { |_, v| v.size > 1 }
102
- .map(&:first)
103
- unless conflicts.empty?
104
- msg = _('Puppetfile cannot contain module names defined by environment %{name}') % {name: self.name}
105
- msg += ' '
106
- msg += _("Remove the conflicting definitions of the following modules: %{conflicts}" % { conflicts: conflicts.join(' ') })
107
- raise R10K::Error.new(msg)
108
- end
109
- end
110
-
111
119
  include R10K::Util::Purgeable
112
120
 
113
121
  # Returns an array of the full paths that can be purged.
data/lib/r10k/git.rb CHANGED
@@ -134,6 +134,7 @@ module R10K
134
134
  extend R10K::Settings::Mixin::ClassMethods
135
135
 
136
136
  def_setting_attr :private_key
137
+ def_setting_attr :oauth_token
137
138
  def_setting_attr :proxy
138
139
  def_setting_attr :username
139
140
  def_setting_attr :repositories, {}
@@ -61,11 +61,41 @@ class R10K::Git::Rugged::Credentials
61
61
  end
62
62
 
63
63
  def get_plaintext_credentials(url, username_from_url)
64
- user = get_git_username(url, username_from_url)
65
- password = URI.parse(url).password || ''
64
+ per_repo_oauth_token = nil
65
+ if per_repo_settings = R10K::Git.get_repo_settings(url)
66
+ per_repo_oauth_token = per_repo_settings[:oauth_token]
67
+ end
68
+
69
+ if token_path = per_repo_oauth_token || R10K::Git.settings[:oauth_token]
70
+ if token_path == '-'
71
+ token = $stdin.read.strip
72
+ logger.debug2 _("Using OAuth token from stdin for URL %{url}") % { url: url }
73
+ elsif File.readable?(token_path)
74
+ token = File.read(token_path).strip
75
+ logger.debug2 _("Using OAuth token from %{token_path} for URL %{url}") % { token_path: token_path, url: url }
76
+ else
77
+ raise R10K::Git::GitError, _("%{path} is missing or unreadable, cannot load OAuth token") % { path: token_path }
78
+ end
79
+
80
+ unless valid_token?(token)
81
+ raise R10K::Git::GitError, _("Supplied OAuth token contains invalid characters.")
82
+ end
83
+
84
+ user = 'x-oauth-token'
85
+ password = token
86
+ else
87
+ user = get_git_username(url, username_from_url)
88
+ password = URI.parse(url).password || ''
89
+ end
66
90
  Rugged::Credentials::UserPassword.new(username: user, password: password)
67
91
  end
68
92
 
93
+ # This regex is the only real requirement for OAuth token format,
94
+ # per https://www.oauth.com/oauth2-servers/access-tokens/access-token-response/
95
+ def valid_token?(token)
96
+ return token =~ /^[\w\-\.~\+\/]+$/
97
+ end
98
+
69
99
  def get_default_credentials(url, username_from_url)
70
100
  Rugged::Credentials::Default.new
71
101
  end
@@ -55,6 +55,7 @@ module R10K
55
55
  with_setting(:private_key) { |value| R10K::Git.settings[:private_key] = value }
56
56
  with_setting(:proxy) { |value| R10K::Git.settings[:proxy] = value }
57
57
  with_setting(:repositories) { |value| R10K::Git.settings[:repositories] = value }
58
+ with_setting(:oauth_token) { |value| R10K::Git.settings[:oauth_token] = value }
58
59
  end
59
60
  end
60
61
 
@@ -171,7 +171,7 @@ class R10K::Module::Forge < R10K::Module::Base
171
171
  if (match = title.match(/\A(\w+)[-\/](\w+)\Z/))
172
172
  [match[1], match[2]]
173
173
  else
174
- raise ArgumentError, _("Forge module names must match 'owner/modulename'")
174
+ raise ArgumentError, _("Forge module names must match 'owner/modulename', instead got #{title}")
175
175
  end
176
176
  end
177
177
  end
@@ -28,6 +28,11 @@ class R10K::Module::Git < R10K::Module::Base
28
28
  # @return [String]
29
29
  attr_reader :default_ref
30
30
 
31
+ # @!attribute [r] default_override_ref
32
+ # @api private
33
+ # @return [String]
34
+ attr_reader :default_override_ref
35
+
31
36
  def initialize(title, dirname, args, environment=nil)
32
37
  super
33
38
 
@@ -37,7 +42,7 @@ class R10K::Module::Git < R10K::Module::Base
37
42
  end
38
43
 
39
44
  def version
40
- validate_ref(@desired_ref, @default_ref)
45
+ validate_ref(@desired_ref, @default_ref, @default_override_ref)
41
46
  end
42
47
 
43
48
  def properties
@@ -63,9 +68,11 @@ class R10K::Module::Git < R10K::Module::Base
63
68
 
64
69
  private
65
70
 
66
- def validate_ref(desired, default)
71
+ def validate_ref(desired, default, default_override)
67
72
  if desired && desired != :control_branch && @repo.resolve(desired)
68
73
  return desired
74
+ elsif default_override && @repo.resolve(default_override)
75
+ return default_override
69
76
  elsif default && @repo.resolve(default)
70
77
  return default
71
78
  else
@@ -81,6 +88,11 @@ class R10K::Module::Git < R10K::Module::Base
81
88
  msg << "Could not determine desired ref"
82
89
  end
83
90
 
91
+ if default_override
92
+ msg << "or resolve the default branch override '%{default_override}',"
93
+ vars[:default_override] = default_override
94
+ end
95
+
84
96
  if default
85
97
  msg << "or resolve default ref '%{default}'"
86
98
  vars[:default] = default
@@ -94,7 +106,7 @@ class R10K::Module::Git < R10K::Module::Base
94
106
 
95
107
  def parse_options(options)
96
108
  ref_opts = [:branch, :tag, :commit, :ref]
97
- known_opts = [:git, :default_branch] + ref_opts
109
+ known_opts = [:git, :default_branch, :default_branch_override] + ref_opts
98
110
 
99
111
  unhandled = options.keys - known_opts
100
112
  unless unhandled.empty?
@@ -105,6 +117,7 @@ class R10K::Module::Git < R10K::Module::Base
105
117
 
106
118
  @desired_ref = ref_opts.find { |key| break options[key] if options.has_key?(key) } || 'master'
107
119
  @default_ref = options[:default_branch]
120
+ @default_override_ref = options[:default_branch_override]
108
121
 
109
122
  if @desired_ref == :control_branch && @environment && @environment.respond_to?(:ref)
110
123
  @desired_ref = @environment.ref