19 Commits

Author SHA1 Message Date
1de32691c6 Corrected git remote command 2025-07-04 23:46:57 +02:00
f05d3940fa Modified git repo url to auth 2025-07-04 23:35:51 +02:00
023daf1d4b Added git clone and git push with credentials 2025-07-04 23:33:28 +02:00
e91273888a Changed command run for git container 2025-07-04 22:59:51 +02:00
247798abc8 Implemented auto commit new app version to GitOps by pipeline 2025-07-04 22:42:51 +02:00
600b5aeba8 Merge branch 'dev' into jenkins-pipeline 2025-07-03 23:36:31 +02:00
c9b1dac864 Added endpoint to fetch app version 2025-07-03 22:47:21 +02:00
a79ae2d50f Apply new features from branch 'dev' into jenkins-pipeline 2025-06-12 18:42:28 +00:00
cd4ab3fd27 Handled more errors during db initialization 2025-06-12 18:42:07 +00:00
301cf5922e Changed docker image base to Alpine and added curl 2025-06-11 22:15:37 +00:00
479ec4f917 Added healthcheck 2025-06-11 22:04:35 +00:00
3f40a6126c Added more descriptions of functions 2025-06-11 20:04:04 +00:00
dd9e9ce110 Improved function body 2025-06-11 19:57:15 +00:00
d3d3c98f99 Moved wait_for_db function to utils module 2025-06-11 19:48:58 +00:00
9e010ed389 Implemented waiting for db readiness 2025-06-11 19:43:47 +00:00
d9fe927832 Removed deprecated label option from pipeline 2025-06-11 17:51:55 +00:00
636a382cf5 Deleted jenkins pipeline from main branch 2025-06-11 17:13:27 +00:00
99cfdfddd0 Added annotation required to run Sysbox on pod 2025-06-11 16:52:16 +00:00
f579e440f8 Updated path to pod template yaml file 2025-06-11 16:43:22 +00:00
8 changed files with 120 additions and 9 deletions

45
.jenkins/Jenkinsfile vendored
View File

@ -1,8 +1,7 @@
pipeline {
agent {
kubernetes {
label 'kubernetes-agent'
yamlFile 'podTemplate.yaml'
yamlFile '.jenkins/podTemplate.yaml'
}
}
@ -11,6 +10,7 @@ pipeline {
CLIENT_ID = 'c302726f-fafb-4143-94c1-67a70975574a'
DOCKER_REGISTRY_URL = 'marcin00.azurecr.io'
DOCKER_IMAGE = "${DOCKER_REGISTRY_URL}/user-microservice:${GIT_COMMIT}"
DEPLOY_REPO = 'https://gitea.marcin00.pl/pikram/user-microservice-deploy.git'
}
stages {
@ -38,13 +38,46 @@ pipeline {
steps {
container('docker') {
sh '''
docker build -t ${DOCKER_IMAGE} .
az login --identity --client-id ${CLIENT_ID}
az acr login --name ${ACR_NAME}
docker push ${DOCKER_IMAGE}
docker build -t ${DOCKER_IMAGE} .
az login --identity --client-id ${CLIENT_ID}
az acr login --name ${ACR_NAME}
docker push ${DOCKER_IMAGE}
'''
}
}
}
stage('Commit new version to GitOps repo') {
steps {
container('git') {
sh '''
git config --global user.name "jenkins[bot]"
git config --global user.email "jenkins@marcin00.pl"
'''
sh 'git clone ${DEPLOY_REPO} --branch jenkins-kubernetes'
dir('user-microservice-deploy') {
sh '''
# Podmień tag obrazu w pliku deploy.yaml
awk -v commit="ssh-creds-id$GIT_COMMIT" '
$0 ~ /name:[[:space:]]*api/ { in_api_container = 1; print; next }
in_api_container && $0 ~ /^[[:space:]]*image:[[:space:]]*/ {
sub(/:[^:[:space:]]+$/, ":" commit)
in_api_container = 0
print
next
}
{ print }
' deploy.yaml > deploy.tmp && mv deploy.tmp deploy.yaml
'''
sh 'git commit -am "JENKINS: Changed deployed version to $GIT_COMMIT"'
sshagent(['gitea-deploy-key']) {
sh '''
git remote set-url origin ssh://git@srv22.mikr.us:20343/pikram/user-microservice-deploy.git
git push
'''
}
}
}
}
}
}
}

View File

