Skip to content
This repository was archived by the owner on Jun 2, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@
*.DS_Store
local_docs/
.scannerwork/
.env
.env.*
!.env.example
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 5000user5000

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
# Cloud Native Configuration Management

![Frontend](https://img.shields.io/badge/frontend-React%20%2B%20Vite-61DAFB)
![Backend](https://img.shields.io/badge/backend-Spring%20Boot-6DB33F)
![Database](https://img.shields.io/badge/database-PostgreSQL-4169E1)
![SonarQube](https://img.shields.io/badge/SonarQube-quality%20gate%20passed-brightgreen)
![Coverage](https://img.shields.io/badge/coverage-80%25%2B-brightgreen)
![Monitoring](https://img.shields.io/badge/monitoring-GCP%20%2B%20Grafana-orange)
![License](https://img.shields.io/badge/license-MIT-blue)

Cloud Native Configuration Management is a full-stack configuration governance workspace for project, factory, environment, template, override, export, and change-history workflows.

The repository is organized as a single workspace with a React frontend, a Spring Boot backend, PostgreSQL persistence, automated tests, coverage reports, and SonarQube quality analysis.

## Live Demo

- GCP frontend: https://mcmp-frontend-service-366904866748.europe-west1.run.app/
- Vercel frontend: https://cloud-native-steel.vercel.app/

The GCP deployment demonstrates the cloud-hosted frontend path. The Vercel deployment is kept as an alternate frontend hosting target for comparison and fallback.

## Project Overview

The application helps teams manage reusable configuration templates and project-specific overrides across deployment scopes.
Expand Down Expand Up @@ -294,3 +309,7 @@ The CI workflow currently covers:
- frontend dependency install, tests, and build;
- backend Maven tests;
- fast feedback for pull requests and branch updates.

## License

This project is licensed under the [MIT License](LICENSE).
8 changes: 8 additions & 0 deletions backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtAuthenticat
.requestMatchers("/error").permitAll()
.requestMatchers("/api/auth/register", "/api/auth/login").permitAll()
.requestMatchers("/api/health").permitAll()
.requestMatchers("/actuator/health", "/actuator/health/**", "/actuator/prometheus").permitAll()
.requestMatchers("/api/system/instance").permitAll()
.anyRequest().authenticated()
)
Expand Down
8 changes: 8 additions & 0 deletions backend/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,11 @@ server.error.include-message=always
logging.level.root=${LOGGING_LEVEL_ROOT:INFO}
logging.level.org.springframework=${LOGGING_LEVEL_SPRING:INFO}
logging.level.org.hibernate=${LOGGING_LEVEL_HIBERNATE:WARN}

management.endpoints.web.exposure.include=health,metrics,prometheus
management.endpoint.health.probes.enabled=true
management.endpoint.health.show-details=when_authorized
management.health.livenessstate.enabled=true
management.health.readinessstate.enabled=true
management.endpoint.health.group.liveness.include=livenessState
management.endpoint.health.group.readiness.include=readinessState,db
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.List;
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.http.MediaType.APPLICATION_JSON;
Expand Down Expand Up @@ -117,4 +118,16 @@ void requiresAuthenticationForConfigurationEndpoints() throws Exception {
mockMvc.perform(get("/api/configurations"))
.andExpect(status().isUnauthorized());
}

@Test
void allowsAnonymousActuatorEndpointsThroughSecurityFilter() throws Exception {
mockMvc.perform(get("/actuator/health"))
.andExpect(result -> assertThat(result.getResponse().getStatus()).isNotEqualTo(401));

mockMvc.perform(get("/actuator/health/readiness"))
.andExpect(result -> assertThat(result.getResponse().getStatus()).isNotEqualTo(401));

mockMvc.perform(get("/actuator/prometheus"))
.andExpect(result -> assertThat(result.getResponse().getStatus()).isNotEqualTo(401));
}
}
60 changes: 58 additions & 2 deletions deploy/production-like/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ It uses common production building blocks without requiring Kubernetes:
- Spring Boot backend containers
- PostgreSQL as the source-of-truth database
- Docker Compose health checks
- Spring Boot Actuator readiness and Prometheus metrics
- Prometheus scraping backend, PostgreSQL, container, and Nginx metrics
- Grafana dashboard provisioning for local monitoring evidence
- PostgreSQL exporter for database-level metrics
- cAdvisor for container CPU/memory metrics
- Nginx Prometheus exporter for reverse proxy metrics
- same-origin frontend API calls through `/api/*`

## Start
Expand All @@ -25,18 +31,50 @@ It uses common production building blocks without requiring Kubernetes:
docker compose -f deploy/production-like/compose.yml up --build --scale backend=2
```

If the host still uses the legacy Compose binary, run the same command as:

```bash
docker-compose -f deploy/production-like/compose.yml up --build --scale backend=2
```

Open:

```text
http://127.0.0.1:8090
Application: http://127.0.0.1:8090
Prometheus: http://127.0.0.1:9090
Grafana: http://127.0.0.1:3001
```

Check the backend health endpoint through the reverse proxy:
Grafana local credentials:

```text
username: admin
password: admin
```

Check the backend health endpoints:

```bash
curl http://127.0.0.1:8090/api/health
docker compose -f deploy/production-like/compose.yml exec backend \
wget -qO- http://127.0.0.1:8080/actuator/health/readiness
```

Check Prometheus metrics from inside the backend container:

```bash
docker compose -f deploy/production-like/compose.yml exec backend \
wget -qO- http://127.0.0.1:8080/actuator/prometheus | head
```

Prometheus target verification:

```text
http://127.0.0.1:9090/targets
```

The `cloud-native-backend`, `cloud-native-postgres`, `cloud-native-containers`, and `cloud-native-nginx` targets should be `UP`.

## Stop

Keep database data:
Expand All @@ -45,6 +83,12 @@ Keep database data:
docker compose -f deploy/production-like/compose.yml down
```

Legacy Compose fallback:

```bash
docker-compose -f deploy/production-like/compose.yml down
```

Reset database data:

```bash
Expand Down Expand Up @@ -100,3 +144,15 @@ Before using this pattern outside local/demo environments:
- restrict `CORS_ALLOWED_ORIGINS` to the real frontend domain
- add centralized logs and uptime monitoring


## Monitoring Story

This stack provides the minimum concrete monitoring evidence for the final report:

- Actuator readiness checks include the database dependency through the `db` health contributor.
- Prometheus scrapes `/actuator/prometheus` from the backend service.
- Prometheus also scrapes PostgreSQL exporter, cAdvisor, and Nginx exporter.
- Grafana auto-loads a `Cloud Native Backend Overview` dashboard.
- The dashboard shows backend availability, request rate, error rate, average latency, JVM memory, process CPU, HikariCP connection pool, PostgreSQL connections/transactions, container CPU/memory, and Nginx connection/request metrics.

This is intentionally a local production-like monitoring setup. It demonstrates observability and dependency-aware readiness without claiming full cloud failover, alert routing, or multi-region disaster recovery.
80 changes: 79 additions & 1 deletion deploy/production-like/compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ services:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8080/api/health | grep -q UP"]
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8080/actuator/health/readiness | grep -q UP"]
interval: 10s
timeout: 3s
retries: 12
Expand All @@ -53,6 +53,84 @@ services:
networks:
- app-network

postgres-exporter:
image: prometheuscommunity/postgres-exporter:v0.15.0
environment:
DATA_SOURCE_NAME: postgresql://cloudnative:cloudnative@postgres:5432/cloud_native_db?sslmode=disable
depends_on:
postgres:
condition: service_healthy
networks:
- app-network

cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.49.1
privileged: true
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker:/var/lib/docker:ro
- /dev/disk:/dev/disk:ro
networks:
- app-network

nginx-exporter:
image: nginx/nginx-prometheus-exporter:1.3.0
command:
- --nginx.scrape-uri=http://reverse-proxy:8080/nginx_status
depends_on:
reverse-proxy:
condition: service_healthy
networks:
- app-network

prometheus:
image: prom/prometheus:v2.54.1
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
depends_on:
backend:
condition: service_healthy
postgres-exporter:
condition: service_started
cadvisor:
condition: service_started
nginx-exporter:
condition: service_started
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:9090/-/ready | grep -q Ready"]
interval: 10s
timeout: 3s
retries: 12
networks:
- app-network

grafana:
image: grafana/grafana:11.1.4
ports:
- "3001:3000"
environment:
GF_SECURITY_ADMIN_USER: admin
GF_SECURITY_ADMIN_PASSWORD: admin
GF_USERS_ALLOW_SIGN_UP: "false"
volumes:
- ./grafana/provisioning:/etc/grafana/provisioning:ro
- ./grafana/dashboards:/var/lib/grafana/dashboards:ro
depends_on:
prometheus:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:3000/api/health | grep -q ok"]
interval: 10s
timeout: 3s
retries: 12
start_period: 20s
networks:
- app-network

reverse-proxy:
image: nginx:1.27-alpine
ports:
Expand Down
Loading
Loading