diff --git a/.github/workflows/SonarQube.yaml b/.github/workflows/SonarQube.yaml deleted file mode 100644 index 16dd20d..0000000 --- a/.github/workflows/SonarQube.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: SonarCloud scan for User Services (Test Stage) -on: - workflow_call: - secrets: - SONAR_TOKEN: - required: true -jobs: - sonar-cloud-scan: - name: Build and analyze - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: 17 - distribution: 'zulu' # Alternative distribution options are available. - - name: Cache SonarCloud packages - uses: actions/cache@v3 - with: - path: ~/.sonar/cache - key: ${{ runner.os }}-sonar - restore-keys: ${{ runner.os }}-sonar - - name: Cache Maven packages - uses: actions/cache@v3 - with: - path: ~/.m2 - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2 - - name: Build and analyze - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=DevOps-Video-Sharing_UserService \ No newline at end of file diff --git a/.github/workflows/build-image.yaml b/.github/workflows/build-image.yaml new file mode 100644 index 0000000..9fd2079 --- /dev/null +++ b/.github/workflows/build-image.yaml @@ -0,0 +1,37 @@ +name: Build Image User Service + +on: + workflow_call: + secrets: + DOCKER_HUB_ACCESS_TOKEN: + required: true + +jobs: + build-image: + name: Build and Push Docker Image + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup JDK 17 + uses: actions/setup-java@v3 + with: + distribution: 'corretto' + java-version: 17 + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: datuits + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + + - name: Build the application + run: | + mvn clean + mvn -B package --file pom.xml + + - name: Build and Push the docker image + run: | + docker build -t datuits/devops-user-service:latest . + docker push datuits/devops-user-service:latest \ No newline at end of file diff --git a/.github/workflows/deploymentCD.yaml b/.github/workflows/deploymentCD.yaml new file mode 100644 index 0000000..3dac504 --- /dev/null +++ b/.github/workflows/deploymentCD.yaml @@ -0,0 +1,58 @@ +name: Continuous Deployment for User Service + +on: + workflow_run: + workflows: ["Continuous Integration for Comment Service"] + types: + - completed + +env: + PROJECT_ID: gke-project-423206 + CLUSTER_NAME: autopilot-cluster-1 + ZONE: us-central1 + +jobs: + deploy: + name: Deploy to GKE Autopilot + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup JDK 17 + uses: actions/setup-java@v3 + with: + distribution: 'corretto' + java-version: 17 + + - name: Build the application + run: | + mvn clean + mvn -B package --file pom.xml + + - name: Authenticate + uses: google-github-actions/auth@v2 + with: + credentials_json: ${{ secrets.GCP_SA_KEY }} + + - name: Configure gcloud + uses: google-github-actions/setup-gcloud@v2 + with: + project_id: ${{ env.PROJECT_ID }} + install_components: 'gke-gcloud-auth-plugin' + + - name: Set cluster context + run: | + gcloud container clusters get-credentials ${{ env.CLUSTER_NAME }} --zone ${{ env.ZONE }} --project ${{ env.PROJECT_ID }} + + - name: Apply Kubernetes manifests + run: | + kubectl apply -f resources.yaml + + notifications: + needs: deploy + uses: ./.github/workflows/notifyCD.yaml + secrets: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL}} + +###Demo### \ No newline at end of file diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index e7812f3..e824872 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -1,13 +1,13 @@ -name: Test Stage for Comment Service +name: Dev Stage for User Service on: push: branches: - - test + - dev jobs: testing: - name: Testing Comment Service + name: Testing User Service runs-on: ubuntu-latest steps: @@ -26,18 +26,15 @@ jobs: - name: Unit Tests run: mvn -B test --file pom.xml - sonar-cloud-scan: + build-image: needs: testing - uses: ./.github/workflows/SonarQube.yaml + uses: ./.github/workflows/build-image.yaml secrets: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - - scan-image: - needs: sonar-cloud-scan - uses: ./.github/workflows/scan-image.yaml - - notify: - needs: scan-image - uses: ./.github/workflows/notifyCI.yaml - secrets: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + DOCKER_HUB_ACCESS_TOKEN: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + + # notify: + # needs: build-image + # uses: ./.github/workflows/notifyCI.yaml + # secrets: + # SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} +###Testing Build### diff --git a/.github/workflows/notifyCD.yaml b/.github/workflows/notifyCD.yaml new file mode 100644 index 0000000..20f2b28 --- /dev/null +++ b/.github/workflows/notifyCD.yaml @@ -0,0 +1,36 @@ +name: Send Slack Notification for User Service + +on: + workflow_call: + secrets: + SLACK_WEBHOOK_URL: + required: true + +jobs: + success_notifier: + if: success() + runs-on: ubuntu-latest + steps: + - name: Send success notification on Slack + uses: slackapi/slack-github-action@v1.24.0 + with: + payload: | + { + "text": "The Continuous Deployment for User Service workflow has completed successfully." + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + + failure_notifier: + if: failure() + runs-on: ubuntu-latest + steps: + - name: Send failure notification on Slack + uses: slackapi/slack-github-action@v1.24.0 + with: + payload: | + { + "text": "The Continuous Deployment for User Service workflow has failed." + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} \ No newline at end of file diff --git a/.github/workflows/notifyCI.yaml b/.github/workflows/notifyCI.yaml index 2dc3428..2b7db47 100644 --- a/.github/workflows/notifyCI.yaml +++ b/.github/workflows/notifyCI.yaml @@ -1,4 +1,4 @@ -name: Send Slack Notification for User Service (Test Stage) +name: Send Slack Notification for User Service on: workflow_call: diff --git a/.github/workflows/scan-image.yaml b/.github/workflows/scan-image.yaml deleted file mode 100644 index d366468..0000000 --- a/.github/workflows/scan-image.yaml +++ /dev/null @@ -1,47 +0,0 @@ -name: Scan Image User Service (Test Stage) -on: - workflow_call: - -jobs: - scan-image: - name: Security Scan - runs-on: ubuntu-latest - steps: - - name: Install Trivy - run: | - sudo apt-get update - sudo apt-get install -y wget - wget https://github.com/aquasecurity/trivy/releases/download/v0.40.0/trivy_0.40.0_Linux-64bit.deb - sudo dpkg -i trivy_0.40.0_Linux-64bit.deb - - - name: Scan Docker image with Trivy - id: scan-image - run: | - trivy image --format json --output scan-results.json datuits/devops-user-service:latest - - - name: Extract high and critical vulnerabilities - id: extract_vulnerabilities - run: | - jq -r ' - def hr(severity): - if severity == "HIGH" or severity == "CRITICAL" then true else false end; - def to_md: - "| " + (.VulnerabilityID // "") + " | " + (.PkgName // "") + " | " + (.InstalledVersion // "") + " | " + (.Severity // "") + " | " + (.Title // "") + " |"; - [ - "# Docker Image Scan Results", - "", - "## High and Critical Vulnerabilities", - "", - "| Vulnerability ID | Package | Version | Severity | Description |", - "|------------------|---------|---------|----------|-------------|", - (.Results[] | .Vulnerabilities[] | select(hr(.Severity)) | to_md), - "" - ] | join("\n") - ' scan-results.json > vulnerability-report.md - - - name: Upload vulnerability report - uses: actions/upload-artifact@v2 - with: - name: vulnerability-report - path: vulnerability-report.md - \ No newline at end of file diff --git a/pom.xml b/pom.xml index c90ca06..c450841 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,11 @@ 2.10.1 - + + net.logstash.logback + logstash-logback-encoder + 7.0.1 + diff --git a/src/main/java/com/programming/userService/controller/UserController.java b/src/main/java/com/programming/userService/controller/UserController.java index 3ccf6d0..054993f 100644 --- a/src/main/java/com/programming/userService/controller/UserController.java +++ b/src/main/java/com/programming/userService/controller/UserController.java @@ -33,17 +33,27 @@ import java.util.Map; import java.util.Base64; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; @RestController @AllArgsConstructor @RequestMapping("/user") public class UserController { + private static final Logger logger = LoggerFactory.getLogger(UserController.class); + @GetMapping("/") public String getServiceName(){ + //ELK + MDC.put("type", "userservice"); + logger.info("User Service Start"); return "User Service"; } - private final AuthUserRepository userRepository; + + + private final AuthUserRepository userRepository; private final PasswordEncoder passwordEncoder; @Autowired private JavaMailSender javaMailSender; @@ -87,6 +97,11 @@ public ResponseEntity registerUser(@RequestBody AuthUser user) { user.setTimestamp(new Date()); user.setAvatar(getDefaultAvatar()); AuthUser save = userRepository.save(user); + AuthUser userFromDb = userRepository.findByUsername(user.getUsername()) + .orElseThrow(() -> new Exception("User not found")); + MDC.put("type", "userservice"); + MDC.put("action", "register"); + logger.info("UserID: " + userFromDb.getId()); return ResponseEntity.ok(save); } catch (Exception e) { return ResponseEntity.internalServerError().body(e.getMessage()); @@ -106,6 +121,9 @@ public ResponseEntity loginUser(@RequestBody AuthUser user) { AuthUser userFromDb = userRepository.findByUsername(user.getUsername()) .orElseThrow(() -> new Exception("User not found")); if (passwordEncoder.matches(user.getPassword(), userFromDb.getPassword())) { + MDC.put("type", "userservice"); + MDC.put("action", "login"); + logger.info("UserID: " + userFromDb.getId()); return ResponseEntity.ok(userFromDb); } else { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials"); @@ -163,6 +181,9 @@ public ResponseEntity updateProfile(@PathVariable("id") String id, @RequestBody userFromDb.setFirstName(user.getFirstName()); userFromDb.setLastName(user.getLastName()); AuthUser save = userRepository.save(userFromDb); + MDC.put("type", "userservice"); + MDC.put("action", "update-profile"); + logger.info("UserID: " + userFromDb.getId()); return ResponseEntity.ok(HttpStatus.OK); } catch (Exception e) { @@ -184,7 +205,9 @@ public ResponseEntity changePassword(@PathVariable("id") String id, userFromDb.setPassword(passwordEncoder.encode(changePasswordRequest.getNewPassword())); AuthUser save = userRepository.save(userFromDb); - + MDC.put("type", "userservice"); + MDC.put("action", "change-password"); + logger.info("UserID: " + userFromDb.getId()); return ResponseEntity.ok("Password changed successfully"); } catch (Exception e) { return ResponseEntity.internalServerError().body(e.getMessage()); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 091832f..d9a056f 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -18,3 +18,6 @@ spring.mail.username=21520714@gm.uit.edu.vn spring.mail.password=vkux umrv rtkd svsy spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true + +# logstash +logging.config=classpath:logback-spring.xml \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..6b0ebf7 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,11 @@ + + + 192.168.120.213:5050 + + + + + + + + \ No newline at end of file diff --git a/src/test/java/TestUserService.java b/src/test/java/TestUserService.java index edff7ed..042a686 100644 --- a/src/test/java/TestUserService.java +++ b/src/test/java/TestUserService.java @@ -17,3 +17,4 @@ public void testGetServiceName() throws Exception { .andExpect(status().isOk()); } } +/////