diff --git a/.gitignore b/.gitignore index 75ec3f0..c8618e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,6 @@ -.vscode/* \ No newline at end of file +.vscode/* +# terraform/ +terraform/.terraform +terraform/.terraform.lock.hcl +terraform/terraform.tfstate +terraform/terraform.tfstate.backup diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6c65a82 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.9-slim + +ENV PYTHONUNBUFFERED 1 + +WORKDIR /app + +COPY requirements.txt . + +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +EXPOSE ${PORT} + +CMD ["python", "hello.py"] diff --git a/Project DevOps DEPI.docx b/Project DevOps DEPI.docx new file mode 100644 index 0000000..ae93018 Binary files /dev/null and b/Project DevOps DEPI.docx differ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6ddd81b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,26 @@ +version: '3.8' + +services: + app: + build: + context: . + dockerfile: Dockerfile + environment: + - ENVIRONMENT=DEV + - HOST=0.0.0.0 + - PORT=8000 + - REDIS_HOST=redis + - REDIS_PORT=6379 + - REDIS_DB=0 + ports: + - "8000:8000" + depends_on: + - redis + command: python hello.py + container_name: app-container + image: johnsami/dockerized-app-build-app:latest + redis: + image: "redis:6.0-alpine" + ports: + - "6379:6379" + container_name: redis-container \ No newline at end of file diff --git a/jenkinsfile b/jenkinsfile new file mode 100644 index 0000000..bdc10ca --- /dev/null +++ b/jenkinsfile @@ -0,0 +1,224 @@ +pipeline { + agent any + + environment { + DOCKER_HUB_CREDENTIALS = credentials('docker-Hub-credentials') + DOCKER_IMAGE_NAME = 'johnsami/dockerized-app-build-app' + DOCKER_IMAGE_TAG = 'latest' + EMAIL_RECIPIENTS = 'johnhana567@gmail.com, khalid.salman1996@gmail.com, andrewadel3322@gmail.com' + } + + stages { + stage('Cleanup') { + steps { + deleteDir() + } + } + + stage('Checkout') { + steps { + git url: 'https://github.com/johnsamey/DEPI-Project.git', branch: 'master', credentialsId: 'github' + } + } + + stage('Prepare Docker Image') { + steps { + script { + sh ''' + cd $WORKSPACE + + # Check if the Docker image exists + IMAGE_EXISTS=$(docker images -q $DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG) + + if [ -n "$IMAGE_EXISTS" ]; then + echo "Docker image exists. Deleting existing image..." + docker rmi $DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG + else + echo "Docker image does not exist. Proceeding to build a new image..." + fi + ''' + } + } + } + + stage('Build Docker Image') { + steps { + script { + sh ''' + cd $WORKSPACE + docker-compose build + docker tag $DOCKER_IMAGE_NAME $DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG + ''' + } + } + } + + stage('Run Tests') { + steps { + sh 'echo "Running tests..."' + } + } + + stage('Push to Docker-Hub') { + steps { + script { + sh ''' + cd $WORKSPACE + echo "$DOCKER_HUB_CREDENTIALS_PSW" | docker login -u "$DOCKER_HUB_CREDENTIALS_USR" --password-stdin + docker-compose push + ''' + } + } + } + } + + // triggers { + // pollSCM('H/5 * * * *') // Poll GitHub every 5 minutes + // } + post { + // success { + // mail to: "${EMAIL_RECIPIENTS}", + // subject: "✅ Build Successful: ${JOB_NAME} - Build #${BUILD_NUMBER}", + // body: """
Good news! The build was successful.
+ //Job: ${JOB_NAME}
+ //Build Number: ${BUILD_NUMBER}
+ //Check the details at ${BUILD_URL}
""", + // mimeType: 'text/html' + // // attachLog: true + // } + + failure { + mail to: "${EMAIL_RECIPIENTS}", + subject: "❌ Build Failed: ${JOB_NAME} - Build #${BUILD_NUMBER}", + body: """Oops! The build has failed.
+Job: ${JOB_NAME}
+Build Number: ${BUILD_NUMBER}
+Check the console output at ${BUILD_URL}
""", + mimeType: 'text/html' + // attachLog: true + } + + unstable { + mail to: "${EMAIL_RECIPIENTS}", + subject: "⚠️ Build Unstable: ${JOB_NAME} - Build #${BUILD_NUMBER}", + body: """The build is unstable.
+Job: ${JOB_NAME}
+Build Number: ${BUILD_NUMBER}
+Check the console output at ${BUILD_URL}
""", + mimeType: 'text/html' + // attachLog: true + } + } +} + // post { + // always { + // emailext( + // to: 'mostafa77744333@gmail.com, johnhana567@gmail.com, khalid.salman1996@gmail.com, andrewadel3322@gmail.com', + // subject: 'Test Email from Jenkins', + // body: 'This is a test email to verify Jenkins email notifications.', + // attachLog: false + // ) + + // mail to: 'mostafa77744333@gmail.com, johnhana567@gmail.com, khalid.salman1996@gmail.com, andrewadel3322@gmail.com', + // subject: 'Test Email from Jenkins', + // body: 'This is a test email to verify Jenkins email notifications.' + // } + + // failure { + // emailext( + // to: 'mostafa77744333@gmail.com, johnhana567@gmail.com, khalid.salman1996@gmail.com, andrewadel3322@gmail.com', + // subject: 'Build failed in Jenkins: ${JOB_NAME} - Build #${BUILD_NUMBER}', + // body: """Build failed in Jenkins:
+ //Job: ${JOB_NAME} - Build #${BUILD_NUMBER}
+ //Check console output at ${BUILD_URL}
""", + // attachLog: true + // ) + // } + // unstable { + // emailext( + // to: 'mostafa77744333@gmail.com, johnhana567@gmail.com, khalid.salman1996@gmail.com, andrewadel3322@gmail.com', + // subject: 'Build failed in Jenkins: ${JOB_NAME} - Build #${BUILD_NUMBER}', + // body: """Build is unstable:
+ //Job: ${JOB_NAME} - Build #${BUILD_NUMBER}
+ //Check console output at ${BUILD_URL}
""", + // attachLog: true + // ) + // } + // success { + // emailext( + // subject: 'Build successful: ${JOB_NAME} [${BUILD_NUMBER}]', + // body: '''The build was successful: + // - Job: ${JOB_NAME} + // - Build number: ${BUILD_NUMBER} + // - Check console output at: ${BUILD_URL}''', + // to: 'mostafa77744333@gmail.com, johnhana567@gmail.com, khalid.salman1996@gmail.com, andrewadel3322@gmail.com', + // attachLog: true + // ) + // } + // } + + +// post { +// failure { +// emailext( +// subject: 'Build failed: ${JOB_NAME} [${BUILD_NUMBER}]', +// body: '''Build failed in Jenkins: +// - Job: ${JOB_NAME} +// - Build number: ${BUILD_NUMBER} +// - Check console output at: ${BUILD_URL}''', +// recipientProviders: [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']], +// to: 'mostafa77744333@gmail.com, johnhana567@gmail.com, khalid.salman1996@gmail.com, andrewadel3322@gmail.com', +// attachLog: true +// ) +// } +// unstable { +// emailext( +// subject: 'Build unstable: ${JOB_NAME} [${BUILD_NUMBER}]', +// body: '''The build is unstable: +// - Job: ${JOB_NAME} +// - Build number: ${BUILD_NUMBER} +// - Check console output at: ${BUILD_URL}''', +// to: 'mostafa77744333@gmail.com, johnhana567@gmail.com, khalid.salman1996@gmail.com, andrewadel3322@gmail.com', +// attachLog: true +// ) +// } +// success { +// emailext( +// subject: 'Build successful: ${JOB_NAME} [${BUILD_NUMBER}]', +// body: '''The build was successful: +// - Job: ${JOB_NAME} +// - Build number: ${BUILD_NUMBER} +// - Check console output at: ${BUILD_URL}''', +// to: 'mostafa77744333@gmail.com, johnhana567@gmail.com, khalid.salman1996@gmail.com, andrewadel3322@gmail.com', +// attachLog: true +// ) +// } +// } + + + + // post { + // always { + // script { + // emailext subject: "Jenkins Job: ${env.JOB_NAME} #${env.BUILD_NUMBER} - ${currentBuild.currentResult}", + // body: """ + // Job Name: ${env.JOB_NAME} + // Build Number: ${env.BUILD_NUMBER} + // Build Status: ${currentBuild.currentResult} + // Build URL: ${env.BUILD_URL} + // """, + // recipientProviders: [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']], + // to: 'johnhana567@gmail.com' + // } + // } + // success { + // emailext subject: "SUCCESS: ${env.JOB_NAME} #${env.BUILD_NUMBER}", + // body: "Good news, the build was successful!", + // to: 'johnhana567@gmail.com' + // } + // failure { + // emailext subject: "FAILURE: ${env.JOB_NAME} #${env.BUILD_NUMBER}", + // body: "Unfortunately, the build has failed.", + // to: 'johnhana567@gmail.com' + // } + // } diff --git a/plan.md b/plan.md new file mode 100644 index 0000000..f1d4126 --- /dev/null +++ b/plan.md @@ -0,0 +1,6 @@ +1. jenkins on an ec2 instance +2. configure docker & ansible agents in jenkins + 2.1. docker agent is used to build the image then run tests then push it to an image registry + 2.2. ansible agent is used to configure the ec2 ( install docker & pull the image ) +3. configure the notification channel in jenkins +4. testing jenkins auto build diff --git a/terraform/auto_var.auto.tfvars b/terraform/auto_var.auto.tfvars new file mode 100644 index 0000000..e3ffdf6 --- /dev/null +++ b/terraform/auto_var.auto.tfvars @@ -0,0 +1,10 @@ + # vpc + vpc_Name = "test-vpc" + vpc_cidr_block = "10.0.0.0/16" + public_subnets_cidr = ["10.0.1.0/24","10.0.2.0/24"] + availability_Zones_subnet = ["us-east-1a","us-east-1b"] # us-east-1a +#ec2 + ec2_Name = ["Docker","Jenkins"] + ami_id = "ami-0e86e20dae9224db8" # ami-0e86e20dae9224db8 + key_Name = "private_key" + instance_type = "t2.micro" # t2.micro \ No newline at end of file diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..79067d0 --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,16 @@ +module "vpc" { + source = "./modules/vpc" + vpc_Name = var.vpc_Name + vpc_cidr_block = var.vpc_cidr_block + public_subnets_cidr = var.public_subnets_cidr + availability_Zones_subnet = var.availability_Zones_subnet +} +module "ec2" { + source = "./modules/ec2" + ec2_Name = var.ec2_Name + vpc_id = module.vpc.vpc_id + public_subnet_ids = module.vpc.public_subnet_ids + ami_id = var.ami_id + key_Name = var.key_Name + instance_type = var.instance_type +} \ No newline at end of file diff --git a/terraform/modules/ec2/main.tf b/terraform/modules/ec2/main.tf new file mode 100644 index 0000000..5a7d47c --- /dev/null +++ b/terraform/modules/ec2/main.tf @@ -0,0 +1,96 @@ + + +resource "aws_instance" "public_ec2"{ + count =length(var.public_subnet_ids) + ami = var.ami_id + instance_type = var.instance_type + subnet_id = var.public_subnet_ids[count.index] + vpc_security_group_ids = [aws_security_group.public_security-group.id] + key_name = aws_key_pair.kp.key_name #key pair attach + associate_public_ip_address = "true" + user_data =file("./modules/ec2/public_user_data.sh") + tags = { + Name = "${var.ec2_Name[count.index]}", + created-by="john" + } + provisioner "local-exec" { + command = <