diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml new file mode 100644 index 0000000..69eed43 --- /dev/null +++ b/.github/workflows/ci-cd.yml @@ -0,0 +1,60 @@ +name: Deploy to AWS ECS +on: + push: + branches: [ deploy-to-ecs ] +jobs: + build-and-deploy: + runs-on: [ ubuntu-latest ] + steps: + - name: Checkout source + uses: actions/checkout@v3 + + - name: Setup Java + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '17' + + - name: Build Project + run: mvn clean install -DskipTests + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: 'eu-north-1' + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + with: + mask-password: 'true' + + - name: Build, tag, and push image to Amazon ECR + id: build-image + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + IMAGE_TAG: ${{ github.sha }} + REPOSITORY: springboot-example + run: | + # Build a docker container and + # push it to ECR so that it can + # be deployed to ECS. + docker build -t $ECR_REGISTRY/$REPOSITORY:$IMAGE_TAG . + docker push $ECR_REGISTRY/$REPOSITORY:$IMAGE_TAG + echo "image=$ECR_REGISTRY/$REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT + - name: Fill in the new image ID in the Amazon ECS task definition + id: task-def + uses: aws-actions/amazon-ecs-render-task-definition@v1 + with: + task-definition: springboot-app-task-definition.json + container-name: springboot-example + image: ${{ steps.build-image.outputs.image }} + - name: Deploy Amazon ECS task definition + uses: aws-actions/amazon-ecs-deploy-task-definition@v1 + with: + task-definition: ${{ steps.task-def.outputs.task-definition }} + service: springboot-example-service + cluster: DevCluster2 + wait-for-service-stability: true diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5119b62 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,5 @@ +FROM eclipse-temurin:17-jdk-alpine +WORKDIR /app +COPY target/springboot-example.jar springboot-example.jar +EXPOSE 8080 +CMD ["java","-jar","springboot-example.jar"] \ No newline at end of file diff --git a/pom.xml b/pom.xml index aeee93a..61f0f27 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,23 @@ org.springframework.boot spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.projectlombok + lombok + true + + + + mysql + mysql-connector-java + 8.0.29 + diff --git a/springboot-app-task-definition.json b/springboot-app-task-definition.json new file mode 100644 index 0000000..fa5d554 --- /dev/null +++ b/springboot-app-task-definition.json @@ -0,0 +1,97 @@ +{ + "compatibilities": [ + "EC2", + "FARGATE" + ], + "containerDefinitions": [ + { + "cpu": 0, + "environment": [], + "environmentFiles": [], + "essential": true, + "image": "493233983811.dkr.ecr.us-east-1.amazonaws.com/springboot-example:latest", + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "/ecs/springboot-app-task-definition", + "awslogs-create-group": "true", + "awslogs-region": "us-east-1", + "awslogs-stream-prefix": "ecs" + }, + "secretOptions": [] + }, + "mountPoints": [], + "name": "springboot-example", + "portMappings": [ + { + "appProtocol": "http", + "containerPort": 80, + "hostPort": 80, + "name": "springboot-example-80-tcp", + "protocol": "tcp" + }, + { + "appProtocol": "http", + "containerPort": 8080, + "hostPort": 8080, + "name": "springboot-example-8080-tcp", + "protocol": "tcp" + } + ], + "systemControls": [], + "ulimits": [], + "volumesFrom": [] + } + ], + "cpu": "1024", + "enableFaultInjection": false, + "executionRoleArn": "arn:aws:iam::493233983811:role/ecsTaskExecutionRole", + "family": "springboot-app-task-definition", + "memory": "3072", + "networkMode": "awsvpc", + "placementConstraints": [], + "registeredAt": "2025-09-23T19:14:25.667Z", + "registeredBy": "arn:aws:iam::493233983811:root", + "requiresAttributes": [ + { + "name": "com.amazonaws.ecs.capability.logging-driver.awslogs" + }, + { + "name": "ecs.capability.execution-role-awslogs" + }, + { + "name": "com.amazonaws.ecs.capability.ecr-auth" + }, + { + "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19" + }, + { + "name": "com.amazonaws.ecs.capability.task-iam-role" + }, + { + "name": "ecs.capability.execution-role-ecr-pull" + }, + { + "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18" + }, + { + "name": "ecs.capability.task-eni" + }, + { + "name": "com.amazonaws.ecs.capability.docker-remote-api.1.29" + } + ], + "requiresCompatibilities": [ + "FARGATE" + ], + "revision": 1, + "runtimePlatform": { + "cpuArchitecture": "X86_64", + "operatingSystemFamily": "LINUX" + }, + "status": "ACTIVE", + "taskDefinitionArn": "arn:aws:ecs:us-east-1:493233983811:task-definition/springboot-app-task-definition:1", + "taskRoleArn": "arn:aws:iam::493233983811:role/ecsTaskExecutionRole", + "volumes": [], + "tags": [] +} diff --git a/src/main/java/com/integrationninjas/springbootexample/controller/TestController.java b/src/main/java/com/integrationninjas/springbootexample/controller/TestController.java deleted file mode 100644 index 9a5f4ba..0000000 --- a/src/main/java/com/integrationninjas/springbootexample/controller/TestController.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.integrationninjas.springbootexample.controller; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -public class TestController { - - @GetMapping - public Object hello() { - Map object = new HashMap<>(); - object.put("name", "Integration Ninjas"); - object.put("email", "integrationninjas@gmail.com"); - return object; - } - -} diff --git a/src/main/java/com/integrationninjas/springbootexample/controller/UserController.java b/src/main/java/com/integrationninjas/springbootexample/controller/UserController.java new file mode 100644 index 0000000..97e6421 --- /dev/null +++ b/src/main/java/com/integrationninjas/springbootexample/controller/UserController.java @@ -0,0 +1,36 @@ +package com.integrationninjas.springbootexample.controller; + +import java.util.List; +import com.integrationninjas.springbootexample.dto.UserDto; +import com.integrationninjas.springbootexample.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class UserController { + + @Autowired + private UserService userService; + + @GetMapping("/users") + public ResponseEntity> getUsers() { + List usersList = userService.getUsers(); + return new ResponseEntity<>(usersList, HttpStatus.OK); + } + + @PostMapping("/users") + public ResponseEntity createUser(@RequestBody UserDto userDto) { + try { + String status = userService.createUser(userDto); + return new ResponseEntity<>(status, HttpStatus.CREATED); + } catch (Exception e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } + } + +} diff --git a/src/main/java/com/integrationninjas/springbootexample/dao/UserDao.java b/src/main/java/com/integrationninjas/springbootexample/dao/UserDao.java new file mode 100644 index 0000000..26e3038 --- /dev/null +++ b/src/main/java/com/integrationninjas/springbootexample/dao/UserDao.java @@ -0,0 +1,7 @@ +package com.integrationninjas.springbootexample.dao; + +import com.integrationninjas.springbootexample.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserDao extends JpaRepository { +} diff --git a/src/main/java/com/integrationninjas/springbootexample/dto/UserDto.java b/src/main/java/com/integrationninjas/springbootexample/dto/UserDto.java new file mode 100644 index 0000000..dd9cf92 --- /dev/null +++ b/src/main/java/com/integrationninjas/springbootexample/dto/UserDto.java @@ -0,0 +1,12 @@ +package com.integrationninjas.springbootexample.dto; + +import lombok.*; + +@Data +public class UserDto { + + private Long id; + private String firstName; + private String lastName; + private String email; +} diff --git a/src/main/java/com/integrationninjas/springbootexample/entity/User.java b/src/main/java/com/integrationninjas/springbootexample/entity/User.java new file mode 100644 index 0000000..3da05fc --- /dev/null +++ b/src/main/java/com/integrationninjas/springbootexample/entity/User.java @@ -0,0 +1,30 @@ +package com.integrationninjas.springbootexample.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "users") +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String firstName; + + @Column(nullable = false) + private String lastName; + + @Column(nullable = false, unique = true) + private String email; + +} diff --git a/src/main/java/com/integrationninjas/springbootexample/service/UserService.java b/src/main/java/com/integrationninjas/springbootexample/service/UserService.java new file mode 100644 index 0000000..dd636a1 --- /dev/null +++ b/src/main/java/com/integrationninjas/springbootexample/service/UserService.java @@ -0,0 +1,13 @@ +package com.integrationninjas.springbootexample.service; + +import com.integrationninjas.springbootexample.dto.UserDto; +import com.integrationninjas.springbootexample.entity.User; + +import java.util.List; + +public interface UserService { + + String createUser(UserDto userDto); + + List getUsers(); +} diff --git a/src/main/java/com/integrationninjas/springbootexample/service/impl/UserServiceImpl.java b/src/main/java/com/integrationninjas/springbootexample/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..734517f --- /dev/null +++ b/src/main/java/com/integrationninjas/springbootexample/service/impl/UserServiceImpl.java @@ -0,0 +1,46 @@ +package com.integrationninjas.springbootexample.service.impl; + +import com.integrationninjas.springbootexample.dao.UserDao; +import com.integrationninjas.springbootexample.dto.UserDto; +import com.integrationninjas.springbootexample.entity.User; +import com.integrationninjas.springbootexample.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Service +public class UserServiceImpl implements UserService { + + @Autowired + UserDao userDao; + + @Override + public String createUser(UserDto userDto) { + User user = new User(); + user.setFirstName(userDto.getFirstName()); + user.setLastName(userDto.getLastName()); + user.setEmail(userDto.getEmail()); + userDao.saveAndFlush(user); + return "User Added Successfully"; + } + + @Override + public List getUsers() { + List usersList = userDao.findAll(); + List dtoList = new ArrayList<>(); + if (!usersList.isEmpty()) { + usersList.forEach(user -> { + UserDto dto = new UserDto(); + dto.setId(user.getId()); + dto.setFirstName(user.getFirstName()); + dto.setLastName(user.getLastName()); + dto.setEmail(user.getEmail()); + dtoList.add(dto); + }); + } + return dtoList; + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8b13789..de4b4b0 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,6 @@ +spring.datasource.url=jdbc:mysql://user-management.cbvxsmsv5dwh.eu-north-1.rds.amazonaws.com:3306/user_management +spring.datasource.username=root +spring.datasource.password=password +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect +spring.jpa.hibernate.ddl-auto=update \ No newline at end of file