r10k 3.7.0 → 3.8.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: 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