Compare commits
1 Commits
5ccd317bec
...
feat/proxy
| Author | SHA1 | Date | |
|---|---|---|---|
| 20ffba604b |
@@ -83,17 +83,7 @@
|
|||||||
"Bash(kubectl -n egress-proxies-operator-system get pods -o wide)",
|
"Bash(kubectl -n egress-proxies-operator-system get pods -o wide)",
|
||||||
"Bash(kubectl -n egress-proxies-operator-system get deploy egress-proxies-operator-controller-manager -o jsonpath='{.spec.template.spec.containers[0].args}')",
|
"Bash(kubectl -n egress-proxies-operator-system get deploy egress-proxies-operator-controller-manager -o jsonpath='{.spec.template.spec.containers[0].args}')",
|
||||||
"Bash(kubectl -n egress-proxies-operator-system logs deploy/egress-proxies-operator-controller-manager)",
|
"Bash(kubectl -n egress-proxies-operator-system logs deploy/egress-proxies-operator-controller-manager)",
|
||||||
"Bash(python3 -c \"import json; d=json.load\\(open\\('docs/deploy/sa_key.json'\\)\\); print\\(d.get\\('type'\\), d.get\\('client_email'\\)\\)\")",
|
"Bash(python3 -c \"import json; d=json.load\\(open\\('docs/deploy/sa_key.json'\\)\\); print\\(d.get\\('type'\\), d.get\\('client_email'\\)\\)\")"
|
||||||
"Bash(tea pr *)",
|
|
||||||
"Bash(git worktree *)",
|
|
||||||
"Bash(python3 -c \"import yaml; yaml.safe_load\\(open\\('.gitea/workflows/build.yaml'\\)\\); print\\('YAML OK'\\)\")",
|
|
||||||
"Bash(ruby -ryaml -e \"YAML.load_file\\('.gitea/workflows/build.yaml'\\); puts 'YAML OK'\")",
|
|
||||||
"Bash(git -C /Users/jan.novak/srv/go/egress-proxies-operator status --short --branch)",
|
|
||||||
"Bash(git -C /Users/jan.novak/srv/go/egress-proxies-operator log --oneline -1)",
|
|
||||||
"Bash(git -C /Users/jan.novak/srv/go/egress-proxies-operator tag 0.01)",
|
|
||||||
"Bash(git -C /Users/jan.novak/srv/go/egress-proxies-operator push origin 0.01)",
|
|
||||||
"Bash(chmod +x docs/demo/run-demo.sh)",
|
|
||||||
"Bash(bash -n docs/demo/run-demo.sh)"
|
|
||||||
],
|
],
|
||||||
"additionalDirectories": [
|
"additionalDirectories": [
|
||||||
"/Users/jan.novak/srv/go/egress-proxies-operator/.claude",
|
"/Users/jan.novak/srv/go/egress-proxies-operator/.claude",
|
||||||
|
|||||||
56
README.md
56
README.md
@@ -136,62 +136,6 @@ Cloud-init from a Secret: the Secret **must** carry the label
|
|||||||
labelled Secrets, so an unlabelled one is invisible (the Proxy reports
|
labelled Secrets, so an unlabelled one is invisible (the Proxy reports
|
||||||
`CloudInitError`). Rotating the Secret's content triggers VM replacement.
|
`CloudInitError`). Rotating the Secret's content triggers VM replacement.
|
||||||
|
|
||||||
## Providers
|
|
||||||
|
|
||||||
`Managed` proxies are provisioned by a provider — a small compute backend
|
|
||||||
behind one minimal interface. Providers are configured in the
|
|
||||||
`--providers-config` YAML file as **named instances**: `spec.provider` on a
|
|
||||||
Proxy refers to an entry's `name`, not its `type`, so `gcp-eu` and `gcp-us`
|
|
||||||
can be two differently-configured instances of the same `gcp` type (see
|
|
||||||
[config/samples/providers-config.yaml](config/samples/providers-config.yaml)).
|
|
||||||
|
|
||||||
### Implemented providers
|
|
||||||
|
|
||||||
| Type | Creates | Per-instance config | Notes |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `kubernetes` | A real Squid pod (`ubuntu/squid:6.6-24.04_edge` by default) in the same cluster the operator runs in | `image` (optional) | Needs no cloud account — local dev, CI, and the kind quickstart. Pods share the cluster's egress IP, so it exercises the full lifecycle but not distinct egress paths. |
|
|
||||||
| `gcp` | A Compute Engine VM with an ephemeral external IP | `project` (required), `network`, `networkTag`, `diskSizeGb` | The real egress fleet. Deliberately uses only four API calls (Insert / Get / Delete / AggregatedList), all fire-and-forget: `Create` returns as soon as the operation is submitted and the reconciler discovers progress by polling `Get`. Auth is Application Default Credentials — workload identity in-cluster, `gcloud` ADC locally; no key-file plumbing. |
|
|
||||||
|
|
||||||
### Adding a provider
|
|
||||||
|
|
||||||
A new backend (Hetzner, AWS, ...) is four pieces; the contract lives in
|
|
||||||
[internal/provider/provider.go](internal/provider/provider.go):
|
|
||||||
|
|
||||||
1. **Implement the 4-method `Provider` interface** in a new
|
|
||||||
`internal/provider/<type>/` package:
|
|
||||||
- `Create` submits and returns — it never blocks until the VM runs, and
|
|
||||||
must be idempotent keyed on `req.Name` (a deterministic name derived
|
|
||||||
from the Proxy's UID), so a repeat call after a crash finds the
|
|
||||||
existing instance instead of duplicating it.
|
|
||||||
- `Get` returns `provider.ErrNotFound` as a *normal* outcome — the
|
|
||||||
reconciler branches on it for replacement and adoption, so don't
|
|
||||||
treat it as exceptional.
|
|
||||||
- `Delete` is idempotent: deleting an already-gone instance is not an
|
|
||||||
error.
|
|
||||||
- `ListByTag` returns every instance the operator ever tagged, for
|
|
||||||
orphan GC.
|
|
||||||
2. **Tag every created resource** with `LabelManaged=true` and
|
|
||||||
`LabelUID=<Proxy UID>`, and report `CreatedAt` — orphan GC relies on
|
|
||||||
all three to find owned resources and skip in-flight creates.
|
|
||||||
3. **Classify every returned error** with `provider.Wrap` into the
|
|
||||||
four-sentinel taxonomy in
|
|
||||||
[internal/provider/errors.go](internal/provider/errors.go)
|
|
||||||
(`ErrNotFound` / `ErrQuotaExceeded` / `ErrTransient` / `ErrPermanent`)
|
|
||||||
— the reconciler decides retry, slow backoff, or latching `Failed`
|
|
||||||
purely from that classification, never from provider-specific types.
|
|
||||||
4. **Wire it up**: add a type-specific config block in
|
|
||||||
[internal/provider/config.go](internal/provider/config.go), and
|
|
||||||
register the constructor in the builtins map in
|
|
||||||
[cmd/main.go](cmd/main.go) (`"<type>": <pkg>.New`). The registry
|
|
||||||
([internal/provider/registry](internal/provider/registry/registry.go))
|
|
||||||
handles named instances, and the metrics wrapper is applied
|
|
||||||
automatically.
|
|
||||||
|
|
||||||
Test against a fake API seam rather than the real cloud — see the
|
|
||||||
`instancesAPI` seam in
|
|
||||||
[internal/provider/gcp/gcp.go](internal/provider/gcp/gcp.go) for the
|
|
||||||
pattern.
|
|
||||||
|
|
||||||
## Caveats — read these two
|
## Caveats — read these two
|
||||||
|
|
||||||
**Changing a proxy changes its IP.** Proxies are immutable cattle: editing
|
**Changing a proxy changes its IP.** Proxies are immutable cattle: editing
|
||||||
|
|||||||
Reference in New Issue
Block a user