Skip to content

Commit

Permalink
Validate & checksum downloaded artifacts
Browse files Browse the repository at this point in the history
  • Loading branch information
hgeraldino committed Dec 27, 2022
1 parent b6d270a commit 5784bed
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 31 deletions.
90 changes: 64 additions & 26 deletions src/main/bash/sdkman-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,36 @@ function __sdk_install() {
}

function __sdkman_install_candidate_version() {
local candidate version

local candidate version base_name headers_file archive_type
local metadata_folder="${SDKMAN_DIR}/var/metadata"

candidate="$1"
version="$2"
base_name="${candidate}-${version}"
headers_file="${metadata_folder}/${base_name}.headers"

mkdir -p ${metadata_folder}

__sdkman_download "$candidate" "$version" || return 1
__sdkman_download "$candidate" "$version" "$headers_file" || return 1
__sdkman_echo_green "Installing: ${candidate} ${version}"

mkdir -p "${SDKMAN_CANDIDATES_DIR}/${candidate}"

rm -rf "${SDKMAN_DIR}/tmp/out"
unzip -oq "${SDKMAN_DIR}/tmp/${candidate}-${version}.bin" -d "${SDKMAN_DIR}/tmp/out"

archive_type=$(sed -n 's/^X-Sdkman-ArchiveType:\(.*\)$/\1/p' ${headers_file} | tr -cd '[:alnum:]')

if [[ "${archive_type}" == 'zip' ]]; then
unzip -oq "${SDKMAN_DIR}/tmp/${base_name}.bin" -d "${SDKMAN_DIR}/tmp/out"
elif [[ "${archive_type}" == 'tar' ]]; then
mkdir -p "${SDKMAN_DIR}/tmp/out"
tar zxf "${SDKMAN_DIR}/tmp/${base_name}.bin" -C "${SDKMAN_DIR}/tmp/out"
else
echo ""
__sdkman_echo_red "Stop! The archive type cannot be determined! Please try installing again."
rm -f "${SDKMAN_DIR}/tmp/${base_name}.bin"
return 1
fi

mv -f "$SDKMAN_DIR"/tmp/out/* "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}"
__sdkman_echo_green "Done installing!"
echo ""
Expand Down Expand Up @@ -114,22 +132,18 @@ function __sdkman_install_local_version() {
}

function __sdkman_download() {
local candidate version
local candidate version headers_file

candidate="$1"
version="$2"
headers_file="$3"

metadata_folder="${SDKMAN_DIR}/var/metadata"
mkdir -p ${metadata_folder}

local platform_parameter="$(echo $SDKMAN_PLATFORM | tr '[:upper:]' '[:lower:]')"
local download_url="${SDKMAN_CANDIDATES_API}/broker/download/${candidate}/${version}/${platform_parameter}"
local base_name="${candidate}-${version}"
local tmp_headers_file="${SDKMAN_DIR}/tmp/${base_name}.headers.tmp"
local headers_file="${metadata_folder}/${base_name}.headers"

export local binary_input="${SDKMAN_DIR}/tmp/${base_name}.bin"
export local zip_output="${SDKMAN_DIR}/tmp/${base_name}.zip"

echo ""
__sdkman_echo_no_colour "Downloading: ${candidate} ${version}"
Expand All @@ -142,26 +156,50 @@ function __sdkman_download() {
grep '^X-Sdkman' "${tmp_headers_file}" > "${headers_file}"
__sdkman_echo_debug "Downloaded binary to: ${binary_input} (HTTP headers written to: ${headers_file})"

__sdkman_validate_zip "${binary_input}" || return 1
__sdkman_checksum_zip "${binary_input}" "${headers_file}" || return 1
echo ""
if [[ ! -s "${headers_file}" ]]; then
echo ""
__sdkman_echo_red "Metadata file not found (or is empty) at '${headers_file}'"
rm -f "${binary_input}"
return 1
else
__sdkman_validate "${binary_input}" "${headers_file}" || return 1
__sdkman_checksum "${binary_input}" "${headers_file}" || return 1
echo ""
fi
}

function __sdkman_validate_zip() {
local zip_archive zip_ok
function __sdkman_validate() {
local -r archive="$1"
local -r headers_file="$2"
local -r archive_type=$(sed -n 's/^X-Sdkman-ArchiveType:\(.*\)$/\1/p' ${headers_file} | tr -cd '[:alnum:]')
local is_ok

__sdkman_echo_debug "Archive Type: ${archive_type}"
__sdkman_echo_debug "Archive: ${archive}"

if [[ "${archive_type}" == 'zip' ]]; then
__sdkman_echo_debug "Checking zip archive"
is_ok=$(unzip -t "$archive" | grep 'No errors detected in compressed data')
elif [[ "${archive_type}" == 'tar' ]]; then
__sdkman_echo_debug "Checking tar archive"
is_ok=$(tar tf "$archive" | grep -v 'Error opening archive')
else
echo ""
__sdkman_echo_red "Stop! The archive type cannot be determined! Please try installing again."
rm -f "${archive}"
return 1
fi

zip_archive="$1"
zip_ok=$(unzip -t "$zip_archive" | grep 'No errors detected in compressed data')
if [ -z "$zip_ok" ]; then
rm -f "$zip_archive"
if [ -z "$is_ok" ]; then
rm -f "$archive"
echo ""
__sdkman_echo_red "Stop! The archive was corrupt and has been removed! Please try installing again."
return 1
fi
}

function __sdkman_checksum_zip() {
local -r zip_archive="$1"
function __sdkman_checksum() {
local -r archive="$1"
local -r headers_file="$2"
local algorithm checksum cmd
local shasum_avail=false
Expand Down Expand Up @@ -198,17 +236,17 @@ function __sdkman_checksum_zip() {
if [[ -n ${algorithm} && -n ${checksum} ]]; then

if [[ "$algorithm" =~ 'SHA' && "$shasum_avail" == 'true' ]]; then
cmd="echo \"${checksum} *${zip_archive}\" | shasum --check --quiet"
cmd="echo \"${checksum} *${archive}\" | shasum --check --quiet"

elif [[ "$algorithm" =~ 'MD5' && "$md5sum_avail" == 'true' ]]; then
cmd="echo \"${checksum} ${zip_archive}\" | md5sum --check --quiet"
cmd="echo \"${checksum} ${archive}\" | md5sum --check --quiet"
fi

if [[ -n $cmd ]]; then
__sdkman_echo_no_colour "Verifying artifact: ${zip_archive} (${algorithm}:${checksum})"
__sdkman_echo_no_colour "Verifying artifact: ${archive} (${algorithm}:${checksum})"

if ! eval "$cmd"; then
rm -f "$zip_archive"
rm -f "$archive"
echo ""
__sdkman_echo_red "Stop! An invalid checksum was detected and the archive removed! Please try re-installing."
return 1
Expand Down
2 changes: 1 addition & 1 deletion src/test/groovy/sdkman/steps/initialisation_steps.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ And(~'^the archive for candidate "([^"]*)" version "([^"]*)" is corrupt$') { Str
}

And(~'^the archive for candidate "([^"]*)" version "([^"]*)" is removed$') { String candidate, String version ->
def archive = new File("${sdkmanDir}/tmp/${candidate}-${version}.zip")
def archive = new File("${sdkmanDir}/tmp/${candidate}-${version}.bin")
assert !archive.exists()
}

Expand Down
24 changes: 22 additions & 2 deletions src/test/groovy/sdkman/steps/stub_steps.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,35 @@ And(~'^an available selfupdate$') { ->

And(~'^the candidate "([^"]*)" version "([^"]*)" is available for download$') { String candidate, String version ->
primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "valid")
primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform())
primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform(),
candidate == "java" ? ["X-Sdkman-ArchiveType": "tar"] : ["X-Sdkman-ArchiveType": "zip"])
primeEndpointWithString("/hooks/pre/${candidate}/${version}/${UnixUtils.inferPlatform()}", preInstallationHookSuccess())
primeEndpointWithString("/hooks/post/${candidate}/${version}/${UnixUtils.inferPlatform()}", postInstallationHookSuccess())
}

And(~'^the candidate "([^"]*)" version "([^"]*)" is available for download with an invalid archive type$') { String candidate, String version ->
primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "valid")
primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform(), ["X-Sdkman-ArchiveType": "docx"])
primeEndpointWithString("/hooks/pre/${candidate}/${version}/${UnixUtils.inferPlatform()}", preInstallationHookSuccess())
primeEndpointWithString("/hooks/post/${candidate}/${version}/${UnixUtils.inferPlatform()}", postInstallationHookSuccess())
}

And(~'^the candidate "([^"]*)" version "([^"]*)" is available for download with no headers$') {
String candidate, String version ->
primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "valid")
primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform(), [:])
primeEndpointWithString("/hooks/pre/${candidate}/${version}/${UnixUtils.inferPlatform()}", preInstallationHookSuccess())
primeEndpointWithString("/hooks/post/${candidate}/${version}/${UnixUtils.inferPlatform()}", postInstallationHookSuccess())
}

And(~'^the candidate "([^"]*)" version "([^"]*)" is available for download with checksum "([^"]*)" using algorithm "([^"]*)"$') {
String candidate, String version, String checksum, String algorithm ->
primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "valid")
primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform(), ["X-Sdkman-Checksum-${algorithm}": "${checksum}"])
primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform(), [
"X-Sdkman-ArchiveType": "zip",
"X-Sdkman-Checksum-${algorithm}": "${checksum}"
]
)
primeEndpointWithString("/hooks/pre/${candidate}/${version}/${UnixUtils.inferPlatform()}", preInstallationHookSuccess())
primeEndpointWithString("/hooks/post/${candidate}/${version}/${UnixUtils.inferPlatform()}", postInstallationHookSuccess())
}
Expand Down
3 changes: 2 additions & 1 deletion src/test/groovy/sdkman/stubs/WebServiceStub.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ class WebServiceStub {
}

static primeDownloadFor(String host, String candidate, String version, String platform) {
primeDownloadFor(host, candidate, version, platform, [:])
def archiveType = (candidate == "java") ? "tar" : "zip"
primeDownloadFor(host, candidate, version, platform, ["X-Sdkman-ArchiveType": archiveType])
}

static primeDownloadFor(String host, String candidate, String version, String platform, Map<String, String> headers) {
Expand Down
Binary file not shown.
30 changes: 29 additions & 1 deletion src/test/resources/features/install_candidate.feature
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,36 @@ Feature: Install Candidate
And the candidate "grails" version "1.3.9" should be the default
And the exit code is 0

Scenario: Install a tarball candidate and choose to make it default
Given the system is bootstrapped
And the candidate "java" version "8.0.111" is available for download
When I enter "sdk install java 8.0.111"
Then I see "Done installing!"
And I do not see "Do you want java 8.0.111 to be set as default? (Y/n)"
And the candidate "java" version "8.0.111" is installed
And the response headers file is created for candidate "java" and version "8.0.111"
And the exit code is 0

# revisit to redownload automatically


Scenario: Don't perform any validations if metadata is not found
Given the system is bootstrapped
And the candidate "grails" version "1.3.6" is available for download with no headers
When I enter "sdk install grails 1.3.6"
Then I see "Metadata file not found (or is empty)"
And the candidate "grails" version "1.3.6" is not installed
And the archive for candidate "grails" version "1.3.6" is removed
And the exit code is 1

Scenario: Abort installation on download of a Candidate without archive type information
Given the system is bootstrapped
And the candidate "grails" version "1.3.6" is available for download with an invalid archive type
When I enter "sdk install grails 1.3.6"
Then I see "Stop! The archive type cannot be determined! Please try installing again."
And the candidate "grails" version "1.3.6" is not installed
And the archive for candidate "grails" version "1.3.6" is removed
And the exit code is 1

Scenario: Abort installation on download of a corrupt Candidate archive
Given the system is bootstrapped
And the candidate "grails" version "1.3.6" is available for download
Expand Down

0 comments on commit 5784bed

Please sign in to comment.