diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..39abf1c
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..878fc8f
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,21 @@
+*.class
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+target
+
+# IDE files
+.idea
+*.iml
+
+# env file
+.env
\ No newline at end of file
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..3c9cccb
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,72 @@
+# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
+# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
+
+name: Java CI with Maven
+
+on:
+ push:
+ branches: [ master ]
+ paths-ignore:
+ - "**/README.md"
+ - "terraform/**"
+ pull_request:
+ branches: [ master ]
+ paths-ignore:
+ - "**/README.md"
+ - "terraform/**"
+
+jobs:
+
+ build_local:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+
+
+ - name: Set up JDK 11
+ uses: actions/setup-java@v2
+ with:
+ java-version: '11'
+ distribution: 'adopt'
+ cache: maven
+
+ - name: Docker run mysql
+ run: docker run -p 3306:3306 --env MYSQL_ROOT_PASSWORD=${{secrets.DatabasePassword}} --env MYSQL_DATABASE='springboot_mysql_example' --env MYSQL_USER='osama' --env MYSQL_PASSWORD=${{secrets.DatabasePassword}} -d mysql
+
+
+
+ - name: Build the JAR file
+ run: mvn clean install -DskipTests
+
+
+ - name: Test
+ run: mvn test
+ env:
+ MYSQL_DB_HOST: 'localhost'
+ MYSQL_DB_PORT: '3306'
+ MYSQL_DB_USERNAME: 'osama'
+ MYSQL_DB_PASSWORD: ${{secrets.DatabasePassword}}
+ MYSQL_DB_DNAME: 'springboot_mysql_example'
+
+
+
+ build_docker:
+ needs: build_local #as we don't push the image unless the build job is done
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out code
+ uses: actions/checkout@v2
+
+
+ - name: Build & push Docker image
+ uses: mr-smithers-excellent/docker-build-push@v5
+ with:
+ image: osamamagdy/online_store
+ tags: v1, latest
+ registry: docker.io
+ dockerfile: Dockerfile
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_PASSWORD }}
diff --git a/.gitignore b/.gitignore
index d46f5ca..8a0d56f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,3 +16,14 @@ target
# IDE files
.idea
*.iml
+
+# env file
+.env
+k8s_yaml/secret.yaml
+
+#terraform files
+terraform/*
+
+## except the *.tf files
+
+!terraform/*.tf
\ No newline at end of file
diff --git a/.project b/.project
new file mode 100644
index 0000000..33768c5
--- /dev/null
+++ b/.project
@@ -0,0 +1,34 @@
+
+
+ spring-boot-mysql
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
+
+ 1636074914226
+
+ 30
+
+ org.eclipse.core.resources.regexFilterMatcher
+ node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
+
+
+
+
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..839d647
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,5 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/main/resources=UTF-8
+encoding//src/test/java=UTF-8
+encoding/=UTF-8
diff --git a/.settings/org.eclipse.jdt.apt.core.prefs b/.settings/org.eclipse.jdt.apt.core.prefs
new file mode 100644
index 0000000..d4313d4
--- /dev/null
+++ b/.settings/org.eclipse.jdt.apt.core.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.apt.aptEnabled=false
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..951833c
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,10 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.methodParameters=generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
+org.eclipse.jdt.core.compiler.processAnnotations=disabled
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000..f897a7f
--- /dev/null
+++ b/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..ca906c2
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,12 @@
+FROM maven:latest as builder
+WORKDIR /app
+COPY . .
+
+RUN ["mvn","clean","install","-DskipTests"]
+
+FROM openjdk:latest
+COPY --from=builder /app/target/spring-boot-mysql-0.0.1-SNAPSHOT.jar /usr/src/myapp/
+WORKDIR /usr/src/myapp
+EXPOSE 8080
+
+CMD ["java", "-jar","spring-boot-mysql-0.0.1-SNAPSHOT.jar"]
\ No newline at end of file
diff --git a/README.md b/README.md
index 2e18ca6..f190d79 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,88 @@
-# Spring Boot MySQL Example
+# Online store
-You can learn more about my courses [here](http://courses.springframework.guru/courses/) on my site.
\ No newline at end of file
+## Resources
+ The original spring-boot application was inspired from [here](https://github.com/springframeworkguru/spring-boot-mysql-example)
+## Steps to build locally
+### 1 - First, ensure to have Java JDK/JRE and maven installed on your local machine
+ a - install Java JDK by running the command `sudo apt install default-jdk -y`
+ b - install Java JRE by running the command `sudo apt install default-jre -y`
+ c - install maven bwith `sudo apt install maven -y`
+### 2 - Second, install and configure mysql
+ a - install from [here](https://linuxize.com/post/how-to-install-mysql-on-ubuntu-18-04/)
+ b - configure mysql by creating a user with `mysql_native_password` for the authentication method. You can take guidance from [here](https://linuxize.com/post/how-to-manage-mysql-databases-and-users-from-the-command-line/#create-a-new-mysql-user-account)
+ c - create database for the application ( use the same guide above )
+### 3 - Put the information in a new `.env` file
+```
+MYSQL_DB_HOST=
+MYSQL_DB_PORT=
+MYSQL_DB_USERNAME=
+MYSQL_DB_PASSWORD=
+MYSQL_DB_DNAME=
+```
+ Note: all names and passwords will be updated in the `src/main/resources/application.properties` file
+### 4 - Build the jar file and run
+ a - Run the command `mvn clean install`. This will create a directory named `target`.
+ b - navigate to the `target` directory and run the jar file with `java -jar `
+## Build locally using Docker
+### 1 - Install Docker & Docker-Compose
+ a - Install docker from the official documentation [here](https://docs.docker.com/engine/install/ubuntu/)
+ b - Install docker-compose from the official documentation [here](https://docs.docker.com/compose/install/)
+ Note: If you choose to build with docker, you will not need all the previous installation for running the project locally. You only need to install docker and docker-compose and it saves you the hassle of installing JDK, JRE, maven, and even mysql.
+
+### 2 - Put the information in a new `.env` file
+ For the sake of consistency, we use the same name while building locally
+```
+MYSQL_DB_HOST=db #this one is necessary as it is the service name in docker-compose.yaml file
+MYSQL_DB_PORT=3306 #this is the default port used by mysql official image so don't choose any port else
+MYSQL_DB_USERNAME=
+MYSQL_DB_PASSWORD=
+MYSQL_DB_DNAME=
+```
+### 3 - Build and Run
+ Run the command `docker-compose up` in the directory where docker files are. Docker will first pull the needed images and start working.
+ If you want to edit the configurations and build again, run `docker-compose up --build` to ignore chache and start building again.
+
+## Deploy with kubernetes
+### 1 - Install minikube
+ a - For testing purposes, we can deploy all our kubernetes deployments on a locally created single-node cluster with minikube [here](https://minikube.sigs.k8s.io/docs/start/)
+ Note: If you are running your ubuntu on a virtual machine upon other OS, you may need to follow this [guide](https://webme.ie/how-to-run-minikube-on-a-virtualbox-vm/) instead.
+
+### 2 - Put the information in a new `secret.yaml` file (all files are in the k8s_yaml directory)
+ a - Here, we will replace the `.env` file with two files. A configmap.yaml which contains non-sensetive data to be accessible by all cluster resources (this one is created for you and is present in the repository.
+ b - A secret.yaml which contains sensetive data that not to be shared on publuic repository so create the file first and fill the data below
+
+```
+apiVersion: v1
+kind: Secret
+metadata:
+ name: mysql-secret
+type: Opaque
+data:
+ MYSQL_DB_PORT:
+ MYSQL_DB_USERNAME:
+ MYSQL_DB_PASSWORD:
+ MYSQL_DB_DNAME:
+```
+ Important Note: you can not just place the values in the secrets file as a plain text, first you will need to encode them to base64 (as k8s will decode them by default). And the way to do this in linux `echo -n text | base64 `, or you can use a website like [here](https://www.base64decode.org/)
+
+### 3 - Build and Run with external ip
+ a - You will need to run `kubectl apply -f .yaml` to apply the configurations of each file. But due to using secrets and services, you will need to execute them in specific order
+ I - secrets file
+ II - mysql deployment files
+ III - mysql service files
+ IV - config map file
+ V - online-store deployment file
+ VI - online-store-service file
+ b - The loadbalancer used in online-store-service will assign an external ip → with using minikube, it will be in pending state until you allow it to take ip by typing “minikube service ”
+
+ Note: If you want to edit any of the configurations, run `kubectl apply -f .yaml`.
+
+### 4 - Build and Run with ingress and domain name
+ a - The configuration files used for this are online-store-ingress.yaml and online-store-internal-service.yaml (this will be applied instead of online-store-service.yaml)
+ b - Install the ingress-controller pod in your k8s cluster → with minikube you type `minikube addons enable ingress`and it will be added in the kube-system namespace
+ c - apply online-store-internal-service.yaml ( note that if you used the above steps exactly, you will have 2 services mapping to the same deployment and it is fine for testing. Otherwise, delete the service online-store-service )
+ d - apply the online-store-ingress.yaml ( note that you will replace the file in step 6 above with )
+ e - this will create an IP address to the hostname osama.com → type `kubectl get ingress` to know
+ f - For testing and with using minikube, this is a dummy hostname that is not available publicly. So we have to map it explicitly to the address created by minkube
+ g - to map in your linux, type `sudo nano /etc/hosts` → add the IP address created in step 5 and the hostname associated with it osama.com
+ h - Go to osama.com in your browser to open the application
diff --git a/docker-compose.yaml b/docker-compose.yaml
new file mode 100644
index 0000000..b2fd539
--- /dev/null
+++ b/docker-compose.yaml
@@ -0,0 +1,40 @@
+version: '3'
+services:
+ db:
+ image: mysql:5.7
+ restart: always
+ environment:
+ MYSQL_DATABASE: "$MYSQL_DB_DNAME"
+ # So you don't have to use root, but you can if you like
+ MYSQL_USER: "$MYSQL_DB_USERNAME"
+ # You can use whatever password you like
+ MYSQL_PASSWORD: "$MYSQL_DB_PASSWORD"
+ # Password for root access
+ MYSQL_ROOT_PASSWORD: "$MYSQL_DB_PASSWORD"
+ ports:
+ # : < MySQL Port running inside container>
+ #Host port is used for outside communication
+ #MYSQL internal port used for Networked service-to-service communication
+ - '3000:3306'
+ volumes:
+ - my-db:/var/lib/mysql
+ web-app:
+ depends_on:
+ - db
+ restart: on-failure
+ links:
+ - "db:db"
+ environment:
+ MYSQL_DB_HOST: ${MYSQL_DB_HOST}
+ MYSQL_DB_PORT: ${MYSQL_DB_PORT}
+ MYSQL_DB_USERNAME: ${MYSQL_DB_USERNAME}
+ MYSQL_DB_PASSWORD: ${MYSQL_DB_PASSWORD}
+ MYSQL_DB_DNAME: ${MYSQL_DB_DNAME}
+ build:
+ context: .
+ dockerfile: Dockerfile
+ ports:
+ - "8080:8080"
+## Names our volume
+volumes:
+ my-db:
\ No newline at end of file
diff --git a/k8s_yaml/configmap.yaml b/k8s_yaml/configmap.yaml
new file mode 100644
index 0000000..fd7ef6c
--- /dev/null
+++ b/k8s_yaml/configmap.yaml
@@ -0,0 +1,6 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: online-store-configmap
+data:
+ MYSQL_DB_HOST: mysql-service
\ No newline at end of file
diff --git a/k8s_yaml/mysql-deployment.yaml b/k8s_yaml/mysql-deployment.yaml
new file mode 100644
index 0000000..58f304e
--- /dev/null
+++ b/k8s_yaml/mysql-deployment.yaml
@@ -0,0 +1,42 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: mysql-deployment
+ labels:
+ app: mysql
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: mysql
+ template:
+ metadata:
+ labels:
+ app: mysql
+ spec:
+ containers:
+ - name: mysql
+ image: mysql
+ ports:
+ - containerPort: 3306
+ env:
+ - name: MYSQL_DATABASE
+ valueFrom:
+ secretKeyRef:
+ name: mysql-secret
+ key: MYSQL_DB_DNAME
+ - name: MYSQL_USER
+ valueFrom:
+ secretKeyRef:
+ name: mysql-secret
+ key: MYSQL_DB_USERNAME
+ - name: MYSQL_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: mysql-secret
+ key: MYSQL_DB_PASSWORD
+ - name: MYSQL_ROOT_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: mysql-secret
+ key: MYSQL_DB_PASSWORD
diff --git a/k8s_yaml/mysql-service.yaml b/k8s_yaml/mysql-service.yaml
new file mode 100644
index 0000000..d67f415
--- /dev/null
+++ b/k8s_yaml/mysql-service.yaml
@@ -0,0 +1,11 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: mysql-service
+spec:
+ selector:
+ app: mysql
+ ports:
+ - protocol: TCP
+ port: 3306
+ targetPort: 3306
\ No newline at end of file
diff --git a/k8s_yaml/online-store-deployment.yaml b/k8s_yaml/online-store-deployment.yaml
new file mode 100644
index 0000000..5b87071
--- /dev/null
+++ b/k8s_yaml/online-store-deployment.yaml
@@ -0,0 +1,47 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: online-store
+ labels:
+ app: online-store
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: online-store
+ template:
+ metadata:
+ labels:
+ app: online-store
+ spec:
+ containers:
+ - name: online-store
+ image: osamamagdy/online_store
+ ports:
+ - containerPort: 8080
+ env:
+ - name: MYSQL_DB_HOST
+ valueFrom:
+ configMapKeyRef:
+ name: online-store-configmap
+ key: MYSQL_DB_HOST
+ - name: MYSQL_DB_PORT
+ valueFrom:
+ secretKeyRef:
+ name: mysql-secret
+ key: MYSQL_DB_PORT
+ - name: MYSQL_DB_USERNAME
+ valueFrom:
+ secretKeyRef:
+ name: mysql-secret
+ key: MYSQL_DB_USERNAME
+ - name: MYSQL_DB_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: mysql-secret
+ key: MYSQL_DB_PASSWORD
+ - name: MYSQL_DB_DNAME
+ valueFrom:
+ secretKeyRef:
+ name: mysql-secret
+ key: MYSQL_DB_DNAME
\ No newline at end of file
diff --git a/k8s_yaml/online-store-ingress.yaml b/k8s_yaml/online-store-ingress.yaml
new file mode 100644
index 0000000..dfc69dd
--- /dev/null
+++ b/k8s_yaml/online-store-ingress.yaml
@@ -0,0 +1,16 @@
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: online-store-ingress
+spec:
+ rules:
+ - host: osama.com
+ http:
+ paths:
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: online-store-internal-service
+ port:
+ number: 8080
\ No newline at end of file
diff --git a/k8s_yaml/online-store-internal-service.yaml b/k8s_yaml/online-store-internal-service.yaml
new file mode 100644
index 0000000..80f0e49
--- /dev/null
+++ b/k8s_yaml/online-store-internal-service.yaml
@@ -0,0 +1,11 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: online-store-internal-service #this is the one used bby ingress controller
+spec:
+ selector:
+ app: online-store
+ ports:
+ - protocol: TCP
+ port: 8080
+ targetPort: 8080
\ No newline at end of file
diff --git a/k8s_yaml/online-store-service.yaml b/k8s_yaml/online-store-service.yaml
new file mode 100644
index 0000000..a945adb
--- /dev/null
+++ b/k8s_yaml/online-store-service.yaml
@@ -0,0 +1,13 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: online-store-service
+spec:
+ selector:
+ app: online-store
+ type: LoadBalancer
+ ports:
+ - protocol: TCP
+ port: 8080
+ targetPort: 8080
+ nodePort: 30000 #this is the port for external ip address that you will type into your browser
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 0002268..29e2e68 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,7 +14,7 @@
org.springframework.boot
spring-boot-starter-parent
- 2.0.0.RELEASE
+ 2.1.6.RELEASE
@@ -47,6 +47,11 @@
spring-boot-starter-test
test
+
+ javax.xml.bind
+ jaxb-api
+ 2.3.0
+
@@ -55,6 +60,11 @@
org.springframework.boot
spring-boot-maven-plugin
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.19.1
+
@@ -95,4 +105,4 @@
-
+
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 8e4417a..0d5c1d3 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -2,9 +2,9 @@
# = DATA SOURCE
# ===============================
# Set here configurations for the database connection
-spring.datasource.url=jdbc:mysql://localhost:3306/springboot_mysql_example
-spring.datasource.username=root
-#spring.datasource.password=YOUR_DB_PASSWORD
+spring.datasource.url=jdbc:mysql://${MYSQL_DB_HOST}:${MYSQL_DB_PORT}/${MYSQL_DB_DNAME}?enabledTLSProtocols=TLSv1.2
+spring.datasource.username=${MYSQL_DB_USERNAME}
+spring.datasource.password=${MYSQL_DB_PASSWORD}
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# Keep the connection alive if idle for a long time (needed in production)
@@ -22,4 +22,4 @@ spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl
spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
# Allows Hibernate to generate SQL optimized for a particular DBMS
-spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
\ No newline at end of file
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
diff --git a/terraform/main.tf b/terraform/main.tf
new file mode 100644
index 0000000..6631829
--- /dev/null
+++ b/terraform/main.tf
@@ -0,0 +1,63 @@
+// Configure the Google Cloud provider
+provider "google" {
+ credentials = file("CRED.json")
+ project = "even-sun-331510"
+ region = "us-west1"
+}
+
+// Terraform plugin for creating random ids
+resource "random_id" "instance_id" {
+ byte_length = 8
+}
+
+
+// A single Compute Engine instance
+resource "google_compute_instance" "default" {
+ name = "capiter-${random_id.instance_id.hex}"
+ machine_type = "f1-micro" //As the server is intended to run calculate cimplex equations
+ zone = "us-west1-a"
+
+ boot_disk {
+ initialize_params {
+ image = "debian-cloud/debian-9"
+ size = "20"
+ }
+ }
+ metadata = {
+ ssh-keys = "osamamagdy174@gmail.com:${file("~/.ssh/id_ed25519.pub")}"
+ }
+
+ network_interface {
+ network = "default"
+
+ access_config {
+ // Include this section to give the VM an external ip address
+ }
+ }
+
+ lifecycle {
+ ignore_changes = [attached_disk]
+ }
+}
+
+resource "google_compute_disk" "default" {
+ name = "capiter-disk"
+ type = "pd-ssd" //better for intensive applications with low latency
+ zone = "us-west1-a"
+ size = "100"
+ labels = {
+ environment = "dev"
+ }
+ physical_block_size_bytes = 4096
+}
+
+//Attach the resource disk to be attached to the compute instance
+resource "google_compute_attached_disk" "default" {
+ disk = google_compute_disk.default.id
+ instance = google_compute_instance.default.id
+}
+
+// A variable for extracting the external IP address of the instance
+output "ip" {
+ value = google_compute_instance.default.network_interface.0.access_config.0.nat_ip
+}
\ No newline at end of file