diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..3fe938d --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,20 @@ +# Use Node.js LTS as the base image +FROM node:18-slim + +# Create and change to the app directory +WORKDIR /usr/src/app + +# Copy package.json and package-lock.json +COPY package*.json ./ + +# Install dependencies +RUN npm install --production + +# Copy the rest of the application code +COPY . . + +# Expose the port the app runs on +EXPOSE 3000 + +# Command to run the application +CMD [ "node", "index.js" ] diff --git a/frontend/Jenkinsfile b/frontend/Jenkinsfile new file mode 100644 index 0000000..51d7601 --- /dev/null +++ b/frontend/Jenkinsfile @@ -0,0 +1,61 @@ +pipeline { + agent any + + environment { + // --- Harbor Configuration --- + HARBOR_URL = 'harbor.example.com' + HARBOR_PROJECT = 'my-todo-app' + IMAGE_NAME = 'todo-frontend' + HARBOR_CREDS = 'harbor-credentials-id' + + // --- Kubernetes Configuration --- + K8S_CREDENTIALS_ID = 'k8s-kubeconfig' + // ---------------------------- + + IMAGE_TAG = "${env.BUILD_ID}" + FULL_IMAGE_PATH = "${HARBOR_URL}/${HARBOR_PROJECT}/${IMAGE_NAME}:${IMAGE_TAG}" + } + + stages { + stage('Install & Test') { + steps { + sh 'npm install' + sh 'npm test || true' + } + } + + stage('Build & Push to Harbor') { + steps { + script { + sh "docker build -t ${FULL_IMAGE_PATH} ." + withCredentials([usernamePassword(credentialsId: "${HARBOR_CREDS}", usernameVariable: 'USER', passwordVariable: 'PASS')]) { + sh "docker login ${HARBOR_URL} -u ${USER} -p ${PASS}" + sh "docker push ${FULL_IMAGE_PATH}" + } + } + } + } + + stage('Deploy to Kubernetes') { + steps { + script { + // 1. Replace placeholder in YAML + sh "sed 's|IMAGE_PATH_PLACEHOLDER|${FULL_IMAGE_PATH}|g' k8s-deployment.yaml > frontend-k8s-applied.yaml" + + // 2. Apply manifest + sh "kubectl apply -f frontend-k8s-applied.yaml" + + // 3. Verify rollout + sh "kubectl rollout status deployment/todo-frontend" + } + } + } + } + + post { + always { + sh "docker logout ${HARBOR_URL}" + sh "rm -f frontend-k8s-applied.yaml" + } + } +} diff --git a/frontend/index.js b/frontend/index.js new file mode 100644 index 0000000..ee12b25 --- /dev/null +++ b/frontend/index.js @@ -0,0 +1,17 @@ +const express = require('express'); +const path = require('path'); +const app = express(); +const port = process.env.PORT || 3000; +const backendUrl = process.env.BACKEND_URL || 'http://localhost:3001'; + +app.get('/config.js', (req, res) => { + res.set('Content-Type', 'application/javascript'); + res.send(`window.BACKEND_URL = "${backendUrl}";`); +}); + +app.use(express.static(path.join(__dirname, 'public'))); + +app.listen(port, () => { + console.log(`Frontend service listening at http://localhost:${port}`); + console.log(`Configured to use backend at: ${backendUrl}`); +}); diff --git a/frontend/k8s-deployment.yaml b/frontend/k8s-deployment.yaml new file mode 100644 index 0000000..1e92416 --- /dev/null +++ b/frontend/k8s-deployment.yaml @@ -0,0 +1,50 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: todo-frontend-config +data: + BACKEND_URL: "http://:30001" # Update with your NodePort IP or LoadBalancer DNS +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: todo-frontend + labels: + app: todo-frontend +spec: + replicas: 2 + selector: + matchLabels: + app: todo-frontend + template: + metadata: + labels: + app: todo-frontend + spec: + containers: + - name: todo-frontend + image: IMAGE_PATH_PLACEHOLDER + ports: + - containerPort: 3000 + env: + - name: PORT + value: "3000" + - name: BACKEND_URL + valueFrom: + configMapKeyRef: + name: todo-frontend-config + key: BACKEND_URL +--- +apiVersion: v1 +kind: Service +metadata: + name: todo-frontend-service +spec: + type: NodePort + selector: + app: todo-frontend + ports: + - protocol: TCP + port: 3000 + targetPort: 3000 + nodePort: 30000