@ -1,6 +1,8 @@
apiVersion: v1
kind: Pod
metadata:
annotations:
io.kubernetes.cri-o.userns-mode: "auto:size=65536"
labels:
jenkins: "slave"
jenkins/label: "kubernetes-agent"
@ -38,6 +40,16 @@ spec:
- name: workspace-volume
mountPath: /home/jenkins/agent
- name: git
image: alpine/git:latest
command:
- cat
tty: true
workingDir: /home/jenkins/agent
volumeMounts:
- name: workspace-volume
mountPath: /home/jenkins/agent
nodeSelector:
kubernetes.io/os: linux

View File

@ -1,5 +1,6 @@
FROM python:3.11.7-slim-bookworm
FROM python:3.11.7-alpine
WORKDIR /app
COPY api .
RUN apk add --no-cache curl
RUN pip install -r requirements.txt
CMD python3 app.py

View File

@ -4,7 +4,8 @@ from flask_jwt_extended import JWTManager
from jwt import ExpiredSignatureError
from models import db, RevokedToken
import os
from utils import init_db
from tech_views import tech_bp
from utils import init_db, wait_for_db
from views import user_bp
from werkzeug.exceptions import HTTPException
@ -26,6 +27,7 @@ def create_app(config_name="default"):
# Blueprints registration
app.register_blueprint(user_bp)
app.register_blueprint(tech_bp)
# Database and JWT initialization
db.init_app(app)
@ -53,6 +55,7 @@ def create_app(config_name="default"):
# Fill database by initial values (only if we are not testing)
with app.app_context():
wait_for_db(max_retries=100)
db.create_all()
if config_name != "testing":
init_db()

20
api/tech_views.py Normal file
View File

@ -0,0 +1,20 @@
from flask import Blueprint, jsonify
from models import db
from sqlalchemy import text
from utils import db_ready
# Blueprint with technical endpoints
tech_bp = Blueprint('tech_bp', __name__)
@tech_bp.route('/health', methods=['GET'])
def health_check():
"Check if service works and database is functional"
try:
with db.engine.connect() as connection:
connection.execute(text("SELECT 1"))
return jsonify(status="healthy"), 200
except Exception:
if db_ready:
return jsonify(status="unhealthy"), 500
else:
return jsonify(status="starting"), 503

View File

@ -2,17 +2,22 @@ from flask import abort
from flask_jwt_extended import get_jwt_identity
from models import User, db
import os
from sqlalchemy import text
from sqlalchemy.exc import DatabaseError, InterfaceError
import time
from werkzeug.security import generate_password_hash
db_ready = False
def admin_required(user_id, message='Access denied.'):
"Check if common user try to make administrative action."
user = db.session.get(User, user_id)
if user is None or user.role != "Administrator":
abort(403, message)
def validate_access(owner_id, message='Access denied.'):
# Check if user try to access or edit resource that does not belong to them
"Check if user try to access or edit resource that does not belong to them."
logged_user_id = int(get_jwt_identity())
logged_user_role = db.session.get(User, logged_user_id).role
if logged_user_role != "Administrator" and logged_user_id != owner_id:
@ -27,6 +32,20 @@ def get_user_or_404(user_id):
return user
def wait_for_db(max_retries):
"Try to connect with database <max_retries> times."
global db_ready
for _ in range(max_retries):
try:
with db.engine.connect() as connection:
connection.execute(text("SELECT 1"))
db_ready = True
return
except DatabaseError | InterfaceError:
time.sleep(3)
raise Exception("Failed to connect to database.")
def init_db():
"""Create default admin account if database is empty"""
with db.session.begin():

View File

@ -2,6 +2,7 @@ from flask import Blueprint, jsonify, request, abort
from flask_jwt_extended import create_access_token, set_access_cookies, jwt_required, \
verify_jwt_in_request, get_jwt_identity, unset_jwt_cookies, get_jwt
from models import db, RevokedToken, User
import os
from utils import admin_required, validate_access, get_user_or_404
from werkzeug.security import check_password_hash, generate_password_hash
@ -110,3 +111,10 @@ def user_logout():
response = jsonify({"msg": "User logged out successfully."})
unset_jwt_cookies(response)
return response
@user_bp.route('/version', methods=['GET'])
def version():
return jsonify({
"version": os.getenv("APP_VERSION", "unknown"),
"build_time": os.getenv("BUILD_DATE", "unknown")
})

View File

@ -7,9 +7,24 @@ services:
build: .
env_file:
- api/.env
ports:
- 80:80
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 10s
timeout: 5s
retries: 5
start_period: 15s
db:
container_name: db
hostname: db
image: mysql:latest
env_file:
- db/.env
ports:
- 3306:3306
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5