Files
devops-lib-gcp/docs/architecture.md
T
2026-08-26 02:02:24 +05:30

255 lines
13 KiB
Markdown

<!--
Auto-generated by /meesho-init Phase 8.
Long-form architecture for devops-lib. CLAUDE.md links here but does not
duplicate the contents.
-->
# devops-lib — Architecture
A Jenkins shared library that every Meesho service repo consumes via
`@Library('devops-lib') _` in its `Jenkinsfile`. It encapsulates the
per-language build, the Helm/ArgoCD deploy ceremony, secret + key handling,
notification routing, and security-scan integration.
## 1. High-level shape
```
service Jenkinsfile (lives in service repo)
└─ buildPipeline { repo_name: ..., build_tool: ..., maintainer: ... }
┌───────────────────────────────────┘
vars/<entry>.groovy ← global step
│ delegates to
src/com/meesho/stages/<stage>.groovy ← stage object
│ uses
src/com/meesho/utilities/<helper>.groovy
│ loads
resources/com/meesho/<template>.{yaml,Dockerfile}
```
### Module boundaries
| Directory | Owns | Outside callers |
|---|---|---|
| `vars/` | Public Jenkins global steps (the "API") | Service Jenkinsfiles |
| `src/com/meesho/stages/` | Stage logic — build, deploy, notify, security scan | `vars/` |
| `src/com/meesho/utilities/` | Pure helpers — git, params, node-pool selection, templating | Stages |
| `resources/com/meesho/` | Per-language Dockerfile + Helm template + values.yaml | Stages (rendered into service workspace) |
| `resources/org/meesho/` | Jenkins agent pod templates (`*-pod.yaml`) | `vars/` (via `libraryResource`) |
| `resources/com/meesho/validate_configs.py` | Helm-values schema validator (monolithic Python) | Stages (invoked via `sh`) |
### Architectural philosophy signals
- **Convention over configuration.** Almost every per-service behaviour is
encoded in `vars/<entry>.groovy` + the language switch in
`buildObjHelper.groovy`. New tech stacks require new files, not new flags.
- **CPS-aware Groovy.** Helpers that touch non-serialisable Java APIs (regex
engines, template engines) sit behind `@NonCPS` boundaries
(`constructTemplate._construct`). Cross those boundaries inside a
`parallel` block and you'll get a serialisation error in production.
- **Per-call freshness vs caching.** `getWhitelistedRepos()` re-clones
`Meesho/whitelists` on every invocation (no caching). The five whitelist
checks each clone independently. This is by design: a DevOps whitelist
change must take effect on the *next* build without a library release.
- **Hard-coded `'Meesho'` org.** Many helpers embed the org as a literal —
parametrising it has knock-on effects across every service Jenkinsfile.
- **Single Python file.** `validate_configs.py` (1207 lines) holds every
CAC/values-schema rule; splitting it has been flagged
([BUGS_AND_IMPROVEMENTS_REPORT §10](../BUGS_AND_IMPROVEMENTS_REPORT.md)).
## 2. Entry points (vars/)
| `vars/<file>.groovy` | Used by | Notes |
|---|---|---|
| `buildPipeline.groovy` | The legacy entry — service `Jenkinsfile` calls `buildPipeline { ... }` | Pins to `node('slave02')` (hard-coded — flagged) |
| `eksCICD.groovy` | EKS-targeted services | Calls `commonCICDFlow()` |
| `gkeCICD.groovy` | GKE-targeted services | Mirror of `eksCICD` for GCP |
| `cloudFunctionCICD.groovy` | GCP cloud-function deploys | Currently a stub (`sh 'ls -al'; echo 'Hello World'` — flagged in BUGS report) |
| `onlyPushtoJfrog.groovy` | One-off artifact push without full pipeline | JDK 11 / JDK 21 selections fall through to JDK 8 (flagged) |
| `createEKSconfigs.groovy` | Bootstrap EKS pod-template configs | |
| `gcpMigration.groovy` | One-off migration helper | |
| `log.groovy` | `log.info` wrapper used across stages | Wraps `echo` — there is no real logger |
| `stageName.groovy` | Stage-name helper | |
| `buildDockerGroovyGke.groovy` | Docker build helper for GKE | |
## 3. Stage layer (src/com/meesho/stages/)
`buildObjHelper.groovy:run(String build_tool)` is the dispatch switch:
```
build_tool → Stage class
─────────────────────────────────
maven → buildMaven
maven-* → buildMaven
gradle → buildGradle
docker → buildDocker
python-* → buildPython
node-* → buildNode
go* → buildGo
php → buildPhp
<other> → defaultBuild (silent no-op — flagged)
```
Other stage files (orthogonal to the build dispatch):
- `checkOut.groovy` — Jenkins SCM checkout. Contains a method named
`chekoutSubmodule` (misspelled — flagged) at line 20.
- `deployArgoCD.groovy` — the deploy ceremony.
- `deployRingmaster.groovy` — Ringmaster / Turbo-Turtle callback.
- `deployJar.groovy` — non-container artifact deploy.
- `notify.groovy` — Slack notification + tracking-API callback. Reads
`config.maintainer` and `config.notify_channel` (defaults to
`ci-cd-status`).
- `securityScan.groovy` — invokes the in-house scanner. Hard-codes
`final String url = '172.31.5.29:63232/scans'` (P0 violation — flagged).
- `automationTest.groovy` — integration-test trigger.
- `hotFix.groovy` — sets `env.hot_fix = true` to skip Sonar / quality gate.
- `helmGenerator.groovy` — renders Helm values; line 103 contains a typo
(`catch (Exceptione)` — flagged).
- `multiBranchPipeline.groovy` — wires `GitHubSCMSource`.
### deployArgoCD step order (load-bearing)
```
update_argo_repo (push new app definition)
refresh_app_of_apps (sync app-of-apps so the new Application object exists)
update_helm_repo (compute xms/xmx from memory_limit, push values)
refresh_and_sync (per-service sync)
```
Steps 2 and 4 are not interchangeable — see [tribal-knowledge §10](tribal-knowledge.md).
Canary enforcement (lines 408-430): if `priority_v2 ∈ {sp0, up0}` and
`envrn == 'prd'`, the deploy fails fast unless canary is properly
configured (`canary.enabled=true`, `skipAnalysis=false`,
`enableManualPromotion=true`). No whitelist or bypass.
JVM memory (lines 220-255): `xms = xmx = memory_limit * 0.5`. There is no
`memory_request * 0.75` formula, no 64m rounding (the older claim in
tribal-knowledge has been reconciled).
## 4. Utilities (src/com/meesho/utilities/)
| File | Owns |
|---|---|
| `constructParam.groovy` | The big one. Loads CAC config, sets `env.BU`, `env.GCPProject`, `env.GCPLBProject`, computes `cicd_environment`, runs the five whitelist gates (`skip-sonar`, `app-config-disabled`, `multizone-enabled`, `allowedNonDevelopPrDeployment`, `ValidateCacConfig`). |
| `gitActions.groovy` | Clone, fetch, status — hard-codes the `Meesho` GitHub org in clone URLs and PR-merge URLs. |
| `nodePoolSelection.groovy` | Maps `cicd_environment` → node-pool selector. `dev`/`ftr`/`stg` all collapse to `${BU}-shared`. |
| `constructTemplate.groovy` | `@NonCPS` template engine wrapper over `SimpleTemplateEngine`. |
| `addSSHKey.groovy` | Writes a Jenkins SSH credential to `./id_github_jenkins`. **Does not** cat the key to stdout (reconciled — the historical PR #634 leak has been remediated). |
| `getDockerParams.groovy` | Helper to assemble `docker run` bindings. |
| `getYamlParameter.groovy` | Reads a single key from a YAML file. |
| `validateBuTeam.groovy` | Cross-checks the BU against team ownership. |
## 5. Resources
### `resources/com/meesho/`
Templates rendered into the service workspace:
- Per-language `Dockerfile` (`java-Dockerfile`, `go-Dockerfile`,
`node-Dockerfile`, `php-Dockerfile`, `python-{2.7,3.7,3.10.12,3.13}-Dockerfile`)
- Per-language Helm values (`go-values.yaml`, `node-values.yaml`,
`python-values.yaml`, `php-values.yaml`, `values.yaml`, `cron-values.yaml`)
- Per-language deployment manifests (`deployment.yaml`, `go-deployment.yaml`,
`node-deployment.yaml`, `php-deployment.yaml`, `python-deployment.yaml`,
`gradle-deployment.yaml`)
- ArgoCD Application template (`argoApp.yaml`)
- `Dockerfile` and `Jenkinsfile` fallbacks (rare path)
- `config.yaml` — default service shape consumed by `constructParam`
- `validate_configs.py` / `validate_configs_v2.py` — the schema validator
(v2 is the eventual replacement; both are referenced today)
### `resources/org/meesho/`
- `dev-pod.yaml`, `stg-pod.yaml`, `prd-pod.yaml` — Jenkins agent pod
templates loaded via `libraryResource("org/meesho/${env.INFRA_ENV}-pod.yaml")`.
- `templates/maven-3.3-jdk-8.sh`, `templates/node-12.22.sh` — bootstrap
shell scripts copied into the agent.
## 6. Downstream services
| Caller | Downstream | Endpoint / mechanism | Resilience |
|---|---|---|---|
| `deployArgoCD.groovy` | ArgoCD | `argocd login ${env.argoURL}:443 --grpc-web`, `argocd app sync`, `argocd app refresh` | `--http-retry-max 3 --retry-backoff-duration 1m` |
| `deployRingmaster.groovy` | Ringmaster *or* Turbo-Turtle | `POST http://turbo-turtle.meeshogcp.in/...` (chosen by `getUserId() == "ringmaster-bot"`) | None — direct `curl` |
| `notify.groovy` | Slack | `slackSend channel: ..., message: ...` | None |
| `notify.groovy` | Deployment Tracker | `POST https://deployment-tracker.meeshoint.in/...` (and `.prd.meesho.int`) | None |
| `buildMaven.groovy` / `buildNode.groovy` / `buildGo.groovy` | JFrog | `mvn deploy` / `npm publish` / artifact upload | Branch-gated: master, main, gcp-main, gcp-master |
| `buildMaven.groovy` / `buildGradle.groovy` etc. | S3 | `aws s3 cp …` | Same branch gate |
| `buildNode.groovy` | Docker registry | `docker push` | `retryDockerPush` retry wrapper |
| `buildMaven.groovy` etc. | SonarQube | `withSonarQubeEnv { … }``sonarqube-prd` | Skipped via `skip-sonar-whitelist.yaml` for Maven prd |
| `constructParam.groovy` | Vault | `vault-prd.meeshogcp.in`, `vault-dev.meeshogcp.in` | None |
| `securityScan.groovy` | In-house scanner | `POST http://172.31.5.29:63232/scans` (P0 — should be DNS, flagged) | None |
## 7. Critical invariants & gotchas
(Most are also enumerated in [docs/tribal-knowledge.md](tribal-knowledge.md);
this section captures the ones that change the *shape* of the code.)
1. **`config.yaml` is read once by `constructParam.run()`.** Everything
downstream reads from `env.*` it set. Don't introduce a second config
read; mutate `env.*` instead.
2. **`hot_fix` shortcuts.** `env.hot_fix = true` (set by `hotFix.groovy:11`)
skips Sonar, quality gates, and several validation steps in
`buildMaven` / `buildGo`. Use it deliberately, not as a "skip everything"
knob.
3. **`branch_name = 'repo'`** appears as a hard-coded string in
`buildGradle.groovy:252,285` (flagged in BUGS report). Do not assume
`branch_name` is dynamic — the comparison against `'master'/'main'`
never matches there.
4. **`rm -rf *`** appears in six locations across `buildPython`, `buildMaven`,
`buildGradle` (flagged). Be aware of the working directory when adding
stages near these — there is no directory guard.
5. **`generic catch (Exception e)`** is used ~87 times across stages
(flagged). New code should prefer typed exceptions, but existing handlers
suppress everything — be cautious assuming a stage "succeeded".
## 8. Configuration touch points
| Config | Where read | Drives |
|---|---|---|
| `config.bu` | `constructParam:178,255,308,318` | Helm chart path, ArgoCD namespace, GCP project name |
| `env.INFRA_ENV` | `eksCICD:57`, `createEKSconfigs:5`, `onlyPushtoJfrog:4` | Jenkins agent pod template |
| `env.CHANGE_ID` | `constructParam:107-110` | PR vs branch detection; `cicd_environment` remap (`prd→int` for main/master PRs, `stg→ftr` for develop PRs) |
| `env.hot_fix` | `buildGo:25-28`, `buildMaven:37-40`, `hotFix:11` | Skip Sonar / quality gate |
| `param.build_tool` | `buildObjHelper:run`, `vars/buildPipeline:12` | Stage class selection |
| `param.maintainer` | `notify:9,31` | Slack mention |
| `param.skip_test` | `buildMaven:17,206-208` | Maven `-DskipTests` |
| `param.skip_sonar` | `buildMaven:18,240-243` | Sonar bypass |
| `param.skip_security_scan` | `securityScan:7-9` | Security scan bypass |
| `param.skip_notify` | `notify:10,26-28` | Slack bypass |
| `param.notify_channel` | `notify:11` | Slack channel (default `ci-cd-status`) |
| `param.push_to_jfrog` | `buildMaven:19,377` | Allow non-default-branch JFrog push |
| `param.push_to_s3` | `buildMaven:20,462` | Allow non-default-branch S3 push |
## 9. What's missing (and known)
`BUGS_AND_IMPROVEMENTS_REPORT.md` is the authoritative catalogue. Highlights
that affect *how* you should approach changes:
- **No test suite** (P0). Don't fabricate test commands.
- **No retry on most stages** (P1) — only ArgoCD sync and Docker push retry.
- **Hard-coded IPs** (P0) — `securityScan.groovy:11` is a known violation
pending remediation.
- **Inconsistent logging** — mix of `log.info()` (which is a thin wrapper)
and bare `echo`. Prefer `log.info` for new code.
- **Commented-out blocks** — `buildPython:13-38`, `buildNode:27-31`,
`buildGradle:470-477` carry large dead sections. Don't extend them; if a
block is genuinely dead, delete it in a separate PR.
## See also
- [`docs/acronyms.md`](acronyms.md)
- [`docs/tribal-knowledge.md`](tribal-knowledge.md)
- [`../review-learnings.md`](../review-learnings.md)
- [`../BUGS_AND_IMPROVEMENTS_REPORT.md`](../BUGS_AND_IMPROVEMENTS_REPORT.md)