{ "description": "AttunePolicy is the Schema for the attunepolicies API.", "properties": { "apiVersion": { "description": "APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", "type": "string" }, "kind": { "description": "Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", "type": "string" }, "metadata": { "type": "object" }, "spec": { "description": "AttunePolicySpec defines the desired state of AttunePolicy.", "properties": { "cpu": { "description": "CPU configures CPU resource recommendations.", "properties": { "allowDecrease": { "description": "AllowDecrease controls whether the resource value can be decreased.\nFor CPU: nil defaults to true (decreases allowed, throttle detected by safety monitor).\nFor memory: nil defaults to false (decreases blocked to prevent OOMKill).", "type": "boolean" }, "burstSensitivity": { "description": "BurstSensitivity controls how much burst detection inflates the\nrecommendation. Expressed as a decimal string multiplied by\nlog2(burstMagnitude). Default \"0.1\" gives ~20% boost for magnitude 4,\n~30% for 8, ~40% for 16. Set \"0\" to disable burst boost entirely\n(e.g. for batch jobs). Must be >= 0, max 1.0.", "type": "string" }, "controlledValues": { "description": "ControlledValues specifies which resource values to manage.\n\"RequestsOnly\" (default) adjusts only requests, leaving limits unchanged.\n\"RequestsAndLimits\" adjusts both requests and limits in lockstep.\nFor Guaranteed-QoS pods (where requests equal limits), use\n\"RequestsAndLimits\" or resizes will be skipped to preserve QoS class.", "enum": [ "RequestsOnly", "RequestsAndLimits" ], "type": "string" }, "decreaseUsageMarginPercent": { "description": "DecreaseUsageMarginPercent is the minimum headroom above recent memory\nusage required when decreasing memory limits (client-side pre-check).\nThe target limit must be at least usage * (1 + margin/100). Defaults to\n10. Only applied on the memory ResourceConfig when a limit decrease is\nattempted; ignored for CPU. Set 0 to require limit strictly above usage.", "format": "int32", "maximum": 100, "minimum": 0, "type": "integer" }, "maxAllowed": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "MaxAllowed is the maximum allowed resource value.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "maxChangePercent": { "description": "MaxChangePercent is the maximum allowed change percentage per\nreconcile cycle for this resource (both directions). Limits how\naggressively the recommendation can deviate from the current value\nin a single step, forcing gradual convergence. Overridden by\nMaxIncreasePercent/MaxDecreasePercent if those are set.\nDefaults to 50 for CPU, 30 for memory.", "format": "int32", "maximum": 100, "minimum": 1, "type": "integer" }, "maxDecreasePercent": { "description": "MaxDecreasePercent is the maximum allowed decrease percentage per\nreconcile cycle. Takes precedence over MaxChangePercent for downward\nchanges. Memory decreases are riskier (OOM), so a lower cap is\nrecommended. Defaults to MaxChangePercent if not set.", "format": "int32", "maximum": 100, "minimum": 1, "type": "integer" }, "maxIncreasePercent": { "description": "MaxIncreasePercent is the maximum allowed increase percentage per\nreconcile cycle. Takes precedence over MaxChangePercent for upward\nchanges. Defaults to MaxChangePercent if not set.", "format": "int32", "maximum": 100, "minimum": 1, "type": "integer" }, "memoryFromCpuRatio": { "description": "MemoryFromCPURatio derives memory recommendations from CPU recommendations\nusing a fixed ratio, instead of using Prometheus memory metrics. Useful for\nJVM, Go, and .NET workloads where heap scales linearly with CPU allocation\nand Prometheus memory metrics are unreliable (JVM reserves heap upfront,\nGo GC targets a fixed percentage of available memory).\nExample: \"2.0\" means memory = 2x the CPU recommendation in bytes\n(e.g., 500m CPU -> 1Gi memory). The derived value still passes through\nminAllowed, maxAllowed, and maxChangePercent bounds.\nOnly valid on the memory ResourceConfig; ignored on CPU.", "type": "string" }, "minAllowed": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "MinAllowed is the minimum allowed resource value.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "overhead": { "description": "Overhead is the percentage of additional resources added on top of the\npercentile recommendation. Expressed as a string (e.g. \"20\" means 20%\nextra headroom above the target percentile). Must be >= 0, max 900.\nDefaults to \"20\" for CPU and \"30\" for memory.", "pattern": "^([0-9]+(\\.[0-9]+)?)?$", "type": "string" }, "percentile": { "description": "Percentile is the usage percentile to target for recommendations.\nSupported values: 50, 90, 95, 99. Omit or set to 0 to use the default\n(95 for CPU, 99 for memory).", "enum": [ 0, 50, 90, 95, 99 ], "format": "int32", "type": "integer" }, "startupBoost": { "description": "StartupBoost temporarily increases CPU requests for newly created or\nrestarted pods to accelerate JVM/.NET class loading, JIT compilation,\nand cache warming. After the duration expires (or the container reaches\nReady), the CPU is reduced to the steady-state recommendation.\nOnly applies to CPU resources.", "properties": { "duration": { "description": "Duration is the maximum time the boost remains active after pod\ncreation or container restart. The boost is removed when the\ncontainer reaches Ready or this duration expires, whichever comes first.\nMust be >= 10s and <= 1h.", "type": "string" }, "multiplier": { "description": "Multiplier scales the recommended CPU request during startup.\nFor example, \"3.0\" means 3x the steady-state recommendation.\nMust be > 1.0 and <= 10.0.", "type": "string" } }, "required": [ "duration", "multiplier" ], "type": "object", "additionalProperties": false } }, "type": "object", "additionalProperties": false }, "excludeKnownSidecars": { "description": "ExcludeKnownSidecars, when true (the default), automatically skips\nwell-known mesh and sidecar container names (for example istio-proxy,\nlinkerd-proxy) in addition to ExcludedContainers. Set to false to\nrestore pre-feature behavior where only ExcludedContainers is used.", "type": "boolean" }, "excludedContainers": { "description": "ExcludedContainers is a list of container names to skip when computing\nrecommendations and performing resizes. Use this for custom sidecars\nor agents. When ExcludeKnownSidecars is true (the default), this list\nis unioned with the built-in known-sidecar names (istio-proxy, etc.).", "items": { "type": "string" }, "maxItems": 100, "type": "array" }, "memory": { "description": "Memory configures memory resource recommendations.", "properties": { "allowDecrease": { "description": "AllowDecrease controls whether the resource value can be decreased.\nFor CPU: nil defaults to true (decreases allowed, throttle detected by safety monitor).\nFor memory: nil defaults to false (decreases blocked to prevent OOMKill).", "type": "boolean" }, "burstSensitivity": { "description": "BurstSensitivity controls how much burst detection inflates the\nrecommendation. Expressed as a decimal string multiplied by\nlog2(burstMagnitude). Default \"0.1\" gives ~20% boost for magnitude 4,\n~30% for 8, ~40% for 16. Set \"0\" to disable burst boost entirely\n(e.g. for batch jobs). Must be >= 0, max 1.0.", "type": "string" }, "controlledValues": { "description": "ControlledValues specifies which resource values to manage.\n\"RequestsOnly\" (default) adjusts only requests, leaving limits unchanged.\n\"RequestsAndLimits\" adjusts both requests and limits in lockstep.\nFor Guaranteed-QoS pods (where requests equal limits), use\n\"RequestsAndLimits\" or resizes will be skipped to preserve QoS class.", "enum": [ "RequestsOnly", "RequestsAndLimits" ], "type": "string" }, "decreaseUsageMarginPercent": { "description": "DecreaseUsageMarginPercent is the minimum headroom above recent memory\nusage required when decreasing memory limits (client-side pre-check).\nThe target limit must be at least usage * (1 + margin/100). Defaults to\n10. Only applied on the memory ResourceConfig when a limit decrease is\nattempted; ignored for CPU. Set 0 to require limit strictly above usage.", "format": "int32", "maximum": 100, "minimum": 0, "type": "integer" }, "maxAllowed": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "MaxAllowed is the maximum allowed resource value.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "maxChangePercent": { "description": "MaxChangePercent is the maximum allowed change percentage per\nreconcile cycle for this resource (both directions). Limits how\naggressively the recommendation can deviate from the current value\nin a single step, forcing gradual convergence. Overridden by\nMaxIncreasePercent/MaxDecreasePercent if those are set.\nDefaults to 50 for CPU, 30 for memory.", "format": "int32", "maximum": 100, "minimum": 1, "type": "integer" }, "maxDecreasePercent": { "description": "MaxDecreasePercent is the maximum allowed decrease percentage per\nreconcile cycle. Takes precedence over MaxChangePercent for downward\nchanges. Memory decreases are riskier (OOM), so a lower cap is\nrecommended. Defaults to MaxChangePercent if not set.", "format": "int32", "maximum": 100, "minimum": 1, "type": "integer" }, "maxIncreasePercent": { "description": "MaxIncreasePercent is the maximum allowed increase percentage per\nreconcile cycle. Takes precedence over MaxChangePercent for upward\nchanges. Defaults to MaxChangePercent if not set.", "format": "int32", "maximum": 100, "minimum": 1, "type": "integer" }, "memoryFromCpuRatio": { "description": "MemoryFromCPURatio derives memory recommendations from CPU recommendations\nusing a fixed ratio, instead of using Prometheus memory metrics. Useful for\nJVM, Go, and .NET workloads where heap scales linearly with CPU allocation\nand Prometheus memory metrics are unreliable (JVM reserves heap upfront,\nGo GC targets a fixed percentage of available memory).\nExample: \"2.0\" means memory = 2x the CPU recommendation in bytes\n(e.g., 500m CPU -> 1Gi memory). The derived value still passes through\nminAllowed, maxAllowed, and maxChangePercent bounds.\nOnly valid on the memory ResourceConfig; ignored on CPU.", "type": "string" }, "minAllowed": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "MinAllowed is the minimum allowed resource value.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "overhead": { "description": "Overhead is the percentage of additional resources added on top of the\npercentile recommendation. Expressed as a string (e.g. \"20\" means 20%\nextra headroom above the target percentile). Must be >= 0, max 900.\nDefaults to \"20\" for CPU and \"30\" for memory.", "pattern": "^([0-9]+(\\.[0-9]+)?)?$", "type": "string" }, "percentile": { "description": "Percentile is the usage percentile to target for recommendations.\nSupported values: 50, 90, 95, 99. Omit or set to 0 to use the default\n(95 for CPU, 99 for memory).", "enum": [ 0, 50, 90, 95, 99 ], "format": "int32", "type": "integer" }, "startupBoost": { "description": "StartupBoost temporarily increases CPU requests for newly created or\nrestarted pods to accelerate JVM/.NET class loading, JIT compilation,\nand cache warming. After the duration expires (or the container reaches\nReady), the CPU is reduced to the steady-state recommendation.\nOnly applies to CPU resources.", "properties": { "duration": { "description": "Duration is the maximum time the boost remains active after pod\ncreation or container restart. The boost is removed when the\ncontainer reaches Ready or this duration expires, whichever comes first.\nMust be >= 10s and <= 1h.", "type": "string" }, "multiplier": { "description": "Multiplier scales the recommended CPU request during startup.\nFor example, \"3.0\" means 3x the steady-state recommendation.\nMust be > 1.0 and <= 10.0.", "type": "string" } }, "required": [ "duration", "multiplier" ], "type": "object", "additionalProperties": false } }, "type": "object", "additionalProperties": false }, "metricsSource": { "description": "MetricsSource configures where and how to collect metrics.", "properties": { "cloudwatch": { "description": "CloudWatch configures an Amazon CloudWatch Container Insights metrics source.", "properties": { "clusterName": { "description": "ClusterName is the EKS cluster name for Container Insights metrics.\nRequired for metric filtering.", "type": "string" }, "region": { "description": "Region is the AWS region (e.g. \"us-east-1\"). Required.", "type": "string" }, "roleArn": { "description": "RoleARN is an optional IAM role ARN to assume for cross-account access.\nIf not set, uses the pod's service account IAM role (IRSA/Pod Identity).", "type": "string" } }, "required": [ "clusterName", "region" ], "type": "object", "additionalProperties": false }, "cpuRecordingMetric": { "description": "CPURecordingMetric is an optional pre-aggregated Prometheus metric name\nused instead of rate(container_cpu_usage_seconds_total). Labels must include\nnamespace, pod, and container. When set, the operator does not wrap the\nmetric in rate(). Pair with MemoryRecordingMetric for a recording-rules-only path.", "type": "string" }, "datadog": { "description": "Datadog configures a Datadog metrics source.", "properties": { "apiKeySecretRef": { "description": "APIKeySecretRef references a Secret containing the Datadog API key.\nThe Secret must contain an \"api-key\" key and optionally an \"app-key\" key.", "properties": { "key": { "description": "Key within the Secret.", "type": "string" }, "name": { "description": "Name of the Secret.", "type": "string" } }, "required": [ "key", "name" ], "type": "object", "additionalProperties": false }, "site": { "default": "datadoghq.com", "description": "Site is the Datadog site (e.g. \"datadoghq.com\", \"datadoghq.eu\", \"us5.datadoghq.com\").\nDefaults to \"datadoghq.com\".", "type": "string" } }, "required": [ "apiKeySecretRef" ], "type": "object", "additionalProperties": false }, "historyWindow": { "description": "HistoryWindow is the time window for historical metrics data.\nDefaults to 7d (168h) if not specified.", "type": "string" }, "memoryRecordingMetric": { "description": "MemoryRecordingMetric is an optional pre-aggregated Prometheus metric name\nused instead of container_memory_working_set_bytes. Same label requirements\nas CPURecordingMetric.", "type": "string" }, "minimumDataPoints": { "description": "MinimumDataPoints is the minimum number of data points required\nbefore generating recommendations. Minimum 1, default 48 samples.\nWith the default queryStep of 5m, 48 samples is about 4 hours of data.\nDefaults to 48 if not set (applied by the controller so that\nAttuneDefaults cluster configuration can override it).", "format": "int32", "minimum": 1, "type": "integer" }, "podAggregation": { "description": "PodAggregation controls how multi-pod series are reduced in Prometheus\nrange queries for this policy.\n Max (default): max by (container) \u2014 size for the busiest pod; O(containers) series.\n Avg: avg by (container) across pods.\n None: no aggregation (one series per pod; expensive for high replica counts).\nDatadog and CloudWatch already group by container; this field applies to Prometheus.", "enum": [ "Max", "Avg", "None" ], "type": "string" }, "prometheus": { "description": "Prometheus configures a Prometheus metrics source.", "properties": { "address": { "description": "Address is the URL of the Prometheus-compatible query endpoint.", "type": "string" }, "bearerTokenSecret": { "description": "BearerTokenSecret references a Kubernetes Secret containing a bearer\ntoken for authenticating with managed Prometheus services.", "properties": { "key": { "description": "Key within the Secret.", "type": "string" }, "name": { "description": "Name of the Secret.", "type": "string" } }, "required": [ "key", "name" ], "type": "object", "additionalProperties": false }, "headers": { "additionalProperties": { "type": "string" }, "description": "Headers are custom HTTP headers added to every query request.\nUse for non-secret tenant or routing headers (e.g. \"X-Scope-OrgID\"\nfor Mimir). Do not put credentials here; use BearerTokenSecret for\nauthentication tokens.", "type": "object" }, "queryParameters": { "additionalProperties": { "type": "string" }, "description": "QueryParameters are appended to every query request URL.\nUse for backend-specific settings such as Thanos deduplication\n(e.g. {\"dedup\": \"true\", \"partial_response\": \"true\"}). Reserved\nquery keys controlled by the operator (`query`, `start`, `end`, `step`,\n`time`, `timeout`) are rejected.", "type": "object" }, "tls": { "description": "TLS configures TLS settings for the connection.", "properties": { "insecureSkipVerify": { "description": "InsecureSkipVerify disables TLS certificate verification.\nUse only for self-signed certificates in development.", "type": "boolean" } }, "type": "object", "additionalProperties": false } }, "required": [ "address" ], "type": "object", "additionalProperties": false }, "queryStep": { "description": "QueryStep is the step interval for Prometheus range queries and ETA\ncalculations. Should match your Prometheus scrape interval for\naccurate time estimates. Minimum 10s, maximum 1h. Default 5m.", "type": "string" }, "rateWindow": { "description": "RateWindow is the window used in the PromQL rate() function for CPU\nqueries. Defaults to queryStep if not set. Must be >= 30s and <= historyWindow.\nAdvanced users may set this independently to control CPU rate smoothing\n(e.g. a short rateWindow for responsive tracking with a longer queryStep).", "type": "string" }, "vpa": { "description": "VPA configures consumption of existing VerticalPodAutoscaler recommendations.", "properties": { "name": { "description": "Name is the name of the VerticalPodAutoscaler object.", "type": "string" }, "namespace": { "description": "Namespace is the namespace of the VPA. Defaults to the policy's namespace.", "type": "string" } }, "required": [ "name" ], "type": "object", "additionalProperties": false } }, "type": "object", "additionalProperties": false }, "paused": { "description": "Paused stops the operator from reconciling this policy. Metrics\ncollection, recommendations, and resizes are all halted. Existing\nresizes are not reverted. Use this during maintenance windows or\nwhen debugging unexpected behavior. The operator sets Ready=False\nwith reason=Paused while this field is true.", "type": "boolean" }, "runtimeProfile": { "description": "RuntimeProfile applies language/runtime-oriented defaults for memory\nresize safety. Unset or \"generic\" leaves policy fields unchanged.\nProfiles document recommended resizePolicy and decrease settings;\nthey do not auto-patch pod specs. Supported values: generic, java,\npython, golang, nodejs.", "enum": [ "generic", "java", "python", "golang", "nodejs" ], "type": "string" }, "targetRef": { "description": "TargetRef identifies the workload(s) to be attuned.", "properties": { "kind": { "description": "Kind is the kind of the target resource.", "enum": [ "Deployment", "StatefulSet", "DaemonSet", "CronJob", "Job", "ReplicaSet" ], "type": "string" }, "name": { "description": "Name is the name of a specific target resource.", "type": "string" }, "selector": { "description": "Selector selects target resources by labels.", "properties": { "matchExpressions": { "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.", "items": { "description": "A label selector requirement is a selector that contains values, a key, and an operator that\nrelates the key and values.", "properties": { "key": { "description": "key is the label key that the selector applies to.", "type": "string" }, "operator": { "description": "operator represents a key's relationship to a set of values.\nValid operators are In, NotIn, Exists and DoesNotExist.", "type": "string" }, "values": { "description": "values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch.", "items": { "type": "string" }, "type": "array", "x-kubernetes-list-type": "atomic" } }, "required": [ "key", "operator" ], "type": "object", "additionalProperties": false }, "type": "array", "x-kubernetes-list-type": "atomic" }, "matchLabels": { "additionalProperties": { "type": "string" }, "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels\nmap is equivalent to an element of matchExpressions, whose key field is \"key\", the\noperator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", "type": "object" } }, "type": "object", "x-kubernetes-map-type": "atomic", "additionalProperties": false } }, "required": [ "kind" ], "type": "object", "additionalProperties": false }, "updateStrategy": { "description": "UpdateStrategy configures how and when to apply resource changes.", "properties": { "autoRevert": { "description": "AutoRevert automatically reverts changes if degradation is detected.\nDefaults to true if not set (applied by the controller so that\nAttuneDefaults cluster configuration can override it).", "type": "boolean" }, "canary": { "description": "Canary configures canary rollout behavior when Type is Canary.", "properties": { "autoPromote": { "description": "AutoPromote controls whether the operator automatically promotes the\nresize to all pods after the observation period passes without safety\nviolations. When false (default), the user must manually switch the\nmode to Auto to resize the remaining pods.", "type": "boolean" }, "observationPeriod": { "description": "ObservationPeriod is how long to observe canary pods before proceeding.", "type": "string" }, "percentage": { "description": "Percentage is the percentage of pods to resize first.", "format": "int32", "maximum": 100, "minimum": 1, "type": "integer" } }, "required": [ "observationPeriod", "percentage" ], "type": "object", "additionalProperties": false }, "cooldown": { "description": "Cooldown is the minimum time between successive resize operations.\nDefaults to 1h if not specified.", "type": "string" }, "export": { "description": "Export configures how recommendations are exported for external\nconsumption (e.g. GitOps workflows with ArgoCD or Flux).", "properties": { "configMap": { "description": "ConfigMap enables exporting recommendations to ConfigMaps.", "type": "boolean" }, "pullRequest": { "description": "PullRequest enables opt-in GitOps PR automation (default off).\nRequires a token Secret and repository identity. Never logs the token.", "properties": { "apiUrl": { "description": "APIURL overrides the API base URL (Enterprise GitHub or self-hosted GitLab).\nDefaults: https://api.github.com or https://gitlab.com/api/v4.", "type": "string" }, "baseBranch": { "description": "BaseBranch is the PR target branch. Defaults to \"main\".", "type": "string" }, "cooldown": { "description": "Cooldown is the minimum time between PR create/update attempts for this\npolicy. Defaults to 24h.", "type": "string" }, "dryRun": { "description": "DryRun logs the intended PR and updates status without calling the\nremote API. Useful for CI and first enablement.", "type": "boolean" }, "enabled": { "description": "Enabled turns on PR automation. Default false.", "type": "boolean" }, "labels": { "description": "Labels are applied to the pull request when supported by the provider.", "items": { "type": "string" }, "maxItems": 20, "type": "array" }, "minChangePercent": { "description": "MinChangePercent is the minimum absolute percent change (per container\nresource vs template) required to open or update a PR. Defaults to 10.", "format": "int32", "maximum": 100, "minimum": 1, "type": "integer" }, "provider": { "description": "Provider is \"github\" or \"gitlab\". Default \"github\".", "enum": [ "github", "gitlab" ], "type": "string" }, "repository": { "description": "Repository is \"owner/name\" (GitHub) or \"group/project\" (GitLab path).\nRequired when Enabled is true.", "type": "string" }, "tokenSecretRef": { "description": "TokenSecretRef references a Secret key holding a PAT / project token.\nGitHub: repo contents + pull requests. GitLab: api scope on the project.\nRequired when Enabled is true.", "properties": { "key": { "description": "Key within the Secret.", "type": "string" }, "name": { "description": "Name of the Secret.", "type": "string" } }, "required": [ "key", "name" ], "type": "object", "additionalProperties": false } }, "type": "object", "additionalProperties": false } }, "type": "object", "additionalProperties": false }, "includeExplanationsInStatus": { "description": "IncludeExplanationsInStatus controls whether recommendation explanation\nchains are written to status. Set to false on large policies to shrink\nCR size. Default: true.", "type": "boolean" }, "initialSizing": { "description": "InitialSizing enables a mutating admission webhook that sets resource\nrequests/limits on new pods at creation time, based on existing\nrecommendations. This eliminates the \"deploy with bad defaults, wait\nfor first reconcile\" gap. Requires the namespace label\nattune.io/initial-sizing=enabled. Defaults to false.", "type": "boolean" }, "maxConcurrentResizes": { "default": 1, "description": "MaxConcurrentResizes is the maximum number of pods to resize\nconcurrently within a single reconcile cycle. Default: 1 (serial).", "format": "int32", "maximum": 50, "minimum": 1, "type": "integer" }, "maxStatusRecommendations": { "description": "MaxStatusRecommendations caps how many workload recommendations are\nwritten to status.recommendations. Resizes still use the full in-memory\nset. When the cap is hit, entries with the largest absolute CPU+memory\nrequest change are kept. Default: 100 (operator may override via flag).", "format": "int32", "maximum": 500, "minimum": 1, "type": "integer" }, "maxTotalCpuIncrease": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "MaxTotalCPUIncrease is the maximum aggregate CPU increase allowed\nacross all pods in a single reconcile cycle (e.g. \"2000m\", \"4\").\nOnce exhausted, remaining pods are deferred to the next cycle.\nDecreases do not consume budget. Default: unlimited.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "maxTotalMemoryIncrease": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "MaxTotalMemoryIncrease is the maximum aggregate memory increase\nallowed across all pods in a single reconcile cycle (e.g. \"4Gi\").\nOnce exhausted, remaining pods are deferred to the next cycle.\nDecreases do not consume budget. Default: unlimited.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "resizeMethod": { "description": "ResizeMethod controls what happens when an in-place resize fails.\n InPlaceOnly (default): skip the pod and retry next cycle.\n InPlaceOrRecreate: fall back to pod eviction if in-place resize\n fails or is marked Infeasible by kubelet. The owning controller\n recreates the pod with updated resources. Evictions respect\n PodDisruptionBudgets and never evict the last replica.\nDefaults to InPlaceOnly if not set (applied by the controller so that\nAttuneDefaults cluster configuration can override it).", "enum": [ "InPlaceOnly", "InPlaceOrRecreate" ], "type": "string" }, "safetyObservationPeriod": { "description": "SafetyObservationPeriod is how long to observe a pod after resize before\nconcluding the resize is safe. Applies to all modes (Auto, OneShot, Canary).\nTakes precedence over canary.observationPeriod when set. Must be >= 1m.\nDefaults to 5m if neither this nor canary.observationPeriod is set.", "type": "string" }, "schedule": { "description": "Schedule restricts when resize operations can occur. Recommendations\nare always computed; only resize execution is gated. If omitted,\nresizes can occur at any time (current behavior).", "properties": { "daysOfWeek": { "description": "DaysOfWeek restricts resizes to specific days. Values: Monday through Sunday.\nIf omitted, all days are allowed.", "items": { "enum": [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ], "type": "string" }, "type": "array" }, "timezone": { "default": "UTC", "description": "Timezone for interpreting window start/end times. Must be a valid\nIANA timezone name (e.g. \"America/New_York\"). Default: \"UTC\".", "type": "string" }, "windows": { "description": "Windows defines time-of-day ranges when resizes are allowed.\nIf multiple windows are specified, resizes are allowed during any of them.", "items": { "description": "TimeWindow defines a daily time range.", "properties": { "end": { "description": "End time in HH:MM format (24-hour). If end < start, the window\nwraps past midnight (e.g. start=22:00, end=06:00).", "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$", "type": "string" }, "start": { "description": "Start time in HH:MM format (24-hour).", "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$", "type": "string" } }, "required": [ "end", "start" ], "type": "object", "additionalProperties": false }, "type": "array" } }, "type": "object", "additionalProperties": false }, "sloGuardrails": { "description": "SLOGuardrails defines application-level SLO metrics to check after\na resize. If any metric breaches its threshold during the safety\nobservation period, the resize is automatically reverted.\nRequires a Prometheus-compatible metrics source.", "items": { "description": "SLOGuardrail defines an application-level metric that is checked after\na resize to detect degradation. If the metric breaches the threshold,\nthe safety monitor triggers an automatic revert.", "properties": { "comparison": { "default": "above", "description": "Comparison is \"above\" or \"below\". \"above\" reverts when value > threshold.", "enum": [ "above", "below" ], "type": "string" }, "evaluationWindow": { "description": "EvaluationWindow is how long after resize to check. Defaults to 5m.", "type": "string" }, "name": { "description": "Name identifies this guardrail for logging and status reporting.", "type": "string" }, "query": { "description": "Query is a PromQL query that returns a scalar value.\nTemplate variables: {{ .Namespace }}, {{ .WorkloadName }}, {{ .PodName }}", "type": "string" }, "threshold": { "description": "Threshold is the value that triggers a revert.", "type": "string" } }, "required": [ "name", "query", "threshold" ], "type": "object", "additionalProperties": false }, "maxItems": 10, "type": "array" }, "templatePersistence": { "description": "TemplatePersistence optionally writes recommended resources into the\nworkload pod template (Deployment/StatefulSet) so new pods start\ncorrectly sized. Default off. Do not enable under unmanaged GitOps\nsync without adopting recommendations in Git; prefer export or\ninitialSizing in that case.", "properties": { "enabled": { "description": "Enabled turns on template persistence. When false or unset, templates\nare never mutated.", "type": "boolean" }, "when": { "description": "When selects the trigger. Defaults to AfterSuccessfulResize when\nEnabled is true and When is empty.", "enum": [ "AfterSuccessfulResize", "OnRecommendation" ], "type": "string" } }, "type": "object", "additionalProperties": false }, "type": { "description": "Mode determines the update behavior, graduated from safe to automated:\n Recommend: collects metrics and writes recommendations to status, no pod changes.\n OneShot: resizes one pod per reconcile cycle.\n Canary: resizes a percentage of pods first, then the rest after observation.\n Auto: resizes all eligible pods each cycle.\n Observe: collects metrics and tracks data points but does not surface recommendations or savings.\nStart with Recommend in production and promote after reviewing status.\nDefaults to Recommend if not set (applied by the controller, not the webhook,\nso that AttuneDefaults cluster configuration can override it).", "enum": [ "Observe", "Recommend", "OneShot", "Canary", "Auto" ], "type": "string" } }, "type": "object", "additionalProperties": false }, "weight": { "default": 100, "description": "Weight determines the priority of this policy when multiple policies\nmatch the same workload. Higher values take precedence.", "format": "int32", "maximum": 1000, "minimum": 1, "type": "integer" } }, "required": [ "cpu", "memory", "metricsSource", "targetRef" ], "type": "object", "additionalProperties": false }, "status": { "description": "AttunePolicyStatus defines the observed state of AttunePolicy.", "properties": { "canary": { "description": "Canary tracks the canary rollout phase when autoPromote is enabled.", "properties": { "observedGeneration": { "description": "ObservedGeneration is the policy generation when this canary cycle\nstarted. If the policy spec changes (generation increments), the\ncanary observation resets so the new configuration is re-validated.", "format": "int64", "type": "integer" }, "phase": { "description": "Phase indicates the current canary state.\nCanaryInProgress: canary pods resized, observing for safety violations.\nFullRollout: observation passed with no violations, all pods are being resized.", "type": "string" }, "pods": { "description": "Pods lists the names of pods selected for the canary subset.\nPopulated when Mode is Canary and pods have been resized.", "items": { "type": "string" }, "maxItems": 100, "type": "array" }, "startTime": { "description": "StartTime is when the canary subset was first resized.", "format": "date-time", "type": "string" } }, "type": "object", "additionalProperties": false }, "conditions": { "description": "Conditions represent the latest available observations of the policy's state.", "items": { "description": "Condition contains details for one aspect of the current state of this API Resource.", "properties": { "lastTransitionTime": { "description": "lastTransitionTime is the last time the condition transitioned from one status to another.\nThis should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.", "format": "date-time", "type": "string" }, "message": { "description": "message is a human readable message indicating details about the transition.\nThis may be an empty string.", "maxLength": 32768, "type": "string" }, "observedGeneration": { "description": "observedGeneration represents the .metadata.generation that the condition was set based upon.\nFor instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date\nwith respect to the current state of the instance.", "format": "int64", "minimum": 0, "type": "integer" }, "reason": { "description": "reason contains a programmatic identifier indicating the reason for the condition's last transition.\nProducers of specific condition types may define expected values and meanings for this field,\nand whether the values are considered a guaranteed API.\nThe value should be a CamelCase string.\nThis field may not be empty.", "maxLength": 1024, "minLength": 1, "pattern": "^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$", "type": "string" }, "status": { "description": "status of the condition, one of True, False, Unknown.", "enum": [ "True", "False", "Unknown" ], "type": "string" }, "type": { "description": "type of condition in CamelCase or in foo.example.com/CamelCase.", "maxLength": 316, "pattern": "^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$", "type": "string" } }, "required": [ "lastTransitionTime", "message", "reason", "status", "type" ], "type": "object", "additionalProperties": false }, "type": "array", "x-kubernetes-list-map-keys": [ "type" ], "x-kubernetes-list-type": "map" }, "cooldown": { "description": "Cooldown exposes the effective cooldown including exponential backoff.", "properties": { "backoffMultiplier": { "description": "BackoffMultiplier is the current backoff multiplier (1, 2, 4, 8, or 16).", "format": "int32", "type": "integer" }, "consecutiveReverts": { "description": "ConsecutiveReverts is the number of consecutive reverts driving the backoff.", "format": "int32", "type": "integer" }, "effectiveCooldown": { "description": "EffectiveCooldown is the current cooldown duration including backoff.", "type": "string" } }, "type": "object", "additionalProperties": false }, "lastReconcileTime": { "description": "LastReconcileTime is the timestamp of the most recent reconciliation.\nServes as a heartbeat to confirm the operator is actively evaluating\nthis policy, even when no state changes occur.", "format": "date-time", "type": "string" }, "recommendations": { "description": "Recommendations contains per-workload resource recommendations.", "items": { "description": "WorkloadRecommendation contains recommendations for a single workload.", "properties": { "containers": { "description": "Containers contains per-container recommendations.", "items": { "description": "ContainerRecommendation contains recommendations for a single container.", "properties": { "confidence": { "description": "Confidence is the confidence score of the recommendation (0-1).", "type": "number" }, "current": { "description": "Current contains the current resource values.", "properties": { "cpuLimit": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "CPULimit is the CPU limit value.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "cpuRequest": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "CPURequest is the CPU request value.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "memoryLimit": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "MemoryLimit is the memory limit value.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "memoryRequest": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "MemoryRequest is the memory request value.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true } }, "required": [ "cpuLimit", "cpuRequest", "memoryLimit", "memoryRequest" ], "type": "object", "additionalProperties": false }, "dataPoints": { "description": "DataPoints is the number of data points used to generate the recommendation.", "format": "int32", "type": "integer" }, "explanation": { "description": "Explanation contains the reasoning chain behind the recommendation.", "properties": { "cpu": { "description": "CPU contains the CPU recommendation reasoning.", "properties": { "afterBounds": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "AfterBounds is the value after bounds clamping.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "afterBurst": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "AfterBurst is the value after applying the burst factor.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "afterChangeFilter": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "AfterChangeFilter is the value after change filtering.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "afterConfidence": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "AfterConfidence is the value after applying the confidence adjustment.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "afterOverhead": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "AfterOverhead is the value after applying the overhead.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "bounds": { "description": "Bounds are the configured minimum and maximum limits.", "properties": { "max": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "Max is the maximum allowed resource value.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "min": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "Min is the minimum allowed resource value.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true } }, "required": [ "max", "min" ], "type": "object", "additionalProperties": false }, "boundsApplied": { "description": "BoundsApplied indicates whether the value was clamped to min, max, or neither.", "type": "string" }, "burstFactor": { "description": "BurstFactor is the multiplier applied when burst is detected (max > 3x p95).\n1.0 when no burst. Uses logarithmic scaling to avoid excessive inflation.", "type": "number" }, "changeFilterApplied": { "description": "ChangeFilterApplied indicates whether the result was filtered or capped.", "type": "string" }, "confidence": { "description": "Confidence is the profile confidence score used for adjustment.", "type": "number" }, "confidenceFactor": { "description": "ConfidenceFactor is the multiplier derived from the confidence score.", "type": "number" }, "final": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "Final is the final resource recommendation after any post-processing.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "finalAdjustment": { "description": "FinalAdjustment describes any controller-level adjustment after the estimator chain.", "type": "string" }, "maxChangePercent": { "description": "MaxChangePercent is the maximum allowed change threshold.", "type": "number" }, "minChangePercent": { "description": "MinChangePercent is the minimum change threshold required to alter the current value.", "type": "number" }, "overhead": { "description": "Overhead is the configured overhead percentage applied to the raw percentile.", "type": "number" }, "rawPercentile": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "RawPercentile is the selected percentile before any adjustments.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true } }, "required": [ "afterBounds", "afterChangeFilter", "afterConfidence", "afterOverhead", "bounds", "confidence", "confidenceFactor", "final", "maxChangePercent", "minChangePercent", "overhead", "rawPercentile" ], "type": "object", "additionalProperties": false }, "memory": { "description": "Memory contains the memory recommendation reasoning.", "properties": { "afterBounds": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "AfterBounds is the value after bounds clamping.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "afterBurst": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "AfterBurst is the value after applying the burst factor.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "afterChangeFilter": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "AfterChangeFilter is the value after change filtering.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "afterConfidence": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "AfterConfidence is the value after applying the confidence adjustment.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "afterOverhead": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "AfterOverhead is the value after applying the overhead.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "bounds": { "description": "Bounds are the configured minimum and maximum limits.", "properties": { "max": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "Max is the maximum allowed resource value.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "min": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "Min is the minimum allowed resource value.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true } }, "required": [ "max", "min" ], "type": "object", "additionalProperties": false }, "boundsApplied": { "description": "BoundsApplied indicates whether the value was clamped to min, max, or neither.", "type": "string" }, "burstFactor": { "description": "BurstFactor is the multiplier applied when burst is detected (max > 3x p95).\n1.0 when no burst. Uses logarithmic scaling to avoid excessive inflation.", "type": "number" }, "changeFilterApplied": { "description": "ChangeFilterApplied indicates whether the result was filtered or capped.", "type": "string" }, "confidence": { "description": "Confidence is the profile confidence score used for adjustment.", "type": "number" }, "confidenceFactor": { "description": "ConfidenceFactor is the multiplier derived from the confidence score.", "type": "number" }, "final": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "Final is the final resource recommendation after any post-processing.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "finalAdjustment": { "description": "FinalAdjustment describes any controller-level adjustment after the estimator chain.", "type": "string" }, "maxChangePercent": { "description": "MaxChangePercent is the maximum allowed change threshold.", "type": "number" }, "minChangePercent": { "description": "MinChangePercent is the minimum change threshold required to alter the current value.", "type": "number" }, "overhead": { "description": "Overhead is the configured overhead percentage applied to the raw percentile.", "type": "number" }, "rawPercentile": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "RawPercentile is the selected percentile before any adjustments.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true } }, "required": [ "afterBounds", "afterChangeFilter", "afterConfidence", "afterOverhead", "bounds", "confidence", "confidenceFactor", "final", "maxChangePercent", "minChangePercent", "overhead", "rawPercentile" ], "type": "object", "additionalProperties": false } }, "type": "object", "additionalProperties": false }, "lastUpdated": { "description": "LastUpdated is the timestamp of the last recommendation update.", "format": "date-time", "type": "string" }, "name": { "description": "Name is the container name.", "type": "string" }, "recommended": { "description": "Recommended contains the recommended resource values.", "properties": { "cpuLimit": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "CPULimit is the CPU limit value.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "cpuRequest": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "CPURequest is the CPU request value.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "memoryLimit": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "MemoryLimit is the memory limit value.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true }, "memoryRequest": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "description": "MemoryRequest is the memory request value.", "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", "x-kubernetes-int-or-string": true } }, "required": [ "cpuLimit", "cpuRequest", "memoryLimit", "memoryRequest" ], "type": "object", "additionalProperties": false } }, "required": [ "confidence", "current", "dataPoints", "lastUpdated", "name", "recommended" ], "type": "object", "additionalProperties": false }, "type": "array" }, "kind": { "description": "Kind is the kind of the workload (e.g. Deployment, StatefulSet).", "type": "string" }, "lastDataTime": { "description": "LastDataTime is the timestamp when Prometheus last returned non-empty\ndata for this workload. Used for staleness detection.", "format": "date-time", "type": "string" }, "stale": { "description": "Stale indicates the recommendation is based on cached data because\nPrometheus did not return fresh data during the most recent query.\nResizes are not executed with stale recommendations.", "type": "boolean" }, "workload": { "description": "Workload is the name of the workload.", "type": "string" } }, "required": [ "containers", "kind", "workload" ], "type": "object", "additionalProperties": false }, "maxItems": 500, "type": "array" }, "resizeHistory": { "description": "ResizeHistory records past resize operations.", "items": { "description": "ResizeHistoryEntry records a single resize operation.", "properties": { "container": { "description": "Container is the name of the resized container.", "type": "string" }, "from": { "description": "From is the previous resource value.", "type": "string" }, "method": { "description": "Method is the resize method used.", "enum": [ "InPlace", "Eviction", "TemplatePersistence" ], "type": "string" }, "reason": { "description": "Reason explains why the resize was reverted or failed.\nOnly populated when Result is Reverted or Failed.\nValues include: oomkill, restart, notready, throttle,\nannotation-conflict, immediate-safety-check, slo:,\ninfeasible (kubelet Infeasible / InPlaceOnly skip).", "type": "string" }, "resource": { "description": "Resource is the resource type that was resized, or \"template\" when the\nhistory entry records a workload template persistence patch.", "enum": [ "cpu", "memory", "cpu+memory", "template" ], "type": "string" }, "result": { "description": "Result is the outcome of the resize operation.", "enum": [ "Success", "Failed", "Reverted", "Evicted", "TemplatePatched" ], "type": "string" }, "timestamp": { "description": "Timestamp is when the resize operation occurred.", "format": "date-time", "type": "string" }, "to": { "description": "To is the new resource value.", "type": "string" }, "workload": { "description": "Workload is the name of the resized workload.", "type": "string" } }, "required": [ "container", "from", "method", "resource", "result", "timestamp", "to", "workload" ], "type": "object", "additionalProperties": false }, "maxItems": 50, "type": "array" }, "savings": { "description": "Savings summarizes estimated resource savings.", "properties": { "cpuRequestIncrease": { "description": "CPURequestIncrease is the total CPU request increase for under-provisioned\nworkloads (e.g. \"500m\"). Empty when all recommendations are decreases.", "type": "string" }, "cpuRequestReduction": { "description": "CPURequestReduction is the total CPU request reduction (e.g. \"200m\").", "type": "string" }, "cpuRequestTotal": { "description": "CPURequestTotal is the total current CPU requests across all workloads (e.g. \"2000m\").", "type": "string" }, "estimatedMonthlyCostIncrease": { "description": "EstimatedMonthlyCostIncrease is the estimated monthly cost increase for\nunder-provisioned workloads based on configured or default pricing (e.g. \"$5.00\").", "type": "string" }, "estimatedMonthlySavings": { "description": "EstimatedMonthlySavings is the estimated monthly cost savings based on\nconfigured or default pricing (e.g. \"$12.50\").", "type": "string" }, "memoryRequestIncrease": { "description": "MemoryRequestIncrease is the total memory request increase for under-provisioned\nworkloads (e.g. \"512Mi\"). Empty when all recommendations are decreases.", "type": "string" }, "memoryRequestReduction": { "description": "MemoryRequestReduction is the total memory request reduction (e.g. \"256Mi\").", "type": "string" }, "memoryRequestTotal": { "description": "MemoryRequestTotal is the total current memory requests across all workloads (e.g. \"2Gi\").", "type": "string" }, "reclaimedCpuRequest": { "description": "ReclaimedCPURequest is estimated freeable CPU request capacity if recommended\ndecreases were applied (same quantity as cpuRequestReduction). Preferred field\nname for bin-packing and cluster-autoscaler capacity planning.", "type": "string" }, "reclaimedMemoryRequest": { "description": "ReclaimedMemoryRequest is estimated freeable memory request capacity if\nrecommended decreases were applied (same quantity as memoryRequestReduction).", "type": "string" } }, "type": "object", "additionalProperties": false }, "workloadErrors": { "description": "WorkloadErrors records per-workload errors from the most recent\nreconcile cycle. Capped at 10 entries to limit status size.", "items": { "description": "WorkloadError records an error encountered while processing a specific workload.", "properties": { "error": { "description": "Error is a human-readable description of the error.", "type": "string" }, "workload": { "description": "Workload is the name of the affected workload.", "type": "string" } }, "required": [ "error", "workload" ], "type": "object", "additionalProperties": false }, "maxItems": 10, "type": "array" }, "workloads": { "description": "Workloads summarizes workload discovery and resize counts.", "properties": { "dataPointsCollected": { "description": "DataPointsCollected is the maximum number of data points collected across\nall containers in the discovered workloads.", "format": "int32", "type": "integer" }, "dataPointsRequired": { "description": "DataPointsRequired is the minimum number of data points needed before\ngenerating recommendations (from metricsSource.minimumDataPoints).", "format": "int32", "type": "integer" }, "deferred": { "description": "Deferred is the number of pods whose in-place resize is Deferred by the\nkubelet (node cannot accept the change yet). Retried automatically when\nthe condition clears on a later reconcile.", "format": "int32", "type": "integer" }, "discovered": { "description": "Discovered is the number of workloads matching the target selector.", "format": "int32", "type": "integer" }, "infeasible": { "description": "Infeasible is the number of pods whose in-place resize is Infeasible on\nthe current node. With resizeMethod InPlaceOnly these pods are skipped;\nwith InPlaceOrRecreate the operator may fall back to eviction.", "format": "int32", "type": "integer" }, "pending": { "description": "Pending is the number of workloads awaiting resize.", "format": "int32", "type": "integer" }, "resized": { "description": "Resized is the number of workloads that have been resized.", "format": "int32", "type": "integer" }, "withRecommendations": { "description": "WithRecommendations is the number of workloads with active recommendations.", "format": "int32", "type": "integer" } }, "required": [ "discovered", "pending", "resized", "withRecommendations" ], "type": "object", "additionalProperties": false } }, "type": "object", "additionalProperties": false } }, "type": "object" }