Skip to content

Commit 7e59944

Browse files
committed
feat: docker image now requires config file at runtime
BREAKING CHANGE: Instead of inserting the API key at build time. It now must be mounted during start up time.
1 parent 6d36759 commit 7e59944

File tree

3 files changed

+75
-59
lines changed

3 files changed

+75
-59
lines changed

Dockerfile

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@ RUN --mount=type=cache,target=${UV_CACHE_DIR} \
3939
./uv/bin/uv sync --group deploy --no-dev --no-editable --no-install-project --find-links=/wheels && \
4040
./uv/bin/uv pip install --find-links=/wheels --no-index getmarcapi --no-deps
4141
EXPOSE 5000
42-
COPY api.cfg /app/settings.cfg
4342
ENV GETMARCAPI_SETTINGS=/app/settings.cfg
44-
RUN ./.venv/bin/python -m getmarcapi --check
4543
HEALTHCHECK --interval=30s --timeout=10s --retries=3 CMD curl -f http://localhost:5000 || exit 1
46-
CMD ./.venv/bin/gunicorn getmarcapi.app:app --bind 0.0.0.0:5000 --log-level=debug
44+
CMD ./.venv/bin/python -m getmarcapi --check && ./.venv/bin/gunicorn getmarcapi.app:app --bind 0.0.0.0:5000 --log-level=debug
4745

README.rst

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,33 @@
11
getmarcapi
22
==========
33

4+
Config File
5+
-----------
6+
7+
This application requires a configuration file to run. It should be in INI format
8+
and contain the following sections and options:
9+
10+
.. code-block:: ini
11+
12+
[ALMA_API]
13+
API_DOMAIN=
14+
API_KEY=
15+
16+
17+
Using the Docker Image
18+
----------------------
19+
20+
This Docker image requires a config file (see Config File section above) to be
21+
mounted into the container in order to function properly. By default, the
22+
container will look for the config file at inside the container at
23+
`/app/settings.cfg` but this can be changed by running the container with the
24+
GETMARCAPI_SETTINGS environment variable set to another path.
25+
26+
Port 5000 inside the container is used for the web server, so this port should
27+
be forwarded to the host machine.
28+
29+
For example, you can run the container like this:
30+
31+
.. code-block:: sh
32+
33+
docker run -d -p 5000:5000 -v /path/to/your/settings.cfg:/app/settings.cfg:ro --name getmarc getmarcapi

vars/runJenkinsPipeline.groovy

