Date: 2025-12-02
Scope: All staging modules in staging/src/k8s.io/
This report provides a comprehensive analysis of cross-dependencies between Kubernetes staging modules to identify opportunities for reducing coupling and module footprint. The analysis goes beyond Issue #127889 to examine the entire staging ecosystem.
Key Findings:
- apimachinery is properly isolated - Only depends on klog, kube-openapi, and k8s.io/utils
- Utility packages are heavily shared - util/sets (198 imports), util/runtime (207 imports), util/wait (106 imports)
- Constant imports are a minor issue - Only a few places import packages just for constants
- Generated code adds complexity - Apply configurations and validation code pull in extra dependencies
- component-base ↔ client-go has bidirectional coupling - Metrics adapters create coupling
- No circular dependencies found - The layering is mostly clean
| Module | apimachinery imports | api imports | client-go imports | Other k8s.io |
|---|---|---|---|---|
| apimachinery | - (self) | 0 | 0 | klog, kube-openapi, utils |
| api | 498 | - (self) | 0 | klog only |
| client-go | 2137 | 1800+ | - (self) | klog, kube-openapi, utils |
| apiserver | 1168 | 200+ | 400+ | component-base, kms, klog |
| component-base | 54 | 0 | 5 | klog, utils |
| kubectl | 546 | 200+ | 300+ | cli-runtime, metrics, component-base |
Level 0 (Foundation):
┌─────────────────────────────────────────────────────────────────┐
│ apimachinery │
│ (depends only on: klog, kube-openapi, k8s.io/utils) │
└─────────────────────────────────────────────────────────────────┘
│
Level 1: │
┌───────────┬───────────────────┼───────────────────┬─────────────┐
│ api │ kms │ cri-api │ mount-utils │
│ │ │ │ externaljwt │
└─────┬─────┴─────────┬─────────┴─────────┬─────────┴─────────────┘
│ │ │
Level 2: │ │
┌─────┴───────────────┴───────────────────┴───────────────────────┐
│ client-go │
│ code-generator, cluster-bootstrap │
└─────────────────────────────┬───────────────────────────────────┘
│
Level 3: │
┌─────────────────────────────┴───────────────────────────────────┐
│ component-base, component-helpers, cli-runtime │
│ cri-client, metrics │
└─────────────────────────────┬───────────────────────────────────┘
│
Level 4+: │
┌─────────────────────────────┴───────────────────────────────────┐
│ apiserver, kubectl, kubelet, kube-scheduler, cloud-provider │
│ controller-manager, kube-aggregator, apiextensions-apiserver │
└─────────────────────────────────────────────────────────────────┘
These packages from k8s.io/apimachinery/pkg/util/* are imported across staging:
| Package | Import Count | Description | Reduction Potential |
|---|---|---|---|
util/runtime |
207 | Error handling, panics, finalizers | Low - fundamental |
util/sets |
198 | Generic set implementations | Low - fundamental |
util/validation/field |
192 | Field-level validation errors | Low - validation infra |
util/managedfields |
167 | Server-side apply logic | Low - core feature |
util/wait |
106 | Polling, backoff, conditions | Low - fundamental |
util/errors |
71 | Error aggregation | Low - fundamental |
util/intstr |
57 | IntOrString type | None - API type |
util/version |
38 | Semantic versioning | Medium - could use stdlib |
util/net |
38 | Network utilities | Low - specialized |
util/validation |
32 | Validation functions + constants | Medium - constants could be extracted |
| Module | Files | Notes |
|---|---|---|
| apiserver | 42 | Heavy use expected |
| kubectl | 30 | Heavy use expected |
| code-generator | 23 | Template generation |
| apimachinery | 17 | Defines the package |
| client-go | 15 | Reasonable |
| dynamic-resource-allocation | 14 | New feature area |
| pod-security-admission | 12 | Policy evaluation |
| apiextensions-apiserver | 9 | CRD handling |
| Others | <10 each | Expected |
Assessment: The sets package is fundamental and appropriately used. No reduction opportunity.
| Module | Files | Primary Usage |
|---|---|---|
| kubectl | 13 | Validation functions + constants |
| apiserver | 7 | Validation functions + constants |
| apimachinery | 4 | Defines the package |
| apiextensions-apiserver | 3 | CRD validation |
| api | 3 | Constants only |
| kube-aggregator | 1 | Validation functions |
| client-go | 1 | Validation functions |
Assessment: Only k8s.io/api uses this package purely for constants - this is the Issue #127889 pattern.
Severity: Low Effort: Low
Packages imported only to access simple constants that could be hardcoded:
| Location | Constant | Value | Source |
|---|---|---|---|
api/resource/v1/types.go:265 |
DNS1123SubdomainMaxLength |
253 | RFC 1123 |
api/resource/v1beta1/types.go:273 |
DNS1123SubdomainMaxLength |
253 | RFC 1123 |
api/resource/v1beta2/types.go:265 |
DNS1123SubdomainMaxLength |
253 | RFC 1123 |
kubectl/pkg/cmd/taint/taint.go |
DNS1123SubdomainMaxLength, LabelValueMaxLength |
253, 63 | Help text |
kubectl/pkg/cmd/label/label.go |
LabelValueMaxLength |
63 | Help text |
kubectl/pkg/cmd/expose/expose.go |
DNS1035LabelMaxLength |
63 | Truncation |
kubectl/pkg/cmd/set/set_selector.go |
LabelValueMaxLength |
63 | Help text |
apiserver/.../handlers/create.go |
FieldManagerMaxLength |
128 | Length check |
Severity: Medium Effort: High
component-base ↔ client-go coupling:
component-base/metrics/prometheus/clientgo/leaderelection/metrics.go
→ imports k8s.io/client-go/tools/leaderelection
component-base/metrics/prometheus/workqueue/metrics.go
→ imports k8s.io/client-go/util/workqueue
component-base/metrics/prometheus/restclient/metrics.go
→ imports k8s.io/client-go/tools/metrics
component-base/tracing/utils.go
→ imports k8s.io/client-go/transport
Why this exists: component-base provides Prometheus metrics adapters that register with client-go's metrics interfaces.
Why it matters: Creates a subtle coupling where component-base (Level 3) imports from client-go (Level 2), but client-go also depends on component-base for metrics.
Potential solutions:
- Move metrics interfaces to a lower-level package
- Accept the coupling as necessary for metrics registration
- Create a dedicated
k8s.io/client-go-metricsinterface package
Severity: Low Effort: Medium
Apply Configurations (client-go):
The client-go/applyconfigurations/ directory contains generated apply configuration types. These files are generated but add to the overall dependency surface.
Generated Validations (api):
api/extensions/v1beta1/zz_generated.validations.go
→ imports validation/field, api/validate, api/safe, api/operation
Questions for discussion:
- Should validation code be generated into
k8s.io/api? - Could validation live in a separate module?
Severity: Low Effort: Low
content.IsDecimalInteger in api/core/v1/toleration.go:
import "k8s.io/apimachinery/pkg/api/validate/content"
func compareNumericValues(...) {
errorMsgs := content.IsDecimalInteger(tolerationVal)
// ...
}The function is ~30 lines and validates decimal integer format. Options:
- Inline the logic
- Use strconv.ParseInt with stricter validation
- Keep as-is (acceptable coupling)
Current imports from apimachinery:
| Package | Count | Classification |
|---|---|---|
pkg/apis/meta/v1 |
195 | Essential - ObjectMeta, TypeMeta |
pkg/runtime |
133 | Essential - Object interface |
pkg/runtime/schema |
84 | Essential - GVK |
pkg/api/resource |
32 | Essential - Quantity |
pkg/util/intstr |
23 | Essential - IntOrString |
pkg/types |
23 | Essential - UID, types |
pkg/util/validation |
3 | Reducible - constants only |
pkg/api/validate/* |
4 | Questionable - generated code |
Reduction opportunity: Remove util/validation import by hardcoding constants.
Import profile (top packages):
- 2137 imports from apimachinery/pkg
- 1800+ imports from api/*
- Heavy but expected for a client library
Notable patterns:
- Generated listers/informers for every API type
- Apply configurations for every API type
- Typed clients for every API group
No significant reduction opportunities - this is the expected role of client-go.
Import profile:
- 1168 from apimachinery/pkg
- 200+ from api/*
- 400+ from client-go
- 73 from component-base/metrics
- 26 from component-base/tracing
Notable dependencies:
- Heavy use of client-go informers
- Metrics and tracing from component-base
- kube-openapi for OpenAPI generation
Potential optimization: Some internal packages could potentially be refactored, but this is major work with unclear benefit.
Minimal footprint:
- 54 from apimachinery/pkg
- 5 from client-go (metrics adapters)
- 22 from klog
The client-go imports are the only questionable area (see Section 3.2).
| # | Action | Files | Impact |
|---|---|---|---|
| 1 | Hardcode DNS1123SubdomainMaxLength=253 in api/resource |
3 | Removes util/validation from api |
| 2 | Hardcode DNS/Label constants in kubectl commands | 4 | Minor cleanup |
| 3 | Hardcode FieldManagerMaxLength=128 in apiserver |
1 | Minor cleanup |
| # | Action | Complexity | Benefit |
|---|---|---|---|
| 1 | Create RFC constants package in k8s.io/api | Medium | Single source of truth |
| 2 | Review generated validation code placement | Medium | Cleaner api module |
| 3 | Inline IsDecimalInteger in toleration.go |
Low | Remove content import |
| Topic | Question | Stakeholders |
|---|---|---|
| Metrics interface location | Should metrics interfaces be in a lower-level package? | sig-instrumentation |
| Validation code generation | Should validation be generated into api? | sig-api-machinery |
| Apply configuration size | Can generated code be reduced? | sig-api-machinery |
- apimachinery has zero dependencies on api or client-go
- api has zero dependencies on client-go
- No circular dependencies between staging modules
- Lower-level modules (apimachinery, api) are properly minimal
- Higher-level modules (kubectl, apiserver) have expected larger dependency sets
- Generated code is isolated in predictable locations
- The core types in meta/v1, runtime, and types are stable and well-defined
- Import patterns are consistent across modules
21 k8s.io/klog/v2
4 k8s.io/utils/clock
4 k8s.io/kube-openapi/pkg/util/proto
3 k8s.io/utils/ptr
3 k8s.io/utils/net
3 k8s.io/kube-openapi/pkg/validation/spec
2 k8s.io/kube-openapi/pkg/schemaconv
1 k8s.io/kube-openapi/pkg/util
1 k8s.io/kube-openapi/pkg/spec3
1 k8s.io/klog/v2
// pkg/util/validation/validation.go
const DNS1123LabelMaxLength int = 63
const DNS1123SubdomainMaxLength int = 253
const DNS1035LabelMaxLength int = 63
// pkg/api/validate/content/kube.go
const LabelValueMaxLength int = 63
// pkg/apis/meta/v1/validation/validation.go
var FieldManagerMaxLength = 128Constants that define Kubernetes API behavior are currently scattered across multiple modules:
| Constant Type | Currently Defined In |
|---|---|
| RFC DNS limits (253, 63) | apimachinery/pkg/util/validation, apimachinery/pkg/api/validate/content |
| Well-known label keys | api/core/v1, api/networking/v1, api/discovery/v1, kubelet/pkg/apis |
| Well-known annotation keys | api/core/v1, cloud-provider/api |
| Well-known taints | api/core/v1, cloud-provider/api |
| Resource/field limits | api/resource/v1, apimachinery/pkg/apis/meta/v1/validation |
Existing well-known constant files:
api/core/v1/well_known_labels.go
api/core/v1/well_known_taints.go
api/core/v1/annotation_key_constants.go
api/networking/v1/well_known_labels.go
api/networking/v1/well_known_annotations.go
api/discovery/v1/well_known_labels.go
cloud-provider/api/well_known_annotations.go
cloud-provider/api/well_known_taints.go
kubelet/pkg/apis/well_known_labels.go
Create a new foundational module at Level -1 (below apimachinery):
k8s.io/constants/
├── rfc/
│ ├── dns.go # DNS1123SubdomainMaxLength=253, DNS1123LabelMaxLength=63
│ └── doc.go # RFC references and documentation
├── limits/
│ └── limits.go # LabelValueMaxLength=63, FieldManagerMaxLength=128
├── labels/
│ └── labels.go # LabelHostname, LabelTopologyZone, LabelOSStable, etc.
├── annotations/
│ └── annotations.go # LastAppliedConfigAnnotation, MirrorPodAnnotationKey, etc.
├── taints/
│ └── taints.go # Well-known taint keys and effects
├── finalizers/
│ └── finalizers.go # Well-known finalizer strings
└── doc.go
New Dependency Graph:
Level -1 (NEW):
┌─────────────────────────────────────────────────────────────────┐
│ k8s.io/constants │
│ (ZERO dependencies - pure const declarations) │
└─────────────────────────────────────────────────────────────────┘
│
Level 0: │
┌───────────────────────────────┴─────────────────────────────────┐
│ apimachinery │
│ (imports constants for validation functions) │
└─────────────────────────────────────────────────────────────────┘
│
Level 1: │
┌───────────────────────────────┴─────────────────────────────────┐
│ api │
│ (imports constants for type definitions & defaults) │
└─────────────────────────────────────────────────────────────────┘
│
(...)
Pros:
- Eliminates Issue #127889 pattern entirely
- Single source of truth for all Kubernetes "magic strings"
- Makes "API constants" explicit - if it's in this module, it's part of the API contract
- Better discoverability - one place to find all kubernetes.io/* strings
- Clear versioning and deprecation path for constants
Cons:
- Yet another module to publish and maintain
- Breaking change requiring import updates across codebase
- Where to draw the line on what's a "constant"?
- Adds complexity to the publishing bot configuration
Instead of a new module, consolidate all well-known constants into a new package within k8s.io/api:
k8s.io/api/
├── constants/ # NEW PACKAGE
│ ├── rfc.go # DNS1123SubdomainMaxLength, etc.
│ ├── labels.go # All well-known label keys
│ ├── annotations.go # All well-known annotation keys
│ ├── taints.go # All well-known taint keys
│ └── limits.go # FieldManagerMaxLength, LabelValueMaxLength, etc.
├── core/v1/
│ ├── well_known_labels.go # DEPRECATED: re-exports from api/constants
│ └── ...
└── ...
Key insight: Constants that define API behavior semantically belong in k8s.io/api, not in apimachinery utilities.
This approach:
- Keeps constants in api (where they semantically belong)
- Doesn't create a new top-level module
- apimachinery would define its own copies of RFC constants (acceptable duplication for foundational values)
- api becomes the authoritative source for "what strings are part of the Kubernetes API"
- Validation functions in apimachinery consume constants, they don't define them
Migration path:
- Create
k8s.io/api/constants/package - Move/copy constants from scattered locations
- Update existing
well_known_*.gofiles to re-export (for compatibility) - Deprecate the scattered files over time
| Aspect | Proposal A (new module) | Proposal B (api/constants) |
|---|---|---|
| Breaking changes | High - new import paths | Medium - can re-export |
| Maintenance burden | Higher - new module | Lower - existing module |
| Semantic correctness | Good - explicit constants module | Better - constants live with API |
| Publishing complexity | Higher - new rules.yaml entry | None |
| apimachinery coupling | Reduced | Requires duplication or reverse dep |
For RFC constants (DNS limits, etc.), some duplication may be acceptable:
// In apimachinery/pkg/util/validation/validation.go
// DNS1123SubdomainMaxLength is the maximum length of a DNS-1123 subdomain (RFC 1123)
const DNS1123SubdomainMaxLength = 253
// In api/constants/rfc.go (or wherever)
// DNS1123SubdomainMaxLength is the maximum length of a DNS-1123 subdomain (RFC 1123)
const DNS1123SubdomainMaxLength = 253Why this might be OK:
- RFC constants are immutable (253 will never change)
- Each module documents the same RFC
- No semantic coupling - they happen to have the same value
- Validation code doesn't need to "agree" with API code, they both follow the same RFC
Create k8s.io/constants as the source of truth, but keep existing constants as re-exports:
k8s.io/constants/ # NEW MODULE - source of truth
├── rfc/
│ └── dns.go # DNS1123SubdomainMaxLength = 253
├── labels/
│ └── labels.go # LabelHostname = "kubernetes.io/hostname"
├── annotations/
│ └── annotations.go
├── limits/
│ └── limits.go # LabelValueMaxLength = 63
└── go.mod # NO dependencies
k8s.io/apimachinery/pkg/util/validation/
└── validation.go
// RE-EXPORT: const DNS1123SubdomainMaxLength = rfc.DNS1123SubdomainMaxLength
k8s.io/api/core/v1/
└── well_known_labels.go
// RE-EXPORT: const LabelHostname = kubernetes.LabelHostname
How it works:
// k8s.io/constants/rfc/dns.go (NEW - source of truth)
package rfc
// DNS1123SubdomainMaxLength is the maximum length per RFC 1123.
// See: https://www.rfc-editor.org/rfc/rfc1123
const DNS1123SubdomainMaxLength = 253
const DNS1123LabelMaxLength = 63
const DNS1035LabelMaxLength = 63// k8s.io/apimachinery/pkg/util/validation/validation.go (UPDATED - re-export)
package validation
import "k8s.io/constants/rfc"
// DNS1123SubdomainMaxLength is re-exported for backwards compatibility.
// Deprecated: Import directly from k8s.io/constants/rfc instead.
const DNS1123SubdomainMaxLength = rfc.DNS1123SubdomainMaxLength
const DNS1123LabelMaxLength = rfc.DNS1123LabelMaxLength
const DNS1035LabelMaxLength = rfc.DNS1035LabelMaxLength// k8s.io/api/core/v1/well_known_labels.go (UPDATED - re-export)
package v1
import "k8s.io/constants/labels"
// LabelHostname is re-exported for backwards compatibility.
// Deprecated: Import directly from k8s.io/constants/labels instead.
const LabelHostname = labels.LabelHostname
const LabelTopologyZone = labels.LabelTopologyZone
// ... etc// k8s.io/api/resource/v1/types.go (UPDATED - uses constants)
package v1
import "k8s.io/constants/rfc"
// PoolNameMaxLength follows RFC 1123 subdomain rules (same as node names)
const PoolNameMaxLength = rfc.DNS1123SubdomainMaxLengthNew Dependency Graph:
Level -1:
┌─────────────────────────────────────────────────────────────────┐
│ k8s.io/constants │
│ (ZERO dependencies) │
└─────────────────────────────────────────────────────────────────┘
│
┌───────────┴───────────┐
▼ ▼
Level 0: ┌─────────────┐ ┌─────────────┐
│apimachinery │ │ (others) │
│ re-exports │ │ │
└──────┬──────┘ └─────────────┘
│
Level 1: ▼
┌─────────────┐
│ api │
│ re-exports │
└─────────────┘
Updates to rules.yaml:
rules:
# NEW: constants is published first (no dependencies)
- destination: constants
branches:
- name: master
source:
branch: master
dirs:
- staging/src/k8s.io/constants
library: true
# UPDATED: apimachinery now depends on constants
- destination: apimachinery
branches:
- name: master
dependencies:
- repository: constants # NEW
branch: master
source:
branch: master
dirs:
- staging/src/k8s.io/apimachinery
library: true
# UPDATED: api now depends on constants (and still apimachinery)
- destination: api
branches:
- name: master
dependencies:
- repository: constants # NEW
branch: master
- repository: apimachinery
branch: master
source:
branch: master
dirs:
- staging/src/k8s.io/api
library: trueWhy this is the best approach:
| Benefit | Explanation |
|---|---|
| Zero breaking changes | All existing imports continue to work |
| Clear migration path | Deprecation warnings guide users to new imports |
| Single source of truth | k8s.io/constants is authoritative |
| Clean dependency graph | constants at Level -1, everything flows down |
| Solves Issue #127889 | api imports constants, not validation |
| Future-proof | Can remove re-exports in a future major version |
Migration timeline:
| Phase | Action | Breaking? |
|---|---|---|
| 1 | Create k8s.io/constants module |
No |
| 2 | Update apimachinery to import & re-export | No |
| 3 | Update api to import & re-export | No |
| 4 | Add deprecation notices to re-exports | No |
| 5 | Update docs to recommend direct imports | No |
| 6 | (Future) Remove re-exports in v2 | Yes (major version) |
// staging/src/k8s.io/constants/go.mod
module k8s.io/constants
go 1.24
// NOTE: No dependencies - this module is pure Go constants// staging/src/k8s.io/constants/doc.go
// Package constants provides canonical definitions for Kubernetes constants.
// This includes RFC-defined limits, well-known labels, annotations, and
// other "magic strings" that are part of the Kubernetes API contract.
//
// Other k8s.io modules re-export these constants for backwards compatibility,
// but new code should import directly from this package.
package constants// staging/src/k8s.io/constants/rfc/dns.go
package rfc
// RFC 1123 DNS naming constraints
// See: https://www.rfc-editor.org/rfc/rfc1123
const (
// DNS1123SubdomainMaxLength is the maximum length of a DNS-1123 subdomain.
// This applies to: node names, namespace names, service names, etc.
DNS1123SubdomainMaxLength = 253
// DNS1123LabelMaxLength is the maximum length of a single DNS-1123 label.
DNS1123LabelMaxLength = 63
)
// RFC 1035 DNS naming constraints
// See: https://www.rfc-editor.org/rfc/rfc1035
const (
// DNS1035LabelMaxLength is the maximum length of a DNS-1035 label.
DNS1035LabelMaxLength = 63
)// staging/src/k8s.io/constants/labels/labels.go
package labels
// Well-known node labels
const (
// LabelHostname is the label key for the node hostname
LabelHostname = "kubernetes.io/hostname"
// LabelTopologyZone is the label key for the topology zone
LabelTopologyZone = "topology.kubernetes.io/zone"
// LabelTopologyRegion is the label key for the topology region
LabelTopologyRegion = "topology.kubernetes.io/region"
// LabelOSStable is the label key for the node operating system
LabelOSStable = "kubernetes.io/os"
// LabelArchStable is the label key for the node architecture
LabelArchStable = "kubernetes.io/arch"
)
// Well-known service labels
const (
// IsHeadlessService indicates a headless service endpoint
IsHeadlessService = "service.kubernetes.io/headless"
)// staging/src/k8s.io/constants/limits/limits.go
package limits
// Kubernetes-specific size limits
const (
// LabelValueMaxLength is the maximum length of a label value
LabelValueMaxLength = 63
// FieldManagerMaxLength is the maximum length of a field manager name
FieldManagerMaxLength = 128
)Based on the analysis, here's what would be consolidated:
Package: k8s.io/constants/rfc
| Constant | Value | Currently In | Used By |
|---|---|---|---|
DNS1123SubdomainMaxLength |
253 | apimachinery/pkg/util/validation | api, kubectl, apiserver |
DNS1123LabelMaxLength |
63 | apimachinery/pkg/util/validation | validation functions |
DNS1035LabelMaxLength |
63 | apimachinery/pkg/util/validation | kubectl |
Package: k8s.io/constants/limits
| Constant | Value | Currently In | Used By |
|---|---|---|---|
LabelValueMaxLength |
63 | apimachinery/pkg/api/validate/content | kubectl, validation |
LabelKeyMaxLength |
63 | apimachinery/pkg/api/validate/content | validation |
FieldManagerMaxLength |
128 | apimachinery/pkg/apis/meta/v1/validation | apiserver |
Package: k8s.io/constants/labels
| Constant | Value | Currently In |
|---|---|---|
LabelHostname |
kubernetes.io/hostname |
api/core/v1 |
LabelTopologyZone |
topology.kubernetes.io/zone |
api/core/v1 |
LabelTopologyRegion |
topology.kubernetes.io/region |
api/core/v1 |
LabelOSStable |
kubernetes.io/os |
api/core/v1 |
LabelArchStable |
kubernetes.io/arch |
api/core/v1 |
LabelInstanceTypeStable |
node.kubernetes.io/instance-type |
api/core/v1 |
LabelWindowsBuild |
node.kubernetes.io/windows-build |
api/core/v1 |
LabelNodeExcludeBalancers |
node.kubernetes.io/exclude-from-external-load-balancers |
api/core/v1 |
LabelMetadataName |
kubernetes.io/metadata.name |
api/core/v1 |
IsHeadlessService |
service.kubernetes.io/headless |
api/core/v1 |
| (+ labels from networking/v1, discovery/v1, kubelet) |
Package: k8s.io/constants/annotations
| Constant | Value | Currently In |
|---|---|---|
LastAppliedConfigAnnotation |
kubectl.kubernetes.io/last-applied-configuration |
api/core/v1 |
MirrorPodAnnotationKey |
kubernetes.io/config.mirror |
api/core/v1 |
AnnotationLoadBalancerSourceRangesKey |
service.beta.kubernetes.io/load-balancer-source-ranges |
api/core/v1 |
AnnotationTopologyMode |
service.kubernetes.io/topology-mode |
api/core/v1 |
EndpointsLastChangeTriggerTime |
endpoints.kubernetes.io/last-change-trigger-time |
api/core/v1 |
EndpointsOverCapacity |
endpoints.kubernetes.io/over-capacity |
api/core/v1 |
PodDeletionCost |
controller.kubernetes.io/pod-deletion-cost |
api/core/v1 |
| (+ deprecated annotations for seccomp, apparmor) |
Package: k8s.io/constants/taints
| Constant | Currently In |
|---|---|
| Well-known taint keys | api/core/v1/well_known_taints.go |
| Cloud provider taints | cloud-provider/api/well_known_taints.go |
Package: k8s.io/constants/finalizers
| Constant | Value | Currently In |
|---|---|---|
CustomResourceCleanupFinalizer |
customresourcecleanup.apiextensions.k8s.io |
apiextensions-apiserver |
| Task | Files Changed | Complexity |
|---|---|---|
| Create k8s.io/constants module structure | ~10 new files | Low |
| Update rules.yaml for publishing | 1 file | Low |
| Update import-restrictions.yaml | 1 file | Low |
| Update apimachinery to re-export | ~5 files | Low |
| Update api/core/v1 to re-export | ~3 files | Low |
| Update api/networking/v1 to re-export | ~2 files | Low |
| Update api/discovery/v1 to re-export | ~1 file | Low |
| Update cloud-provider/api to re-export | ~2 files | Low |
| Update kubelet/pkg/apis to re-export | ~1 file | Low |
| Fix Issue #127889 (api/resource uses constants) | 3 files | Low |
| Update kubectl to use constants | 4 files | Low |
| Update apiserver to use constants | 1 file | Low |
| Total | ~35 files | Low-Medium |
-
Naming:
k8s.io/constantsvsk8s.io/api-constantsvsk8s.io/well-known? -
Scope: Should this include:
- Feature gate names?
- Default resource quantities?
- Timeout values?
- Or strictly "API contract" constants only?
-
Deprecation timeline: How long to keep re-exports before removing?
-
Documentation: Should constants have extensive godoc explaining their purpose, or minimal?
-
Versioning: If a constant is deprecated (like beta labels), how to handle in this module?
BEFORE: Current State - Constants Scattered Across Modules
flowchart TD
subgraph "Current Dependency Flow for Constants"
API[k8s.io/api<br/>PoolNameMaxLength = validation.DNS1123SubdomainMaxLength]
APM[k8s.io/apimachinery<br/>DNS1123SubdomainMaxLength = 253<br/>DNS1123LabelMaxLength = 63<br/>LabelValueMaxLength = 63]
KC[kubectl<br/>imports validation for constants]
AS[apiserver<br/>imports validation for FieldManagerMaxLength]
API -->|imports pkg/util/validation| APM
KC -->|imports pkg/util/validation| APM
AS -->|imports meta/v1/validation| APM
end
subgraph "Problem Areas"
P1[❌ api depends on apimachinery<br/>for a simple integer]
P2[❌ Constants buried in<br/>validation packages]
P3[❌ 9+ files define<br/>well-known labels separately]
end
style API fill:#ffcccc
style P1 fill:#ffcccc
style P2 fill:#ffcccc
style P3 fill:#ffcccc
AFTER: With k8s.io/constants Module
flowchart TD
subgraph "New Dependency Flow"
CONST[k8s.io/constants<br/>ZERO dependencies<br/>Single source of truth]
APM2[k8s.io/apimachinery<br/>Re-exports for compatibility]
API2[k8s.io/api<br/>Re-exports for compatibility<br/>Uses constants directly]
KC2[kubectl<br/>Can import constants directly]
AS2[apiserver<br/>Can import constants directly]
EXT[External consumers<br/>Can import constants directly]
CONST --> APM2
CONST --> API2
CONST --> KC2
CONST --> AS2
CONST --> EXT
APM2 -.->|deprecated re-exports| API2
end
subgraph "Benefits"
B1[✅ Zero-dependency<br/>foundation module]
B2[✅ Single source of truth<br/>for all constants]
B3[✅ External consumers<br/>get minimal dependency]
end
style CONST fill:#ccffcc
style B1 fill:#ccffcc
style B2 fill:#ccffcc
style B3 fill:#ccffcc
flowchart LR
subgraph "BEFORE: Tight Coupling"
direction TB
A1[api/resource/v1] -->|imports| V1[apimachinery/pkg/util/validation]
A2[api/resource/v1beta1] -->|imports| V1
A3[api/resource/v1beta2] -->|imports| V1
V1 -->|contains| C1[DNS constants<br/>+ validation funcs<br/>+ regex patterns<br/>+ error handling]
end
subgraph "AFTER: Loose Coupling"
direction TB
B1[api/resource/v1] -->|imports| K1[constants/rfc]
B2[api/resource/v1beta1] -->|imports| K1
B3[api/resource/v1beta2] -->|imports| K1
K1 -->|contains| C2[DNS constants only<br/>3 integers<br/>~20 lines]
end
style V1 fill:#ffcccc
style C1 fill:#ffcccc
style K1 fill:#ccffcc
style C2 fill:#ccffcc
| Module | Current Imports for Constants | After Change | Reduction |
|---|---|---|---|
| k8s.io/api | apimachinery/pkg/util/validation (2000+ LOC) |
constants/rfc (~20 LOC) |
99% |
| kubectl | apimachinery/pkg/util/validation (2000+ LOC) |
constants/rfc + constants/limits (~40 LOC) |
98% |
| apiserver | apimachinery/pkg/apis/meta/v1/validation (500+ LOC) |
constants/limits (~20 LOC) |
96% |
BEFORE: External package wants DNS1123SubdomainMaxLength
└── imports k8s.io/apimachinery/pkg/util/validation
└── imports k8s.io/apimachinery/pkg/util/validation/field
└── imports k8s.io/apimachinery/pkg/util/errors
└── imports regexp, net, strings, unicode, ...
└── Total: ~15 transitive imports, ~3000 LOC
AFTER: External package wants DNS1123SubdomainMaxLength
└── imports k8s.io/constants/rfc
└── (no dependencies)
└── Total: 0 transitive imports, ~20 LOC
| Metric | Before | After | Improvement |
|---|---|---|---|
| Transitive imports for constants | 15+ packages | 0 packages | 100% reduction |
| LOC pulled in for constants | ~3000 | ~20 | 99.3% reduction |
| Compile time impact | Pulls in regex engine | Pure constants | Significant |
| Binary size contribution | Validation code included | Constants only | Reduced |
pie title "Current Distribution of Well-Known Constants"
"api/core/v1" : 45
"api/networking/v1" : 8
"api/discovery/v1" : 5
"cloud-provider/api" : 12
"kubelet/pkg/apis" : 15
"apimachinery/validation" : 8
"Other locations" : 7
| Current Location | Constant Count | After Consolidation |
|---|---|---|
api/core/v1/well_known_labels.go |
~25 | → constants/labels/ |
api/core/v1/well_known_taints.go |
~10 | → constants/taints/ |
api/core/v1/annotation_key_constants.go |
~10 | → constants/annotations/ |
api/networking/v1/well_known_labels.go |
~5 | → constants/labels/ |
api/networking/v1/well_known_annotations.go |
~3 | → constants/annotations/ |
api/discovery/v1/well_known_labels.go |
~5 | → constants/labels/ |
cloud-provider/api/well_known_*.go |
~12 | → constants/ |
kubelet/pkg/apis/well_known_labels.go |
~15 | → constants/labels/ |
apimachinery/pkg/util/validation/ |
~5 | → constants/rfc/ |
apimachinery/pkg/api/validate/content/ |
~3 | → constants/limits/ |
| Total | ~93 constants | 1 module |
quadrantChart
title Effort vs Impact Analysis
x-axis Low Effort --> High Effort
y-axis Low Impact --> High Impact
quadrant-1 "Do First"
quadrant-2 "Plan Carefully"
quadrant-3 "Quick Wins"
quadrant-4 "Reconsider"
"Hardcode DNS constants in api": [0.15, 0.25]
"Create k8s.io/constants module": [0.45, 0.85]
"Update apimachinery re-exports": [0.25, 0.35]
"Update api re-exports": [0.25, 0.40]
"Consolidate well-known labels": [0.55, 0.70]
"External consumer documentation": [0.20, 0.50]
"Remove re-exports (future v2)": [0.75, 0.30]
| Action | Effort | Impact | Risk | Priority |
|---|---|---|---|---|
Create k8s.io/constants module |
Medium | High | Low | P0 |
| Add to publishing rules | Low | High | Low | P0 |
| Update apimachinery re-exports | Low | Medium | Low | P1 |
| Update api re-exports | Low | Medium | Low | P1 |
| Consolidate scattered well-known files | Medium | High | Medium | P1 |
| Update kubectl to use constants | Low | Low | Low | P2 |
| Update apiserver to use constants | Low | Low | Low | P2 |
| Document for external consumers | Low | Medium | None | P2 |
| Remove re-exports (breaking) | Low | Low | High | P3 (future) |
Who benefits from k8s.io/constants?
flowchart TD
subgraph "External Consumers"
OP[Operators & Controllers]
CL[CLI Tools]
WH[Webhooks]
AU[Automation Scripts]
TP[Third-party Libraries]
end
subgraph "What They Need"
L[Label Keys<br/>e.g., kubernetes.io/hostname]
A[Annotation Keys<br/>e.g., last-applied-config]
T[Taint Keys<br/>e.g., node.kubernetes.io/not-ready]
V[Validation Constants<br/>e.g., DNS1123SubdomainMaxLength]
end
subgraph "Current Pain"
P1[Must import large modules]
P2[Constants scattered across packages]
P3[Hard to discover all constants]
P4[Version skew between copies]
end
subgraph "With k8s.io/constants"
S1[Single lightweight import]
S2[One place to find everything]
S3[Clear API contract]
S4[Minimal dependency footprint]
end
OP --> L
OP --> A
CL --> V
WH --> L
AU --> A
TP --> V
L --> P1
A --> P2
T --> P3
V --> P4
P1 -.->|solved by| S1
P2 -.->|solved by| S2
P3 -.->|solved by| S3
P4 -.->|solved by| S4
style P1 fill:#ffcccc
style P2 fill:#ffcccc
style P3 fill:#ffcccc
style P4 fill:#ffcccc
style S1 fill:#ccffcc
style S2 fill:#ccffcc
style S3 fill:#ccffcc
style S4 fill:#ccffcc
Before:
import (
corev1 "k8s.io/api/core/v1" // Pulls in ALL of api/core/v1
)
func isNodeInZone(node *corev1.Node, zone string) bool {
return node.Labels[corev1.LabelTopologyZone] == zone
}
// Dependency: k8s.io/api/core/v1 → k8s.io/apimachinery (all of it)After:
import (
"k8s.io/constants/labels" // Just the constants, nothing else
)
func isNodeInZone(node *Node, zone string) bool {
return node.Labels[labels.LabelTopologyZone] == zone
}
// Dependency: k8s.io/constants/labels (zero transitive dependencies)| Module | Approximate Size | What You Get |
|---|---|---|
k8s.io/apimachinery |
~2.5 MB | Everything: types, validation, serialization, utilities |
k8s.io/api |
~15 MB | All Kubernetes API types |
k8s.io/client-go |
~25 MB | Full client library |
k8s.io/constants |
~10 KB | Just constants - nothing else |
Size reduction for "just need a constant":
Before: Import k8s.io/apimachinery → 2.5 MB dependency
After: Import k8s.io/constants → 10 KB dependency
Reduction: 99.6%
gantt
title Implementation Phases
dateFormat X
axisFormat %s
section Phase 1: Foundation
Create k8s.io/constants module :a1, 0, 1
Add rfc, limits, labels packages :a2, 0, 1
Update rules.yaml :a3, 1, 2
section Phase 2: Integration
Update apimachinery re-exports :b1, 2, 3
Update api re-exports :b2, 2, 3
Update import-restrictions.yaml :b3, 3, 4
section Phase 3: Adoption
Fix Issue #127889 :c1, 4, 5
Update kubectl uses :c2, 4, 5
Update apiserver uses :c3, 4, 5
section Phase 4: Documentation
Add deprecation notices :d1, 5, 6
Update contributor docs :d2, 5, 6
Announce to community :d3, 6, 7
| Justification | Evidence |
|---|---|
| Solves real problem | Issue #127889 and similar patterns in kubectl, apiserver |
| Zero breaking changes | Re-exports maintain full backwards compatibility |
| Minimal effort | ~35 files, mostly mechanical changes |
| High discoverability | All Kubernetes "magic strings" in one searchable place |
| External consumer win | 99%+ reduction in dependency size for constant imports |
| Future-proof | Clean path to remove re-exports in hypothetical v2 |
| Precedent exists | Similar to how k8s.io/utils was extracted |
| Low risk | Constants don't change, no runtime behavior affected |
- Issue #127889: Avoid adding a dependency to k8s.io/api on an apimachinery constant
staging/publishing/rules.yaml: Defines publishing dependenciesstaging/publishing/import-restrictions.yaml: Defines allowed imports
Report generated by analyzing kubernetes/kubernetes staging modules Total staging modules analyzed: 31