-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: serve airgap samples Signed-off-by: Anatolii Bazko <[email protected]>
- Loading branch information
Showing
28 changed files
with
705 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
#!/bin/sh | ||
# | ||
# Copyright (c) 2021-2024 Red Hat, Inc. | ||
# This program and the accompanying materials are made | ||
# available under the terms of the Eclipse Public License 2.0 | ||
# which is available at https://www.eclipse.org/legal/epl-2.0/ | ||
# | ||
# SPDX-License-Identifier: EPL-2.0 | ||
# | ||
|
||
# The script is used to download resources (projects and devfiles) | ||
# for air-gapped (offline) environments. Only https://github.com is supported for now. | ||
|
||
set -e | ||
|
||
init() { | ||
unset AIRGAP_RESOURCES_DIR | ||
|
||
while [ "$#" -gt 0 ]; do | ||
case $1 in | ||
'--airgap-resources-dir'|'-d') AIRGAP_RESOURCES_DIR=$2; shift 1;; | ||
'--help'|'-h') usage; exit;; | ||
esac | ||
shift 1 | ||
done | ||
|
||
[ -z "${AIRGAP_RESOURCES_DIR}" ] && { usage; exit; } | ||
SAMPLES_JSON_PATH="${AIRGAP_RESOURCES_DIR}/index.json" | ||
} | ||
|
||
usage() { | ||
cat <<EOF | ||
Usage: $0 [OPTIONS] | ||
Options: | ||
--airgap-resources-dir, -d Directory where airgap resources are stored | ||
--help, -h Show this help message | ||
EOF | ||
} | ||
|
||
run() { | ||
samplesNum=$(jq -r '. | length' "${SAMPLES_JSON_PATH}") | ||
|
||
i=0 | ||
while [ "${i}" -lt "${samplesNum}" ]; do | ||
url=$(jq -r '.['${i}'].url' "${SAMPLES_JSON_PATH}") | ||
name=$(jq -r '.['${i}'].displayName' "${SAMPLES_JSON_PATH}") | ||
encodedName=$(echo "${name}" | jq -Rr @uri) | ||
|
||
if [ "${url}" != "null" ]; then | ||
strippedURL="${url#https://github.com/}" | ||
organization="$(echo "${strippedURL}" | cut -d '/' -f 1)" | ||
repository="$(echo "${strippedURL}" | cut -d '/' -f 2)" | ||
ref="$(echo "${strippedURL}" | cut -d '/' -f 4)" | ||
|
||
if [ -n "${ref}" ]; then | ||
archiveFileName="${organization}-${repository}-${ref}.zip" | ||
devfileFileName="${organization}-${repository}-${ref}-devfile.yaml" | ||
projectDownloadLink="https://api.github.com/repos/${organization}/${repository}/zipball/${ref}" | ||
devfileDownloadLink="https://api.github.com/repos/${organization}/${repository}/contents/devfile.yaml?ref=${ref}" | ||
else | ||
archiveFileName="${organization}-${repository}.zip" | ||
devfileFileName="${organization}-${repository}-devfile.yaml" | ||
projectDownloadLink="https://api.github.com/repos/${organization}/${repository}/zipball" | ||
devfileDownloadLink="https://api.github.com/repos/${organization}/${repository}/contents/devfile.yaml" | ||
fi | ||
|
||
echo "[INFO] Downloading ${url} into ${archiveFileName}" | ||
processSample \ | ||
"${archiveFileName}" \ | ||
"${devfileFileName}" \ | ||
"${projectDownloadLink}" \ | ||
"${devfileDownloadLink}" \ | ||
"${encodedName}" \ | ||
"${repository}" | ||
fi | ||
|
||
i=$((i+1)) | ||
done | ||
} | ||
|
||
processSample() { | ||
archiveFileName=$1 | ||
devfileFileName=$2 | ||
projectDownloadLink=$3 | ||
devfileDownloadLink=$4 | ||
encodedName=$5 | ||
repository=$6 | ||
|
||
curl -L \ | ||
-H "Accept: application/vnd.github.raw+json" \ | ||
-H "X-GitHub-Api-Version: 2022-11-28" \ | ||
-H "Authorization: token ${GITHUB_TOKEN}" \ | ||
"${devfileDownloadLink}" \ | ||
-o "${AIRGAP_RESOURCES_DIR}/${devfileFileName}" | ||
|
||
curl -L \ | ||
-H "Accept: application/vnd.github+json" \ | ||
-H "X-GitHub-Api-Version: 2022-11-28" \ | ||
-H "Authorization: token ${GITHUB_TOKEN}" \ | ||
"${projectDownloadLink}" \ | ||
-o "${AIRGAP_RESOURCES_DIR}/${archiveFileName}" | ||
|
||
# CHE_DASHBOARD_INTERNAL_URL is a placeholder that will be replaced | ||
# by the actual URL in entrypoint.sh | ||
devfileLink="CHE_DASHBOARD_INTERNAL_URL/dashboard/api/airgap-sample/devfile/download?name=${encodedName}" | ||
projectLink="CHE_DASHBOARD_INTERNAL_URL/dashboard/api/airgap-sample/project/download?name=${encodedName}" | ||
|
||
echo "$(jq '(.['${i}'].url) = '\"${devfileLink}\" ${SAMPLES_JSON_PATH})" > "${SAMPLES_JSON_PATH}" | ||
echo "$(jq '(.['${i}'].project.zip.filename) = '\"${archiveFileName}\" ${SAMPLES_JSON_PATH})" > "${SAMPLES_JSON_PATH}" | ||
echo "$(jq '(.['${i}'].devfile.filename) = '\"${devfileFileName}\" ${SAMPLES_JSON_PATH})" > "${SAMPLES_JSON_PATH}" | ||
|
||
# Update the devfile with the project link | ||
yq -riY '.projects=[{name: "'${repository}'", zip: {location: "'${projectLink}'"}}]' "${AIRGAP_RESOURCES_DIR}/${devfileFileName}" | ||
} | ||
|
||
init "$@" | ||
run |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
114 changes: 114 additions & 0 deletions
114
...ages/dashboard-backend/src/devworkspaceClient/services/__tests__/airGapSamplesApi.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
/* | ||
* Copyright (c) 2018-2024 Red Hat, Inc. | ||
* This program and the accompanying materials are made | ||
* available under the terms of the Eclipse Public License 2.0 | ||
* which is available at https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* Red Hat, Inc. - initial API and implementation | ||
*/ | ||
|
||
import path from 'path'; | ||
|
||
import { AirGapSampleApiService } from '@/devworkspaceClient/services/airGapSampleApi'; | ||
|
||
describe('Getting Started Samples API Service', () => { | ||
let airGapSampleApiService: AirGapSampleApiService; | ||
|
||
beforeEach(() => { | ||
jest.resetModules(); | ||
airGapSampleApiService = new AirGapSampleApiService( | ||
path.join(__dirname, 'fixtures', 'air-gap'), | ||
); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
test('fetching metadata', async () => { | ||
const res = await airGapSampleApiService.list(); | ||
expect(res.length).toEqual(6); | ||
}); | ||
|
||
test('reading devfile', async () => { | ||
const airGapResource = await airGapSampleApiService.downloadDevfile('Sample_devfile'); | ||
const devfileContent = await streamToString(airGapResource.stream); | ||
expect(devfileContent.length).toEqual(airGapResource.size); | ||
expect(devfileContent).toEqual( | ||
'schemaVersion: 2.2.0\n' + 'metadata:\n' + ' generateName: empty\n', | ||
); | ||
}); | ||
|
||
test('reading project', async () => { | ||
const airGapResource = await airGapSampleApiService.downloadProject('Sample_project'); | ||
const devfileContent = await streamToString(airGapResource.stream); | ||
expect(devfileContent.length).toEqual(airGapResource.size); | ||
expect(devfileContent).toEqual('project'); | ||
}); | ||
|
||
test('error reading devfile, if field not specified', async () => { | ||
try { | ||
await airGapSampleApiService.downloadDevfile('Sample_no_devfile_filename'); | ||
fail('should throw an error'); | ||
} catch (e: any) { | ||
expect(e.message).toEqual('filename not defined'); | ||
} | ||
}); | ||
|
||
test('error reading project, if field not specified', async () => { | ||
try { | ||
await airGapSampleApiService.downloadProject('Sample_no_project_filename'); | ||
fail('should throw an error'); | ||
} catch (e: any) { | ||
expect(e.message).toEqual('filename not defined'); | ||
} | ||
}); | ||
|
||
test('error reading devfile, if devfile does not exist', async () => { | ||
try { | ||
await airGapSampleApiService.downloadDevfile('Sample_devfile_not_exists'); | ||
fail('should throw an error'); | ||
} catch (e: any) { | ||
expect(e.message).toEqual('File not found'); | ||
} | ||
}); | ||
|
||
test('error reading project, if project does not exist', async () => { | ||
try { | ||
await airGapSampleApiService.downloadProject('Sample_project_not_exists'); | ||
fail('should throw an error'); | ||
} catch (e: any) { | ||
expect(e.message).toEqual('File not found'); | ||
} | ||
}); | ||
|
||
test('error reading devfile, sample not found', async () => { | ||
try { | ||
await airGapSampleApiService.downloadDevfile('Sample_not_exists'); | ||
fail('should throw an error'); | ||
} catch (e: any) { | ||
expect(e.message).toEqual('Sample not found'); | ||
} | ||
}); | ||
|
||
test('error reading project, sample not found', async () => { | ||
try { | ||
await airGapSampleApiService.downloadProject('Sample_not_exists'); | ||
fail('should throw an error'); | ||
} catch (e: any) { | ||
expect(e.message).toEqual('Sample not found'); | ||
} | ||
}); | ||
}); | ||
|
||
function streamToString(stream: NodeJS.ReadableStream): Promise<string> { | ||
const chunks: any[] = []; | ||
return new Promise((resolve, reject) => { | ||
stream.on('data', chunk => chunks.push(chunk)); | ||
stream.on('error', reject); | ||
stream.on('end', () => resolve(Buffer.concat(chunks).toString())); | ||
}); | ||
} |
Oops, something went wrong.