wjordan-mixlib-install 0.8.0.alpha.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +10 -0
  5. data/.travis.yml +8 -0
  6. data/CONTRIBUTING.md +14 -0
  7. data/Gemfile +5 -0
  8. data/LICENSE +201 -0
  9. data/PRODUCT_MATRIX.md +24 -0
  10. data/README.md +55 -0
  11. data/Rakefile +53 -0
  12. data/acceptance/.gitignore +2 -0
  13. data/acceptance/Gemfile +12 -0
  14. data/acceptance/current/.acceptance/acceptance-cookbook/.gitignore +2 -0
  15. data/acceptance/current/.acceptance/acceptance-cookbook/metadata.rb +1 -0
  16. data/acceptance/current/.acceptance/acceptance-cookbook/recipes/destroy.rb +3 -0
  17. data/acceptance/current/.acceptance/acceptance-cookbook/recipes/provision.rb +3 -0
  18. data/acceptance/current/.acceptance/acceptance-cookbook/recipes/verify.rb +3 -0
  19. data/acceptance/current/.kitchen.yml +41 -0
  20. data/acceptance/unstable/.acceptance/acceptance-cookbook/.gitignore +2 -0
  21. data/acceptance/unstable/.acceptance/acceptance-cookbook/metadata.rb +1 -0
  22. data/acceptance/unstable/.acceptance/acceptance-cookbook/recipes/destroy.rb +3 -0
  23. data/acceptance/unstable/.acceptance/acceptance-cookbook/recipes/provision.rb +3 -0
  24. data/acceptance/unstable/.acceptance/acceptance-cookbook/recipes/verify.rb +3 -0
  25. data/acceptance/unstable/.kitchen.yml +41 -0
  26. data/lib/mixlib/install/artifact_info.rb +76 -0
  27. data/lib/mixlib/install/backend/artifactory.rb +214 -0
  28. data/lib/mixlib/install/backend/omnitruck.rb +76 -0
  29. data/lib/mixlib/install/backend.rb +36 -0
  30. data/lib/mixlib/install/generator/base.rb +65 -0
  31. data/lib/mixlib/install/generator/bourne/scripts/artifactory_urls.sh.erb +31 -0
  32. data/lib/mixlib/install/generator/bourne/scripts/fetch_metadata.sh.erb +48 -0
  33. data/lib/mixlib/install/generator/bourne/scripts/fetch_package.sh +53 -0
  34. data/lib/mixlib/install/generator/bourne/scripts/helpers.sh +340 -0
  35. data/lib/mixlib/install/generator/bourne/scripts/install_package.sh +33 -0
  36. data/lib/mixlib/install/generator/bourne/scripts/platform_detection.sh +165 -0
  37. data/lib/mixlib/install/generator/bourne/scripts/script_cli_parameters.sh +36 -0
  38. data/lib/mixlib/install/generator/bourne.rb +71 -0
  39. data/lib/mixlib/install/generator/powershell/scripts/get_project_metadata.ps1.erb +96 -0
  40. data/lib/mixlib/install/generator/powershell/scripts/get_project_metadata_for_artifactory.ps1.erb +92 -0
  41. data/lib/mixlib/install/generator/powershell/scripts/helpers.ps1 +69 -0
  42. data/lib/mixlib/install/generator/powershell/scripts/install_project.ps1 +99 -0
  43. data/lib/mixlib/install/generator/powershell.rb +94 -0
  44. data/lib/mixlib/install/generator.rb +33 -0
  45. data/lib/mixlib/install/options.rb +152 -0
  46. data/lib/mixlib/install/product.rb +288 -0
  47. data/lib/mixlib/install/script_generator.rb +217 -0
  48. data/lib/mixlib/install/util.rb +124 -0
  49. data/lib/mixlib/install/version.rb +5 -0
  50. data/lib/mixlib/install.rb +124 -0
  51. data/mixlib-install.gemspec +27 -0
  52. data/support/install_command.ps1 +84 -0
  53. data/support/install_command.sh +229 -0
  54. metadata +185 -0