Lines changed: 44 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def get_sonarqube_unresolved_issues(report_task_file){
3232
def getVersion(){
3333
node(){
3434
checkout scm
35-
return readTOML( file: 'pyproject.toml')['project']
35+
return readTOML( file: 'pyproject.toml')['project']['version']
3636
}
3737
}
3838

@@ -51,7 +51,7 @@ def call(){
5151
booleanParam(name: 'TEST_PACKAGES', defaultValue: true, description: 'Test Python packages by installing them and running tests on the installed package')
5252
booleanParam(name: 'DEPLOY_PYPI', defaultValue: false, description: 'Deploy to pypi')
5353
booleanParam(name: 'DEPLOY_DOCS', defaultValue: false, description: '')
54-
booleanParam(name: 'DEPLOY_TO_PRODUCTION', defaultValue: false, description: 'Deploy to Production Server')
54+
booleanParam(name: 'DEPLOY_DOCKER_IMAGE', defaultValue: false, description: 'Create and deploy docker image to Docker registry')
5555
}
5656
stages {
5757
stage('Building and Testing'){
@@ -579,15 +579,15 @@ def call(){
579579
when{
580580
anyOf {
581581
equals expected: true, actual: params.DEPLOY_PYPI
582-
equals expected: true, actual: params.DEPLOY_TO_PRODUCTION
582+
equals expected: true, actual: params.DEPLOY_DOCKER_IMAGE
583583
}
584584
}
585585
stages{
586586
stage('Additional Deploy') {
587587
when{
588588
anyOf {
589589
equals expected: true, actual: params.DEPLOY_PYPI
590-
equals expected: true, actual: params.DEPLOY_TO_PRODUCTION
590+
equals expected: true, actual: params.DEPLOY_DOCKER_IMAGE
591591
}
592592
}
593593
parallel{
@@ -641,7 +641,7 @@ def call(){
641641
}
642642
stage('Deploy Docker'){
643643
when{
644-
equals expected: true, actual: params.DEPLOY_TO_PRODUCTION
644+
equals expected: true, actual: params.DEPLOY_DOCKER_IMAGE
645645
beforeAgent true
646646
beforeInput true
647647
}
@@ -659,59 +659,47 @@ def call(){
659659
}
660660
steps{
661661
script{
662-
withCredentials([string(credentialsId: 'ALMA_API_KEY', variable: 'API_KEY')]) {
663-
writeFile(
664-
file: 'api.cfg',
665-
text: '''[ALMA_API]
666-
API_DOMAIN=https://api-na.hosted.exlibrisgroup.com
667-
API_KEY=${API_KEY}
668-
'''
669-
)
670-
}
671662
configFileProvider([configFile(fileId: 'getmarc_deployapi', variable: 'CONFIG_FILE')]) {
672-
def CONFIG = readJSON(file: CONFIG_FILE)['deploy']
673-
def build_args = CONFIG['docker']['build']['buildArgs'].collect{"--build-arg=${it}"}.join(' ')
674-
docker.withRegistry(CONFIG['docker']['server']['registry'], 'jenkins-nexus'){
675-
def dockerImage = docker.build("${IMAGE_NAME}:${DOCKER_TAG}", "${build_args} .")
676-
dockerImage.push()
677-
dockerImage.push('latest')
663+
try{
664+
def CONFIG = readJSON(file: CONFIG_FILE)['deploy']
665+
def build_args = CONFIG['docker']['build']['buildArgs'].collect{"--build-arg=${it}"}.join(' ')
666+
def registryUrl = CONFIG['docker']['server']['registry']
667+
def remoteRegistryImageName = "${registryUrl.replace('http://', '').replace('https://', '')}/${IMAGE_NAME}:${DOCKER_TAG}"
668+
def localImageName = "${IMAGE_NAME}:${DOCKER_TAG}"
669+
670+
} catch(e){
671+
error """======================================================
672+
Config file is not valid
673+
------------------------------------------------------
674+
Details:
675+
676+
${e.message}
677+
------------------------------------------------------
678+
The config file must be a JSON file and be in the following format.
679+
680+
{
681+
"deploy": {
682+
"docker": {
683+
"build": {
684+
"buildArgs": []
685+
},
686+
"server": {
687+
"registry": "FILL THIS OUT WITH YOUR DOCKER REGISTRY URL"
688+
}
689+
}
690+
}
691+
}
692+
693+
======================================================
694+
"""
678695
}
679-
}
680-
}
681-
}
682-
}
683-
stage('Deploy to Production server'){
684-
agent{
685-
label 'linux && docker'
686-
}
687-
input {
688-
message 'Deploy to live server?'
689-
parameters {
690-
string defaultValue: 'getmarc2', description: 'Name of Docker container to use', name: 'CONTAINER_NAME', trim: true
691-
booleanParam defaultValue: true, description: 'Remove any containers with the same name first', name: 'REMOVE_EXISTING_CONTAINER'
692-
}
693-
}
694-
options{
695-
timeout(time: 1, unit: 'DAYS')
696-
retry(3)
697-
}
698-
steps{
699-
script{
700-
configFileProvider([configFile(fileId: 'getmarc_deployapi', variable: 'CONFIG_FILE')]) {
701-
def CONFIG = readJSON(file: CONFIG_FILE).deploy
702-
docker.withServer(CONFIG.docker.server.apiUrl, 'DOCKER_TYKO'){
703-
if(REMOVE_EXISTING_CONTAINER == true){
704-
sh(
705-
label:"Stopping ${CONTAINER_NAME} if exists",
706-
script: "docker stop ${CONTAINER_NAME}",
707-
returnStatus: true
708-
)
709-
}
710-
docker.withRegistry(CONFIG.docker.server.registry, 'jenkins-nexus'){
711-
def imageName = CONFIG.docker.server.registry.replace('http://', '') + "/${IMAGE_NAME}:${DOCKER_TAG}"
712-
def containerPortsArg = CONFIG.docker.container.ports.collect{"-p ${it}"}.join(' ')
713-
docker.image(imageName).run("${containerPortsArg} --name ${CONTAINER_NAME} --rm")
714-
}
696+
docker.withRegistry(registryUrl, 'jenkins-nexus'){
697+
def dockerImage = docker.build(localImageName, "${build_args} .")
698+
sh(label: 'Uploading docker images to registry',
699+
script: """docker tag ${localImageName} ${remoteRegistryImageName}
700+
docker push ${remoteRegistryImageName}
701+
"""
702+
)
715703
}
716704
}
717705
}

0 commit comments

Comments
 (0)