mixlib-install 3.15.0 → 3.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +0 -14
- data/lib/mixlib/install/backend/base.rb +1 -1
- data/lib/mixlib/install/backend/package_router.rb +101 -12
- data/lib/mixlib/install/cli.rb +5 -1
- data/lib/mixlib/install/dist.rb +24 -4
- data/lib/mixlib/install/generator/base.rb +1 -14
- data/lib/mixlib/install/generator/bourne/scripts/check_product.sh +1 -1
- data/lib/mixlib/install/generator/bourne/scripts/fetch_metadata.sh.erb +49 -19
- data/lib/mixlib/install/generator/bourne/scripts/fetch_package.sh +47 -23
- data/lib/mixlib/install/generator/bourne/scripts/helpers.sh.erb +40 -38
- data/lib/mixlib/install/generator/bourne/scripts/install_package.sh +2 -2
- data/lib/mixlib/install/generator/bourne/scripts/platform_detection.sh +22 -22
- data/lib/mixlib/install/generator/bourne/scripts/proxy_env.sh +4 -4
- data/lib/mixlib/install/generator/bourne/scripts/script_cli_parameters.sh.erb +9 -3
- data/lib/mixlib/install/generator/bourne.rb +5 -20
- data/lib/mixlib/install/generator/powershell/scripts/get_project_metadata.ps1.erb +60 -36
- data/lib/mixlib/install/generator/powershell/scripts/helpers.ps1.erb +8 -4
- data/lib/mixlib/install/generator/powershell/scripts/install_project.ps1.erb +36 -16
- data/lib/mixlib/install/generator/powershell/scripts/platform_detection.ps1 +1 -0
- data/lib/mixlib/install/generator/powershell.rb +5 -9
- data/lib/mixlib/install/generator.rb +5 -4
- data/lib/mixlib/install/options.rb +40 -0
- data/lib/mixlib/install/product_matrix.rb +6 -1
- data/lib/mixlib/install/script_generator.rb +72 -22
- data/lib/mixlib/install/version.rb +1 -1
- data/lib/mixlib/install.rb +91 -16
- data/mixlib-install.gemspec +2 -0
- data/support/install_command.ps1 +3 -0
- metadata +17 -3
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
################ helpers.ps1
|
|
2
|
+
# Set strict error handling to ensure errors cause script failures
|
|
3
|
+
$ErrorActionPreference = 'Stop'
|
|
4
|
+
|
|
5
|
+
try { [Console]::OutputEncoding = New-Object -typename System.Text.ASCIIEncoding } catch { }
|
|
2
6
|
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]'Tls,Tls11,Tls12'
|
|
3
7
|
|
|
4
8
|
function Get-PlatformVersion {
|
|
@@ -168,11 +172,11 @@ function Test-ProjectPackage {
|
|
|
168
172
|
[cmdletbinding()]
|
|
169
173
|
param ($Path, $Algorithm = 'SHA256', $Hash)
|
|
170
174
|
if (!$env:Valid_ProjectPackage){
|
|
171
|
-
Write-
|
|
175
|
+
Write-Host "Testing the $Algorithm hash for $path."
|
|
172
176
|
$ActualHash = (Custom-GetFileHash -Algorithm $Algorithm -Path $Path).Hash.ToLower()
|
|
173
177
|
|
|
174
|
-
Write-
|
|
175
|
-
Write-
|
|
178
|
+
Write-Host "`tDesired Hash - '$Hash'"
|
|
179
|
+
Write-Host "`tActual Hash - '$ActualHash'"
|
|
176
180
|
$env:Valid_ProjectPackage = $ActualHash -eq $Hash
|
|
177
181
|
if (-not $env:Valid_ProjectPackage) {
|
|
178
182
|
Write-Error "Failed to validate the downloaded installer. The expected $Algorithm hash was '$Hash' and the actual hash was '$ActualHash' for $path"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
################ install_project.ps1
|
|
1
2
|
function Install-Project {
|
|
2
3
|
<#
|
|
3
4
|
.SYNOPSIS
|
|
@@ -5,12 +6,11 @@ function Install-Project {
|
|
|
5
6
|
.DESCRIPTION
|
|
6
7
|
Install a Chef Software, Inc. product
|
|
7
8
|
.EXAMPLE
|
|
8
|
-
iex (new-object net.webclient).downloadstring('https://
|
|
9
|
+
iex (new-object net.webclient).downloadstring('<%= base_url || "https://chefdownload-commercial.chef.io" %>/install.ps1<%= base_url && base_url.include?("chefdownload") ? "?license_id=$env:CHEF_LICENSE_KEY" : "" %>'); Install-Project -project chef -channel stable
|
|
9
10
|
|
|
10
11
|
Installs the latest stable version of Chef.
|
|
11
12
|
.EXAMPLE
|
|
12
|
-
iex (irm 'https://
|
|
13
|
-
|
|
13
|
+
iex (irm '<<%= base_url || "https://chefdownload-commercial.chef.io" %>/install.ps1<%= base_url && base_url.include?("chefdownload") ? "?license_id=$env:CHEF_LICENSE_KEY" : "" %>'); Install-Project -project chef -channel current
|
|
14
14
|
Installs the latest integration build of the Chef Development Kit
|
|
15
15
|
#>
|
|
16
16
|
[cmdletbinding(SupportsShouldProcess=$true)]
|
|
@@ -57,9 +57,15 @@ function Install-Project {
|
|
|
57
57
|
# Set to 'once' to skip install if project is detected
|
|
58
58
|
[string]
|
|
59
59
|
$install_strategy,
|
|
60
|
+
# Base server URI for metadata endpoint
|
|
61
|
+
[uri]
|
|
62
|
+
$base_server_uri,
|
|
60
63
|
# License ID for commercial API access
|
|
61
64
|
[string]
|
|
62
|
-
$license_id
|
|
65
|
+
$license_id <%= "= '#{license_id}'" if license_id && !license_id.to_s.empty? %>,
|
|
66
|
+
# Package manager override (e.g. msi, zip). If omitted, the server derives it from platform.
|
|
67
|
+
[string]
|
|
68
|
+
$package_manager
|
|
63
69
|
)
|
|
64
70
|
|
|
65
71
|
# Use CHEF_LICENSE_KEY environment variable if license_id not provided
|
|
@@ -103,7 +109,7 @@ function Install-Project {
|
|
|
103
109
|
$download_url = $download_url_override
|
|
104
110
|
$sha256 = $checksum
|
|
105
111
|
} else {
|
|
106
|
-
$package_metadata = Get-ProjectMetadata -project $project -channel $channel -version $version -prerelease:$prerelease -nightlies:$nightlies -architecture $architecture -license_id $license_id
|
|
112
|
+
$package_metadata = Get-ProjectMetadata -project $project -channel $channel -version $version -prerelease:$prerelease -nightlies:$nightlies -architecture $architecture -base_server_uri $base_server_uri -license_id $license_id -package_manager $package_manager
|
|
107
113
|
$download_url = $package_metadata.url
|
|
108
114
|
$sha256 = $package_metadata.sha256
|
|
109
115
|
}
|
|
@@ -116,12 +122,18 @@ function Install-Project {
|
|
|
116
122
|
}
|
|
117
123
|
}
|
|
118
124
|
else {
|
|
119
|
-
#
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
125
|
+
# Extract filename from URL path (without query params)
|
|
126
|
+
$urlPath = (([System.Uri]$download_url).AbsolutePath -split '/')[-1]
|
|
127
|
+
|
|
128
|
+
# Check if URL path has a package file extension
|
|
129
|
+
$hasPackageExtension = $urlPath -match '\.(msi|appx|pkg|dmg|rpm|deb)$'
|
|
130
|
+
|
|
131
|
+
# For licensed downloads or URLs without package extensions, we won't know the filename until after download
|
|
132
|
+
if (-not [string]::IsNullOrEmpty($license_id) -or -not $hasPackageExtension) {
|
|
123
133
|
$filename = "chef-download-temp-$PID"
|
|
124
|
-
Write-Host "Using temporary filename for
|
|
134
|
+
Write-Host "Using temporary filename for content-disposition download: $filename"
|
|
135
|
+
} else {
|
|
136
|
+
$filename = $urlPath
|
|
125
137
|
}
|
|
126
138
|
}
|
|
127
139
|
Write-Host "Download directory: $download_directory"
|
|
@@ -162,8 +174,8 @@ function Install-Project {
|
|
|
162
174
|
Write-Host "Downloading $project from $($download_url) to $download_destination."
|
|
163
175
|
$download_result = Get-WebContent $download_url -filepath $download_destination
|
|
164
176
|
|
|
165
|
-
#
|
|
166
|
-
if (-
|
|
177
|
+
# Extract actual filename from Content-Disposition if it was a temp filename
|
|
178
|
+
if ($filename -like "chef-download-temp-*" -and $download_result -and $download_result.Filename) {
|
|
167
179
|
$actual_filename = $download_result.Filename
|
|
168
180
|
Write-Host "Extracted filename from Content-Disposition: $actual_filename"
|
|
169
181
|
|
|
@@ -178,8 +190,8 @@ function Install-Project {
|
|
|
178
190
|
move-item $download_destination $final_destination -force
|
|
179
191
|
$download_destination = $final_destination
|
|
180
192
|
}
|
|
181
|
-
} elseif (-
|
|
182
|
-
Write-Host "Warning: Could not extract filename from Content-Disposition header
|
|
193
|
+
} elseif ($filename -like "chef-download-temp-*") {
|
|
194
|
+
Write-Host "Warning: Could not extract filename from Content-Disposition header."
|
|
183
195
|
Write-Host "Using temporary filename. Package installation may fail."
|
|
184
196
|
}
|
|
185
197
|
}
|
|
@@ -193,17 +205,25 @@ function Install-Project {
|
|
|
193
205
|
Write-Host "Installing $project from $download_destination"
|
|
194
206
|
$installingProject = $True
|
|
195
207
|
$installAttempts = 0
|
|
208
|
+
$maxAttempts = 5
|
|
196
209
|
while ($installingProject) {
|
|
197
210
|
$installAttempts++
|
|
198
211
|
$result = $false
|
|
199
|
-
if($download_destination.EndsWith(".appx")) {
|
|
212
|
+
if ($download_destination.EndsWith(".appx")) {
|
|
200
213
|
$result = Install-ChefAppx $download_destination $project
|
|
201
214
|
}
|
|
202
215
|
else {
|
|
203
216
|
$result = Install-ChefMsi $download_destination $daemon
|
|
204
217
|
}
|
|
205
|
-
if(!$result) {
|
|
218
|
+
if (!$result) {
|
|
219
|
+
if ($installAttempts -ge $maxAttempts) {
|
|
220
|
+
Write-Host "Failed to install $project after $installAttempts attempts."
|
|
221
|
+
throw "Installation failed after $installAttempts attempts."
|
|
222
|
+
}
|
|
223
|
+
continue
|
|
224
|
+
}
|
|
206
225
|
$installingProject = $False
|
|
226
|
+
Write-Host "$project installation completed successfully."
|
|
207
227
|
}
|
|
208
228
|
}
|
|
209
229
|
}
|
|
@@ -25,15 +25,10 @@ module Mixlib
|
|
|
25
25
|
install_project_module = []
|
|
26
26
|
install_project_module << get_script("helpers.ps1", context)
|
|
27
27
|
install_project_module << get_script("get_project_metadata.ps1", context)
|
|
28
|
-
install_project_module << get_script("install_project.ps1")
|
|
28
|
+
install_project_module << get_script("install_project.ps1", context)
|
|
29
29
|
|
|
30
30
|
install_command = []
|
|
31
31
|
install_command << ps1_modularize(install_project_module.join("\n"), "Installer-Module")
|
|
32
|
-
# If license_id is provided in context, add it to the default install command
|
|
33
|
-
if context[:license_id] && !context[:license_id].to_s.empty?
|
|
34
|
-
install_command << "# License ID provided via context - adding to install command"
|
|
35
|
-
install_command << "install -license_id '#{context[:license_id]}'"
|
|
36
|
-
end
|
|
37
32
|
install_command.join("\n\n")
|
|
38
33
|
end
|
|
39
34
|
|
|
@@ -51,8 +46,8 @@ module Mixlib
|
|
|
51
46
|
def install_command
|
|
52
47
|
install_project_module = []
|
|
53
48
|
install_project_module << get_script("helpers.ps1", user_agent_headers: options.user_agent_headers)
|
|
54
|
-
install_project_module << get_script("get_project_metadata.ps1")
|
|
55
|
-
install_project_module << get_script("install_project.ps1")
|
|
49
|
+
install_project_module << get_script("get_project_metadata.ps1", license_id: options.license_id, base_url: options.base_url)
|
|
50
|
+
install_project_module << get_script("install_project.ps1", license_id: options.license_id)
|
|
56
51
|
install_command = []
|
|
57
52
|
install_command << ps1_modularize(install_project_module.join("\n"), "Installer-Module")
|
|
58
53
|
install_command << render_command
|
|
@@ -72,10 +67,11 @@ module Mixlib
|
|
|
72
67
|
end
|
|
73
68
|
|
|
74
69
|
def render_command
|
|
75
|
-
cmd = "
|
|
70
|
+
cmd = "Install-Project -project #{options.product_name}"
|
|
76
71
|
cmd << " -version #{options.product_version}"
|
|
77
72
|
cmd << " -channel #{options.channel}"
|
|
78
73
|
cmd << " -architecture #{options.architecture}" if options.architecture
|
|
74
|
+
cmd << " -base_server_uri '#{options.base_url}'" if options.base_url && !options.base_url.to_s.empty?
|
|
79
75
|
cmd << " -license_id #{options.license_id}" if options.license_id && !options.license_id.to_s.empty?
|
|
80
76
|
cmd << install_command_params if options.install_command_options
|
|
81
77
|
cmd << "\n"
|
|
@@ -22,10 +22,11 @@ module Mixlib
|
|
|
22
22
|
class Install
|
|
23
23
|
class Generator
|
|
24
24
|
def self.install_command(options)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
if options.for_ps1?
|
|
26
|
+
PowerShell.new(options).install_command
|
|
27
|
+
else
|
|
28
|
+
Bourne.new(options).install_command
|
|
29
|
+
end
|
|
29
30
|
end
|
|
30
31
|
end
|
|
31
32
|
end
|
|
@@ -64,6 +64,7 @@ module Mixlib
|
|
|
64
64
|
:user_agent_headers,
|
|
65
65
|
:install_command_options,
|
|
66
66
|
:license_id,
|
|
67
|
+
:base_url,
|
|
67
68
|
]
|
|
68
69
|
|
|
69
70
|
SUPPORTED_WINDOWS_DESKTOP_VERSIONS = %w{10}
|
|
@@ -90,6 +91,8 @@ module Mixlib
|
|
|
90
91
|
|
|
91
92
|
map_windows_versions!
|
|
92
93
|
|
|
94
|
+
enforce_trial_api_defaults!
|
|
95
|
+
|
|
93
96
|
validate!
|
|
94
97
|
end
|
|
95
98
|
|
|
@@ -257,6 +260,43 @@ Must provide platform (-p), platform version (-l) and architecture (-a) when spe
|
|
|
257
260
|
def all_or_none?(items)
|
|
258
261
|
items.all? || items.compact.empty?
|
|
259
262
|
end
|
|
263
|
+
|
|
264
|
+
# Trial API only supports stable channel and latest version
|
|
265
|
+
# If using trial API with other settings, default them and warn the user
|
|
266
|
+
def enforce_trial_api_defaults!
|
|
267
|
+
return unless use_trial_api?
|
|
268
|
+
|
|
269
|
+
original_channel = channel
|
|
270
|
+
original_version = product_version
|
|
271
|
+
|
|
272
|
+
# Force stable channel for trial API
|
|
273
|
+
if channel != :stable
|
|
274
|
+
options[:channel] = :stable
|
|
275
|
+
warn "WARNING: Trial API only supports 'stable' channel. Changing from '#{original_channel}' to 'stable'."
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# Force latest version for trial API
|
|
279
|
+
if product_version != :latest && product_version.to_sym != :latest
|
|
280
|
+
options[:product_version] = :latest
|
|
281
|
+
warn "WARNING: Trial API only supports 'latest' version. Changing from '#{original_version}' to 'latest'."
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def use_trial_api?
|
|
286
|
+
license_id = options[:license_id] || options["license_id"] || default_options[:license_id]
|
|
287
|
+
Mixlib::Install::Dist.trial_license?(license_id)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# Validate that licensed API usage conforms to API restrictions
|
|
291
|
+
# This is informational only - enforce_trial_api_defaults! already handles corrections
|
|
292
|
+
def validate_licensed_api_restrictions
|
|
293
|
+
return unless license_id && !license_id.to_s.empty?
|
|
294
|
+
|
|
295
|
+
if use_trial_api?
|
|
296
|
+
# These are enforced by enforce_trial_api_defaults! but we can add extra validation here if needed
|
|
297
|
+
# Currently no additional validation needed since defaults are auto-applied
|
|
298
|
+
end
|
|
299
|
+
end
|
|
260
300
|
end
|
|
261
301
|
end
|
|
262
302
|
end
|
|
@@ -132,7 +132,7 @@ PRODUCT_MATRIX = Mixlib::Install::ProductMatrix.new do
|
|
|
132
132
|
end
|
|
133
133
|
config_file "/etc/delivery/delivery.rb"
|
|
134
134
|
github_repo "chef/automate"
|
|
135
|
-
downloads_product_page_url "
|
|
135
|
+
downloads_product_page_url "#{Mixlib::Install::Dist::DOWNLOADS_PAGE}/automate"
|
|
136
136
|
end
|
|
137
137
|
|
|
138
138
|
product "ha" do
|
|
@@ -155,6 +155,11 @@ PRODUCT_MATRIX = Mixlib::Install::ProductMatrix.new do
|
|
|
155
155
|
package_name "inspec"
|
|
156
156
|
end
|
|
157
157
|
|
|
158
|
+
product "inspec-enterprise" do
|
|
159
|
+
product_name "Chef InSpec Enterprise"
|
|
160
|
+
package_name "inspec-enterprise"
|
|
161
|
+
end
|
|
162
|
+
|
|
158
163
|
product "mac-bootstrapper" do
|
|
159
164
|
product_name "Habitat Mac Bootstrapper"
|
|
160
165
|
package_name "mac-bootstrapper"
|
|
@@ -21,7 +21,6 @@ require_relative "util"
|
|
|
21
21
|
require_relative "generator/powershell"
|
|
22
22
|
require_relative "dist"
|
|
23
23
|
require "cgi"
|
|
24
|
-
require "net/http" unless defined?(Net::HTTP)
|
|
25
24
|
|
|
26
25
|
module Mixlib
|
|
27
26
|
class Install
|
|
@@ -58,18 +57,23 @@ module Mixlib
|
|
|
58
57
|
attr_accessor :omnibus_url
|
|
59
58
|
attr_accessor :install_msi_url
|
|
60
59
|
|
|
60
|
+
attr_accessor :license_id
|
|
61
|
+
attr_accessor :base_url
|
|
62
|
+
|
|
61
63
|
VALID_INSTALL_OPTS = %w{omnibus_url
|
|
62
64
|
endpoint
|
|
63
65
|
http_proxy
|
|
64
66
|
https_proxy
|
|
65
67
|
install_flags
|
|
66
68
|
install_msi_url
|
|
69
|
+
license_id
|
|
67
70
|
nightlies
|
|
68
71
|
prerelease
|
|
69
72
|
project
|
|
70
73
|
root
|
|
71
74
|
use_sudo
|
|
72
|
-
sudo_command
|
|
75
|
+
sudo_command
|
|
76
|
+
base_url}
|
|
73
77
|
|
|
74
78
|
def initialize(version, powershell = false, opts = {})
|
|
75
79
|
@version = (version || "latest").to_s.downcase
|
|
@@ -80,17 +84,29 @@ module Mixlib
|
|
|
80
84
|
@prerelease = false
|
|
81
85
|
@nightlies = false
|
|
82
86
|
@endpoint = "metadata"
|
|
83
|
-
@omnibus_url = "
|
|
87
|
+
@omnibus_url = "#{Mixlib::Install::Dist::OMNITRUCK_ENDPOINT}/install.sh"
|
|
84
88
|
@use_sudo = true
|
|
85
89
|
@sudo_command = "sudo -E"
|
|
90
|
+
@license_id = nil
|
|
91
|
+
@project = Mixlib::Install::Dist::DEFAULT_PRODUCT.freeze
|
|
92
|
+
@channel = "stable"
|
|
86
93
|
|
|
87
94
|
@root = if powershell
|
|
88
|
-
"$env:systemdrive\\#{Mixlib::Install::Dist::
|
|
95
|
+
"$env:systemdrive\\#{Mixlib::Install::Dist::OMNIBUS_WINDOWS_INSTALL_DIR}\\#{Mixlib::Install::Dist::DEFAULT_PRODUCT}"
|
|
89
96
|
else
|
|
90
|
-
"#{Mixlib::Install::Dist::
|
|
97
|
+
"#{Mixlib::Install::Dist::OMNIBUS_LINUX_INSTALL_DIR}/#{Mixlib::Install::Dist::DEFAULT_PRODUCT}"
|
|
91
98
|
end
|
|
92
99
|
|
|
93
100
|
parse_opts(opts)
|
|
101
|
+
|
|
102
|
+
# Update root for chef-ice to use Habitat install directories
|
|
103
|
+
if @project&.casecmp("chef-ice") == 0
|
|
104
|
+
@root = if powershell
|
|
105
|
+
"$env:systemdrive\\#{Mixlib::Install::Dist::HABITAT_WINDOWS_INSTALL_DIR}\\chef\\chef-infra-client\\*\\*"
|
|
106
|
+
else
|
|
107
|
+
"#{Mixlib::Install::Dist::HABITAT_LINUX_INSTALL_DIR}/chef/chef-infra-client/*/*"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
94
110
|
end
|
|
95
111
|
|
|
96
112
|
def install_command
|
|
@@ -102,17 +118,6 @@ module Mixlib
|
|
|
102
118
|
shell_code_from_file(vars)
|
|
103
119
|
end
|
|
104
120
|
|
|
105
|
-
def install_command_from_omnitruck(url)
|
|
106
|
-
uri = URI.parse(url)
|
|
107
|
-
response = Net::HTTP.get_response(uri)
|
|
108
|
-
|
|
109
|
-
if response.code == "200"
|
|
110
|
-
response.body
|
|
111
|
-
else
|
|
112
|
-
raise StandardError, "unable to fetch the install.sh"
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
121
|
private
|
|
117
122
|
|
|
118
123
|
# Generates the install command variables for Bourne shell-based
|
|
@@ -124,11 +129,12 @@ module Mixlib
|
|
|
124
129
|
flags = %w{latest true nightlies}.include?(version) ? "" : "-v #{CGI.escape(version)}"
|
|
125
130
|
flags << " " << "-n" if nightlies
|
|
126
131
|
flags << " " << "-p" if prerelease
|
|
132
|
+
flags << " " << "-l #{license_id}" if license_id && !license_id.to_s.empty?
|
|
127
133
|
flags << " " << install_flags if install_flags
|
|
128
134
|
|
|
129
135
|
[
|
|
130
136
|
shell_var("chef_omnibus_root", root),
|
|
131
|
-
shell_var("chef_omnibus_url",
|
|
137
|
+
shell_var("chef_omnibus_url", omnibus_url_for_license),
|
|
132
138
|
shell_var("install_flags", flags.strip),
|
|
133
139
|
shell_var("pretty_version", Util.pretty_version(version)),
|
|
134
140
|
shell_var("sudo_sh", sudo("sh")),
|
|
@@ -151,6 +157,7 @@ module Mixlib
|
|
|
151
157
|
shell_var("msi", "#{download_directory}\\chef-#{version}.msi"),
|
|
152
158
|
shell_var("download_directory", download_directory),
|
|
153
159
|
].tap do |vars|
|
|
160
|
+
vars << shell_var("license_id", license_id) if license_id && !license_id.to_s.empty?
|
|
154
161
|
if install_msi_url
|
|
155
162
|
vars << shell_var("chef_msi_url", install_msi_url)
|
|
156
163
|
else
|
|
@@ -172,6 +179,7 @@ module Mixlib
|
|
|
172
179
|
validate_opts!(opt)
|
|
173
180
|
case opt.to_s
|
|
174
181
|
when "project", "endpoint"
|
|
182
|
+
@project = setting if opt.to_s == "project"
|
|
175
183
|
self.endpoint = metadata_endpoint_from_project(setting)
|
|
176
184
|
else
|
|
177
185
|
send("#{opt.to_sym}=", setting)
|
|
@@ -216,23 +224,65 @@ module Mixlib
|
|
|
216
224
|
|
|
217
225
|
# @return the correct Chef Omnitruck API metadata endpoint, based on project
|
|
218
226
|
def metadata_endpoint_from_project(project = nil)
|
|
219
|
-
if project.nil? || project.casecmp(
|
|
227
|
+
if project.nil? || project.casecmp(Mixlib::Install::Dist::DEFAULT_PRODUCT) == 0
|
|
220
228
|
"metadata"
|
|
221
229
|
else
|
|
222
230
|
"metadata-#{project.downcase}"
|
|
223
231
|
end
|
|
224
232
|
end
|
|
225
233
|
|
|
234
|
+
# Returns the appropriate omnibus URL based on whether license_id is provided
|
|
235
|
+
# @return [String] the omnibus URL (commercial/trial or standard omnitruck)
|
|
236
|
+
# @api private
|
|
237
|
+
def omnibus_url_for_license
|
|
238
|
+
return omnibus_url if license_id.nil? || license_id.to_s.empty? || omnibus_url != "#{Mixlib::Install::Dist::OMNITRUCK_ENDPOINT}/install.sh"
|
|
239
|
+
|
|
240
|
+
# Use custom base_url if provided, otherwise determine from license type
|
|
241
|
+
endpoint_base = if @base_url
|
|
242
|
+
@base_url
|
|
243
|
+
elsif license_id.start_with?("free-", "trial-")
|
|
244
|
+
Mixlib::Install::Dist::TRIAL_API_ENDPOINT
|
|
245
|
+
else
|
|
246
|
+
Mixlib::Install::Dist::COMMERCIAL_API_ENDPOINT
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Add license_id as query param when using licensed endpoints
|
|
250
|
+
"#{endpoint_base}/install.sh?license_id=#{CGI.escape(license_id)}"
|
|
251
|
+
end
|
|
252
|
+
|
|
226
253
|
def windows_metadata_url
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
254
|
+
# Determine if we're using commercial/trial API
|
|
255
|
+
using_licensed_api = license_id && !license_id.to_s.empty?
|
|
256
|
+
|
|
257
|
+
if using_licensed_api
|
|
258
|
+
# Commercial/trial API: <base_url>/<channel>/<project>/metadata
|
|
259
|
+
# Use custom base_url if provided, otherwise determine from license type
|
|
260
|
+
endpoint_base = if @base_url
|
|
261
|
+
@base_url
|
|
262
|
+
elsif license_id.start_with?("free-", "trial-")
|
|
263
|
+
Mixlib::Install::Dist::TRIAL_API_ENDPOINT
|
|
264
|
+
else
|
|
265
|
+
Mixlib::Install::Dist::COMMERCIAL_API_ENDPOINT
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
product_name = @project
|
|
269
|
+
url = "#{endpoint_base}/#{@channel}/#{product_name}/metadata"
|
|
270
|
+
else
|
|
271
|
+
# Omnitruck API: use base from omnibus_url + endpoint
|
|
272
|
+
base = if omnibus_url_for_license.match?(%r{/install.sh})
|
|
273
|
+
# Ensure base URL ends with /
|
|
274
|
+
base_url = File.dirname(omnibus_url_for_license)
|
|
275
|
+
base_url += "/" unless base_url.end_with?("/")
|
|
276
|
+
base_url
|
|
277
|
+
end
|
|
278
|
+
url = "#{base}#{endpoint}"
|
|
279
|
+
end
|
|
230
280
|
|
|
231
|
-
url = "#{base}#{endpoint}"
|
|
232
281
|
url << "?p=windows&m=$platform_architecture&pv=$platform_version"
|
|
233
282
|
url << "&v=#{CGI.escape(version)}" unless %w{latest true nightlies}.include?(version)
|
|
234
283
|
url << "&prerelease=true" if prerelease
|
|
235
284
|
url << "&nightlies=true" if nightlies
|
|
285
|
+
url << "&license_id=#{CGI.escape(license_id)}" if license_id && !license_id.to_s.empty?
|
|
236
286
|
url
|
|
237
287
|
end
|
|
238
288
|
|
data/lib/mixlib/install.rb
CHANGED
|
@@ -63,14 +63,18 @@ module Mixlib
|
|
|
63
63
|
#
|
|
64
64
|
# @param [String, Symbol] channel
|
|
65
65
|
#
|
|
66
|
+
# @param [String] license_id (optional)
|
|
67
|
+
#
|
|
66
68
|
# @return [Array<String>] list of available versions for the given
|
|
67
69
|
# product_name and channel.
|
|
68
|
-
def self.available_versions(product_name, channel)
|
|
70
|
+
def self.available_versions(product_name, channel, license_id: nil)
|
|
71
|
+
opts = {
|
|
72
|
+
product_name: product_name,
|
|
73
|
+
channel: channel.to_sym,
|
|
74
|
+
}
|
|
75
|
+
opts[:license_id] = license_id if license_id
|
|
69
76
|
Backend.available_versions(
|
|
70
|
-
Mixlib::Install::Options.new(
|
|
71
|
-
product_name: product_name,
|
|
72
|
-
channel: channel.to_sym
|
|
73
|
-
)
|
|
77
|
+
Mixlib::Install::Options.new(opts)
|
|
74
78
|
)
|
|
75
79
|
end
|
|
76
80
|
|
|
@@ -103,6 +107,7 @@ module Mixlib
|
|
|
103
107
|
uri = URI.parse(artifact.url)
|
|
104
108
|
filename = nil
|
|
105
109
|
final_body = nil
|
|
110
|
+
final_uri = uri
|
|
106
111
|
|
|
107
112
|
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
|
|
108
113
|
# Build the request path including query string
|
|
@@ -112,23 +117,27 @@ module Mixlib
|
|
|
112
117
|
# Get the response, following redirects
|
|
113
118
|
response = http.request_get(request_path)
|
|
114
119
|
|
|
120
|
+
# Try to extract filename from Content-Disposition in initial response
|
|
121
|
+
if response["content-disposition"]
|
|
122
|
+
filename = response["content-disposition"][/filename="?([^"]+)"?/, 1]
|
|
123
|
+
end
|
|
124
|
+
|
|
115
125
|
# Follow redirects
|
|
116
126
|
redirect_limit = 5
|
|
117
127
|
while response.is_a?(Net::HTTPRedirection) && redirect_limit > 0
|
|
118
128
|
redirect_uri = URI.parse(response["location"])
|
|
119
129
|
# Handle relative redirects
|
|
120
130
|
redirect_uri = uri + redirect_uri if redirect_uri.relative?
|
|
131
|
+
final_uri = redirect_uri
|
|
121
132
|
|
|
122
133
|
Net::HTTP.start(redirect_uri.host, redirect_uri.port, use_ssl: redirect_uri.scheme == "https") do |redirect_http|
|
|
123
134
|
redirect_path = redirect_uri.path
|
|
124
135
|
redirect_path += "?#{redirect_uri.query}" if redirect_uri.query
|
|
125
136
|
response = redirect_http.request_get(redirect_path)
|
|
126
137
|
|
|
127
|
-
# Try to get filename from Content-Disposition
|
|
128
|
-
if response["content-disposition"]
|
|
138
|
+
# Try to get filename from Content-Disposition in redirect response
|
|
139
|
+
if response["content-disposition"] && filename.nil?
|
|
129
140
|
filename = response["content-disposition"][/filename="?([^"]+)"?/, 1]
|
|
130
|
-
else
|
|
131
|
-
filename = File.basename(redirect_uri.path)
|
|
132
141
|
end
|
|
133
142
|
end
|
|
134
143
|
|
|
@@ -136,9 +145,23 @@ module Mixlib
|
|
|
136
145
|
end
|
|
137
146
|
|
|
138
147
|
final_body = response.body
|
|
148
|
+
|
|
149
|
+
# Try Content-Disposition from final successful response
|
|
150
|
+
if response["content-disposition"] && filename.nil?
|
|
151
|
+
filename = response["content-disposition"][/filename="?([^"]+)"?/, 1]
|
|
152
|
+
end
|
|
139
153
|
end
|
|
140
154
|
|
|
141
|
-
#
|
|
155
|
+
# Fallback: extract filename from final URL path (works for direct package URLs)
|
|
156
|
+
if filename.nil?
|
|
157
|
+
path_filename = File.basename(final_uri.path.split("?").first)
|
|
158
|
+
# Only use path filename if it looks like a package file
|
|
159
|
+
if /\.(rpm|deb|pkg|msi|dmg|bff|p5p|sh|tar|gz|appx)$/.match?(path_filename)
|
|
160
|
+
filename = path_filename
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Final fallback: use basename of original URL
|
|
142
165
|
filename ||= File.basename(uri.path)
|
|
143
166
|
file = File.join(directory, filename)
|
|
144
167
|
|
|
@@ -158,10 +181,19 @@ module Mixlib
|
|
|
158
181
|
def root
|
|
159
182
|
# This only works for chef and chefdk but they are the only projects
|
|
160
183
|
# we are supporting as of now.
|
|
161
|
-
|
|
162
|
-
|
|
184
|
+
# chef-ice uses Habitat install directories
|
|
185
|
+
if options.product_name.casecmp("chef-ice") == 0
|
|
186
|
+
if options.for_ps1?
|
|
187
|
+
"$env:systemdrive\\#{Mixlib::Install::Dist::HABITAT_WINDOWS_INSTALL_DIR}\\chef\\chef-infra-client\\*\\*"
|
|
188
|
+
else
|
|
189
|
+
"#{Mixlib::Install::Dist::HABITAT_LINUX_INSTALL_DIR}/chef/chef-infra-client/*/*"
|
|
190
|
+
end
|
|
163
191
|
else
|
|
164
|
-
|
|
192
|
+
if options.for_ps1?
|
|
193
|
+
"$env:systemdrive\\#{Mixlib::Install::Dist::OMNIBUS_WINDOWS_INSTALL_DIR}\\#{options.product_name}"
|
|
194
|
+
else
|
|
195
|
+
"#{Mixlib::Install::Dist::OMNIBUS_LINUX_INSTALL_DIR}/#{options.product_name}"
|
|
196
|
+
end
|
|
165
197
|
end
|
|
166
198
|
end
|
|
167
199
|
|
|
@@ -175,10 +207,19 @@ module Mixlib
|
|
|
175
207
|
# install directory which can be different than the product name (e.g.
|
|
176
208
|
# chef-server -> /opt/opscode). But this is OK for now since
|
|
177
209
|
# chef & chefdk are the only supported products.
|
|
178
|
-
|
|
179
|
-
|
|
210
|
+
# chef-ice uses Habitat install directories
|
|
211
|
+
version_manifest_file = if options.product_name.casecmp("chef-ice") == 0
|
|
212
|
+
if options.for_ps1?
|
|
213
|
+
"$env:systemdrive\\#{Mixlib::Install::Dist::HABITAT_WINDOWS_INSTALL_DIR}\\chef\\chef-infra-client\\*\\*\\version-manifest.json"
|
|
214
|
+
else
|
|
215
|
+
"#{Mixlib::Install::Dist::HABITAT_LINUX_INSTALL_DIR}/chef/chef-infra-client/*/*/version-manifest.json"
|
|
216
|
+
end
|
|
180
217
|
else
|
|
181
|
-
|
|
218
|
+
if options.for_ps1?
|
|
219
|
+
"$env:systemdrive\\#{Mixlib::Install::Dist::OMNIBUS_WINDOWS_INSTALL_DIR}\\#{options.product_name}\\version-manifest.json"
|
|
220
|
+
else
|
|
221
|
+
"/opt/#{options.product_name}/version-manifest.json"
|
|
222
|
+
end
|
|
182
223
|
end
|
|
183
224
|
|
|
184
225
|
if File.exist? version_manifest_file
|
|
@@ -258,8 +299,25 @@ module Mixlib
|
|
|
258
299
|
# ------------------
|
|
259
300
|
# base_url [String]
|
|
260
301
|
# url pointing to the omnitruck to be queried by the script.
|
|
302
|
+
# license_id [String]
|
|
303
|
+
# license ID for commercial or trial API access.
|
|
304
|
+
# If license_id starts with 'free-' or 'trial-', trial API defaults are enforced.
|
|
261
305
|
#
|
|
262
306
|
def self.install_sh(context = {})
|
|
307
|
+
# Apply trial API defaults if license_id indicates trial
|
|
308
|
+
if context[:license_id] && Mixlib::Install::Dist.trial_license?(context[:license_id])
|
|
309
|
+
# Warn and override if non-compliant values provided
|
|
310
|
+
if context[:channel] && context[:channel].to_s != "stable"
|
|
311
|
+
warn "WARNING: Trial API only supports 'stable' channel. Changing from '#{context[:channel]}' to 'stable'."
|
|
312
|
+
context[:channel] = "stable"
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
if context[:version] && !["latest", nil].include?(context[:version].to_s)
|
|
316
|
+
warn "WARNING: Trial API only supports 'latest' version. Changing from '#{context[:version]}' to 'latest'."
|
|
317
|
+
context[:version] = "latest"
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
263
321
|
Mixlib::Install::Generator::Bourne.install_sh(context)
|
|
264
322
|
end
|
|
265
323
|
|
|
@@ -269,8 +327,25 @@ module Mixlib
|
|
|
269
327
|
# ------------------
|
|
270
328
|
# base_url [String]
|
|
271
329
|
# url pointing to the omnitruck to be queried by the script.
|
|
330
|
+
# license_id [String]
|
|
331
|
+
# license ID for commercial or trial API access.
|
|
332
|
+
# If license_id starts with 'free-' or 'trial-', trial API defaults are enforced.
|
|
272
333
|
#
|
|
273
334
|
def self.install_ps1(context = {})
|
|
335
|
+
# Apply trial API defaults if license_id indicates trial
|
|
336
|
+
if context[:license_id] && Mixlib::Install::Dist.trial_license?(context[:license_id])
|
|
337
|
+
# Warn and override if non-compliant values provided
|
|
338
|
+
if context[:channel] && context[:channel].to_s != "stable"
|
|
339
|
+
warn "WARNING: Trial API only supports 'stable' channel. Changing from '#{context[:channel]}' to 'stable'."
|
|
340
|
+
context[:channel] = "stable"
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
if context[:version] && !["latest", nil].include?(context[:version].to_s)
|
|
344
|
+
warn "WARNING: Trial API only supports 'latest' version. Changing from '#{context[:version]}' to 'latest'."
|
|
345
|
+
context[:version] = "latest"
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
|
|
274
349
|
Mixlib::Install::Generator::PowerShell.install_ps1(context)
|
|
275
350
|
end
|
|
276
351
|
end
|