Skip to content

Commit

Permalink
Adding one more integration test interacting with a local registry
Browse files Browse the repository at this point in the history
  • Loading branch information
eoftedal committed Mar 8, 2023
1 parent 1a65a8e commit 9df77b7
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 36 deletions.
49 changes: 25 additions & 24 deletions src/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import * as fss from "fs";

import * as fileutil from "./fileutil";
import logger from "./logger";
import {Config, Image, Index, Layer, Manifest, IndexManifest, PartialManifestConfig, Platform} from "./types";
import {DockerV2, OCI} from "./MIMETypes";
import {getLayerTypeFileEnding} from "./utils";
import { Config, Image, Index, Layer, Manifest, IndexManifest, PartialManifestConfig, Platform } from "./types";
import { DockerV2, OCI } from "./MIMETypes";
import { getLayerTypeFileEnding } from "./utils";

type Headers = Record<string, string>;

Expand Down Expand Up @@ -153,7 +153,7 @@ function uploadContent(
}

export function createRegistry(registryBaseUrl: string, token: string) {
const auth = "Bearer " + token;
const auth = token.startsWith("Basic ") ? token : "Bearer " + token;

async function exists(image: Image, layer: Layer) {
const url = `${registryBaseUrl}${image.path}/blobs/${layer.digest}`;
Expand Down Expand Up @@ -193,54 +193,53 @@ export function createRegistry(registryBaseUrl: string, token: string) {
}

async function dlManifest(image: Image, preferredPlatform: Platform): Promise<Manifest> {

// Accept both manifests and index/manifest lists
const res = await dlJson<Manifest|Index>(
const res = await dlJson<Manifest | Index>(
`${registryBaseUrl}${image.path}/manifests/${image.tag}`,
buildHeaders(`${OCI.index}, ${OCI.manifest}, ${DockerV2.index}, ${DockerV2.manifest}`, auth),
);

// We've received an OCI Index or Docker Manifest List and need to find which manifest we want
if (res.mediaType === OCI.index || res.mediaType === DockerV2.index) {
const availableManifests = (res as Index).manifests
const adequateManifest = pickManifest(availableManifests, preferredPlatform)
return dlManifest({...image, tag: adequateManifest.digest}, preferredPlatform)
const availableManifests = (res as Index).manifests;
const adequateManifest = pickManifest(availableManifests, preferredPlatform);
return dlManifest({ ...image, tag: adequateManifest.digest }, preferredPlatform);
}

return res as Manifest
return res as Manifest;
}

function pickManifest(manifests: IndexManifest[], preferredPlatform: Platform): IndexManifest {
const matchingArchitectures = new Set<IndexManifest>;
const matchingOSes = new Set<IndexManifest>;
const matchingArchitectures = new Set<IndexManifest>();
const matchingOSes = new Set<IndexManifest>();
// Find sets of matching architecture and os
for (const manifest of manifests) {
if (manifest.platform.architecture === preferredPlatform.architecture) {
matchingArchitectures.add(manifest)
matchingArchitectures.add(manifest);
}
if (manifest.platform.os === preferredPlatform.os) {
matchingOSes.add(manifest)
matchingOSes.add(manifest);
}
}

// If the intersection of matching architectures and OS is one we've found our optimal match
const intersection = new Set([...matchingArchitectures].filter((x) => matchingOSes.has(x)));
if (intersection.size == 1) {
return intersection.values().next().value
return intersection.values().next().value;
}

// If we don't have a perfect match we give a warning and try the first matching architecture
if (matchingArchitectures.size >= 1) {
const matchingArch = matchingArchitectures.values().next().value
logger.info(`[WARN] Preferred OS '${preferredPlatform.os}' not available.`)
logger.info('[WARN] Using closest available manifest:', JSON.stringify(matchingArch.platform))
return matchingArch
const matchingArch = matchingArchitectures.values().next().value;
logger.info(`[WARN] Preferred OS '${preferredPlatform.os}' not available.`);
logger.info("[WARN] Using closest available manifest:", JSON.stringify(matchingArch.platform));
return matchingArch;
}

// If there's no image matching the wanted architecture we bail
logger.error(`No image matching requested architecture: '${preferredPlatform.architecture}'`)
logger.error('Available platforms:', JSON.stringify(manifests.map(m => m.platform)))
throw new Error('No image matching requested architecture')
logger.error(`No image matching requested architecture: '${preferredPlatform.architecture}'`);
logger.error("Available platforms:", JSON.stringify(manifests.map((m) => m.platform)));
throw new Error("No image matching requested architecture");
}

async function dlConfig(image: Image, config: Manifest["config"]): Promise<Config> {
Expand Down Expand Up @@ -309,10 +308,12 @@ export function createRegistry(registryBaseUrl: string, token: string) {
const config = await dlConfig(image, manifest.config);

if (config.architecture != preferredPlatform.architecture) {
logger.info(`[WARN] Image architecture (${config.architecture}) does not match preferred architecture (${preferredPlatform.architecture}).`)
logger.info(
`[WARN] Image architecture (${config.architecture}) does not match preferred architecture (${preferredPlatform.architecture}).`,
);
}
if (config.os != preferredPlatform.os) {
logger.info(`[WARN] Image OS (${config.os}) does not match preferred OS (${preferredPlatform.os}).`)
logger.info(`[WARN] Image OS (${config.os}) does not match preferred OS (${preferredPlatform.os}).`);
}

await fs.writeFile(path.join(folder, "config.json"), JSON.stringify(config));
Expand Down
25 changes: 13 additions & 12 deletions tests/integration/app/package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
{
"name": "demoapp",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.2"
}
"name": "demoapp",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.2"
}
}
68 changes: 68 additions & 0 deletions tests/localtest/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/bin/bash

set -e

TESTUSER=testuser
TESTPASSWORD=testpassword
BASICAUTH=$(echo -n "$TESTUSER:$TESTPASSWORD" | base64)

rm -rf tmp
mkdir -p tmp/certs
mkdir -p tmp/auth

printf "Generating key for local registry...\n"
openssl req \
-newkey rsa:4096 -nodes -sha256 -keyout tmp/certs/domain.key \
-addext "subjectAltName = DNS:myregistry.domain.com" \
-subj "/C=NO/ST=Doqr/L=Doqr/O=Doqr Integration/OU=Test Department/CN=doqr.test" \
-x509 -days 365 -out tmp/certs/domain.crt


printf "\nGenerating password for local registry...\n"
docker run \
--entrypoint htpasswd \
httpd:2 -Bbn $TESTUSER $TESTPASSWORD > tmp/auth/htpasswd

printf "\nStopping any running local doqr test registry...\n"
docker stop registry-doqr-test || echo "No running container registry, so nothing to stop"

printf "\nStarting local doqr test registry on port 5443...\n"
docker run -d \
--rm \
--name registry-doqr-test \
-v "$(pwd)"/tmp/certs:/certs \
-v "$(pwd)"/tmp/auth:/auth \
-e REGISTRY_HTTP_ADDR=0.0.0.0:5443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-p 5443:5443 \
registry:2

printf "\nPulling node:alpine as base image...\n"
docker pull node:alpine

printf "\nPushing base image to local doqr test registry...\n"
docker tag node:alpine localhost:5443/node
docker push localhost:5443/node

printf "\nRunning doqr to pull from and push result to the local doqr test registry...\n"
../../lib/cli.js --fromImage node --registry https://localhost:5443/v2/ --toImage doqr-integration-test:localtest --folder ../integration/app --setTimeStamp "2023-03-07T12:53:10.471Z" --allowInsecureRegistries --token "Basic $BASICAUTH"


printf "\nPulling image from registry to local docker daemon...\n"
docker pull localhost:5443/doqr-integration-test:localtest

printf "\nRunning image on local docker daemon...\n"
docker run --rm -it localhost:5443/doqr-integration-test:localtest

printf "\nDeleting image from registry to local docker daemon...\n"
docker rmi localhost:5443/doqr-integration-test:localtest

printf "\nStopping local doqr test registry...\n"
docker stop registry-doqr-test


printf "\nSUCCESS!\n"

0 comments on commit 9df77b7

Please sign in to comment.