15 Commits

Author SHA1 Message Date
33ebb4af89 Minor fixes
Helm Chart CI & Release / Lint Helm Chart (push) Successful in 10s
Helm Chart CI & Release / Semantic Release (push) Successful in 2m7s
2026-02-20 13:38:28 +00:00
8e99fe7614 Add Trivy CVE scan, container registry docs, and update diagrams
Helm Chart CI & Release / Lint Helm Chart (push) Successful in 9s
Helm Chart CI & Release / Semantic Release (push) Successful in 2m1s
- Add Trivy vulnerability scan step to CI (HIGH/CRITICAL, warn-only)
- Add Container Registry section to README with pull examples
- Update architecture doc and HLD with crane + Trivy details

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-19 21:38:05 +00:00
bd723dbd05 Rename container package to flamingo-tech-test
Helm Chart CI & Release / Lint Helm Chart (push) Successful in 9s
Helm Chart CI & Release / Semantic Release (push) Successful in 2m1s
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-19 21:30:33 +00:00
55d7345854 Fix CI: lowercase OCI reference and use REPO_TOKEN secret
Helm Chart CI & Release / Lint Helm Chart (push) Successful in 8s
Helm Chart CI & Release / Semantic Release (push) Successful in 2m12s
OCI spec requires lowercase repository names; eSlider was
causing crane to reject the reference. Use secrets.REPO_TOKEN
for registry authentication.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-19 21:25:43 +00:00
87ce0ec6ee Fix CI: use owner-scoped registry path for crane copy
Helm Chart CI & Release / Lint Helm Chart (push) Successful in 9s
Helm Chart CI & Release / Semantic Release (push) Failing after 14s
Gitea OCI registry expects host/owner/package:tag format,
not host/owner/repo/package:tag.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-19 21:24:12 +00:00
53da6fe547 Fix CI: use crane to mirror image (no Docker daemon in runner)
Helm Chart CI & Release / Lint Helm Chart (push) Successful in 9s
Helm Chart CI & Release / Semantic Release (push) Failing after 15s
The Gitea Actions runner container lacks Docker. Use crane
(daemonless) to copy the upstream FleetDM image directly to
the Gitea OCI registry.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-19 21:22:59 +00:00
6adc52830f Fix CI: mirror upstream FleetDM image instead of docker build
Helm Chart CI & Release / Lint Helm Chart (push) Successful in 10s
Helm Chart CI & Release / Semantic Release (push) Failing after 9s
No Dockerfile exists — this is a Helm chart project. Mirror the
upstream fleetdm/fleet image to the Gitea container registry,
tagged with both appVersion and chart version.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-19 21:21:22 +00:00
e6176999c1 Add containerisation strategy details and CI image build step
Helm Chart CI & Release / Lint Helm Chart (push) Successful in 10s
Helm Chart CI & Release / Semantic Release (push) Failing after 9s
Expand architecture doc section 4.5 with image building process,
container registry management, and deployment pipeline prose.
Add Docker build & push to Gitea OCI registry in CI workflow.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-19 21:19:31 +00:00
ec03bcf778 Update docs 2026-02-19 21:04:40 +00:00
e535a75649 Update README with installation instructions and add .gitignore for IDE files
Helm Chart CI & Release / Lint Helm Chart (push) Successful in 9s
Helm Chart CI & Release / Semantic Release (push) Successful in 10s
- Enhanced README.md with additional installation steps and a new image.
- Added .gitignore file to exclude IDE-specific files and directories.
2026-02-19 20:59:37 +00:00
a44aef5381 Simplify docs exclusion: use paths-ignore on push trigger
Helm Chart CI & Release / Lint Helm Chart (push) Successful in 10s
Helm Chart CI & Release / Semantic Release (push) Successful in 10s
Replace in-job file check with paths-ignore filter.
Workflow won't trigger at all for docs-only changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-19 20:58:48 +00:00
4a278b1419 Fix CI checkout: use token auth for git clone
Helm Chart CI & Release / Lint Helm Chart (push) Successful in 9s
Helm Chart CI & Release / Semantic Release (push) Successful in 10s
Repo requires authentication; use gitea.token in clone URLs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-19 20:56:50 +00:00
698c977511 Skip release for docs-only changes
Helm Chart CI & Release / Lint Helm Chart (push) Successful in 10s
Helm Chart CI & Release / Semantic Release (push) Successful in 10s
Semantic release now checks changed files and skips tag/publish
when only docs, README, STATUS, AGENTS, or .gitignore are modified.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-19 20:54:42 +00:00
86108f5b75 Minor docs change
Helm Chart CI & Release / Lint Helm Chart (push) Successful in 9s
Helm Chart CI & Release / Semantic Release (push) Successful in 9s
2026-02-19 20:40:22 +00:00
fb92b4c000 Minor docs change 2026-02-19 20:35:53 +00:00
8 changed files with 140 additions and 23 deletions
+45 -8
View File
@@ -1,5 +1,5 @@
# FleetDM Stack - Gitea Actions
# CI: lint on every push
# CI: lint on every push (skips docs-only changes)
# Semantic Release: auto-bump version on push to main/master
# - merge from feature/* branch → major bump
# - any other commit (fix, chore, etc.) → patch bump
@@ -12,6 +12,14 @@ on:
branches:
- main
- master
paths-ignore:
- 'docs/**'
- 'README.md'
- 'STATUS.md'
- 'AGENTS.md'
- 'TASKS.md'
- '.gitignore'
- 'djinni-*/**'
pull_request:
branches:
- main
@@ -24,7 +32,7 @@ jobs:
steps:
- name: Checkout
run: |
git clone --depth=1 https://git.produktor.io/${{ gitea.repository }}.git .
git clone --depth=1 https://${{ gitea.actor }}:${{ gitea.token }}@git.produktor.io/${{ gitea.repository }}.git .
git checkout ${{ gitea.sha }}
- name: Install Helm
@@ -48,7 +56,7 @@ jobs:
steps:
- name: Checkout (full history for tags)
run: |
git clone https://git.produktor.io/${{ gitea.repository }}.git .
git clone https://${{ gitea.actor }}:${{ gitea.token }}@git.produktor.io/${{ gitea.repository }}.git .
git fetch --tags
- name: Determine version bump
@@ -60,13 +68,11 @@ jobs:
fi
echo "Latest tag: $LATEST_TAG"
# Strip 'v' prefix and split
VER="${LATEST_TAG#v}"
MAJOR=$(echo "$VER" | cut -d. -f1)
MINOR=$(echo "$VER" | cut -d. -f2)
PATCH=$(echo "$VER" | cut -d. -f3)
# Check if this commit is a merge from a feature/* branch
COMMIT_MSG=$(git log -1 --format='%s' ${{ gitea.sha }})
echo "Commit message: $COMMIT_MSG"
@@ -74,7 +80,6 @@ jobs:
if echo "$COMMIT_MSG" | grep -qiE "^Merge.*feature/"; then
IS_FEATURE="true"
fi
# Also check parent branches for merge commits
if git log -1 --format='%P' ${{ gitea.sha }} | grep -q ' '; then
MERGE_BRANCH=$(git log -1 --format='%s' ${{ gitea.sha }} | grep -oE "feature/[^ '\"]*" || true)
if [ -n "$MERGE_BRANCH" ]; then
@@ -116,6 +121,40 @@ jobs:
mv fleetdm-stack-*.tgz .tmp/
ls -la .tmp/
- name: Mirror FleetDM image to Gitea registry
run: |
CRANE_VER="v0.20.3"
curl -fsSL "https://github.com/google/go-containerregistry/releases/download/${CRANE_VER}/go-containerregistry_Linux_x86_64.tar.gz" \
| tar -xz -C /usr/local/bin crane
APP_VER=$(grep '^appVersion:' fleetdm-stack/Chart.yaml | awk '{print $2}' | tr -d '"')
CHART_TAG="${{ steps.version.outputs.new_tag }}"
SRC="docker.io/fleetdm/fleet:v${APP_VER}"
OWNER=$(echo "${{ gitea.repository_owner }}" | tr '[:upper:]' '[:lower:]')
DST="git.produktor.io/${OWNER}/flamingo-tech-test"
crane auth login git.produktor.io -u "${{ gitea.actor }}" -p "${{ secrets.REPO_TOKEN }}"
crane copy "${SRC}" "${DST}:${APP_VER}"
crane tag "${DST}:${APP_VER}" "${CHART_TAG}"
crane tag "${DST}:${APP_VER}" "latest"
echo "Mirrored ${SRC} → ${DST}:{${APP_VER},${CHART_TAG},latest}"
- name: Trivy vulnerability scan
run: |
TRIVY_VER="0.58.2"
curl -fsSL "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VER}/trivy_${TRIVY_VER}_Linux-64bit.tar.gz" \
| tar -xz -C /usr/local/bin trivy
APP_VER=$(grep '^appVersion:' fleetdm-stack/Chart.yaml | awk '{print $2}' | tr -d '"')
OWNER=$(echo "${{ gitea.repository_owner }}" | tr '[:upper:]' '[:lower:]')
IMAGE="git.produktor.io/${OWNER}/flamingo-tech-test:${APP_VER}"
export TRIVY_USERNAME="${{ gitea.actor }}"
export TRIVY_PASSWORD="${{ secrets.REPO_TOKEN }}"
echo "Scanning ${IMAGE} for vulnerabilities..."
trivy image --severity HIGH,CRITICAL --exit-code 0 "${IMAGE}"
- name: Create tag
run: |
git config user.name "Gitea Actions"
@@ -130,7 +169,6 @@ jobs:
API="https://git.produktor.io/api/v1/repos/${{ gitea.repository }}/releases"
TOKEN="${{ gitea.token }}"
# Create release
RELEASE=$(curl -sf -X POST "$API" \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
@@ -138,7 +176,6 @@ jobs:
RELEASE_ID=$(echo "$RELEASE" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2)
echo "Created release ID: $RELEASE_ID"
# Upload chart package
for f in .tmp/*.tgz; do
FNAME=$(basename "$f")
curl -sf -X POST "$API/$RELEASE_ID/assets?name=$FNAME" \
Vendored
+1
View File
@@ -5,3 +5,4 @@ fleetdm-stack/charts/*.tgz
.helm/
*.log
/*-flamingo
.idea
Generated Vendored
+8
View File
@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
+28 -3
View File
@@ -1,4 +1,4 @@
# FleetDM Stack — Flamingo DevOps Assignment
# 🦩 FleetDM Stack
Helm chart deploying **FleetDM Server** with **MySQL** and **Redis** to Kubernetes. Suitable for local development (Kind/Minikube) and adaptable for production.
@@ -23,6 +23,8 @@ make verify
make port-forward
```
## Installation
### 1. Create local cluster
@@ -63,6 +65,10 @@ make port-forward FLEET_PORT=9090
Open **https://localhost:8585** in your browser (accept the self-signed certificate).
Fleet setup wizard will guide you through initial configuration.
![1771534082840](assets/1771534082840.png)
## Teardown
```bash
@@ -83,7 +89,7 @@ Verification checklist:
| Component | Check |
| ----------- | ----------------------------------------------------------------- |
| **FleetDM** | Pods running; `make port-forward` → https://localhost:8585 |
| **FleetDM** | Pods running; `make port-forward` → https://localhost:8585 |
| **MySQL** | `fleetdm-stack-mysql` service; Fleet connects and runs migrations |
| **Redis** | `fleetdm-stack-redis-master` service; Fleet uses it for cache |
@@ -138,6 +144,25 @@ For production, configure proper TLS and a stable DNS name for agents.
1. **Basic CI pipeline** — Gitea Actions lint on push, release on tag (see [.github/workflows/release.yaml](.github/workflows/release.yaml))
2. **Exposed Fleet UI**`make port-forward` on port 8585 (+ ingress `fleet.localhost`)
3. **`fleet prepare db`** — Handled by `autoApplySQLMigrations: true` in the Fleet Helm chart
4. **Container image mirroring** — CI mirrors the upstream `fleetdm/fleet` image to the [Gitea OCI registry](https://git.produktor.io/eSlider/-/packages) using `crane` (daemonless)
5. **Trivy vulnerability scan** — Each release scans the mirrored image for HIGH/CRITICAL CVEs
## Container Registry
The CI pipeline mirrors the FleetDM image to the Gitea container registry on every release.
```bash
# Pull the latest mirrored image
docker pull git.produktor.io/eslider/flamingo-tech-test:latest
# Pull a specific app version
docker pull git.produktor.io/eslider/flamingo-tech-test:4.80.1
# Pull by chart release tag
docker pull git.produktor.io/eslider/flamingo-tech-test:v0.0.10
```
Browse all available tags at [git.produktor.io/eSlider/-/packages/container/flamingo-tech-test/latest](https://git.produktor.io/eSlider/-/packages/container/flamingo-tech-test/latest).
## Project Structure
@@ -162,5 +187,5 @@ tech-task/
The architectural design document for "Company Inc." is in `docs/`:
- [Architecture Design Document](docs/architecture-design-company-inc.md) — 12 page design (convert to PDF for submission)
- [Architecture Design Document](docs/architecture-design-company-inc.md) — 1-2 page design (convert to PDF for submission)
- [High-Level Diagram](docs/architecture-hld.md) — Mermaid diagrams (infra, CI/CD, network security)
+1 -1
View File
@@ -85,4 +85,4 @@ Company Inc. is a small startup developing a web application and planning to dep
We value your time and want to gain a clear understanding of how you approach DevOps tasks in a clean, thoughtful, and structured manner.
**Good luck, and happy deploying\! 🦩**
**Good luck, and happy deploying! 🦩**
Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

+50 -7
View File
@@ -106,6 +106,7 @@ flowchart TD
Zero-downtime releases without duplicating infrastructure. Both versions run inside the **same GKE cluster**; the load balancer switches traffic atomically.
```mermaid
flowchart LR
LB[Load Balancer]
@@ -113,7 +114,7 @@ flowchart LR
LB -.->|0% traffic| Blue[Blue — v1.3.0<br/>new release]
Blue -.->|smoke tests pass| LB
```
---
| Phase | Action |
|-------|--------|
| **Deploy** | New version deployed to the idle slot (blue) |
@@ -124,13 +125,55 @@ flowchart LR
**Cost impact:** Near-zero — both slots share the same node pool; the idle slot consumes minimal resources until traffic is switched. Argo Rollouts automates the full lifecycle within ArgoCD.
### 4.5 Containerisation and CI/CD
### 4.5 Containerisation Strategy
#### Image Building Process
Each service (Flask backend, React frontend) has its own **multi-stage Dockerfile**:
1. **Build stage** — installs dependencies and compiles artefacts in a full SDK image (e.g. `python:3.12`, `node:20`).
2. **Runtime stage** — copies only the built artefacts into a minimal base image (e.g. `python:3.12-slim`, `nginx:alpine`). This cuts image size by 6080% and removes build tools from the attack surface.
3. **Non-root user** — the runtime stage runs as a dedicated unprivileged user (`appuser`), never as root.
4. **Reproducible builds** — dependency lock files (`requirements.txt` / `package-lock.json`) are copied and installed before application code to maximise Docker layer caching.
**Tagging convention:** images are tagged with the **git SHA** for traceability and a `latest` alias for convenience. Semantic version tags (e.g. `v1.3.0`) are added on release.
#### Container Registry Management
All container images are stored in **GCP Artifact Registry** in the `company-inc-shared` project:
- **Single source of truth** — one registry serves both staging and production via cross-project IAM pull permissions.
- **Vulnerability scanning** — Artifact Registry's built-in scanning is enabled; CI fails if critical CVEs are detected.
- **Image retention policy** — keep the latest 10 tagged images per service; automatically garbage-collect untagged manifests older than 30 days.
- **Access control** — CI service account has `roles/artifactregistry.writer`; GKE node service accounts have `roles/artifactregistry.reader`. No human push access.
*For self-hosted Git platforms (e.g. Gitea), the built-in OCI container registry can serve the same role at zero additional cost. In the practical part of this project, this is demonstrated: the CI pipeline mirrors the upstream FleetDM image to the Gitea OCI registry using `crane` (a daemonless image tool), then scans it with **Trivy** for HIGH/CRITICAL CVEs before publishing the release.*
#### Deployment Pipelines (CI/CD Integration)
The pipeline follows a **GitOps** model with clear separation between CI and CD:
| Phase | Tool | What happens |
|-------|------|-------------|
| **Lint & Test** | Gitea / GitHub Actions | Unit tests, linting, Helm lint on every push |
| **Build & Push** | Gitea / GitHub Actions | `docker build` → tag with git SHA → push to registry |
| **Security Scan** | Trivy (in CI) | Scan image for OS and library CVEs; block on critical findings |
| **Manifest Update** | CI job | Update image tag in the GitOps manifests repo (or Helm values) |
| **Sync & Deploy** | ArgoCD | Detects manifest drift → triggers blue-green rollout via Argo Rollouts |
| **Promotion** | Argo Rollouts | Automated analysis (metrics, health checks) → promote or rollback |
**Key properties:**
- **CI never touches the cluster directly** — it only builds images and updates manifests. ArgoCD is the sole deployer.
- **Rollback is instant** — revert the manifest repo to the previous commit; ArgoCD syncs automatically.
- **Audit trail** — every deployment maps to a git commit in the manifests repo.
### 4.6 CI/CD Summary
| Aspect | Approach |
|-------|----------|
| **Image build** | Dockerfile per service; multi-stage builds; non-root user |
| **Registry** | Artifact Registry in `company-inc-shared` |
| **CI** | GitHub/Gitea Actions — build, test, security scan |
| **Image build** | Multi-stage Dockerfile; layer caching; non-root; git-SHA tags |
| **Registry** | Artifact Registry in `company-inc-shared` (or Gitea built-in OCI registry) |
| **CI** | Gitea / GitHub Actions — lint, test, build, scan, push |
| **CD** | ArgoCD + Argo Rollouts — GitOps with blue-green strategy |
| **Secrets** | External Secrets Operator + GCP Secret Manager |
@@ -207,7 +250,7 @@ Not everything in a "best practices" architecture is worth implementing on day o
## 7. High-Level Architecture Diagram
```mermaid
flowchart TB
flowchart TD
Users((Users))
Users --> CDN[Cloud CDN<br/>Static Assets]
@@ -230,7 +273,7 @@ flowchart TB
API --> Mongo
API --> Secrets
GKE --> Registry
GKE ----> Registry
```
---
+7 -4
View File
@@ -29,7 +29,7 @@ flowchart TB
Blue[Blue — new release<br/>smoke tests]
end
subgraph Workloads
API[Backend — Python / Flask<br/>HPA · 23 replicas]
API[Backend — Python / Flask<br/>HPA · 2-3 replicas]
SPA[Frontend — React SPA<br/>Nginx]
end
Redis[Redis — Memorystore<br/>Session / Cache]
@@ -67,7 +67,7 @@ flowchart TB
Git --> Actions
Actions --> Registry
Argo --> GKE
Argo ----> GKE
```
## Blue-Green Deployment Flow
@@ -94,16 +94,19 @@ flowchart LR
flowchart LR
Dev[Developer] -->|push| Repo[Git Repo]
Repo -->|webhook| CI[CI Pipeline<br/>lint · test · build]
CI -->|push image| Registry[Artifact Registry]
CI -->|crane copy / docker push| Registry[Container Registry<br/>Artifact Registry / Gitea OCI]
Registry -->|scan image| Trivy[Trivy<br/>HIGH + CRITICAL CVEs]
Trivy -->|pass| CI2[Publish Release]
CI -->|update manifests| GitOps[GitOps Repo]
GitOps -->|sync| Argo[ArgoCD]
Argo -->|blue-green deploy| GKE[GKE Cluster]
GKE -->|pull image| Registry
```
## Network Security Layers
```mermaid
flowchart TD
flowchart LR
Internet((Internet)) --> FW[VPC Firewall<br/>Default deny]
FW --> LB[Load Balancer<br/>HTTPS only]
LB --> NP[K8s Network Policies]