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
PDK_TEMPLATE_URL =
'https://github.com/puppetlabs/pdk-templates'.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)



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

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 PDK::Util::TemplateURI.packaged_template?(uri)
               PDK::Util::TemplateURI.default_template_addressable_uri.tap { |default| default.fragment = ref unless ref.nil? || ref.empty? }
             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
           PDK::Util::TemplateURI.first_valid_uri(PDK::Util::TemplateURI.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



23
24
25
# File 'lib/pdk/util/template_uri.rb', line 23

def uri
  @uri
end

Class Method Details

.bare_uri(uri) ⇒ Object

Remove the fragment off of URI. Useful for removing the branch for Git based URIs



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

def self.bare_uri(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

.default_template_addressable_uriObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



135
136
137
138
139
140
141
142
143
144
# File 'lib/pdk/util/template_uri.rb', line 135

def self.default_template_addressable_uri
  require 'pdk/util'
  require 'addressable'

  if PDK::Util.package_install?
    Addressable::URI.new(scheme: 'file', host: '', path: File.join(PDK::Util.package_cachedir, 'pdk-templates.git'))
  else
    Addressable::URI.parse(PDK_TEMPLATE_URL)
  end
end

.default_template_ref(uri = nil) ⇒ Object



240
241
242
243
244
245
246
247
248
249
# File 'lib/pdk/util/template_uri.rb', line 240

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_uriObject



126
127
128
129
130
131
# File 'lib/pdk/util/template_uri.rb', line 126

def self.default_template_uri
  require 'pdk/util'
  require 'addressable'

  PDK::Util::TemplateURI.new(default_template_addressable_uri)
end

.first_valid_uri(templates_array) ⇒ Object



252
253
254
255
256
257
258
259
260
261
# File 'lib/pdk/util/template_uri.rb', line 252

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

.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.



161
162
163
# File 'lib/pdk/util/template_uri.rb', line 161

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

.packaged_template?(path) ⇒ Boolean

Returns:

  • (Boolean)


290
291
292
# File 'lib/pdk/util/template_uri.rb', line 290

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

.parse_scp_url(url) ⇒ Object



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/pdk/util/template_uri.rb', line 165

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.



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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/pdk/util/template_uri.rb', line 195

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 && PDK::Util::Filesystem.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
  default_template_url = PDK.config.get_within_scopes('module_defaults.template-url')
  answers_uri = if [PACKAGED_TEMPLATE_KEYWORD, DEPRECATED_TEMPLATE_URL].include?(default_template_url)
                  Addressable::URI.parse(default_template_uri)
                elsif default_template_url
                  new(uri_safe(default_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.



151
152
153
154
# File 'lib/pdk/util/template_uri.rb', line 151

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

.valid_template?(template, context = PDK.context) ⇒ Boolean

Returns:

  • (Boolean)


263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/pdk/util/template_uri.rb', line 263

def self.valid_template?(template, context = PDK.context)
  require 'addressable'

  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?(bare_uri(template[:uri]))
  path = human_readable(template[:uri].path)
  if PDK::Util::Filesystem.directory?(path)
    # We know that it's not a git repository, but it's a valid path on disk
    begin
      renderer = PDK::Template::Renderer.instance(path, template[:uri], context)
      return !renderer.nil?
    rescue StandardError
      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



60
61
62
# File 'lib/pdk/util/template_uri.rb', line 60

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

#bare_uriObject



64
65
66
# File 'lib/pdk/util/template_uri.rb', line 64

def bare_uri
  PDK::Util::TemplateURI.bare_uri(@uri)
end

#default?Boolean

Returns:

  • (Boolean)


93
94
95
# File 'lib/pdk/util/template_uri.rb', line 93

def default?
  bare_uri == PDK::Util::TemplateURI.bare_uri(PDK::Util::TemplateURI.default_template_addressable_uri)
end

#default_ref?Boolean

Returns:

  • (Boolean)


97
98
99
# File 'lib/pdk/util/template_uri.rb', line 97

def default_ref?
  uri_fragment == self.class.default_template_ref(self)
end

#metadata_formatObject Also known as: to_s, to_str

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



72
73
74
75
76
77
78
# File 'lib/pdk/util/template_uri.rb', line 72

def 
  @metadata_format ||= if PDK::Util::TemplateURI.packaged_template?(bare_uri)
                         PDK::Util::TemplateURI.human_readable("pdk-default##{uri_fragment}")
                       else
                         PDK::Util::TemplateURI.human_readable(@uri.to_s)
                       end
end

#puppetlabs_template?Boolean

Returns:

  • (Boolean)


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

def puppetlabs_template?
  self.class.packaged_template?(bare_uri) || bare_uri == PDK_TEMPLATE_URL
end

#shell_pathObject

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



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

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

#uri_fragmentObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the fragment of the URI, of the default template’s ref if one does not exist



85
86
87
# File 'lib/pdk/util/template_uri.rb', line 85

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

#uri_fragment=(fragment) ⇒ Object



89
90
91
# File 'lib/pdk/util/template_uri.rb', line 89

def uri_fragment=(fragment)
  @uri.fragment = fragment
end