Objective
Kubernetes removes deprecated API versions with minor version upgrades. Applying manifests with removed APIs to an upgraded cluster results in errors. Pluto detects these proactively — before the upgrade — so you can fix them first. This exercise scans both live cluster resources and local manifest files, and walks through the corrections needed for a 1.27 → 1.29 upgrade.
Prerequisites
- Pluto CLI installed:
brew install fairwinds-ops/tap/plutoor download from github.com/FairwindsOps/pluto - kubectl configured to a cluster (can be any version)
- A directory of Kubernetes YAML manifests to scan
Common API Deprecations: 1.27 → 1.29
| Resource | Deprecated API | Current API | Removed In |
|---|---|---|---|
| HorizontalPodAutoscaler | autoscaling/v2beta2 | autoscaling/v2 | 1.26 |
| CronJob | batch/v1beta1 | batch/v1 | 1.25 |
| PodDisruptionBudget | policy/v1beta1 | policy/v1 | 1.25 |
| PodSecurityPolicy | policy/v1beta1 | (removed — use PSS) | 1.25 |
| EndpointSlice | discovery.k8s.io/v1beta1 | discovery.k8s.io/v1 | 1.25 |
| RuntimeClass | node.k8s.io/v1beta1 | node.k8s.io/v1 | 1.25 |
Steps
01
Create sample deprecated manifests for testing
# Create a directory with deprecated API manifests mkdir deprecated-manifests # bad-hpa.yaml — uses deprecated autoscaling/v2beta2 cat > deprecated-manifests/bad-hpa.yaml << 'EOF' apiVersion: autoscaling/v2beta2 kind: HorizontalPodAutoscaler metadata: name: my-hpa namespace: default spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: my-app minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 EOF # bad-pdb.yaml — uses deprecated policy/v1beta1 cat > deprecated-manifests/bad-pdb.yaml << 'EOF' apiVersion: policy/v1beta1 kind: PodDisruptionBudget metadata: name: my-pdb spec: minAvailable: 1 selector: matchLabels: app: my-app EOF # bad-cronjob.yaml — uses deprecated batch/v1beta1 cat > deprecated-manifests/bad-cronjob.yaml << 'EOF' apiVersion: batch/v1beta1 kind: CronJob metadata: name: my-cronjob spec: schedule: "0 0 * * *" jobTemplate: spec: template: spec: containers: - name: job image: busybox restartPolicy: OnFailure EOF
02
Scan manifest files with Pluto
# Scan all YAML files in the directory pluto detect-files \ -d deprecated-manifests/ \ --target-versions k8s=v1.29.0 # Expected output: # NAME KIND VERSION REPLACEMENT REMOVED DEPRECATED # my-hpa HorizontalPodAutoscaler autoscaling/v2beta2 autoscaling/v2 false true # my-pdb PodDisruptionBudget policy/v1beta1 policy/v1 true true # my-cronjob CronJob batch/v1beta1 batch/v1 true true # Get detailed output in wide format pluto detect-files \ -d deprecated-manifests/ \ --target-versions k8s=v1.29.0 \ -o wide # Output as JSON for pipeline integration pluto detect-files \ -d deprecated-manifests/ \ --target-versions k8s=v1.29.0 \ -o json | jq '.items[] | {name:.Name, removed:.Removed, replacement:.Replacement}'
03
Scan live cluster resources
Scanning the cluster catches resources that were deployed with old manifests and are still running with deprecated API versions tracked in etcd.
# Apply the deprecated manifests to test scanning in-cluster kubectl apply -f deprecated-manifests/ # Note: Some will succeed (deprecated but not removed in your K8s version) # Some may fail if you're on 1.25+ and the API is removed # Scan all cluster resources for deprecated APIs pluto detect-all-in-cluster \ --target-versions k8s=v1.29.0 # Scan specific Helm releases pluto detect-helm \ --target-versions k8s=v1.29.0 # Output: shows which Helm releases contain deprecated resources # RELEASE NAMESPACE NAME KIND VERSION REMOVED DEPRECATED # Count issues by severity pluto detect-all-in-cluster \ --target-versions k8s=v1.29.0 \ -o json | python3 -c " import json, sys data = json.load(sys.stdin) removed = [i for i in data.get('items',[]) if i.get('Removed')] deprecated = [i for i in data.get('items',[]) if i.get('Deprecated') and not i.get('Removed')] print(f'Removed APIs (blocking): {len(removed)}') print(f'Deprecated APIs (warning): {len(deprecated)}') "
04
Fix the deprecated manifests
# Fixed HPA — autoscaling/v2beta2 → autoscaling/v2 cat > deprecated-manifests/bad-hpa.yaml << 'EOF' apiVersion: autoscaling/v2 # FIXED: was autoscaling/v2beta2 kind: HorizontalPodAutoscaler metadata: name: my-hpa namespace: default spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: my-app minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 EOF # Fixed PDB — policy/v1beta1 → policy/v1 cat > deprecated-manifests/bad-pdb.yaml << 'EOF' apiVersion: policy/v1 # FIXED: was policy/v1beta1 kind: PodDisruptionBudget metadata: name: my-pdb spec: minAvailable: 1 selector: matchLabels: app: my-app EOF # Fixed CronJob — batch/v1beta1 → batch/v1 cat > deprecated-manifests/bad-cronjob.yaml << 'EOF' apiVersion: batch/v1 # FIXED: was batch/v1beta1 kind: CronJob metadata: name: my-cronjob spec: schedule: "0 0 * * *" jobTemplate: spec: template: spec: containers: - name: job image: busybox restartPolicy: OnFailure EOF
05
Validate the fixes with Pluto
# Re-scan — should show no deprecated APIs pluto detect-files \ -d deprecated-manifests/ \ --target-versions k8s=v1.29.0 # Expected: "There were no resources found with known deprecations." # Verify manifests are valid Kubernetes YAML kubectl apply -f deprecated-manifests/ --dry-run=client # Expected: no errors (dry-run validates against current API server) # Add Pluto to your CI pipeline to prevent future regressions # GitHub Actions step example: # - name: Pluto API deprecation check # run: | # pluto detect-files -d k8s/ --target-versions k8s=v1.29.0 # pluto detect-helm --target-versions k8s=v1.29.0
Success Criteria
Further Reading
- Pluto documentation — pluto.docs.fairwinds.com
- Kubernetes deprecation policy — kubernetes.io/docs/reference/using-api/deprecation-policy
- Kubernetes version skew policy — kubernetes.io/releases/version-skew-policy
- API migration guide 1.29 — kubernetes.io/docs/reference/using-api/deprecation-guide