workload-translation
Skill0 starsChanged 6 months agoApache-2.0View on GitHub
Translate Kubernetes manifests, Helm charts, and Kustomize overlays from EKS conventions to GKE conventions. Rewrites AWS-specific annotations, replaces ALB Ingress with Gateway API, swaps ebs.csi.aws.com StorageClasses to pd.csi.storage.gke.io, fixes node selectors and tolerations, removes IRSA annotations in favor of Workload Identity, and produces a clean diff per workload. Use when "translate this Helm chart for GKE", "convert manifests to GKE", or after identity-translation in a Portage migration.
1 other project uses a version of this.
Adopted by
Most recently taken up by
- GoogleCloudPlatform/cloud-solutionstheir own version, first seen Nov 8, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- GoogleCloudPlatform/cloud-solutions★ 139same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository13 other files it gives its agents.
- data-migrationSkill
- eks-discoverySkill
- gke-landing-zoneSkill
- identity-translationSkill
- migration-assessmentSkill
- network-translationSkill
- observability-translationSkill
- portage-orchestratorSkill
- post-migration-opsSkill
- registry-migrationSkill
- rollback-playbookSkill
- storage-translationSkill
- traffic-cutoverSkill
The file
We split this skill into the rules it gives an agent
11 rules. About 0 are common, 3 are distinctive, and 8 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
8
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 19
Do NOT use to change application code or container images (that's registry-migration).
- Very specific: line 136
Use the mapping file 08-registry-migration/image-map.json produced by registry-migration.
- Distinctive: line 142
If the chart is publicly maintained and offers GKE-compatible values, switch values rather than the chart.
- Very specific: line 167
If the chart is internal and embeds AWS assumptions in templates, fork the chart, mark the fork with a MIGRATION_NOTES.md documenting every …
- Very specific: line 173
Add a new overlay overlays/gke-prod/.
- Very specific: line 174
Use patches: to apply transformations rather than editing the base.
- Very specific: line 243
Every Deployment, StatefulSet, DaemonSet, Job, CronJob in inventory.json has a translated artifact OR is explicitly marked …
- Very specific: line 245
Every image reference matches an entry in image-map.json.
- Very specific: line 246
Every KSA reference matches an entry in identity-map.json.
- Distinctive: line 247
Every StorageClass reference matches a translated StorageClass.
- Distinctive: line 261
Operator CRDs missing: If an operator (e.g., Postgres operator) is in EKS, you have to install it on GKE before its CRs apply.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Workload Translation
You translate Kubernetes manifests, Helm charts, and Kustomize overlays so they run unchanged on GKE. Every change is a tracked diff with a rationale.
Purpose
Convert EKS-conventioned workload definitions to GKE-conventioned equivalents. Produce auditable per-workload diffs with rationale. Do not modify business logic.
When to use this skill
- Phase 3 of a Portage migration.
- A user asks to "translate manifests", "convert this Helm chart", "make this work on GKE".
Do NOT use to change application code or container images (that's registry-migration).
Prerequisites
01-discovery/inventory.jsonand the raw workloads YAML.04-network-translation/manifests/for Gateway/HTTPRoute targets.05-identity-translation/identity-map.jsonfor KSA → GSA mapping.
Procedure
Step 1 — Build a per-workload manifest set
For each workload, gather:
- The current YAML manifest (from discovery raw or from the source repo).
- The originating Helm chart and
values.yaml(if Helm-managed). - The originating Kustomize overlay (if Kustomize-managed).
For Helm:
helm get manifest <release> -n <ns> > <release>.rendered.yaml
helm get values <release> -n <ns> > <release>.values.yaml
helm get all <release> -n <ns> > <release>.helm-all.yamlFor Kustomize:
kubectl kustomize ./overlays/prod > overlays-prod.rendered.yamlStep 2 — Apply the translation rules
Walk every resource and apply rules. Each rule has a category (drop, replace, add, rewrite) and a rationale.
Annotations to drop
| Annotation | Action |
eks.amazonaws.com/role-arn (on ServiceAccount) | drop; replaced by iam.gke.io/gcp-service-account from identity-translation |
eks.amazonaws.com/compute-type: fargate | drop; map workload to Autopilot if cluster is Autopilot |
kubernetes.io/ingress.class: alb | drop; replaced by Gateway API resources |
alb.ingress.kubernetes.io/* | drop; covered by Gateway/HTTPRoute/HealthCheckPolicy/GCPBackendPolicy |
service.beta.kubernetes.io/aws-load-balancer-* | drop; covered by Gateway API or cloud.google.com/* annotations |
external-dns.alpha.kubernetes.io/aws-* | drop; replace external-dns config with Cloud DNS provider (handled in network-translation) |
Annotations to add
| Annotation | When |
iam.gke.io/gcp-service-account: <gsa-email> (on ServiceAccount) | When KSA appears in identity-map |
cloud.google.com/load-balancer-type: "Internal" (on LB Service) | For internal LBs replacing internal NLBs |
networking.gke.io/load-balancer-class: "regionalExternal" | For pass-through L4 external |
cloud.google.com/neg: '{"ingress": true}' | For Services backing GKE Gateway |
Resource type swaps
| Source | Target | Note |
Ingress (with alb class) | Gateway + HTTPRoute | Take from 04-network-translation/manifests/ |
Service type=LoadBalancer annotated for AWS NLB | Same Service, swap annotations | Don't change kind; rewrite the annotation set |
StorageClass with provisioner: ebs.csi.aws.com | provisioner: pd.csi.storage.gke.io | Rewrite parameters per storage-translation |
StorageClass with provisioner: efs.csi.aws.com | provisioner: filestore.csi.storage.gke.io | Rewrite per storage-translation |
EndpointSlice with addressType: IPv4 referencing AWS DNS LB | Reuse | No change |
MutatingWebhookConfiguration for aws-pod-identity-webhook | drop | Pod identity injection is native on GKE |
nodeSelector / tolerations / affinity
Map common EKS labels to GKE labels:
| EKS | GKE |
eks.amazonaws.com/nodegroup: <ng> | cloud.google.com/gke-nodepool: <pool> |
node.kubernetes.io/instance-type: m5.large | node.kubernetes.io/instance-type: e2-standard-2 (or actual GCE) |
topology.kubernetes.io/zone: us-east-1a | topology.kubernetes.io/zone: us-central1-a (target region) |
karpenter.sh/capacity-type: spot | cloud.google.com/gke-spot: "true" |
karpenter.sh/nodepool: gpu | matched node pool with the same labels (created in landing zone) |
| Bottlerocket-specific selectors | drop; COS-only on GKE |
For Karpenter NodePools: do not translate the NodePool resource. Instead, build the equivalent GKE Node Pools in the landing zone with the labels and taints captured during discovery. Then nodeSelectors in workloads continue to match.
Topology spread
topologySpreadConstraints mostly translate unchanged. Validate that topologyKey: topology.kubernetes.io/zone works against GKE's regional cluster zones.
Resource requests/limits
Translate verbatim. If targeting Autopilot, validate against Autopilot resource ranges. Workloads outside the ranges either need to move to a Standard cluster or have requests rounded to allowed values. Surface as escalation, do not silently round.
hostPath, hostNetwork, hostPID
hostPathreferencing AWS-specific paths (/var/lib/kubelet/plugins/...) — usually applies only to CSI/CNI-level DaemonSets. Validate per workload.hostNetwork: true— preserve unchanged. Confirms with discovery escalation list.hostPID: true— preserve unchanged.
In Autopilot clusters: hostNetwork, hostPID, hostPath, and most privileged options are denied. Workloads using them must run on a Standard cluster or be redesigned. This is an escalation, not a silent translation.
Pod Security
EKS cluster defaults rarely enforce PSA. GKE Autopilot enforces restricted by default. Standard clusters can configure PSA.
For each namespace:
- Set
pod-security.kubernetes.io/enforce: baseline(start) orrestricted(target). - Validate every workload manifest against the chosen level. Workloads that fail are surfaced as escalations with a list of violating fields.
Image references
Rewrite image refs to point at Artifact Registry equivalents (output of registry-migration):
123456789012.dkr.ecr.us-east-1.amazonaws.com/payments/api:1.4.2
→
us-central1-docker.pkg.dev/artifact-registry-prod/payments/api:1.4.2Use the mapping file 08-registry-migration/image-map.json produced by registry-migration. If a workload references an image not yet in that map, surface as an escalation rather than guessing.
Step 3 — Helm-specific translation
For each Helm release in scope:
- If the chart is publicly maintained and offers GKE-compatible values, switch values rather than the chart.
- Produce a delta
values.yamlwith only the changed keys, namedvalues.gke.yaml. Examples:
# values.gke.yaml — overrides for GKE
serviceAccount:
annotations:
iam.gke.io/gcp-service-account: payments-api@gke-prod-clusters.iam.gserviceaccount.com
ingress:
enabled: false # using Gateway API resources instead
gateway:
enabled: true
className: gke-l7-global-external-managed
hostnames:
- api.example.com
persistence:
storageClass: pd-balanced
nodeSelector:
cloud.google.com/gke-nodepool: payments- If the chart is internal and embeds AWS assumptions in templates, fork the chart, mark the fork with a
MIGRATION_NOTES.mddocumenting every template change, and store under06-workload-translation/charts/<chart>-gke/.
Step 4 — Kustomize-specific translation
For each Kustomize root:
- Add a new overlay
overlays/gke-prod/. - Use
patches:to apply transformations rather than editing the base. - Where possible, build a
gke-common/component (KSA annotations, NodePool selectors) that all GKE overlays import.
Step 5 — Per-workload diff and rationale
For every workload, output a side-by-side diff and a rationale list:
# Workload: storefront/web
## Changes
| File | Before | After | Rationale |
|--------------------------------|------------------------------------------|-----------------------------------------|-----------|
| Deployment.spec.template…annotations | eks.amazonaws.com/skip-* removed | (removed) | EKS-only, no GKE equivalent |
| Deployment.spec.template…serviceAccount | (none) | web | Needed for WI binding |
| ServiceAccount.metadata.annotations | eks.amazonaws.com/role-arn | iam.gke.io/gcp-service-account: … | identity-translation |
| Ingress (kind: Ingress) | spec… | DROPPED → see HTTPRoute web.yaml | Gateway API |
| StorageClass: gp3 | provisioner: ebs.csi.aws.com | provisioner: pd.csi.storage.gke.io | storage-translation |
| Container.image | …ecr…/web:1.2.0 | …pkg.dev…/web:1.2.0 | registry-migration |
| Tolerations / nodeSelector | karpenter.sh/capacity-type: spot | cloud.google.com/gke-spot: "true" | node label mapping |Step 6 — Validate
For each workload:
# Lint
kubectl --dry-run=server apply -f 06-workload-translation/manifests/<workload>/
# Server-side dry-run with PSA enforcement
kubectl --dry-run=server apply --validate=strict -f ...
# Helm template validation
helm template release-name ./chart -f values.gke.yaml | kubectl --dry-run=server apply -f -For Autopilot targets, also run:
gcloud container clusters describe <cluster> --location <region> \
--format='value(autopilot.enabled)'
# If true: validate workloads don't use forbidden features.Decision points
| Decision | Default | When to deviate |
| Fork Helm chart vs values overlay | Values overlay | Fork only when the chart blocks WI annotations or uses AWS-only resources |
| Convert Ingress to HTTPRoute now vs at cutover | At translation time | At cutover if the user wants to ship Ingress first and Gateway later |
| Default PSA level | baseline enforced, restricted warned | restricted enforced if compliance requires |
| Spot tolerations | Translate karpenter.sh/capacity-type=spot to cloud.google.com/gke-spot=true | Preserve mixed-instance with affinity if cost-sensitive |
Outputs / Deliverables
06-workload-translation/
├── manifests/ # Per-workload translated YAML
│ └── <namespace>/<workload>/
├── charts/ # Forked Helm charts (only when forked)
├── values/ # values.gke.yaml per release
├── kustomize/ # gke overlays
├── diffs/ # Diff + rationale per workload (Markdown)
├── psa-violations.md # Workloads that fail target PSA level
└── escalations.mdValidation
- Every Deployment, StatefulSet, DaemonSet, Job, CronJob in
inventory.jsonhas a translated artifact OR is explicitly markedintentionally-not-migrated(e.g., AWS-only DaemonSet that has no GKE equivalent). kubectl --dry-run=serverpasses for every translated manifest against the target cluster.- Every image reference matches an entry in
image-map.json. - Every KSA reference matches an entry in
identity-map.json. - Every StorageClass reference matches a translated StorageClass.
Escalation triggers
- Workload uses an admission webhook that doesn't have a GKE-compatible version (e.g.,
aws-pod-identity-webhook, custom webhooks tied to AWS auth). - Workload references a CRD whose operator isn't translated (still on EKS).
- Autopilot target with workloads outside resource bounds.
- Helm chart with templates that conditionally render AWS-only resources and don't have a flag to disable.
Common pitfalls
- Translating image refs by string-replace in YAML. Some manifests embed images in non-obvious places (init containers, sidecars in CRD specs, JSON-encoded strings). Walk the entire YAML tree.
- Forgetting CronJob backoff differences. GKE Autopilot warns/denies certain CronJob configurations.
- PodTopologySpread + cluster autoscaler. Underprovisioned clusters with strict spread will leave pods Pending. Tune CA's max bounds first.
- Operator CRDs missing. If an operator (e.g., Postgres operator) is in EKS, you have to install it on GKE before its CRs apply. Surface in the workload-translation phase, not at apply time.
- Karpenter NodePools do not translate to GKE. Capture taints/labels and rebuild as GKE node pools (or NAP rules) in
gke-landing-zone. The community Karpenter-on-GCP provider exists but is not production-ready. See LFF-16. - Workloads tuned to Karpenter's seconds-class scale-up will queue under NAP. Increase HPA
minReplicasand tune target utilization down for these workloads. See LFF-17. - App Mesh retirement. App Mesh retires 2026-09-30; target for a GKE move is Cloud Service Mesh. See LFF-29.
References
- Canonical sources: reference/sources.md.
- Autopilot resource requests/limits — the deny list for Autopilot.
- Pod Security Standards on GKE.
- Removed Kubernetes APIs by version — manifest validity check.
- Migrate containers to Google Cloud: Migrate from Kubernetes to GKE.
- identity-translation
- storage-translation
- registry-migration
- reference/api-translation.md — annotation-by-annotation map.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

