puppet_metadata 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1b9da6033c7aa055e1e88289211ad3356764ea737184fd594fce1c31e1e94c27
4
+ data.tar.gz: 8fdfd523619505b15568769eb276424cffcc5c257c3a08377ee8a7abd8571701
5
+ SHA512:
6
+ metadata.gz: 574fb26918bdd3c5b7a7ccad604557cb130797f704c87102d552fe3ef5a121acd15d4be22ba67bd273ba3d0d35a4c5b44c1ed8fe48e09b019da3785f9a149a9f
7
+ data.tar.gz: fcf0a1b46a59fdfbd3f6269f430495a63acbfc387c187658b25b4466fd8c0a0b5835cf221a5f6b7fcfdcd8f15208ab28141815edb8701666f9cefc47a422f99b
@@ -0,0 +1,3 @@
1
+ # puppet_metadata
2
+
3
+ The gem intends to provide an abstraction over Puppet's metadata.json file. Its API allow easy iteration over its illogical data structures.
@@ -0,0 +1,20 @@
1
+ # A module that provides abstractions around Puppet's metadata format.
2
+ module PuppetMetadata
3
+ autoload :Beaker, 'puppet_metadata/beaker'
4
+ autoload :Metadata, 'puppet_metadata/metadata'
5
+ autoload :OperatingSystem, 'puppet_metadata/operatingsystem'
6
+
7
+ # Parse a JSON encoded metadata string
8
+ # @param data A JSON encoded metadata string
9
+ # @return [PuppetMetadata::Metadata] A Metadata object
10
+ def self.parse(data)
11
+ Metadata.new(JSON.parse(data))
12
+ end
13
+
14
+ # Read and parse a path containing metadata
15
+ # @param path The path metadata.json
16
+ # @return [PuppetMetadata::Metadata] A Metadata object
17
+ def self.read(path)
18
+ parse(File.read(path))
19
+ end
20
+ end
@@ -0,0 +1,51 @@
1
+ module PuppetMetadata
2
+ # A class to provide abstractions for integration with beaker
3
+ #
4
+ # @see https://rubygems.org/gems/beaker
5
+ # @see https://rubygems.org/gems/beaker-hostgenerator
6
+ class Beaker
7
+ # Convert an Operating System name with a release to a Beaker setfile
8
+ #
9
+ # @param [String] os
10
+ # The Operating System string as metadata.json knows it, which in turn is
11
+ # based on Facter's operatingsystem fact.
12
+ # @param [String] release The OS release
13
+ # @param [Boolean] use_fqdn
14
+ # Whether or not to use a FQDN, ensuring a domain
15
+ # @param [Boolean] pidfile_workaround
16
+ # Whether or not to apply the systemd PIDFile workaround. This is only
17
+ # needed when the daemon uses PIDFile in its service file and using
18
+ # Docker as a Beaker hypervisor. This is to work around Docker's
19
+ # limitations.
20
+ #
21
+ # @return [String] The beaker setfile description or nil
22
+ def self.os_release_to_setfile(os, release, use_fqdn: false, pidfile_workaround: false)
23
+ return unless os_supported?(os)
24
+
25
+ name = "#{os.downcase}#{release.tr('.', '')}-64"
26
+
27
+ options = {}
28
+ options[:hostname] = "#{name}.example.com" if use_fqdn
29
+ # Docker messes up cgroups and modern systemd can't deal with that when
30
+ # PIDFile is used.
31
+ if pidfile_workaround
32
+ case os
33
+ when 'CentOS'
34
+ options[:image] = 'centos:7.6.1810' if release == '7'
35
+ when 'Ubuntu'
36
+ options[:image] = 'ubuntu:xenial-20191212' if release == '16.04'
37
+ end
38
+ end
39
+
40
+ setfile = name
41
+ setfile += "{#{options.map { |key, value| "#{key}=#{value}" }.join(',')}}" if options.any?
42
+ setfile
43
+ end
44
+
45
+ # Return whether a Beaker setfile can be generated for the given OS
46
+ # @param [String] os The operating system
47
+ def self.os_supported?(os)
48
+ ['CentOS', 'Fedora', 'Debian', 'Ubuntu'].include?(os)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,210 @@
1
+ require 'json'
2
+
3
+ module PuppetMetadata
4
+ # An exception indicating the metadata is invalid
5
+ #
6
+ # @attr_reader [Array[Hash[Symbol, String]]] errors
7
+ # The errors in the metadata
8
+ class InvalidMetadataException < Exception
9
+ attr_reader :errors
10
+
11
+ # Returns a new instance of InvalidMetadataException
12
+ # @param [Array[Hash[Symbol, String]]] errors
13
+ # The errors in the metadata
14
+ def initialize(errors)
15
+ messages = errors.map { |error| error[:message] }
16
+ super("Invalid metadata: #{messages.join(', ')}")
17
+ @errors = errors
18
+ end
19
+ end
20
+
21
+ # An abstraction over Puppet metadata
22
+ class Metadata
23
+ attr_reader :metadata
24
+
25
+ # @param [Hash[String, Any]] metadata
26
+ # The metadata as a data structure
27
+ # @param [Boolean] validate
28
+ # Whether to validate the metadata using metadata-json-lint
29
+ # @raise [PuppetMetadata::InvalidMetadataException]
30
+ # When the provided metadata is invalid according to metadata.json-lint
31
+ # @see PuppetMetadata.parse
32
+ # @see PuppetMetadata.read
33
+ # @see https://rubygems.org/gems/metadata-json-lint
34
+ def initialize(metadata, validate = true)
35
+ if validate
36
+ require 'metadata_json_lint'
37
+ schema_errors = MetadataJsonLint::Schema.new.validate(metadata)
38
+ raise InvalidMetadataException.new(schema_errors) if schema_errors.any?
39
+ end
40
+
41
+ @metadata = metadata
42
+ end
43
+
44
+ # The name
45
+ # @return [String] The name
46
+ def name
47
+ metadata['name']
48
+ end
49
+
50
+ # The version
51
+ # @return [String]
52
+ def version
53
+ metadata['version']
54
+ end
55
+
56
+ # The license
57
+ # @return [String] The license
58
+ def license
59
+ metadata['license']
60
+ end
61
+
62
+ # @return [Hash[String, Array[String]]]
63
+ # The supported operating system and its major releases
64
+ def operatingsystems
65
+ @operatingsystems ||= begin
66
+ return {} if metadata['operatingsystem_support'].nil?
67
+
68
+ supported = metadata['operatingsystem_support'].map do |os|
69
+ next unless os['operatingsystem']
70
+ [os['operatingsystem'], os['operatingsystemrelease']]
71
+ end
72
+
73
+ Hash[supported.compact]
74
+ end
75
+ end
76
+
77
+ # Returns whether an operating system's release is supported
78
+ #
79
+ # @param [String] operatingsystem The operating system
80
+ # @param [String] release The major release of the OS
81
+ # @return [Boolean] Whether an operating system's release is supported
82
+ def os_release_supported?(operatingsystem, release)
83
+ # If no OS is present, everything is supported. An example of this is
84
+ # modules with only functions.
85
+ return true if operatingsystems.empty?
86
+
87
+ # if the key present, nil indicates all releases are supported
88
+ return false unless operatingsystems.key?(operatingsystem)
89
+
90
+ releases = operatingsystems[operatingsystem]
91
+ releases.nil? || releases.include?(release)
92
+ end
93
+
94
+ # @param [Date] at The date to check the EOL time. Today is used when nil.
95
+ # @return [Hash[String, Array[String]]]
96
+ def eol_operatingsystems(at = nil)
97
+ at ||= Date.today
98
+
99
+ unsupported = operatingsystems.map do |os, rels|
100
+ next unless rels
101
+ eol = rels.select { |rel| OperatingSystem.eol?(os, rel, at) }
102
+ [os, eol] if eol.any?
103
+ end
104
+
105
+ Hash[unsupported.compact]
106
+ end
107
+
108
+ # A hash representation of the requirements
109
+ #
110
+ # Every element in the original array is converted. The name is used as a
111
+ # key while version_requirement is used as a value. No value indicates any
112
+ # version is accepted.
113
+ #
114
+ # @see #satisfies_requirement?
115
+ # @return [Hash[String, SemanticPuppet::VersionRange]]
116
+ # The requirements of the module
117
+ #
118
+ # @example
119
+ # metadata = Metadata.new(data)
120
+ # metadata.requirements.each do |requirement, version_requirement|
121
+ # if version_requirement
122
+ # puts "#{metadata.name} requires #{requirement} #{version_requirement}"
123
+ # else
124
+ # puts "#{metadata.name} requires any #{requirement}"
125
+ # end
126
+ # end
127
+ def requirements
128
+ @requirements ||= build_version_requirement_hash(metadata['requirements'])
129
+ end
130
+
131
+ # @param [String] name The name of the requirement
132
+ # @param [String] version The version of the requirement
133
+ # @see #requirements
134
+ def satisfies_requirement?(name, version)
135
+ matches?(requirements[name], version)
136
+ end
137
+
138
+ # A hash representation of the dependencies
139
+ #
140
+ # Every element in the original array is converted. The name is used as a
141
+ # key while version_requirement is used as a value. No value indicates any
142
+ # version is accepted.
143
+ #
144
+ # @see #satisfies_dependency?
145
+ # @return [Hash[String, SemanticPuppet::VersionRange]]
146
+ # The dependencies of the module
147
+ #
148
+ # @example
149
+ # metadata = Metadata.new(data)
150
+ # metadata.dependencies.each do |os, releases|
151
+ # if releases
152
+ # releases.each do |release|
153
+ # puts "#{metadata.name} supports #{os} #{release}"
154
+ # end
155
+ # else
156
+ # puts "#{metadata.name} supports all #{os} releases"
157
+ # end
158
+ # end
159
+ def dependencies
160
+ @dependencies ||= build_version_requirement_hash(metadata['dependencies'])
161
+ end
162
+
163
+ # @param [String] name The name of the dependency
164
+ # @param [String] version The version of the dependency
165
+ # @see #dependencies
166
+ def satisfies_dependency?(name, version)
167
+ matches?(dependencies[name], version)
168
+ end
169
+
170
+ # @param [Boolean] use_fqdn
171
+ # Whether to set the hostname to a fully qualified domain name
172
+ # @param [Boolean] pidfile_workaround
173
+ # Whether to apply the Docker pidfile workaround
174
+ # @yieldparam [String] setfile
175
+ # The supported setfile configurations
176
+ # @see PuppetMetadata::Beaker#os_release_to_setfile
177
+ def beaker_setfiles(use_fqdn: false, pidfile_workaround: false)
178
+ operatingsystems.each do |os, releases|
179
+ next unless PuppetMetadata::Beaker.os_supported?(os)
180
+ releases&.each do |release|
181
+ setfile = PuppetMetadata::Beaker.os_release_to_setfile(os, release, use_fqdn: use_fqdn, pidfile_workaround: pidfile_workaround)
182
+ yield setfile if setfile
183
+ end
184
+ end
185
+ end
186
+
187
+ private
188
+
189
+ def build_version_requirement_hash(array)
190
+ return {} if array.nil?
191
+
192
+ require 'semantic_puppet'
193
+
194
+ reqs = array.map do |requirement|
195
+ next unless requirement['name']
196
+ version_requirement = requirement['version_requirement'] || '>= 0'
197
+ [requirement['name'], SemanticPuppet::VersionRange.parse(version_requirement)]
198
+ end
199
+
200
+ Hash[reqs.compact]
201
+ end
202
+
203
+ def matches?(required_version, provided_version)
204
+ return false unless required_version
205
+
206
+ provided_version = SemanticPuppet::Version.parse(provided_version) if provided_version.is_a?(String)
207
+ required_version.include?(provided_version)
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,159 @@
1
+ require 'date'
2
+
3
+ module PuppetMetadata
4
+ # An abstraction layer over operating systems. Mostly to determine End Of
5
+ # Life dates.
6
+ #
7
+ # @see https://endoflife.software/operating-systems
8
+ class OperatingSystem
9
+ # The EOL dates for the various operating systems
10
+ # @see .eol_date
11
+ EOL_DATES = {
12
+ # https://endoflife.software/operating-systems/linux/centos
13
+ 'CentOS' => {
14
+ '8' => '2029-05-31',
15
+ '7' => '2024-06-30',
16
+ '6' => '2020-11-30',
17
+ '5' => '2017-03-31',
18
+ '4' => '2012-02-29',
19
+ '3' => '2010-10-30',
20
+ },
21
+ # https://endoflife.software/operating-systems/linux/debian
22
+ # https://wiki.debian.org/DebianReleases
23
+ 'Debian' => {
24
+ # TODO: EOL is standard support, not the extended life cycle
25
+ '10' => nil, # '~2022',
26
+ '9' => nil, # '~2020',
27
+ '8' => '2018-06-06',
28
+ '7' => '2016-04-26',
29
+ '6' => '2015-05-31',
30
+ '5' => '2012-02-06',
31
+ '4' => '2010-02-15',
32
+ '3.1' => '2008-03-31',
33
+ '3.0' => '2006-06-30',
34
+ '2.2' => '2003-06-30',
35
+ '2.1' => '2000-09-30',
36
+ },
37
+ # https://endoflife.software/operating-systems/linux/fedora
38
+ 'Fedora' => {
39
+ '32' => nil,
40
+ '31' => nil,
41
+ '30' => nil,
42
+ '29' => '2019-11-26',
43
+ '28' => '2019-05-28',
44
+ '27' => '2018-11-30',
45
+ '26' => '2018-05-29',
46
+ '25' => '2017-12-12',
47
+ '24' => '2017-08-08',
48
+ '23' => '2016-12-20',
49
+ '22' => '2016-07-19',
50
+ '21' => '2015-12-01',
51
+ '20' => '2015-06-23',
52
+ '19' => '2015-01-06',
53
+ '18' => '2014-01-14',
54
+ '17' => '2013-07-30',
55
+ '16' => '2013-02-12',
56
+ '15' => '2012-06-26',
57
+ '14' => '2011-12-08',
58
+ '13' => '2011-06-24',
59
+ '12' => '2010-12-02',
60
+ '11' => '2010-06-25',
61
+ '10' => '2009-12-18',
62
+ '9' => '2009-07-10',
63
+ '8' => '2009-01-07',
64
+ '7' => '2008-06-13',
65
+ '6' => '2007-12-07',
66
+ '5' => '2007-07-02',
67
+ '4' => '2006-08-07',
68
+ '3' => '2006-01-16',
69
+ '2' => '2005-04-11',
70
+ '1' => '2004-09-20',
71
+ },
72
+ # https://endoflife.software/operating-systems/linux/red-hat-enterprise-linux-rhel
73
+ 'RedHat' => {
74
+ # TODO: EOL is standard support, not the extended life cycle
75
+ '8' => '2029-05-31',
76
+ '7' => '2024-06-30',
77
+ '6' => '2020-11-30',
78
+ '5' => '2017-03-31',
79
+ '4' => '2012-02-29',
80
+ '3' => '2010-10-31',
81
+ },
82
+ # https://endoflife.software/operating-systems/linux/ubuntu
83
+ 'Ubuntu' => {
84
+ '20.04' => '2025-04-15',
85
+ '19.10' => '2020-07-15',
86
+ '19.04' => '2020-01-15',
87
+ '18.10' => '2019-07-15',
88
+ '18.04' => '2023-04-15',
89
+ '17.10' => '2018-07-15',
90
+ '17.04' => '2018-01-15',
91
+ '16.10' => '2017-07-20',
92
+ '16.04' => '2021-04-15',
93
+ '15.10' => '2016-07-28',
94
+ '15.04' => '2016-02-04',
95
+ '14.10' => '2015-07-23',
96
+ '14.04' => '2019-04-15',
97
+ '13.10' => '2014-07-17',
98
+ '13.04' => '2014-01-27',
99
+ '12.04' => '2017-04-28',
100
+ '11.10' => '2013-05-09',
101
+ '11.04' => '2012-10-28',
102
+ '10.10' => '2012-04-10',
103
+ '10.04' => '2015-04-30',
104
+ '9.10' => '2011-04-30',
105
+ '9.04' => '2010-10-23',
106
+ '8.10' => '2010-04-30',
107
+ '8.04' => '2013-05-09',
108
+ '7.10' => '2009-04-18',
109
+ '7.04' => '2008-10-19',
110
+ '6.10' => '2008-04-26',
111
+ '6.06' => '2011-06-01',
112
+ '5.10' => '2007-04-13',
113
+ '5.04' => '2006-10-31',
114
+ '4.10' => '2006-04-30',
115
+ },
116
+ }
117
+
118
+ # Return the EOL date for the given operating system release
119
+ # @param [String] operatingsystem
120
+ # The operating system
121
+ # @param [String] release
122
+ # The major version of the operating system
123
+ # @return [optional, Date]
124
+ # The EOL date for the given operating system release. Nil is returned
125
+ # when the either when the OS, the release or the EOL date is unknown
126
+ def self.eol_date(operatingsystem, release)
127
+ releases = EOL_DATES[operatingsystem]
128
+ return unless releases
129
+ date = releases[release]
130
+ return unless date
131
+ Date.parse(date)
132
+ end
133
+
134
+ # Return whether the given operating system release is EOL at the given
135
+ # date
136
+ #
137
+ # @param [String] operatingsystem
138
+ # The operating system
139
+ # @param [String] release
140
+ # The major version of the operating system
141
+ # @return [Boolean]
142
+ # The EOL date for the given operating system release. Nil is returned
143
+ # when the either when the OS, the release or the EOL date is unknown
144
+ def self.eol?(operatingsystem, release, at = nil)
145
+ date = eol_date(operatingsystem, release)
146
+ date && date < (at || Date.today)
147
+ end
148
+
149
+ # Return the latest known release for a given operating system
150
+ # @param [String] operatingsystem The operating system
151
+ # @return [optional, String]
152
+ # The latest major release for the given operating system, if any is
153
+ # known
154
+ def self.latest_release(operatingsystem)
155
+ releases = EOL_DATES[operatingsystem]
156
+ releases&.keys&.max_by { |release| Gem::Version.new(release) }
157
+ end
158
+ end
159
+ end
metadata ADDED
@@ -0,0 +1,150 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: puppet_metadata
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Vox Pupuli
8
+ - Ewoud Kohl van Wijngaarden
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2020-10-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: metadata-json-lint
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '2.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '2.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: semantic_puppet
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '13.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '13.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rspec
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '3.0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '3.0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rspec-its
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '1.0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '1.0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rdoc
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '6.0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '6.0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: yard
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '0.9'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '0.9'
112
+ description: A package that provides abstractions for the Puppet Metadata
113
+ email:
114
+ - voxpupuli@groups.io
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files:
118
+ - README.md
119
+ files:
120
+ - README.md
121
+ - lib/puppet_metadata.rb
122
+ - lib/puppet_metadata/beaker.rb
123
+ - lib/puppet_metadata/metadata.rb
124
+ - lib/puppet_metadata/operatingsystem.rb
125
+ homepage: http://github.com/voxpupuli/puppet_metadata
126
+ licenses:
127
+ - Apache-2.0
128
+ metadata: {}
129
+ post_install_message:
130
+ rdoc_options:
131
+ - "--main"
132
+ - README.md
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ requirements: []
146
+ rubygems_version: 3.1.2
147
+ signing_key:
148
+ specification_version: 4
149
+ summary: Data structures for the Puppet Metadata
150
+ test_files: []