Class: PDK::Util::TemplateURI

Inherits:
Object
  • Object
show all
Defined in:
lib/pdk/util/template_uri.rb

Constant Summary collapse

SCP_PATTERN =
%r{\A(?!\w+://)(?:(?<user>.+?)@)?(?<host>[^:/]+):(?<path>.+)\z}
PACKAGED_TEMPLATE_KEYWORD =
'pdk-default'.freeze
DEPRECATED_TEMPLATE_URL =
'https://github.com/puppetlabs/pdk-module-template'.freeze
LEGACY_PACKAGED_TEMPLATE_PATHS =
{
  'windows' => 'file:///C:/Program Files/Puppet Labs/DevelopmentKit/share/cache/pdk-templates.git',
  'macos'   => 'file:///opt/puppetlabs/pdk/share/cache/pdk-templates.git',
  'linux'   => 'file:///opt/puppetlabs/pdk/share/cache/pdk-templates.git',
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts_or_uri) ⇒ TemplateURI

input/output formats:

file:///c:/foo (git clone location) c:/foo (shell paths) file:///c:/foo#master (only for metadata) c:/foo#master (only for metadata)

non output formats:

/c:/foo (internal use only) /c:/foo#master (internal use only)



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/pdk/util/template_uri.rb', line 34

def initialize(opts_or_uri)
  require 'addressable'

  # If a uri string is passed, skip the valid uri finding code.
  @uri = if opts_or_uri.is_a?(self.class)
           opts_or_uri.uri
         elsif opts_or_uri.is_a?(String)
           begin
             uri, ref = opts_or_uri.split('#', 2)
             if self.class.packaged_template?(uri)
               self.class.default_template_uri(ref).uri
             else
               Addressable::URI.parse(opts_or_uri)
             end
           rescue Addressable::URI::InvalidURIError
             raise PDK::CLI::FatalError, _('PDK::Util::TemplateURI attempted initialization with a non-uri string: {string}') % { string: opts_or_uri }
           end
         elsif opts_or_uri.is_a?(Addressable::URI)
           opts_or_uri.dup
         else
           self.class.first_valid_uri(self.class.templates(opts_or_uri))
         end
end

Instance Attribute Details

#uriObject (readonly)

XXX Previously

  • template_uri used to get the string form of the uri when generating the module and written to pdk answers and metadata

  • template_path or deuri_path used for humans to see and commands to run

  • uri_path used only internally by the template selection code; move out

  • template_ref used by git checkout



20
21
22
# File 'lib/pdk/util/template_uri.rb', line 20

def uri
  @uri
end

Class Method Details

.default_template_ref(uri = nil) ⇒ Object



224
225
226
227
228
229
230
231
232
233
# File 'lib/pdk/util/template_uri.rb', line 224

def self.default_template_ref(uri = nil)
  require 'pdk/util'
  require 'pdk/version'

  return 'master' if PDK::Util.development_mode?
  return PDK::TEMPLATE_REF if uri.nil?

  uri = new(uri) unless uri.is_a?(self)
  uri.default? ? PDK::TEMPLATE_REF : 'master'
end

.default_template_uri(ref = nil) ⇒ Object



109
110
111
112
113
114
115
116
117
118
# File 'lib/pdk/util/template_uri.rb', line 109

def self.default_template_uri(ref = nil)
  require 'pdk/util'
  require 'addressable'

  if PDK::Util.package_install?
    PDK::Util::TemplateURI.new(Addressable::URI.new(scheme: 'file', host: '', path: File.join(PDK::Util.package_cachedir, 'pdk-templates.git'), fragment: ref))
  else
    PDK::Util::TemplateURI.new(Addressable::URI.new(scheme: 'https', host: 'github.com', path: '/puppetlabs/pdk-templates', fragment: ref))
  end
end

.first_valid_uri(templates_array) ⇒ Object



236
237
238
239
240
241
242
243
244
245
# File 'lib/pdk/util/template_uri.rb', line 236

def self.first_valid_uri(templates_array)
  # 1. Get the four sources of URIs
  # 2. Pick the first non-nil URI
  # 3. Error if the URI is not a valid git repo (missing directory or http 404)
  # 4. Leave updating answers/metadata to other code
  found_template = templates_array.find { |t| valid_template?(t) }

  raise PDK::CLI::FatalError, _('Unable to find a valid module template to use.') if found_template.nil?
  found_template[:uri]
end

.git_remote(uri) ⇒ Object



83
84
85
86
87
88
89
90
91
# File 'lib/pdk/util/template_uri.rb', line 83

def self.git_remote(uri)
  require 'addressable'

  if uri.is_a?(Addressable::URI) && uri.fragment
    human_readable(uri.to_s.chomp('#' + uri.fragment))
  else
    human_readable(uri.to_s)
  end
end

.human_readable(string) ⇒ Object

If the passed value is a URI-safe windows path such as ‘/C:…` then it should be changed to a human-friendly `C:…` form. Otherwise the passed value is left alone.



145
146
147
# File 'lib/pdk/util/template_uri.rb', line 145

def self.human_readable(string)
  (Gem.win_platform? && string =~ %r{^\/[a-zA-Z][\|:]}) ? string[1..-1] : string
end

.packaged_template?(path) ⇒ Boolean

Returns:

  • (Boolean)


276
277
278
# File 'lib/pdk/util/template_uri.rb', line 276

def self.packaged_template?(path)
  path == PACKAGED_TEMPLATE_KEYWORD || LEGACY_PACKAGED_TEMPLATE_PATHS.value?(path)
end

.parse_scp_url(url) ⇒ Object



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/pdk/util/template_uri.rb', line 149

def self.parse_scp_url(url)
  require 'pathname'
  require 'addressable'

  # Valid URIs to avoid catching:
  # - absolute local paths
  # - have :'s in paths when preceeded by a slash
  # - have only digits following the : and preceeding a / or end-of-string that is 0-65535
  # The last item is ambiguous in the case of scp/git paths vs. URI port
  # numbers, but can be made unambiguous by making the form to
  # ssh://[email protected]/1234/repo.git or
  # ssh://[email protected]:1234/user/repo.git
  scp_url = url.match(SCP_PATTERN)
  return url unless Pathname.new(url).relative? && scp_url

  uri = Addressable::URI.new(scheme: 'ssh', user: scp_url[:user], host: scp_url[:host], path: scp_url[:path])
  PDK.logger.warn _('%{scp_uri} appears to be an SCP style URL; it will be converted to an RFC compliant URI: %{rfc_uri}') % {
    scp_uri: url,
    rfc_uri: uri.to_s,
  }

  uri.to_s
end

.templates(opts) ⇒ Array<Hash{Symbol => Object}>

Returns an array of hashes. Each hash contains 3 keys: :type contains a String that describes the template directory, :url contains a String with the URL to the template directory, and :allow_fallback contains a Boolean that specifies if the lookup process should proceed to the next template directory if the template file is not in this template directory.

Returns:

  • (Array<Hash{Symbol => Object}>)

    an array of hashes. Each hash contains 3 keys: :type contains a String that describes the template directory, :url contains a String with the URL to the template directory, and :allow_fallback contains a Boolean that specifies if the lookup process should proceed to the next template directory if the template file is not in this template directory.



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/pdk/util/template_uri.rb', line 180

def self.templates(opts)
  require 'pdk/answer_file'
  require 'pdk/util'
  require 'addressable'

  explicit_url = opts.fetch(:'template-url', nil)
  explicit_ref = opts.fetch(:'template-ref', nil)

  # 1. Get the CLI, metadata (or answers if no metadata), and default URIs
  # 2. Construct the hash
  if explicit_url
    explicit_uri = Addressable::URI.parse(uri_safe(explicit_url))
    explicit_uri.fragment = explicit_ref || default_template_ref(new(explicit_uri))
  else
    explicit_uri = nil
  end
   = if PDK::Util.module_root && File.file?(File.join(PDK::Util.module_root, 'metadata.json'))
                   if PDK::Util.['template-url']
                     new(uri_safe(PDK::Util.['template-url'])).uri
                   else
                     nil
                   end
                 else
                   nil
                 end
  answers_uri = if [PACKAGED_TEMPLATE_KEYWORD, DEPRECATED_TEMPLATE_URL].include?(PDK.answers['template-url'])
                  Addressable::URI.parse(default_template_uri)
                elsif PDK.answers['template-url']
                  new(uri_safe(PDK.answers['template-url'])).uri
                else
                  nil
                end
  default_uri = default_template_uri.uri
  default_uri.fragment = default_template_ref(default_template_uri)

  ary = []
  ary << { type: _('--template-url'), uri: explicit_uri, allow_fallback: false } if explicit_url
  ary << { type: _('metadata.json'), uri: , allow_fallback: true } if 
  ary << { type: _('PDK answers'), uri: answers_uri, allow_fallback: true } if answers_uri
  ary << { type: _('default'), uri: default_uri, allow_fallback: false }
  ary
end

.uri_safe(string) ⇒ Object

‘C:…` urls are not URI-safe. They should be of the form `/C:…` to be URI-safe. scp-like urls like `user@host:/path` are not URI-safe either and so are subsequently converted to ssh:// URIs.



135
136
137
138
# File 'lib/pdk/util/template_uri.rb', line 135

def self.uri_safe(string)
  url = (Gem.win_platform? && string =~ %r{^[a-zA-Z][\|:]}) ? "/#{string}" : string
  parse_scp_url(url)
end

.valid_template?(template) ⇒ Boolean

Returns:

  • (Boolean)


247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/pdk/util/template_uri.rb', line 247

def self.valid_template?(template)
  require 'addressable'
  require 'pdk/util/git'
  require 'pdk/module/templatedir'

  return false if template.nil? || !template.is_a?(Hash)
  return false if template[:uri].nil? || !template[:uri].is_a?(Addressable::URI)

  return true if PDK::Util::Git.repo?(git_remote(template[:uri]))

  path = human_readable(template[:uri].path)
  if File.directory?(path)
    begin
      PDK::Module::TemplateDir.new(path) {}
      return true
    rescue ArgumentError
      nil
    end
  end

  unless template[:allow_fallback]
    raise PDK::CLI::FatalError, _('Unable to find a valid template at %{uri}') % {
      uri: template[:uri].to_s,
    }
  end

  false
end

Instance Method Details

#==(other) ⇒ Object



58
59
60
# File 'lib/pdk/util/template_uri.rb', line 58

def ==(other)
  @uri == other.uri
end

#default?Boolean

Returns:

  • (Boolean)


120
121
122
# File 'lib/pdk/util/template_uri.rb', line 120

def default?
  git_remote == self.class.default_template_uri.git_remote
end

#git_refObject



100
101
102
# File 'lib/pdk/util/template_uri.rb', line 100

def git_ref
  @uri.fragment || self.class.default_template_ref(self)
end

#git_ref=(ref) ⇒ Object



104
105
106
# File 'lib/pdk/util/template_uri.rb', line 104

def git_ref=(ref)
  @uri.fragment = ref
end

#git_remoteObject

This is the url without a fragment, suitable for git clones.



79
80
81
# File 'lib/pdk/util/template_uri.rb', line 79

def git_remote
  self.class.git_remote(@uri)
end

#metadata_formatObject Also known as: to_s, to_str

This is the URI represented in a format suitable for writing to metadata.



66
67
68
69
70
71
72
# File 'lib/pdk/util/template_uri.rb', line 66

def 
  if self.class.packaged_template?(git_remote)
    self.class.human_readable("pdk-default##{git_ref}")
  else
    self.class.human_readable(@uri.to_s)
  end
end

#ref_is_tag?Boolean

Returns:

  • (Boolean)


124
125
126
127
128
# File 'lib/pdk/util/template_uri.rb', line 124

def ref_is_tag?
  require 'pdk/util/git'

  PDK::Util::Git.git('ls-remote', '--tags', '--exit-code', git_remote, git_ref)[:exit_code].zero?
end

#shell_pathObject

This is the path of the URI, suitable for accessing directly from the shell.



95
96
97
# File 'lib/pdk/util/template_uri.rb', line 95

def shell_path
  self.class.human_readable(@uri.path)
end