@@ -0,0 +1,214 @@
1
+ #
2
+ # Author:: Patrick Wright (<patrick@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require "net/http"
20
+ require "json"
21
+ require "mixlib/install/artifact_info"
22
+ require "artifactory"
23
+
24
+ module Mixlib
25
+ class Install
26
+ class Backend
27
+ class Artifactory
28
+ class ConnectionError < StandardError; end
29
+ class AuthenticationError < StandardError; end
30
+ class NoArtifactsError < StandardError; end
31
+
32
+ ENDPOINT = "http://artifactory.chef.co".freeze
33
+
34
+ attr_accessor :options
35
+
36
+ def initialize(options)
37
+ @options = options
38
+ end
39
+
40
+ # Create filtered list of artifacts
41
+ #
42
+ # @return [Array<ArtifactInfo>] list of artifacts for the configured
43
+ # channel, product name, and product version.
44
+ # @return [ArtifactInfo] arifact info for the configured
45
+ # channel, product name, product version and platform info
46
+ #
47
+ def info
48
+ artifacts = if options.latest_version?
49
+ artifactory_latest
50
+ else
51
+ artifactory_artifacts(options.product_version)
52
+ end
53
+
54
+ if options.platform
55
+ artifacts.select! do |a|
56
+ a.platform == options.platform &&
57
+ a.platform_version == options.platform_version &&
58
+ a.architecture == options.architecture
59
+ end
60
+ end
61
+
62
+ artifacts.length == 1 ? artifacts.first : artifacts
63
+ end
64
+
65
+ #
66
+ # Get artifacts for the latest version, channel and product_name
67
+ #
68
+ # @return [Array<ArtifactInfo>] Array of info about found artifacts
69
+ def artifactory_latest
70
+ # Get the list of builds from the REST api.
71
+ # We do this because a user in the readers group does not have
72
+ # permissions to run aql against builds.
73
+ builds = client.get("/api/build/#{options.product_name}")
74
+
75
+ if builds.nil?
76
+ raise NoArtifactsError, <<-MSG
77
+ Can not find any builds for #{options.product_name} in #{::Artifactory.endpoint}.
78
+ MSG
79
+ end
80
+
81
+ # Output we get is something like:
82
+ # {
83
+ # "buildsNumbers": [
84
+ # {"uri"=>"/12.5.1+20151213083009", "started"=>"2015-12-13T08:40:19.238+0000"},
85
+ # {"uri"=>"/12.6.0+20160111212038", "started"=>"2016-01-12T00:25:35.762+0000"},
86
+ # ...
87
+ # ]
88
+ # }
89
+ # First we sort based on started
90
+ builds["buildsNumbers"].sort_by! { |b| b["started"] }.reverse!
91
+
92
+ # Now check if the build is in the requested channel or not
93
+ # Note that if you do this for any channel other than :unstable
94
+ # it will run a high number of queries but it is fine because we
95
+ # are using artifactory only for :unstable channel
96
+ builds["buildsNumbers"].each do |build|
97
+ version = build["uri"].gsub("/", "")
98
+ artifacts = artifactory_artifacts(version)
99
+
100
+ return artifacts unless artifacts.empty?
101
+ end
102
+
103
+ # we could not find any matching artifacts
104
+ []
105
+ end
106
+
107
+ #
108
+ # Get artifacts for a given version, channel and product_name
109
+ #
110
+ # @return [Array<ArtifactInfo>] Array of info about found artifacts
111
+ def artifactory_artifacts(version)
112
+ results = artifactory_query(<<-QUERY)
113
+ items.find(
114
+ {"repo": "omnibus-#{options.channel}-local"},
115
+ {"@omnibus.project": "#{options.product_name}"},
116
+ {"@omnibus.version": "#{version}"}
117
+ ).include("repo", "path", "name", "property")
118
+ QUERY
119
+
120
+ # Merge artifactory properties and downloadUri to a flat Hash
121
+ results.collect! do |result|
122
+ { "downloadUri" => generate_download_uri(result) }.merge(
123
+ map_properties(result["properties"])
124
+ )
125
+ end
126
+
127
+ # Convert results to build records
128
+ results.map { |a| create_artifact(a) }
129
+ end
130
+
131
+ #
132
+ # Run an artifactory query for the given query.
133
+ #
134
+ # @return [Array<Hash>] Array of results returned from artifactory
135
+ def artifactory_query(query)
136
+ results = artifactory_request do
137
+ client.post("/api/search/aql", query, "Content-Type" => "text/plain")
138
+ end
139
+
140
+ results["results"]
141
+ end
142
+
143
+ def create_artifact(artifact_map)
144
+ ArtifactInfo.new(
145
+ md5: artifact_map["omnibus.md5"],
146
+ sha256: artifact_map["omnibus.sha256"],
147
+ version: artifact_map["omnibus.version"],
148
+ platform: artifact_map["omnibus.platform"],
149
+ platform_version: artifact_map["omnibus.platform_version"],
150
+ architecture: artifact_map["omnibus.architecture"],
151
+ url: artifact_map["downloadUri"]
152
+ )
153
+ end
154
+
155
+ private
156
+
157
+ # Converts Array<Hash> where the Hash is a key pair and
158
+ # value pair to a simplifed key/pair Hash
159
+ #
160
+ def map_properties(properties)
161
+ return {} if properties.nil?
162
+ properties.each_with_object({}) do |prop, h|
163
+ h[prop["key"]] = prop["value"]
164
+ end
165
+ end
166
+
167
+ # Construct the downloadUri from raw artifactory data
168
+ #
169
+ def generate_download_uri(result)
170
+ uri = []
171
+ uri << endpoint.sub(/\/$/, "")
172
+ uri << result["repo"]
173
+ uri << result["path"]
174
+ uri << result["name"]
175
+ uri.join("/")
176
+ end
177
+
178
+ def client
179
+ @client ||= ::Artifactory::Client.new(
180
+ endpoint: endpoint,
181
+ username: ENV["ARTIFACTORY_USERNAME"],
182
+ password: ENV["ARTIFACTORY_PASSWORD"]
183
+ )
184
+ end
185
+
186
+ def endpoint
187
+ @endpoint ||= ENV.fetch("ARTIFACTORY_ENDPOINT", ENDPOINT)
188
+ end
189
+
190
+ def artifactory_request
191
+ begin
192
+ results = yield
193
+ rescue Errno::ETIMEDOUT, ::Artifactory::Error::ConnectionError
194
+ raise ConnectionError, <<-EOS
195
+ Artifactory endpoint '#{::Artifactory.endpoint}' is unreachable. Check that
196
+ the endpoint is correct and there is an open connection to Chef's private network.
197
+ EOS
198
+ rescue ::Artifactory::Error::HTTPError => e
199
+ if e.code == 401 && e.message =~ /Bad credentials/
200
+ raise AuthenticationError, <<-EOS
201
+ Artifactory server denied credentials. Verify ARTIFACTORY_USERNAME and
202
+ ARTIFACTORY_PASSWORD environment variables are configured properly.
203
+ EOS
204
+ else
205
+ raise e
206
+ end
207
+ end
208
+
209
+ results
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,76 @@
1
+ #
2
+ # Author:: Patrick Wright (<patrick@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require "net/http"
20
+ require "json"
21
+ require "mixlib/install/artifact_info"
22
+
23
+ module Mixlib
24
+ class Install
25
+ class Backend
26
+ class Omnitruck
27
+ ENDPOINT = "https://omnitruck.chef.io/".freeze
28
+
29
+ attr_accessor :options
30
+
31
+ def initialize(options)
32
+ @options = options
33
+ end
34
+
35
+ def info
36
+ # If we are querying a single platform we need to call metadata
37
+ # endpoint otherwise we need to call versions endpoint in omnitruck
38
+ if options.platform
39
+ build = omnitruck_get("metadata", p: options.platform,
40
+ pv: options.platform_version,
41
+ m: options.architecture,
42
+ v: options.product_version
43
+ )
44
+ ArtifactInfo.from_json(build,
45
+ platform: options.platform,
46
+ platform_version: options.platform_version,
47
+ architecture: options.architecture
48
+ )
49
+ else
50
+ builds = omnitruck_get("versions", v: options.product_version)
51
+ ArtifactInfo.from_metadata_map(builds)
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def omnitruck_get(resource, parameters)
58
+ uri = URI.parse(ENDPOINT)
59
+ http = Net::HTTP.new(uri.host, uri.port)
60
+ http.use_ssl = (uri.scheme == "https")
61
+
62
+ path = "/#{options.channel}/#{options.product_name}/#{resource}"
63
+ full_path = [path, URI.encode_www_form(parameters)].join("?")
64
+ request = Net::HTTP::Get.new(full_path)
65
+ request["Accept"] = "application/json"
66
+
67
+ res = http.request(request)
68
+
69
+ # Raise if response is not 2XX
70
+ res.value
71
+ res.body
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,36 @@
1
+ #
2
+ # Author:: Patrick Wright (<patrick@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require "mixlib/install/backend/omnitruck"
20
+ require "mixlib/install/backend/artifactory"
21
+
22
+ module Mixlib
23
+ class Install
24
+ class Backend
25
+ def self.info(options)
26
+ backend = if options.for_omnitruck?
27
+ Backend::Omnitruck.new(options)
28
+ elsif options.for_artifactory?
29
+ Backend::Artifactory.new(options)
30
+ end
31
+
32
+ backend.info
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,65 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2015 Chef, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require "erb"
19
+ require "ostruct"
20
+ require "mixlib/install/backend/omnitruck"
21
+
22
+ module Mixlib
23
+ class Install
24
+ class Generator
25
+ class Base
26
+ attr_reader :options
27
+
28
+ def initialize(options)
29
+ @options = options
30
+ end
31
+
32
+ #
33
+ # Returns the base path where the script fragments are located for
34
+ # the generator as a String.
35
+ #
36
+ def self.script_base_path
37
+ raise "You must define a script_base_path for your Generator::Base class."
38
+ end
39
+
40
+ #
41
+ # Gets the contents of the given script.
42
+ #
43
+ def self.get_script(name, context = {})
44
+ script_path = File.join(script_base_path, name)
45
+
46
+ # If there is an erb template we render it, otherwise we just read
47
+ # and returnt the contents of the script
48
+ if File.exist? "#{script_path}.erb"
49
+ # Default values to use incase they are not set in the context
50
+ context[:base_url] ||= Mixlib::Install::Backend::Omnitruck::ENDPOINT
51
+
52
+ context_object = OpenStruct.new(context).instance_eval { binding }
53
+ ERB.new(File.read("#{script_path}.erb")).result(context_object)
54
+ else
55
+ File.read(script_path)
56
+ end
57
+ end
58
+
59
+ def get_script(name, context = {})
60
+ self.class.get_script(name, context)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,31 @@
1
+ # artifactory_urls.sh
2
+ ############
3
+ # This section sets up the information for a build to be installed from artifactory
4
+ # then uses $platform, $platform_version and $machine to select the correct
5
+ # version.
6
+ #
7
+ # Inputs:
8
+ # $platform:
9
+ # $platform_version:
10
+ # $machine:
11
+ #
12
+ # Outputs:
13
+ # $download_url:
14
+ # $sha256:
15
+ # $md5:
16
+ ############
17
+
18
+ <% artifacts.each do |artifact| %>
19
+ artifact_info_dir="$tmp_dir/artifact_info/<%= File.join(artifact.platform, artifact.platform_version, artifact.architecture)%>"
20
+ mkdir -p $artifact_info_dir
21
+ touch "$artifact_info_dir/artifact_info"
22
+ echo "url <%= artifact.url%>" >> "$artifact_info_dir/artifact_info"
23
+ echo "md5 <%= artifact.md5%>" >> "$artifact_info_dir/artifact_info"
24
+ echo "sha256 <%= artifact.sha256%>" >> "$artifact_info_dir/artifact_info"
25
+ <% end %>
26
+
27
+ artifact_info_for_platform="$tmp_dir/artifact_info/$platform/$platform_version/$machine/artifact_info"
28
+
29
+ download_url=`awk '$1 == "url" { print $2 }' "$artifact_info_for_platform"`
30
+ sha256=`awk '$1 == "sha256" { print $2 }' "$artifact_info_for_platform"`
31
+ md5=`awk '$1 == "md5" { print $2 }' "$artifact_info_for_platform"`
@@ -0,0 +1,48 @@
1
+ # fetch_metadata.sh
2
+ ############
3
+ # This section calls omnitruck to get the information about the build to be
4
+ # installed.
5
+ #
6
+ # Inputs:
7
+ # $channel:
8
+ # $project:
9
+ # $version:
10
+ # $platform:
11
+ # $platform_version:
12
+ # $machine:
13
+ # $tmp_dir:
14
+ #
15
+ # Outputs:
16
+ # $download_url:
17
+ # $sha256:
18
+ # $md5:
19
+ ############
20
+
21
+ echo "Getting information for $project $channel $version for $platform..."
22
+
23
+ metadata_filename="$tmp_dir/metadata.txt"
24
+ metadata_url="<%= base_url %>$channel/$project/metadata?v=$version&p=$platform&pv=$platform_version&m=$machine"
25
+
26
+ do_download "$metadata_url" "$metadata_filename"
27
+
28
+ cat "$metadata_filename"
29
+
30
+ echo ""
31
+ # check that all the mandatory fields in the downloaded metadata are there
32
+ if grep '^url' $metadata_filename > /dev/null && grep '^sha256' $metadata_filename > /dev/null && grep '^md5' $metadata_filename > /dev/null; then
33
+ echo "downloaded metadata file looks valid..."
34
+ else
35
+ echo "downloaded metadata file is corrupted or an uncaught error was encountered in downloading the file..."
36
+ # this generally means one of the download methods downloaded a 404 or something like that and then reported a successful exit code,
37
+ # and this should be fixed in the function that was doing the download.
38
+ report_bug
39
+ exit 1
40
+ fi
41
+
42
+ download_url=`awk '$1 == "url" { print $2 }' "$metadata_filename"`
43
+ sha256=`awk '$1 == "sha256" { print $2 }' "$metadata_filename"`
44
+ md5=`awk '$1 == "md5" { print $2 }' "$metadata_filename"`
45
+
46
+ ############
47
+ # end of fetch_metadata.sh
48
+ ############
@@ -0,0 +1,53 @@
1
+ # fetch_package.sh
2
+ ############
3
+ # This section fetchs a package from $download_url and verifies its metadata.
4
+ #
5
+ # Inputs:
6
+ # $download_url:
7
+ # $tmp_dir:
8
+ # Optional Inputs:
9
+ # $cmdline_filename: Name of the package downloaded on local disk.
10
+ # $cmdline_dl_dir: Name of the directory downloaded package will be saved to on local disk.
11
+ #
12
+ # Outputs:
13
+ # $download_filename: Name of the downloaded file on local disk.
14
+ # $filetype: Type of the file downloaded.
15
+ ############
16
+
17
+ filename=`echo $download_url | sed -e 's/^.*\///'`
18
+ filetype=`echo $filename | sed -e 's/^.*\.//'`
19
+
20
+ # use either $tmp_dir, the provided directory (-d) or the provided filename (-f)
21
+ if test "x$cmdline_filename" != "x"; then
22
+ download_filename="$cmdline_filename"
23
+ elif test "x$cmdline_dl_dir" != "x"; then
24
+ download_filename="$cmdline_dl_dir/$filename"
25
+ else
26
+ download_filename="$tmp_dir/$filename"
27
+ fi
28
+
29
+ # ensure the parent directory where to download the installer always exists
30
+ download_dir=`dirname $download_filename`
31
+ (umask 077 && mkdir -p $download_dir) || exit 1
32
+
33
+ # check if we have that file locally available and if so verify the checksum
34
+ cached_file_available="false"
35
+ if test -f $download_filename; then
36
+ echo "$download_filename already exists, verifiying checksum..."
37
+ if do_checksum "$download_filename" "$sha256" "$md5"; then
38
+ echo "checksum compare succeeded, using existing file!"
39
+ cached_file_available="true"
40
+ else
41
+ echo "checksum mismatch, downloading latest version of the file"
42
+ fi
43
+ fi
44
+
45
+ # download if no local version of the file available
46
+ if test "x$cached_file_available" != "xtrue"; then
47
+ do_download "$download_url" "$download_filename"
48
+ do_checksum "$download_filename" "$sha256" "$md5" || checksum_mismatch
49
+ fi
50
+
51
+ ############
52
+ # end of fetch_package.sh
53
+ ############