Skip to content

Commit 8982e4a

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 a9e0442 commit 8982e4a

File tree

3 files changed

+87
-59
lines changed

3 files changed

+87
-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: 56 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,18 @@ 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
}
585+
options{
586+
lock('deploy_getmarcapi')
587+
}
585588
stages{
586589
stage('Additional Deploy') {
587590
when{
588591
anyOf {
589592
equals expected: true, actual: params.DEPLOY_PYPI
590-
equals expected: true, actual: params.DEPLOY_TO_PRODUCTION
593+
equals expected: true, actual: params.DEPLOY_DOCKER_IMAGE
591594
}
592595
}
593596
parallel{
@@ -641,7 +644,7 @@ def call(){
641644
}
642645
stage('Deploy Docker'){
643646
when{
644-
equals expected: true, actual: params.DEPLOY_TO_PRODUCTION
647+
equals expected: true, actual: params.DEPLOY_DOCKER_IMAGE
645648
beforeAgent true
646649
beforeInput true
647650
}
@@ -659,59 +662,51 @@ def call(){
659662
}
660663
steps{
661664
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-
}
665+
def registryUrl
666+
def localImageName
667+
def build_args
668+
def remoteRegistryImageName
671669
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')
670+
try{
671+
def CONFIG = readJSON(file: CONFIG_FILE)['deploy']
672+
build_args = CONFIG['docker']['build']['buildArgs'].collect{"--build-arg=${it}"}.join(' ')
673+
registryUrl = CONFIG['docker']['server']['registry']
674+
remoteRegistryImageName = "${registryUrl.replace('http://', '').replace('https://', '')}/${IMAGE_NAME}:${DOCKER_TAG}"
675+
localImageName = "${IMAGE_NAME}:${DOCKER_TAG}"
676+
677+
} catch(e){
678+
error """======================================================
679+
Config file is not valid
680+
------------------------------------------------------
681+
Details:
682+
683+
${e.message}
684+
------------------------------------------------------
685+
The config file must be a JSON file and be in the following format.
686+
687+
{
688+
"deploy": {
689+
"docker": {
690+
"build": {
691+
"buildArgs": []
692+
},
693+
"server": {
694+
"registry": "FILL THIS OUT WITH YOUR DOCKER REGISTRY URL"
695+
}
696+
}
697+
}
698+
}
699+
700+
======================================================
701+
"""
678702
}
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-
}
703+
docker.withRegistry(registryUrl, 'jenkins-nexus'){
704+
def dockerImage = docker.build(localImageName, "${build_args} .")
705+
sh(label: 'Uploading docker images to registry',
706+
script: """docker tag ${localImageName} ${remoteRegistryImageName}
707+
docker push ${remoteRegistryImageName}
708+
"""
709+
)
715710
}
716711
}
717712
}
@@ -720,6 +715,11 @@ def call(){
720715
}
721716
}
722717
}
718+
post{
719+
always{
720+
milestone 2
721+
}
722+
}
723723
}
724724
}
725725
}

0 commit comments

Comments
 (0)