Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

algorithm-deps-install #1081

Merged
merged 7 commits into from
Jan 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion core/algorithm-builder/.dockerignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
builds
uploads
uploads
node_modules
4 changes: 2 additions & 2 deletions core/algorithm-builder/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ class Bootstrap {
const storageManager = require('@hkube/storage-manager');
const fse = require('fs-extra');
const { buildId } = mockBuild;
await stateManger.insertBuild(mockBuild);
await storageManager.hkubeBuilds.putStream({ buildId, data: fse.createReadStream(tar) });
const {path: filePath} = await storageManager.hkubeBuilds.putStream({ buildId, data: fse.createReadStream(tar) });
await stateManger.insertBuild({...mockBuild, filePath});
config.buildId = buildId;
}
}
Expand Down
8 changes: 8 additions & 0 deletions core/algorithm-builder/dockerfile/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
ARG BASE_PRIVATE_REGISTRY=""
FROM ${BASE_PRIVATE_REGISTRY}node:14.5.0 as install
ADD ./package-lock.json ./package.json /hkube/algorithm-builder/
WORKDIR /hkube/algorithm-builder
RUN npm ci --production
RUN echo stam

ARG BASE_PRIVATE_REGISTRY=""
FROM ${BASE_PRIVATE_REGISTRY}hkube/base-node:v1.2.0
LABEL maintainer="hkube.dev@gmail.com"
Expand All @@ -6,4 +13,5 @@ RUN apt update && apt install -y git gettext-base && rm -rf /var/lib/apt/lists/*
RUN mkdir -p /hkube/algorithm-builder
WORKDIR /hkube/algorithm-builder
COPY . /hkube/algorithm-builder
COPY --from=install /hkube/algorithm-builder/node_modules /hkube/algorithm-builder/node_modules
CMD ["npm", "start"]
8 changes: 5 additions & 3 deletions core/algorithm-builder/dockerfile/build.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#!/usr/bin/env bash
set -eo pipefail
SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )"
${SCRIPTPATH}/get-deps-python.sh
${SCRIPTPATH}/get-deps-nodejs.sh
${SCRIPTPATH}/get-deps-java.sh
if [ -v $SKIP_DOWNLOAD_PACKAGES ]; then
${SCRIPTPATH}/get-deps-python.sh
${SCRIPTPATH}/get-deps-nodejs.sh
${SCRIPTPATH}/get-deps-java.sh
fi
REPO_NAME=$1
if [ -v PRIVATE_REGISTRY ]
then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ WORKDIR /hkube/algorithm-runner/algorithm_unique_folder
ENV packagesRegistry=${packagesRegistry}
ENV packagesToken=${packagesToken}
ENV packagesAuth=${packagesAuth}
ENV dependency_install_cmd=${dependency_install_cmd}
RUN ../dockerfile/requirements.sh

# Second build
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

set -e

PACKAGES_REGISTRY=${packagesRegistry}
export PACKAGES_REGISTRY=${packagesRegistry}
PACKAGES_REGISTRY_HOST=${PACKAGES_REGISTRY#http://}
PACKAGES_REGISTRY_HOST=${PACKAGES_REGISTRY_HOST#https://}
PACKAGES_TOKEN=${packagesToken}
PACKAGES_AUTH=${packagesAuth}
export PACKAGES_REGISTRY_HOST=${PACKAGES_REGISTRY_HOST#https://}
export PACKAGES_TOKEN=${packagesToken}
export PACKAGES_AUTH=${packagesAuth}

if [ ! -z ${PACKAGES_REGISTRY} ]; then
echo "found npm registry ${PACKAGES_REGISTRY}"
Expand All @@ -16,6 +16,14 @@ if [ ! -z ${PACKAGES_REGISTRY} ]; then
echo "//${PACKAGES_REGISTRY_HOST}:_auth=${PACKAGES_AUTH}" > ${HOME}/.npmrc
echo "//${PACKAGES_REGISTRY_HOST}:always-auth=true" >> ${HOME}/.npmrc
fi
fi
if [ ! -z ${dependency_install_cmd} ]; then
echo "found dependency install script"
SCRIPT_CWD="${PWD}"
echo "running ${dependency_install_cmd} in folder ${SCRIPT_CWD}"
sh -c "cd ${SCRIPT_CWD} && sh ${dependency_install_cmd}"
echo "${dependency_install_cmd} execution done with code $?"
elif [ ! -z ${PACKAGES_REGISTRY} ]; then
npm install --registry=${PACKAGES_REGISTRY}
rm -f .npmrc
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ COPY ./dockerfile/* /hkube/algorithm-runner/dockerfile/
COPY ./packages/* /hkube/packages/
COPY ./nodemon ./docker-entrypoint.sh /hkube/
WORKDIR /hkube/algorithm-runner
RUN ./dockerfile/requirements.sh ${packagesRegistry} ${packagesToken}
RUN export dependency_install_cmd=${dependency_install_cmd} && ./dockerfile/requirements.sh ${packagesRegistry} ${packagesToken}

ENV PYTHONPATH=$PYTHONPATH:/hkube/algorithm-runner/algorithm_unique_folder
ENTRYPOINT ["/hkube/docker-entrypoint.sh"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,29 @@

set -e

PACKAGES_REGISTRY=$1
PACKAGES_TOKEN=$2
export PACKAGES_REGISTRY=$1
export PACKAGES_TOKEN=$2
REQUIRMENTS=./algorithm_unique_folder/requirements.txt
TRUSTED_HOST=pypi.python.org
if [ ! -z ${PACKAGES_REGISTRY} ]; then
export PACKAGES_REGISTRY_HOST=$(echo $PACKAGES_REGISTRY | sed -e "s/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/")
fi
# install hkube + dependencies
echo install hkube + dependencies
if [ ! -z ${PACKAGES_REGISTRY} ]; then
PACKAGES_REGISTRY_HOST=$(echo $PACKAGES_REGISTRY | sed -e "s/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/")
echo "found pip registry ${PACKAGES_REGISTRY}. Setting trusted host to ${PACKAGES_REGISTRY_HOST}"
pip install --trusted-host "${PACKAGES_REGISTRY_HOST}" --index-url "${PACKAGES_REGISTRY}" --find-links /hkube/packages/ -r /hkube/algorithm-runner/requirements.txt
else
pip install --find-links /hkube/packages/ -r /hkube/algorithm-runner/requirements.txt
fi

if [ -f ${REQUIRMENTS} ]; then
if [ ! -z ${dependency_install_cmd} ]; then
echo "found dependency install script"
SCRIPT_CWD="${PWD}/algorithm_unique_folder"
echo "running ${dependency_install_cmd} in folder ${SCRIPT_CWD}"
sh -c "cd ${SCRIPT_CWD} && sh ${dependency_install_cmd}"
echo "${dependency_install_cmd} execution done with code $?"
elif [ -f ${REQUIRMENTS} ]; then
echo "found requirements.txt"
if [ ! -z ${PACKAGES_REGISTRY} ]; then
PACKAGES_REGISTRY_HOST=$(echo $PACKAGES_REGISTRY | sed -e "s/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/")
Expand Down
5 changes: 3 additions & 2 deletions core/algorithm-builder/lib/builds/build-utils.sh
Original file line number Diff line number Diff line change
Expand Up @@ -96,27 +96,28 @@ dockerBuildKaniko() {
export insecure_pull=${INSECURE_PULL}
export skip_tls_verify=${SKIP_TLS_VERIFY}
export skip_tls_verify_pull=${SKIP_TLS_VERIFY_PULL}
export dependency_install_cmd=${DEPENDENCY_INSTALL_CMD}

echo "Building image ${image}"
echo copy context from ${buildPath} to ${workspace}
cp -r ${buildPath}/* ${workspace}

envsubst < ${workspace}/dockerfile/DockerfileTemplate > ${workspace}/dockerfile/Dockerfile
sed -i '/^ARG /d' ${workspace}/dockerfile/Dockerfile

options=""
if [[ $insecure == true ]]; then options="${options} --insecure"; fi
if [[ $insecure_pull == true ]]; then options="${options} --insecure-pull"; fi
if [[ $skip_tls_verify == true ]]; then options="${options} --skip-tls-verify"; fi
if [[ $skip_tls_verify_pull == true ]]; then options="${options} --skip-tls-verify-pull"; fi

if [[ $NO_PUSH == true ]]; then options="${options} --no-push"; fi
echo "/kaniko/executor \
--dockerfile ./dockerfile/Dockerfile \
${options} --context dir:///workspace/ \
--build-arg packagesRegistry=${packagesRegistry} \
--build-arg packagesRegistryUser=${packagesRegistryUser} \
--build-arg packagesToken=${packagesToken} \
--build-arg baseImage=${baseImage} \
--build-arg dependency_install_cmd=${dependency_install_cmd} \
--destination $image" > ${commands}/run

chmod +x ${commands}/run
Expand Down
7 changes: 4 additions & 3 deletions core/algorithm-builder/lib/builds/docker-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ const _overrideVersion = async (env, buildPath, version) => {
}
}

const buildAlgorithmImage = async ({ buildMode, env, docker, algorithmName, imageTag, buildPath, rmi, baseImage, tmpFolder, packagesRepo, buildId }) => {
const buildAlgorithmImage = async ({ buildMode, env, docker, algorithmName, imageTag, buildPath, rmi, baseImage, tmpFolder, packagesRepo, buildId, dependencyInstallCmd }) => {
const pushRegistry = _createURL(docker.push);
const algorithmImage = `${path.join(pushRegistry, algorithmName)}:v${imageTag}`;
const packages = packagesRepo[env];
Expand All @@ -393,6 +393,7 @@ const buildAlgorithmImage = async ({ buildMode, env, docker, algorithmName, imag
_envsHelper(envs, 'REMOVE_IMAGE', rmi);
_envsHelper(envs, 'BUILD_PATH', buildPath);
_envsHelper(envs, 'BASE_IMAGE', baseImageName);
_envsHelper(envs, 'DEPENDENCY_INSTALL_CMD', dependencyInstallCmd);
_envsHelper(envs, 'BUILD_ID', buildId);
_envsHelper(envs, 'WRAPPER_VERSION', wrapperVersion);

Expand Down Expand Up @@ -475,7 +476,7 @@ const runBuild = async (options) => {
await _setBuildStatus({ buildId, progress, status: STATES.ACTIVE });

const overwrite = true;
const { env, imageTag, fileExt, filePath, baseImage, type, gitRepository } = build;
const { env, imageTag, fileExt, filePath, baseImage, type, gitRepository, dependencyInstallCmd } = build;
const { docker, buildDirs, tmpFolder, packagesRepo } = options;
buildMode = options.buildMode;
algorithmName = build.algorithmName;
Expand All @@ -495,7 +496,7 @@ const runBuild = async (options) => {
}
await _prepareBuild({ buildPath, env, dest, overwrite });
await _setBuildStatus({ buildId, progress, status: STATES.ACTIVE });
result = await buildAlgorithmImage({ buildMode, env, docker, algorithmName, imageTag, buildPath, rmi: 'True', baseImage, tmpFolder, packagesRepo, buildId });
result = await buildAlgorithmImage({ buildMode, env, docker, algorithmName, imageTag, buildPath, rmi: 'True', baseImage, tmpFolder, packagesRepo, buildId, dependencyInstallCmd });
}
catch (e) {
error = e.message;
Expand Down
4 changes: 3 additions & 1 deletion core/algorithm-builder/tests/mocks/java/build.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"algorithmName": "sort-alg",
"env": "java",
"version": "1.0.0",
"imageTag": "1.0.0",
"fileExt": "gz",
"status": "pending",
"baseImage": "",
Expand All @@ -11,5 +12,6 @@
"result": null,
"progress": 0,
"startTime": 1556799896273,
"endTime": null
"endTime": null,
"dependencyInstallCmd": "./install.sh"
}
Binary file modified core/algorithm-builder/tests/mocks/nodejs/alg.tar.gz
Binary file not shown.
4 changes: 3 additions & 1 deletion core/algorithm-builder/tests/mocks/nodejs/build.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
"algorithmName": "sort-alg",
"env": "nodejs",
"version": "5.0.0",
"imageTag": "5.0.0",
"fileExt": "gz",
"status": "pending",
"error": null,
"stack": null,
"result": null,
"progress": 0,
"startTime": 1556799896273,
"endTime": null
"endTime": null,
"dependencyInstallCmd": "./install.sh"
}
Binary file modified core/algorithm-builder/tests/mocks/python/alg.tar.gz
Binary file not shown.
4 changes: 3 additions & 1 deletion core/algorithm-builder/tests/mocks/python/build.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"algorithmName": "sort-alg",
"env": "python",
"version": "1.0.0",
"imageTag": "1.0.0",
"fileExt": "gz",
"status": "pending",
"baseImage": "",
Expand All @@ -11,5 +12,6 @@
"result": null,
"progress": 0,
"startTime": 1556799896273,
"endTime": null
"endTime": null,
"dependencyInstallCmd": "./install.sh"
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ properties:
- url
entryPoint:
type: string
dependencyInstallCmd:
type: string
description: |
Command to run to install algorithm dependencies. CWD is the algorithm root folder
Defaults to language specific defaults.
e.g. For python: pip install -r requirements.txt
baseImage:
type: string
description: Custom docker image to be used as base to the newly built algorithm image
Expand Down
2 changes: 1 addition & 1 deletion core/api-server/lib/consts/builds.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const BUILD_TRIGGERS = ['checksum', 'env', 'commit', 'baseImage'];
const BUILD_TRIGGERS = ['checksum', 'env', 'commit', 'baseImage', 'dependencyInstallCmd'];

const BUILD_GUIDE = 'use the Hkube dashboard or the Hkube API to follow the build progress';

Expand Down
1 change: 1 addition & 0 deletions core/api-server/lib/service/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Build {
this.gitRepository = options.gitRepository;
this.type = options.type;
this.baseImage = options.baseImage;
this.dependencyInstallCmd = options.dependencyInstallCmd;
}

_createBuildID(algorithmName) {
Expand Down
7 changes: 4 additions & 3 deletions core/api-server/lib/service/builds.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ class Builds {
algorithmName: newAlgorithm.name,
gitRepository: newAlgorithm.gitRepository,
type: newAlgorithm.type,
baseImage: newAlgorithm.baseImage
baseImage: newAlgorithm.baseImage,
dependencyInstallCmd: newAlgorithm.dependencyInstallCmd
});
buildId = build.buildId;
if (fileInfo && !fileInfo.path) {
Expand Down Expand Up @@ -221,10 +222,10 @@ class Builds {
}

_formatDiff(algorithm) {
const { fileInfo, env, baseImage, gitRepository } = algorithm;
const { fileInfo, env, baseImage, gitRepository, dependencyInstallCmd } = algorithm;
const checksum = fileInfo?.checksum;
const commit = gitRepository?.commit?.id;
return { checksum, env, commit, baseImage };
return { checksum, env, commit, baseImage, dependencyInstallCmd };
}
}

Expand Down
26 changes: 26 additions & 0 deletions core/api-server/tests/algorithms-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,32 @@ describe('Store/Algorithms', () => {
expect(response2.body).to.have.property('buildId');
expect(response2.body.messages[0]).to.contains('a build was triggered due to change in checksum');
});
it('should succeed to apply algorithm with buildId due to change in dependencyInstallCmd', async () => {
const body1 = {
name: `my-alg-${uuid()}`,
env: 'python'
}
const body2 = {
...body1,
dependencyInstallCmd: './foo'
}
const formData1 = {
payload: JSON.stringify(body1),
file: fse.createReadStream('tests/mocks/algorithm.tar.gz')
};
const formData2 = {
payload: JSON.stringify(body2),
file: fse.createReadStream('tests/mocks/algorithm.tar.gz')
};
const uri = applyPath;
const options1 = { uri, formData: formData1 };
const options2 = { uri, formData: formData2 };
const response1 = await request(options1);
const response2 = await request(options2)
expect(response1.body).to.have.property('buildId');
expect(response2.body).to.have.property('buildId');
expect(response2.body.messages[0]).to.contains('a build was triggered due to change in dependencyInstallCmd');
});
it('should succeed to apply with build due to change in baseImage', async () => {
const body1 = {
name: `my-alg-${uuid()}`,
Expand Down
54 changes: 54 additions & 0 deletions core/api-server/tests/builds.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,60 @@ describe('Builds', () => {
expect(response.response.statusCode).to.equal(HttpStatus.OK);
expect(response.body.baseImage).to.equal('userOwnBaseImage');
});
it('should succeed to get dependencyInstallCmd', async () => {
const payload = {
name: `my-alg-${uuid()}`,
mem: "50Mi",
cpu: 1,
version: '1.9.0',
env: 'nodejs',
baseImage: 'userOwnBaseImage',
dependencyInstallCmd: 'install.sh'
}
const formData = {
payload: JSON.stringify(payload),
file: fse.createReadStream('tests/mocks/algorithm.tar.gz')
};
const opt = {
uri: restUrl + '/store/algorithms/apply',
formData
};
const res = await request(opt);

const options = {
uri: restPath + `/${res.body.buildId}`,
method: 'GET'
};
const response = await request(options);
expect(response.response.statusCode).to.equal(HttpStatus.OK);
expect(response.body.dependencyInstallCmd).to.equal('install.sh');
});
it('should work without dependencyInstallCmd', async () => {
const payload = {
name: `my-alg-${uuid()}`,
mem: "50Mi",
cpu: 1,
version: '1.9.0',
env: 'nodejs',
}
const formData = {
payload: JSON.stringify(payload),
file: fse.createReadStream('tests/mocks/algorithm.tar.gz')
};
const opt = {
uri: restUrl + '/store/algorithms/apply',
formData
};
const res = await request(opt);

const options = {
uri: restPath + `/${res.body.buildId}`,
method: 'GET'
};
const response = await request(options);
expect(response.response.statusCode).to.equal(HttpStatus.OK);
expect(response.body.dependencyInstallCmd).to.not.exist;
});
});
describe('stop', () => {
let restPath = null;
Expand Down