diff --git a/.openshift-tests-extension/openshift_payload_cluster-version-operator.json b/.openshift-tests-extension/openshift_payload_cluster-version-operator.json index caca88f1d..6c5a18f5d 100644 --- a/.openshift-tests-extension/openshift_payload_cluster-version-operator.json +++ b/.openshift-tests-extension/openshift_payload_cluster-version-operator.json @@ -86,16 +86,6 @@ "lifecycle": "blocking", "environmentSelector": {} }, - { - "name": "[Jira:\"Cluster Version Operator\"] cluster-version-operator should install light speed CRDs correctly", - "labels": {}, - "resources": { - "isolation": {} - }, - "source": "openshift:payload:cluster-version-operator", - "lifecycle": "blocking", - "environmentSelector": {} - }, { "name": "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals", "labels": { diff --git a/cmd/cluster-version-operator-tests/main.go b/cmd/cluster-version-operator-tests/main.go index 537ef43fb..472130282 100644 --- a/cmd/cluster-version-operator-tests/main.go +++ b/cmd/cluster-version-operator-tests/main.go @@ -36,6 +36,9 @@ func main() { `(name.contains("[Serial]") || "Serial" in labels) && !("Local" in labels)`, }, }) + ext.IgnoreObsoleteTests( + `[Jira:"Cluster Version Operator"] cluster-version-operator should install light speed CRDs correctly`, + ) specs, err := g.BuildExtensionTestSpecsFromOpenShiftGinkgoSuite() if err != nil { diff --git a/install/0000_00_cluster-version-operator_45_lightspeed-crd-proposals.yaml b/install/0000_00_cluster-version-operator_45_lightspeed-crd-proposals.yaml deleted file mode 100644 index 621779646..000000000 --- a/install/0000_00_cluster-version-operator_45_lightspeed-crd-proposals.yaml +++ /dev/null @@ -1,2263 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - include.release.openshift.io/self-managed-high-availability: "true" - release.openshift.io/feature-set: TechPreviewNoUpgrade - controller-gen.kubebuilder.io/version: v0.19.0 - name: proposals.agentic.openshift.io -spec: - group: agentic.openshift.io - names: - kind: Proposal - listKind: ProposalList - plural: proposals - singular: proposal - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.request - name: Request - priority: 1 - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: "Proposal represents a unit of work managed by the agentic platform.\nIt - is the primary resource component teams and adapters interact with.\n\nA - Proposal defines the workflow shape inline: which steps run and which\nagent - handles each step. Analysis is always required. Omit execution\nand/or verification - to skip those steps.\n\nExample — analysis only (advisory):\n\n\tapiVersion: - agentic.openshift.io/v1alpha1\n\tkind: Proposal\n\tmetadata:\n\t name: - one-off-investigation\n\tspec:\n\t request: \"Investigate why pod foo is - crashlooping\"\n\t targetNamespaces:\n\t - lightspeed-demo\n\t tools:\n\t - \ skills:\n\t - image: registry.redhat.io/acs/acs-lightspeed-skills:latest\n\t - \ analysis:\n\t agent: smart\n\nExample — full remediation (analyze → - execute → verify):\n\n\tapiVersion: agentic.openshift.io/v1alpha1\n\tkind: - Proposal\n\tmetadata:\n\t name: fix-nginx-cve-2024-1234\n\t namespace: - stackrox\n\tspec:\n\t request: \"Fix CVE-2024-1234 in nginx:1.21\"\n\t - \ targetNamespaces:\n\t - lightspeed-demo\n\t tools:\n\t skills:\n\t - \ - image: registry.redhat.io/acs/acs-lightspeed-skills:latest\n\t requiredSecrets:\n\t - \ - name: acs-api-token\n\t mountAs:\n\t type: EnvVar\n\t - \ envVar:\n\t name: ACS_API_TOKEN\n\t analysis:\n\t - \ agent: smart\n\t execution: {}\n\t verification:\n\t agent: fast" - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More 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. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: spec defines the desired state of Proposal. - properties: - analysis: - description: |- - analysis defines per-step configuration for the analysis step, - including which agent handles it and any per-step tools. - - Immutable: agent and per-step tools are fixed at creation. - minProperties: 1 - properties: - agent: - description: |- - agent is the name of the cluster-scoped Agent CR to use for this step. - Defaults to "default" when omitted. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS subdomain: lowercase alphanumeric - characters, hyphens, and dots' - rule: '!format.dns1123Subdomain().validate(self).hasValue()' - tools: - description: |- - tools provides per-step tools that replace the shared spec.tools - for this step. Use this when different steps need different skills. - minProperties: 1 - properties: - mcpServers: - description: |- - mcpServers defines external MCP (Model Context Protocol) servers the - agent can connect to for additional tools and context. - items: - description: "MCPServerConfig defines the configuration - for an MCP (Model Context Protocol)\nserver that the agent - can connect to for additional tools and context.\nMCP - servers extend the agent's capabilities beyond its built-in - skills.\n\nExample — connecting to an OpenShift MCP server - with SA token auth:\n\n\tmcpServers:\n\t - name: openshift\n\t - \ url: https://mcp.openshift-lightspeed.svc:8443/sse\n\t - \ timeoutSeconds: 10\n\t headers:\n\t - name: - Authorization\n\t valueFrom:\n\t type: - ServiceAccountToken\n\nExample — connecting to an external - API with secret-based auth:\n\n\tmcpServers:\n\t - name: - pagerduty\n\t url: https://mcp-pagerduty.example.com/sse\n\t - \ headers:\n\t - name: X-API-Key\n\t valueFrom:\n\t - \ type: Secret\n\t secret:\n\t name: - pagerduty-api-key" - properties: - headers: - description: headers to send to the MCP server. Maximum - 20 items. - items: - description: |- - MCPHeader defines an HTTP header to send with every request to an - MCP server. Used for authentication and routing. - properties: - name: - description: |- - name of the header (e.g., "Authorization", "X-API-Key"). - Must be at least 1 character, containing only letters, digits, and hyphens. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: name must start with a letter and contain - only letters, digits, and hyphens - rule: self.matches('^[A-Za-z][A-Za-z0-9-]*$') - valueFrom: - description: valueFrom is the source of the header - value. - properties: - secret: - description: |- - secret references a Secret containing the header value. - Required when type is "Secret". - properties: - name: - description: name of the Secret. Must - be a valid RFC 1123 DNS subdomain. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS subdomain: - lowercase alphanumeric characters, - hyphens, and dots' - rule: '!format.dns1123Subdomain().validate(self).hasValue()' - required: - - name - type: object - type: - description: |- - type specifies the source type for the header value. Allowed values: - - "Secret" — reads the value from a Kubernetes Secret (use for - API keys and tokens). Requires the secret field to be set. - - "ServiceAccountToken" — auto-injects a Kubernetes service account token - (for MCP servers that accept K8s auth). - - "Client" — the value is provided by the calling client at - runtime (e.g., forwarded from a user session). - enum: - - Secret - - ServiceAccountToken - - Client - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: secret is required when type is Secret, - and forbidden otherwise - rule: 'self.type == ''Secret'' ? has(self.secret) - : !has(self.secret)' - required: - - name - - valueFrom - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - name: - description: |- - name of the MCP server. Must start with a letter and contain only - lowercase alphanumeric characters and hyphens. Must be 1-253 characters. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: name must start with a lowercase letter and - contain only lowercase alphanumerics and hyphens - rule: self.matches('^[a-z][a-z0-9-]*$') - timeoutSeconds: - default: 5 - description: |- - timeoutSeconds is the per-request timeout for calls to this MCP server, - in seconds. Default is 5. - Valid range: 1-300. - format: int32 - maximum: 300 - minimum: 1 - type: integer - url: - description: |- - url of the MCP server (HTTP/HTTPS). Must be an HTTP or HTTPS URL, - maximum 2048 characters. - maxLength: 2048 - minLength: 1 - type: string - x-kubernetes-validations: - - message: url must be a valid HTTP or HTTPS URL - rule: isURL(self) && url(self).getScheme() in ['http', - 'https'] - required: - - name - - url - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - requiredSecrets: - description: |- - requiredSecrets declares Kubernetes Secrets that the sandbox pod - needs at runtime. The cluster admin creates the actual Secrets - in the same namespace as the Proposal. - items: - description: |- - SecretRequirement declares a Kubernetes Secret that the sandbox needs - at runtime. The cluster admin creates the actual Secret in the same - namespace as the Proposal. - properties: - description: - description: |- - description explains what this secret is used for, helping the - cluster admin understand what credentials to provide. - maxLength: 1024 - minLength: 1 - type: string - mountAs: - description: mountAs specifies how the secret is exposed - in the sandbox pod. - properties: - envVar: - description: |- - envVar configures environment variable injection. - Required when type is "EnvVar". - properties: - name: - description: |- - name is the environment variable name (e.g., "GITHUB_TOKEN"). - Must be uppercase letters, digits, and underscores, starting - with a letter or underscore. - maxLength: 256 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid environment variable - name: uppercase letters, digits, and underscores, - starting with a letter or underscore' - rule: self.matches('^[A-Z_][A-Z0-9_]*$') - required: - - name - type: object - filePath: - description: |- - filePath configures file mount. - Required when type is "FilePath". - properties: - path: - description: |- - path is the absolute file path (e.g., "/etc/secrets/tls.crt"). - Must start with a forward slash. - maxLength: 512 - minLength: 2 - type: string - x-kubernetes-validations: - - message: path must be an absolute path starting - with '/' - rule: self.startsWith('/') - required: - - path - type: object - type: - description: |- - type specifies how the secret is exposed. Allowed values: "EnvVar", - "FilePath". - - When set to EnvVar, the secret value is injected as an environment - variable, and the 'envVar' field must be configured. - - When set to FilePath, the secret is mounted as a file, and the - 'filePath' field must be configured. - enum: - - EnvVar - - FilePath - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: envVar is required when type is EnvVar, and - forbidden otherwise - rule: 'self.type == ''EnvVar'' ? has(self.envVar) - : !has(self.envVar)' - - message: filePath is required when type is FilePath, - and forbidden otherwise - rule: 'self.type == ''FilePath'' ? has(self.filePath) - : !has(self.filePath)' - name: - description: |- - name of the Secret (must exist in the same namespace as the Proposal). - Must be a valid RFC 1123 DNS subdomain. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS subdomain: lowercase - alphanumeric characters, hyphens, and dots' - rule: '!format.dns1123Subdomain().validate(self).hasValue()' - required: - - mountAs - - name - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - skills: - description: |- - skills defines one or more OCI images containing skills to mount - in the agent's sandbox pod. The operator creates Kubernetes image - volumes (requires K8s 1.34+) and mounts them into the agent's - skills directory. Each image must be unique within the list. - items: - description: "SkillsSource defines an OCI image containing - skills and optionally which\npaths within that image to - mount. Skills are mounted as Kubernetes image\nvolumes - in the agent's sandbox pod.\n\nWhen paths is omitted, - the entire image is mounted. When paths is specified,\nonly - those directories are mounted (each as a separate subPath - volumeMount),\nallowing selective composition of skills - from large shared images.\n\nExample — mount all skills - from a custom image:\n\n\tskills:\n\t - image: quay.io/my-org/my-skills:latest\n\nExample - — selectively mount two skills from a shared image:\n\n\tskills:\n\t - \ - image: registry.ci.openshift.org/ocp/5.0:agentic-skills\n\t - \ paths:\n\t - /skills/prometheus\n\t - /skills/cluster-update/update-advisor" - properties: - image: - description: |- - image is the OCI image reference containing skills. - The operator mounts this as a Kubernetes image volume (requires K8s 1.34+). - Must be a valid OCI image pullspec: a domain, followed by a repository path, - ending with either a tag (:tag) or a digest (@algorithm:hex). - Must be 1-512 characters. - maxLength: 512 - minLength: 1 - type: string - x-kubernetes-validations: - - message: must start with a valid domain. valid domains - must be alphanumeric characters (lowercase and uppercase) - separated by the '.' character. - rule: self.matches('^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])((\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]))+)?(:[0-9]+)?\\b') - - message: a valid name is required. valid names must - contain lowercase alphanumeric characters separated - only by the '.', '_', '__', '-' characters. - rule: self.find('(/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?((/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?)+)?)') - != '' - - message: must end with a digest or a tag - rule: self.find('(@.*:)') != '' || self.find(':.*$') - != '' - - message: tag must not be more than 127 characters - rule: 'self.find(''(@.*:)'') == '''' ? (self.find('':.*$'') - != '''' ? self.find('':.*$'').substring(1).size() - <= 127 : true) : true' - - message: tag is invalid. valid tags must begin with - a word character followed by word characters, '.', - or '-' - rule: 'self.find(''(@.*:)'') == '''' ? (self.find('':.*$'') - != '''' ? self.find('':.*$'').matches('':[\\w][\\w.-]*$'') - : true) : true' - - message: digest algorithm is not valid. valid algorithms - must start with an alpha character followed by alphanumeric - characters and may contain '-', '_', '+', and '.' - characters. - rule: 'self.find(''(@.*:)'') != '''' ? self.find(''(@.*:)'').matches(''(@[A-Za-z][A-Za-z0-9]*([_+.][A-Za-z][A-Za-z0-9]*)*[:])'') - : true' - - message: digest must be at least 32 characters - rule: 'self.find(''(@.*:)'') != '''' ? self.find('':.*$'').substring(1).size() - >= 32 : true' - - message: digest must only contain hex characters (A-F, - a-f, 0-9) - rule: 'self.find(''(@.*:)'') != '''' ? self.find('':.*$'').matches('':[0-9A-Fa-f]*$'') - : true' - paths: - description: |- - paths restricts which directories from the image are mounted. - Each path is mounted as a separate subPath volumeMount into the agent's - skills directory. The last segment of each path becomes the mount name - (e.g., "/skills/prometheus" mounts as "prometheus"). - - Each path must be an absolute file path: starts with "/", no ".." - or "." segments, no double slashes, no trailing slash, and only - alphanumeric characters, hyphens, underscores, dots, and slashes. - - When omitted, the entire image is mounted as a single volume. - Maximum 50 items. - items: - maxLength: 512 - minLength: 2 - type: string - maxItems: 50 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - x-kubernetes-validations: - - message: each path must be 2-512 characters - rule: self.all(p, p.size() >= 2 && p.size() <= 512) - - message: each path must be absolute (start with '/') - rule: self.all(p, p.startsWith('/')) - - message: paths must not end with '/' - rule: self.all(p, !p.endsWith('/')) - - message: paths must not contain double slashes - rule: self.all(p, !p.contains('//')) - - message: paths must not contain '.' or '..' segments - rule: self.all(p, !p.contains('/../') && !p.endsWith('/..') - && !p.contains('/./') && !p.endsWith('/.')) - - message: paths may only contain alphanumeric characters, - '/', '_', '.', and '-' - rule: self.all(p, p.matches('^[a-zA-Z0-9/_.-]+$')) - required: - - image - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - image - x-kubernetes-list-type: map - type: object - type: object - execution: - description: |- - execution defines per-step configuration for the execution step. - Omit to skip execution (advisory/assisted patterns). - - Immutable: agent and per-step tools are fixed at creation. - minProperties: 1 - properties: - agent: - description: |- - agent is the name of the cluster-scoped Agent CR to use for this step. - Defaults to "default" when omitted. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS subdomain: lowercase alphanumeric - characters, hyphens, and dots' - rule: '!format.dns1123Subdomain().validate(self).hasValue()' - tools: - description: |- - tools provides per-step tools that replace the shared spec.tools - for this step. Use this when different steps need different skills. - minProperties: 1 - properties: - mcpServers: - description: |- - mcpServers defines external MCP (Model Context Protocol) servers the - agent can connect to for additional tools and context. - items: - description: "MCPServerConfig defines the configuration - for an MCP (Model Context Protocol)\nserver that the agent - can connect to for additional tools and context.\nMCP - servers extend the agent's capabilities beyond its built-in - skills.\n\nExample — connecting to an OpenShift MCP server - with SA token auth:\n\n\tmcpServers:\n\t - name: openshift\n\t - \ url: https://mcp.openshift-lightspeed.svc:8443/sse\n\t - \ timeoutSeconds: 10\n\t headers:\n\t - name: - Authorization\n\t valueFrom:\n\t type: - ServiceAccountToken\n\nExample — connecting to an external - API with secret-based auth:\n\n\tmcpServers:\n\t - name: - pagerduty\n\t url: https://mcp-pagerduty.example.com/sse\n\t - \ headers:\n\t - name: X-API-Key\n\t valueFrom:\n\t - \ type: Secret\n\t secret:\n\t name: - pagerduty-api-key" - properties: - headers: - description: headers to send to the MCP server. Maximum - 20 items. - items: - description: |- - MCPHeader defines an HTTP header to send with every request to an - MCP server. Used for authentication and routing. - properties: - name: - description: |- - name of the header (e.g., "Authorization", "X-API-Key"). - Must be at least 1 character, containing only letters, digits, and hyphens. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: name must start with a letter and contain - only letters, digits, and hyphens - rule: self.matches('^[A-Za-z][A-Za-z0-9-]*$') - valueFrom: - description: valueFrom is the source of the header - value. - properties: - secret: - description: |- - secret references a Secret containing the header value. - Required when type is "Secret". - properties: - name: - description: name of the Secret. Must - be a valid RFC 1123 DNS subdomain. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS subdomain: - lowercase alphanumeric characters, - hyphens, and dots' - rule: '!format.dns1123Subdomain().validate(self).hasValue()' - required: - - name - type: object - type: - description: |- - type specifies the source type for the header value. Allowed values: - - "Secret" — reads the value from a Kubernetes Secret (use for - API keys and tokens). Requires the secret field to be set. - - "ServiceAccountToken" — auto-injects a Kubernetes service account token - (for MCP servers that accept K8s auth). - - "Client" — the value is provided by the calling client at - runtime (e.g., forwarded from a user session). - enum: - - Secret - - ServiceAccountToken - - Client - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: secret is required when type is Secret, - and forbidden otherwise - rule: 'self.type == ''Secret'' ? has(self.secret) - : !has(self.secret)' - required: - - name - - valueFrom - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - name: - description: |- - name of the MCP server. Must start with a letter and contain only - lowercase alphanumeric characters and hyphens. Must be 1-253 characters. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: name must start with a lowercase letter and - contain only lowercase alphanumerics and hyphens - rule: self.matches('^[a-z][a-z0-9-]*$') - timeoutSeconds: - default: 5 - description: |- - timeoutSeconds is the per-request timeout for calls to this MCP server, - in seconds. Default is 5. - Valid range: 1-300. - format: int32 - maximum: 300 - minimum: 1 - type: integer - url: - description: |- - url of the MCP server (HTTP/HTTPS). Must be an HTTP or HTTPS URL, - maximum 2048 characters. - maxLength: 2048 - minLength: 1 - type: string - x-kubernetes-validations: - - message: url must be a valid HTTP or HTTPS URL - rule: isURL(self) && url(self).getScheme() in ['http', - 'https'] - required: - - name - - url - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - requiredSecrets: - description: |- - requiredSecrets declares Kubernetes Secrets that the sandbox pod - needs at runtime. The cluster admin creates the actual Secrets - in the same namespace as the Proposal. - items: - description: |- - SecretRequirement declares a Kubernetes Secret that the sandbox needs - at runtime. The cluster admin creates the actual Secret in the same - namespace as the Proposal. - properties: - description: - description: |- - description explains what this secret is used for, helping the - cluster admin understand what credentials to provide. - maxLength: 1024 - minLength: 1 - type: string - mountAs: - description: mountAs specifies how the secret is exposed - in the sandbox pod. - properties: - envVar: - description: |- - envVar configures environment variable injection. - Required when type is "EnvVar". - properties: - name: - description: |- - name is the environment variable name (e.g., "GITHUB_TOKEN"). - Must be uppercase letters, digits, and underscores, starting - with a letter or underscore. - maxLength: 256 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid environment variable - name: uppercase letters, digits, and underscores, - starting with a letter or underscore' - rule: self.matches('^[A-Z_][A-Z0-9_]*$') - required: - - name - type: object - filePath: - description: |- - filePath configures file mount. - Required when type is "FilePath". - properties: - path: - description: |- - path is the absolute file path (e.g., "/etc/secrets/tls.crt"). - Must start with a forward slash. - maxLength: 512 - minLength: 2 - type: string - x-kubernetes-validations: - - message: path must be an absolute path starting - with '/' - rule: self.startsWith('/') - required: - - path - type: object - type: - description: |- - type specifies how the secret is exposed. Allowed values: "EnvVar", - "FilePath". - - When set to EnvVar, the secret value is injected as an environment - variable, and the 'envVar' field must be configured. - - When set to FilePath, the secret is mounted as a file, and the - 'filePath' field must be configured. - enum: - - EnvVar - - FilePath - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: envVar is required when type is EnvVar, and - forbidden otherwise - rule: 'self.type == ''EnvVar'' ? has(self.envVar) - : !has(self.envVar)' - - message: filePath is required when type is FilePath, - and forbidden otherwise - rule: 'self.type == ''FilePath'' ? has(self.filePath) - : !has(self.filePath)' - name: - description: |- - name of the Secret (must exist in the same namespace as the Proposal). - Must be a valid RFC 1123 DNS subdomain. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS subdomain: lowercase - alphanumeric characters, hyphens, and dots' - rule: '!format.dns1123Subdomain().validate(self).hasValue()' - required: - - mountAs - - name - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - skills: - description: |- - skills defines one or more OCI images containing skills to mount - in the agent's sandbox pod. The operator creates Kubernetes image - volumes (requires K8s 1.34+) and mounts them into the agent's - skills directory. Each image must be unique within the list. - items: - description: "SkillsSource defines an OCI image containing - skills and optionally which\npaths within that image to - mount. Skills are mounted as Kubernetes image\nvolumes - in the agent's sandbox pod.\n\nWhen paths is omitted, - the entire image is mounted. When paths is specified,\nonly - those directories are mounted (each as a separate subPath - volumeMount),\nallowing selective composition of skills - from large shared images.\n\nExample — mount all skills - from a custom image:\n\n\tskills:\n\t - image: quay.io/my-org/my-skills:latest\n\nExample - — selectively mount two skills from a shared image:\n\n\tskills:\n\t - \ - image: registry.ci.openshift.org/ocp/5.0:agentic-skills\n\t - \ paths:\n\t - /skills/prometheus\n\t - /skills/cluster-update/update-advisor" - properties: - image: - description: |- - image is the OCI image reference containing skills. - The operator mounts this as a Kubernetes image volume (requires K8s 1.34+). - Must be a valid OCI image pullspec: a domain, followed by a repository path, - ending with either a tag (:tag) or a digest (@algorithm:hex). - Must be 1-512 characters. - maxLength: 512 - minLength: 1 - type: string - x-kubernetes-validations: - - message: must start with a valid domain. valid domains - must be alphanumeric characters (lowercase and uppercase) - separated by the '.' character. - rule: self.matches('^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])((\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]))+)?(:[0-9]+)?\\b') - - message: a valid name is required. valid names must - contain lowercase alphanumeric characters separated - only by the '.', '_', '__', '-' characters. - rule: self.find('(/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?((/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?)+)?)') - != '' - - message: must end with a digest or a tag - rule: self.find('(@.*:)') != '' || self.find(':.*$') - != '' - - message: tag must not be more than 127 characters - rule: 'self.find(''(@.*:)'') == '''' ? (self.find('':.*$'') - != '''' ? self.find('':.*$'').substring(1).size() - <= 127 : true) : true' - - message: tag is invalid. valid tags must begin with - a word character followed by word characters, '.', - or '-' - rule: 'self.find(''(@.*:)'') == '''' ? (self.find('':.*$'') - != '''' ? self.find('':.*$'').matches('':[\\w][\\w.-]*$'') - : true) : true' - - message: digest algorithm is not valid. valid algorithms - must start with an alpha character followed by alphanumeric - characters and may contain '-', '_', '+', and '.' - characters. - rule: 'self.find(''(@.*:)'') != '''' ? self.find(''(@.*:)'').matches(''(@[A-Za-z][A-Za-z0-9]*([_+.][A-Za-z][A-Za-z0-9]*)*[:])'') - : true' - - message: digest must be at least 32 characters - rule: 'self.find(''(@.*:)'') != '''' ? self.find('':.*$'').substring(1).size() - >= 32 : true' - - message: digest must only contain hex characters (A-F, - a-f, 0-9) - rule: 'self.find(''(@.*:)'') != '''' ? self.find('':.*$'').matches('':[0-9A-Fa-f]*$'') - : true' - paths: - description: |- - paths restricts which directories from the image are mounted. - Each path is mounted as a separate subPath volumeMount into the agent's - skills directory. The last segment of each path becomes the mount name - (e.g., "/skills/prometheus" mounts as "prometheus"). - - Each path must be an absolute file path: starts with "/", no ".." - or "." segments, no double slashes, no trailing slash, and only - alphanumeric characters, hyphens, underscores, dots, and slashes. - - When omitted, the entire image is mounted as a single volume. - Maximum 50 items. - items: - maxLength: 512 - minLength: 2 - type: string - maxItems: 50 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - x-kubernetes-validations: - - message: each path must be 2-512 characters - rule: self.all(p, p.size() >= 2 && p.size() <= 512) - - message: each path must be absolute (start with '/') - rule: self.all(p, p.startsWith('/')) - - message: paths must not end with '/' - rule: self.all(p, !p.endsWith('/')) - - message: paths must not contain double slashes - rule: self.all(p, !p.contains('//')) - - message: paths must not contain '.' or '..' segments - rule: self.all(p, !p.contains('/../') && !p.endsWith('/..') - && !p.contains('/./') && !p.endsWith('/.')) - - message: paths may only contain alphanumeric characters, - '/', '_', '.', and '-' - rule: self.all(p, p.matches('^[a-zA-Z0-9/_.-]+$')) - required: - - image - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - image - x-kubernetes-list-type: map - type: object - type: object - outputSchema: - description: |- - outputSchema is a JSON Schema injected as a required "components" - property in the analysis output. Use this to require adapter-specific - structured data beyond the base analysis schema (diagnosis, proposal, - RBAC, verification plan). - - Immutable: the output contract is fixed at creation. - type: object - x-kubernetes-preserve-unknown-fields: true - request: - description: |- - request is the user's original request, alert description, or a - description of what triggered this proposal. This text is passed to - the analysis agent as the primary input. - - Immutable: Proposals are run-to-completion (like Jobs). To change - the request, create a new Proposal. Use spec.revisionFeedback for - iterative feedback on an existing analysis. - maxLength: 32768 - minLength: 1 - type: string - x-kubernetes-validations: - - message: request is immutable after creation - rule: self == oldSelf - revisionFeedback: - description: |- - revisionFeedback is the user's free-text feedback requesting changes - to the analysis. Patching this field bumps metadata.generation, which - the operator detects (generation > observedGeneration) and triggers - re-analysis with the feedback appended to the original request. - - Mutable: this is the only mutable spec field. All other spec fields - are immutable via CEL rules, so generation changes signal revision. - maxLength: 32768 - minLength: 1 - type: string - targetNamespaces: - description: |- - targetNamespaces are the Kubernetes namespace(s) this proposal - operates on. Used for RBAC scoping and context to the analysis agent. - - When omitted, the proposal is not namespace-scoped — the analysis - agent determines the relevant namespaces from the request context. - Adapters (AlertManager, ACS) typically set this automatically from - the source event. - - Immutable: RBAC scoping is fixed at creation. Changing target - namespaces mid-flight would invalidate the analysis and any - granted execution RBAC. - items: - maxLength: 63 - minLength: 1 - type: string - maxItems: 50 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - x-kubernetes-validations: - - message: each namespace must be a valid DNS label - rule: self.all(ns, !format.dns1123Label().validate(ns).hasValue()) - tools: - description: |- - tools defines the default tools for all steps: skills images, - MCP servers, and required secrets. Per-step tools - (analysis.tools, execution.tools, verification.tools) replace - this default for individual steps. - - Immutable: the skills and secrets available to the agent are - fixed at creation. Changing tools mid-flight could violate the - assumptions of an in-progress analysis or execution. - minProperties: 1 - properties: - mcpServers: - description: |- - mcpServers defines external MCP (Model Context Protocol) servers the - agent can connect to for additional tools and context. - items: - description: "MCPServerConfig defines the configuration for - an MCP (Model Context Protocol)\nserver that the agent can - connect to for additional tools and context.\nMCP servers - extend the agent's capabilities beyond its built-in skills.\n\nExample - — connecting to an OpenShift MCP server with SA token auth:\n\n\tmcpServers:\n\t - \ - name: openshift\n\t url: https://mcp.openshift-lightspeed.svc:8443/sse\n\t - \ timeoutSeconds: 10\n\t headers:\n\t - name: Authorization\n\t - \ valueFrom:\n\t type: ServiceAccountToken\n\nExample - — connecting to an external API with secret-based auth:\n\n\tmcpServers:\n\t - \ - name: pagerduty\n\t url: https://mcp-pagerduty.example.com/sse\n\t - \ headers:\n\t - name: X-API-Key\n\t valueFrom:\n\t - \ type: Secret\n\t secret:\n\t name: - pagerduty-api-key" - properties: - headers: - description: headers to send to the MCP server. Maximum - 20 items. - items: - description: |- - MCPHeader defines an HTTP header to send with every request to an - MCP server. Used for authentication and routing. - properties: - name: - description: |- - name of the header (e.g., "Authorization", "X-API-Key"). - Must be at least 1 character, containing only letters, digits, and hyphens. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: name must start with a letter and contain - only letters, digits, and hyphens - rule: self.matches('^[A-Za-z][A-Za-z0-9-]*$') - valueFrom: - description: valueFrom is the source of the header - value. - properties: - secret: - description: |- - secret references a Secret containing the header value. - Required when type is "Secret". - properties: - name: - description: name of the Secret. Must be a - valid RFC 1123 DNS subdomain. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS subdomain: - lowercase alphanumeric characters, hyphens, - and dots' - rule: '!format.dns1123Subdomain().validate(self).hasValue()' - required: - - name - type: object - type: - description: |- - type specifies the source type for the header value. Allowed values: - - "Secret" — reads the value from a Kubernetes Secret (use for - API keys and tokens). Requires the secret field to be set. - - "ServiceAccountToken" — auto-injects a Kubernetes service account token - (for MCP servers that accept K8s auth). - - "Client" — the value is provided by the calling client at - runtime (e.g., forwarded from a user session). - enum: - - Secret - - ServiceAccountToken - - Client - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: secret is required when type is Secret, - and forbidden otherwise - rule: 'self.type == ''Secret'' ? has(self.secret) - : !has(self.secret)' - required: - - name - - valueFrom - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - name: - description: |- - name of the MCP server. Must start with a letter and contain only - lowercase alphanumeric characters and hyphens. Must be 1-253 characters. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: name must start with a lowercase letter and contain - only lowercase alphanumerics and hyphens - rule: self.matches('^[a-z][a-z0-9-]*$') - timeoutSeconds: - default: 5 - description: |- - timeoutSeconds is the per-request timeout for calls to this MCP server, - in seconds. Default is 5. - Valid range: 1-300. - format: int32 - maximum: 300 - minimum: 1 - type: integer - url: - description: |- - url of the MCP server (HTTP/HTTPS). Must be an HTTP or HTTPS URL, - maximum 2048 characters. - maxLength: 2048 - minLength: 1 - type: string - x-kubernetes-validations: - - message: url must be a valid HTTP or HTTPS URL - rule: isURL(self) && url(self).getScheme() in ['http', - 'https'] - required: - - name - - url - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - requiredSecrets: - description: |- - requiredSecrets declares Kubernetes Secrets that the sandbox pod - needs at runtime. The cluster admin creates the actual Secrets - in the same namespace as the Proposal. - items: - description: |- - SecretRequirement declares a Kubernetes Secret that the sandbox needs - at runtime. The cluster admin creates the actual Secret in the same - namespace as the Proposal. - properties: - description: - description: |- - description explains what this secret is used for, helping the - cluster admin understand what credentials to provide. - maxLength: 1024 - minLength: 1 - type: string - mountAs: - description: mountAs specifies how the secret is exposed - in the sandbox pod. - properties: - envVar: - description: |- - envVar configures environment variable injection. - Required when type is "EnvVar". - properties: - name: - description: |- - name is the environment variable name (e.g., "GITHUB_TOKEN"). - Must be uppercase letters, digits, and underscores, starting - with a letter or underscore. - maxLength: 256 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid environment variable - name: uppercase letters, digits, and underscores, - starting with a letter or underscore' - rule: self.matches('^[A-Z_][A-Z0-9_]*$') - required: - - name - type: object - filePath: - description: |- - filePath configures file mount. - Required when type is "FilePath". - properties: - path: - description: |- - path is the absolute file path (e.g., "/etc/secrets/tls.crt"). - Must start with a forward slash. - maxLength: 512 - minLength: 2 - type: string - x-kubernetes-validations: - - message: path must be an absolute path starting - with '/' - rule: self.startsWith('/') - required: - - path - type: object - type: - description: |- - type specifies how the secret is exposed. Allowed values: "EnvVar", - "FilePath". - - When set to EnvVar, the secret value is injected as an environment - variable, and the 'envVar' field must be configured. - - When set to FilePath, the secret is mounted as a file, and the - 'filePath' field must be configured. - enum: - - EnvVar - - FilePath - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: envVar is required when type is EnvVar, and forbidden - otherwise - rule: 'self.type == ''EnvVar'' ? has(self.envVar) : !has(self.envVar)' - - message: filePath is required when type is FilePath, and - forbidden otherwise - rule: 'self.type == ''FilePath'' ? has(self.filePath) - : !has(self.filePath)' - name: - description: |- - name of the Secret (must exist in the same namespace as the Proposal). - Must be a valid RFC 1123 DNS subdomain. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS subdomain: lowercase alphanumeric - characters, hyphens, and dots' - rule: '!format.dns1123Subdomain().validate(self).hasValue()' - required: - - mountAs - - name - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - skills: - description: |- - skills defines one or more OCI images containing skills to mount - in the agent's sandbox pod. The operator creates Kubernetes image - volumes (requires K8s 1.34+) and mounts them into the agent's - skills directory. Each image must be unique within the list. - items: - description: "SkillsSource defines an OCI image containing skills - and optionally which\npaths within that image to mount. Skills - are mounted as Kubernetes image\nvolumes in the agent's sandbox - pod.\n\nWhen paths is omitted, the entire image is mounted. - When paths is specified,\nonly those directories are mounted - (each as a separate subPath volumeMount),\nallowing selective - composition of skills from large shared images.\n\nExample - — mount all skills from a custom image:\n\n\tskills:\n\t - - image: quay.io/my-org/my-skills:latest\n\nExample — selectively - mount two skills from a shared image:\n\n\tskills:\n\t - - image: registry.ci.openshift.org/ocp/5.0:agentic-skills\n\t - \ paths:\n\t - /skills/prometheus\n\t - /skills/cluster-update/update-advisor" - properties: - image: - description: |- - image is the OCI image reference containing skills. - The operator mounts this as a Kubernetes image volume (requires K8s 1.34+). - Must be a valid OCI image pullspec: a domain, followed by a repository path, - ending with either a tag (:tag) or a digest (@algorithm:hex). - Must be 1-512 characters. - maxLength: 512 - minLength: 1 - type: string - x-kubernetes-validations: - - message: must start with a valid domain. valid domains - must be alphanumeric characters (lowercase and uppercase) - separated by the '.' character. - rule: self.matches('^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])((\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]))+)?(:[0-9]+)?\\b') - - message: a valid name is required. valid names must contain - lowercase alphanumeric characters separated only by - the '.', '_', '__', '-' characters. - rule: self.find('(/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?((/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?)+)?)') - != '' - - message: must end with a digest or a tag - rule: self.find('(@.*:)') != '' || self.find(':.*$') != - '' - - message: tag must not be more than 127 characters - rule: 'self.find(''(@.*:)'') == '''' ? (self.find('':.*$'') - != '''' ? self.find('':.*$'').substring(1).size() <= - 127 : true) : true' - - message: tag is invalid. valid tags must begin with a - word character followed by word characters, '.', or - '-' - rule: 'self.find(''(@.*:)'') == '''' ? (self.find('':.*$'') - != '''' ? self.find('':.*$'').matches('':[\\w][\\w.-]*$'') - : true) : true' - - message: digest algorithm is not valid. valid algorithms - must start with an alpha character followed by alphanumeric - characters and may contain '-', '_', '+', and '.' characters. - rule: 'self.find(''(@.*:)'') != '''' ? self.find(''(@.*:)'').matches(''(@[A-Za-z][A-Za-z0-9]*([_+.][A-Za-z][A-Za-z0-9]*)*[:])'') - : true' - - message: digest must be at least 32 characters - rule: 'self.find(''(@.*:)'') != '''' ? self.find('':.*$'').substring(1).size() - >= 32 : true' - - message: digest must only contain hex characters (A-F, - a-f, 0-9) - rule: 'self.find(''(@.*:)'') != '''' ? self.find('':.*$'').matches('':[0-9A-Fa-f]*$'') - : true' - paths: - description: |- - paths restricts which directories from the image are mounted. - Each path is mounted as a separate subPath volumeMount into the agent's - skills directory. The last segment of each path becomes the mount name - (e.g., "/skills/prometheus" mounts as "prometheus"). - - Each path must be an absolute file path: starts with "/", no ".." - or "." segments, no double slashes, no trailing slash, and only - alphanumeric characters, hyphens, underscores, dots, and slashes. - - When omitted, the entire image is mounted as a single volume. - Maximum 50 items. - items: - maxLength: 512 - minLength: 2 - type: string - maxItems: 50 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - x-kubernetes-validations: - - message: each path must be 2-512 characters - rule: self.all(p, p.size() >= 2 && p.size() <= 512) - - message: each path must be absolute (start with '/') - rule: self.all(p, p.startsWith('/')) - - message: paths must not end with '/' - rule: self.all(p, !p.endsWith('/')) - - message: paths must not contain double slashes - rule: self.all(p, !p.contains('//')) - - message: paths must not contain '.' or '..' segments - rule: self.all(p, !p.contains('/../') && !p.endsWith('/..') - && !p.contains('/./') && !p.endsWith('/.')) - - message: paths may only contain alphanumeric characters, - '/', '_', '.', and '-' - rule: self.all(p, p.matches('^[a-zA-Z0-9/_.-]+$')) - required: - - image - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - image - x-kubernetes-list-type: map - type: object - verification: - description: |- - verification defines per-step configuration for the verification step. - Omit to skip verification. - - Immutable: agent and per-step tools are fixed at creation. - minProperties: 1 - properties: - agent: - description: |- - agent is the name of the cluster-scoped Agent CR to use for this step. - Defaults to "default" when omitted. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS subdomain: lowercase alphanumeric - characters, hyphens, and dots' - rule: '!format.dns1123Subdomain().validate(self).hasValue()' - tools: - description: |- - tools provides per-step tools that replace the shared spec.tools - for this step. Use this when different steps need different skills. - minProperties: 1 - properties: - mcpServers: - description: |- - mcpServers defines external MCP (Model Context Protocol) servers the - agent can connect to for additional tools and context. - items: - description: "MCPServerConfig defines the configuration - for an MCP (Model Context Protocol)\nserver that the agent - can connect to for additional tools and context.\nMCP - servers extend the agent's capabilities beyond its built-in - skills.\n\nExample — connecting to an OpenShift MCP server - with SA token auth:\n\n\tmcpServers:\n\t - name: openshift\n\t - \ url: https://mcp.openshift-lightspeed.svc:8443/sse\n\t - \ timeoutSeconds: 10\n\t headers:\n\t - name: - Authorization\n\t valueFrom:\n\t type: - ServiceAccountToken\n\nExample — connecting to an external - API with secret-based auth:\n\n\tmcpServers:\n\t - name: - pagerduty\n\t url: https://mcp-pagerduty.example.com/sse\n\t - \ headers:\n\t - name: X-API-Key\n\t valueFrom:\n\t - \ type: Secret\n\t secret:\n\t name: - pagerduty-api-key" - properties: - headers: - description: headers to send to the MCP server. Maximum - 20 items. - items: - description: |- - MCPHeader defines an HTTP header to send with every request to an - MCP server. Used for authentication and routing. - properties: - name: - description: |- - name of the header (e.g., "Authorization", "X-API-Key"). - Must be at least 1 character, containing only letters, digits, and hyphens. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: name must start with a letter and contain - only letters, digits, and hyphens - rule: self.matches('^[A-Za-z][A-Za-z0-9-]*$') - valueFrom: - description: valueFrom is the source of the header - value. - properties: - secret: - description: |- - secret references a Secret containing the header value. - Required when type is "Secret". - properties: - name: - description: name of the Secret. Must - be a valid RFC 1123 DNS subdomain. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS subdomain: - lowercase alphanumeric characters, - hyphens, and dots' - rule: '!format.dns1123Subdomain().validate(self).hasValue()' - required: - - name - type: object - type: - description: |- - type specifies the source type for the header value. Allowed values: - - "Secret" — reads the value from a Kubernetes Secret (use for - API keys and tokens). Requires the secret field to be set. - - "ServiceAccountToken" — auto-injects a Kubernetes service account token - (for MCP servers that accept K8s auth). - - "Client" — the value is provided by the calling client at - runtime (e.g., forwarded from a user session). - enum: - - Secret - - ServiceAccountToken - - Client - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: secret is required when type is Secret, - and forbidden otherwise - rule: 'self.type == ''Secret'' ? has(self.secret) - : !has(self.secret)' - required: - - name - - valueFrom - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - name: - description: |- - name of the MCP server. Must start with a letter and contain only - lowercase alphanumeric characters and hyphens. Must be 1-253 characters. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: name must start with a lowercase letter and - contain only lowercase alphanumerics and hyphens - rule: self.matches('^[a-z][a-z0-9-]*$') - timeoutSeconds: - default: 5 - description: |- - timeoutSeconds is the per-request timeout for calls to this MCP server, - in seconds. Default is 5. - Valid range: 1-300. - format: int32 - maximum: 300 - minimum: 1 - type: integer - url: - description: |- - url of the MCP server (HTTP/HTTPS). Must be an HTTP or HTTPS URL, - maximum 2048 characters. - maxLength: 2048 - minLength: 1 - type: string - x-kubernetes-validations: - - message: url must be a valid HTTP or HTTPS URL - rule: isURL(self) && url(self).getScheme() in ['http', - 'https'] - required: - - name - - url - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - requiredSecrets: - description: |- - requiredSecrets declares Kubernetes Secrets that the sandbox pod - needs at runtime. The cluster admin creates the actual Secrets - in the same namespace as the Proposal. - items: - description: |- - SecretRequirement declares a Kubernetes Secret that the sandbox needs - at runtime. The cluster admin creates the actual Secret in the same - namespace as the Proposal. - properties: - description: - description: |- - description explains what this secret is used for, helping the - cluster admin understand what credentials to provide. - maxLength: 1024 - minLength: 1 - type: string - mountAs: - description: mountAs specifies how the secret is exposed - in the sandbox pod. - properties: - envVar: - description: |- - envVar configures environment variable injection. - Required when type is "EnvVar". - properties: - name: - description: |- - name is the environment variable name (e.g., "GITHUB_TOKEN"). - Must be uppercase letters, digits, and underscores, starting - with a letter or underscore. - maxLength: 256 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid environment variable - name: uppercase letters, digits, and underscores, - starting with a letter or underscore' - rule: self.matches('^[A-Z_][A-Z0-9_]*$') - required: - - name - type: object - filePath: - description: |- - filePath configures file mount. - Required when type is "FilePath". - properties: - path: - description: |- - path is the absolute file path (e.g., "/etc/secrets/tls.crt"). - Must start with a forward slash. - maxLength: 512 - minLength: 2 - type: string - x-kubernetes-validations: - - message: path must be an absolute path starting - with '/' - rule: self.startsWith('/') - required: - - path - type: object - type: - description: |- - type specifies how the secret is exposed. Allowed values: "EnvVar", - "FilePath". - - When set to EnvVar, the secret value is injected as an environment - variable, and the 'envVar' field must be configured. - - When set to FilePath, the secret is mounted as a file, and the - 'filePath' field must be configured. - enum: - - EnvVar - - FilePath - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: envVar is required when type is EnvVar, and - forbidden otherwise - rule: 'self.type == ''EnvVar'' ? has(self.envVar) - : !has(self.envVar)' - - message: filePath is required when type is FilePath, - and forbidden otherwise - rule: 'self.type == ''FilePath'' ? has(self.filePath) - : !has(self.filePath)' - name: - description: |- - name of the Secret (must exist in the same namespace as the Proposal). - Must be a valid RFC 1123 DNS subdomain. - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS subdomain: lowercase - alphanumeric characters, hyphens, and dots' - rule: '!format.dns1123Subdomain().validate(self).hasValue()' - required: - - mountAs - - name - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - skills: - description: |- - skills defines one or more OCI images containing skills to mount - in the agent's sandbox pod. The operator creates Kubernetes image - volumes (requires K8s 1.34+) and mounts them into the agent's - skills directory. Each image must be unique within the list. - items: - description: "SkillsSource defines an OCI image containing - skills and optionally which\npaths within that image to - mount. Skills are mounted as Kubernetes image\nvolumes - in the agent's sandbox pod.\n\nWhen paths is omitted, - the entire image is mounted. When paths is specified,\nonly - those directories are mounted (each as a separate subPath - volumeMount),\nallowing selective composition of skills - from large shared images.\n\nExample — mount all skills - from a custom image:\n\n\tskills:\n\t - image: quay.io/my-org/my-skills:latest\n\nExample - — selectively mount two skills from a shared image:\n\n\tskills:\n\t - \ - image: registry.ci.openshift.org/ocp/5.0:agentic-skills\n\t - \ paths:\n\t - /skills/prometheus\n\t - /skills/cluster-update/update-advisor" - properties: - image: - description: |- - image is the OCI image reference containing skills. - The operator mounts this as a Kubernetes image volume (requires K8s 1.34+). - Must be a valid OCI image pullspec: a domain, followed by a repository path, - ending with either a tag (:tag) or a digest (@algorithm:hex). - Must be 1-512 characters. - maxLength: 512 - minLength: 1 - type: string - x-kubernetes-validations: - - message: must start with a valid domain. valid domains - must be alphanumeric characters (lowercase and uppercase) - separated by the '.' character. - rule: self.matches('^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])((\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]))+)?(:[0-9]+)?\\b') - - message: a valid name is required. valid names must - contain lowercase alphanumeric characters separated - only by the '.', '_', '__', '-' characters. - rule: self.find('(/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?((/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?)+)?)') - != '' - - message: must end with a digest or a tag - rule: self.find('(@.*:)') != '' || self.find(':.*$') - != '' - - message: tag must not be more than 127 characters - rule: 'self.find(''(@.*:)'') == '''' ? (self.find('':.*$'') - != '''' ? self.find('':.*$'').substring(1).size() - <= 127 : true) : true' - - message: tag is invalid. valid tags must begin with - a word character followed by word characters, '.', - or '-' - rule: 'self.find(''(@.*:)'') == '''' ? (self.find('':.*$'') - != '''' ? self.find('':.*$'').matches('':[\\w][\\w.-]*$'') - : true) : true' - - message: digest algorithm is not valid. valid algorithms - must start with an alpha character followed by alphanumeric - characters and may contain '-', '_', '+', and '.' - characters. - rule: 'self.find(''(@.*:)'') != '''' ? self.find(''(@.*:)'').matches(''(@[A-Za-z][A-Za-z0-9]*([_+.][A-Za-z][A-Za-z0-9]*)*[:])'') - : true' - - message: digest must be at least 32 characters - rule: 'self.find(''(@.*:)'') != '''' ? self.find('':.*$'').substring(1).size() - >= 32 : true' - - message: digest must only contain hex characters (A-F, - a-f, 0-9) - rule: 'self.find(''(@.*:)'') != '''' ? self.find('':.*$'').matches('':[0-9A-Fa-f]*$'') - : true' - paths: - description: |- - paths restricts which directories from the image are mounted. - Each path is mounted as a separate subPath volumeMount into the agent's - skills directory. The last segment of each path becomes the mount name - (e.g., "/skills/prometheus" mounts as "prometheus"). - - Each path must be an absolute file path: starts with "/", no ".." - or "." segments, no double slashes, no trailing slash, and only - alphanumeric characters, hyphens, underscores, dots, and slashes. - - When omitted, the entire image is mounted as a single volume. - Maximum 50 items. - items: - maxLength: 512 - minLength: 2 - type: string - maxItems: 50 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - x-kubernetes-validations: - - message: each path must be 2-512 characters - rule: self.all(p, p.size() >= 2 && p.size() <= 512) - - message: each path must be absolute (start with '/') - rule: self.all(p, p.startsWith('/')) - - message: paths must not end with '/' - rule: self.all(p, !p.endsWith('/')) - - message: paths must not contain double slashes - rule: self.all(p, !p.contains('//')) - - message: paths must not contain '.' or '..' segments - rule: self.all(p, !p.contains('/../') && !p.endsWith('/..') - && !p.contains('/./') && !p.endsWith('/.')) - - message: paths may only contain alphanumeric characters, - '/', '_', '.', and '-' - rule: self.all(p, p.matches('^[a-zA-Z0-9/_.-]+$')) - required: - - image - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - image - x-kubernetes-list-type: map - type: object - type: object - required: - - analysis - - request - type: object - x-kubernetes-validations: - - message: analysis must be provided - rule: has(self.analysis) - - message: targetNamespaces is immutable once set - rule: '!has(oldSelf.targetNamespaces) || (has(self.targetNamespaces) - && self.targetNamespaces == oldSelf.targetNamespaces)' - - message: outputSchema is immutable once set - rule: '!has(oldSelf.outputSchema) || (has(self.outputSchema) && self.outputSchema - == oldSelf.outputSchema)' - - message: tools is immutable once set - rule: '!has(oldSelf.tools) || (has(self.tools) && self.tools == oldSelf.tools)' - - message: analysis is immutable once set - rule: '!has(oldSelf.analysis) || (has(self.analysis) && self.analysis - == oldSelf.analysis)' - - message: execution is immutable once set - rule: '!has(oldSelf.execution) || (has(self.execution) && self.execution - == oldSelf.execution)' - - message: verification is immutable once set - rule: '!has(oldSelf.verification) || (has(self.verification) && self.verification - == oldSelf.verification)' - status: - description: status defines the observed state of Proposal. - minProperties: 1 - properties: - conditions: - description: |- - conditions represent the latest available observations using the - standard Kubernetes condition pattern. Condition types include: - Analyzed, Approved, Executed, Verified, and Escalated. - 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. - This 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. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with 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. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This 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 - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - steps: - description: |- - steps contains the per-step observed state (analysis, execution, - verification). Each step independently tracks its timing, sandbox - info, and references to result CRs. - minProperties: 1 - properties: - analysis: - description: analysis is the observed state of the analysis step. - minProperties: 1 - properties: - conditions: - description: conditions for this step. - 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. - This 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. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with 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. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This 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 - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - results: - description: |- - results references AnalysisResult CRs, newest last. - Each entry corresponds to one analysis attempt. - items: - description: |- - StepResultRef is a lightweight reference to a result CR with an inline - success field for quick scanning without fetching the CR. - properties: - name: - description: name is the name of the result CR. - maxLength: 253 - minLength: 1 - type: string - outcome: - description: |- - outcome indicates the result of this step attempt. - Must be one of: Succeeded, Failed. - enum: - - Succeeded - - Failed - type: string - required: - - name - - outcome - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - sandbox: - description: sandbox tracks the sandbox used. - properties: - claimName: - description: |- - claimName is the name of the SandboxClaim resource that owns the - sandbox pod. Maximum 253 characters. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - namespace is the namespace where the SandboxClaim and its pod live. - Must be a valid RFC 1123 DNS label. - maxLength: 63 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS label: lowercase alphanumeric - characters and hyphens, starting with an alphabetic - character and ending with an alphanumeric character' - rule: '!format.dns1123Label().validate(self).hasValue()' - required: - - claimName - - namespace - type: object - type: object - escalation: - description: escalation is the observed state of the escalation - step. - minProperties: 1 - properties: - conditions: - description: conditions for this step. - 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. - This 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. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with 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. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This 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 - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - results: - description: results references EscalationResult CRs, newest - last. - items: - description: |- - StepResultRef is a lightweight reference to a result CR with an inline - success field for quick scanning without fetching the CR. - properties: - name: - description: name is the name of the result CR. - maxLength: 253 - minLength: 1 - type: string - outcome: - description: |- - outcome indicates the result of this step attempt. - Must be one of: Succeeded, Failed. - enum: - - Succeeded - - Failed - type: string - required: - - name - - outcome - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - sandbox: - description: sandbox tracks the sandbox used. - properties: - claimName: - description: |- - claimName is the name of the SandboxClaim resource that owns the - sandbox pod. Maximum 253 characters. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - namespace is the namespace where the SandboxClaim and its pod live. - Must be a valid RFC 1123 DNS label. - maxLength: 63 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS label: lowercase alphanumeric - characters and hyphens, starting with an alphabetic - character and ending with an alphanumeric character' - rule: '!format.dns1123Label().validate(self).hasValue()' - required: - - claimName - - namespace - type: object - type: object - execution: - description: execution is the observed state of the execution - step. - minProperties: 1 - properties: - conditions: - description: conditions for this step. - 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. - This 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. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with 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. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This 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 - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - results: - description: |- - results references ExecutionResult CRs, newest last. - Each entry corresponds to one execution attempt (including retries). - items: - description: |- - StepResultRef is a lightweight reference to a result CR with an inline - success field for quick scanning without fetching the CR. - properties: - name: - description: name is the name of the result CR. - maxLength: 253 - minLength: 1 - type: string - outcome: - description: |- - outcome indicates the result of this step attempt. - Must be one of: Succeeded, Failed. - enum: - - Succeeded - - Failed - type: string - required: - - name - - outcome - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - retryCount: - description: |- - retryCount tracks how many times execution+verification has been - retried for the current analysis option. Reset when a new analysis - is run (initial or revision). The operator increments this on each - objective verification failure before retrying execution. - format: int32 - minimum: 0 - type: integer - sandbox: - description: sandbox tracks the sandbox used. - properties: - claimName: - description: |- - claimName is the name of the SandboxClaim resource that owns the - sandbox pod. Maximum 253 characters. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - namespace is the namespace where the SandboxClaim and its pod live. - Must be a valid RFC 1123 DNS label. - maxLength: 63 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS label: lowercase alphanumeric - characters and hyphens, starting with an alphabetic - character and ending with an alphanumeric character' - rule: '!format.dns1123Label().validate(self).hasValue()' - required: - - claimName - - namespace - type: object - type: object - verification: - description: verification is the observed state of the verification - step. - minProperties: 1 - properties: - conditions: - description: conditions for this step. - 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. - This 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. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with 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. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This 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 - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - results: - description: |- - results references VerificationResult CRs, newest last. - Each entry corresponds to one verification attempt (including retries). - items: - description: |- - StepResultRef is a lightweight reference to a result CR with an inline - success field for quick scanning without fetching the CR. - properties: - name: - description: name is the name of the result CR. - maxLength: 253 - minLength: 1 - type: string - outcome: - description: |- - outcome indicates the result of this step attempt. - Must be one of: Succeeded, Failed. - enum: - - Succeeded - - Failed - type: string - required: - - name - - outcome - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - sandbox: - description: sandbox tracks the sandbox used. - properties: - claimName: - description: |- - claimName is the name of the SandboxClaim resource that owns the - sandbox pod. Maximum 253 characters. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - namespace is the namespace where the SandboxClaim and its pod live. - Must be a valid RFC 1123 DNS label. - maxLength: 63 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS label: lowercase alphanumeric - characters and hyphens, starting with an alphabetic - character and ending with an alphanumeric character' - rule: '!format.dns1123Label().validate(self).hasValue()' - required: - - claimName - - namespace - type: object - type: object - type: object - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} diff --git a/install/0000_00_cluster-version-operator_46_lightspeed-crd-agents.yaml b/install/0000_00_cluster-version-operator_46_lightspeed-crd-agents.yaml deleted file mode 100644 index e49810173..000000000 --- a/install/0000_00_cluster-version-operator_46_lightspeed-crd-agents.yaml +++ /dev/null @@ -1,207 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - include.release.openshift.io/self-managed-high-availability: "true" - release.openshift.io/feature-set: TechPreviewNoUpgrade - controller-gen.kubebuilder.io/version: v0.20.1 - name: agents.agentic.openshift.io -spec: - group: agentic.openshift.io - names: - kind: Agent - listKind: AgentList - plural: agents - singular: agent - scope: Cluster - versions: - - additionalPrinterColumns: - - jsonPath: .spec.llmProvider.name - name: LLM - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: "Agent defines a cluster-scoped agent tier (e.g., \"default\", - \"smart\", \"fast\").\nThe cluster admin creates Agent resources to configure - LLM infrastructure\nand runtime settings. Proposals reference agents by - name per step.\n\nAgent is cluster-scoped. The metadata.name serves as the - tier identifier.\nThe \"default\" agent must exist; \"smart\" and \"fast\" - are optional (the\noperator auto-links to \"default\" if absent).\n\nExample - — a high-capability agent tier:\n\n\tapiVersion: agentic.openshift.io/v1alpha1\n\tkind: - Agent\n\tmetadata:\n\t name: smart\n\tspec:\n\t llmProvider:\n\t name: - vertex-opus\n\t timeouts:\n\t analysisSeconds: 300\n\t executionSeconds: - 600\n\t maxTurns: 200\n\t providerSettings:\n\t reasoningEffort: \"high\"\n\nExample - — a fast, cost-efficient agent tier:\n\n\tapiVersion: agentic.openshift.io/v1alpha1\n\tkind: - Agent\n\tmetadata:\n\t name: fast\n\tspec:\n\t llmProvider:\n\t name: - vertex-haiku\n\t timeouts:\n\t analysisSeconds: 120\n\t executionSeconds: - 300\n\t maxTurns: 100\n\t providerSettings:\n\t reasoningEffort: \"low\"" - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More 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. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: spec defines the desired state of Agent. - properties: - llmProvider: - description: |- - llmProvider references a cluster-scoped LLMProvider CR that supplies the - LLM backend for this agent tier. - properties: - name: - description: name of the LLMProvider. - maxLength: 253 - minLength: 1 - type: string - required: - - name - type: object - maxTurns: - description: |- - maxTurns is the maximum number of tool-use turns the agent may take - in a single step invocation. Prevents runaway loops. - format: int32 - maximum: 500 - minimum: 1 - type: integer - providerSettings: - additionalProperties: - type: string - description: |- - providerSettings is a freeform key-value map passed through to the - LLM SDK. Use this for provider-specific tuning parameters such as - temperature, reasoningEffort, topP, etc. The operator does not - validate these keys — they are forwarded as-is to the SDK. - type: object - timeouts: - description: timeouts configures per-step and per-turn timeout limits. - properties: - analysisSeconds: - description: analysisSeconds is the timeout for the analysis step - in seconds. - format: int32 - maximum: 3600 - minimum: 1 - type: integer - chatSeconds: - description: chatSeconds is the timeout for each chat turn with - the LLM in seconds. - format: int32 - maximum: 600 - minimum: 1 - type: integer - executionSeconds: - description: executionSeconds is the timeout for the execution - step in seconds. - format: int32 - maximum: 3600 - minimum: 1 - type: integer - verificationSeconds: - description: verificationSeconds is the timeout for the verification - step in seconds. - format: int32 - maximum: 3600 - minimum: 1 - type: integer - type: object - required: - - llmProvider - type: object - status: - description: status defines the observed state of Agent. - properties: - conditions: - description: |- - conditions represent the latest available observations of the - Agent's state. The Ready condition summarizes whether all - referenced resources (LLMProvider, Secrets) are present. - Empty until the operator's first reconcile. - 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. - This 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. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with 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. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This 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 - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} diff --git a/install/0000_00_cluster-version-operator_47_lightspeed-crd-analysisresults.yaml b/install/0000_00_cluster-version-operator_47_lightspeed-crd-analysisresults.yaml deleted file mode 100644 index 6c09d321c..000000000 --- a/install/0000_00_cluster-version-operator_47_lightspeed-crd-analysisresults.yaml +++ /dev/null @@ -1,620 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - include.release.openshift.io/self-managed-high-availability: "true" - release.openshift.io/feature-set: TechPreviewNoUpgrade - controller-gen.kubebuilder.io/version: v0.19.0 - name: analysisresults.agentic.openshift.io -spec: - group: agentic.openshift.io - names: - kind: AnalysisResult - listKind: AnalysisResultList - plural: analysisresults - singular: analysisresult - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.proposalName - name: Proposal - type: string - - jsonPath: .status.conditions[?(@.type=="Completed")].reason - name: Outcome - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: |- - AnalysisResult records the output of a single analysis step execution. - Created by the operator after the analysis agent completes. Owned by - the parent Proposal for garbage collection. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More 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. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: spec contains the immutable identity fields for this result. - properties: - proposalName: - description: proposalName is the name of the parent Proposal in the - same namespace. - maxLength: 253 - minLength: 1 - type: string - required: - - proposalName - type: object - x-kubernetes-validations: - - message: spec is immutable - rule: self == oldSelf - status: - description: status contains result data and conditions. - minProperties: 1 - properties: - conditions: - description: conditions track the lifecycle of this result. - 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. - This 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. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with 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. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This 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 - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - failureReason: - description: failureReason is populated when the step failed due to - a system error. - maxLength: 8192 - minLength: 1 - type: string - options: - description: options contains the remediation options returned by - the analysis agent. - items: - description: |- - RemediationOption represents a single remediation approach produced by - the analysis agent. The agent may return multiple options, each with - its own diagnosis, remediation plan, verification strategy, and RBAC - requirements. The user selects one option after analysis - (recorded in AnalysisStepStatus.selectedOption), and the operator uses - that option's RBAC and plan for the execution step. - - The components field is an extensibility point for adapter-specific UI - data. For example, an ACS adapter might include violation details or - affected deployment information as components that the console plugin - renders with custom components. - properties: - components: - description: |- - components contains optional adapter-defined structured data whose - shape is determined by spec.outputSchema on the Proposal. The - operator passes this through to the AnalysisResult CR; the console - renders it using adapter-specific UI components. - x-kubernetes-preserve-unknown-fields: true - diagnosis: - description: diagnosis contains the root cause analysis specific - to this option. - properties: - confidence: - description: |- - confidence is the agent's self-assessed confidence in its diagnosis. - Higher confidence generally correlates with clearer symptoms and - more deterministic root causes. - enum: - - Low - - Medium - - High - type: string - rootCause: - description: |- - rootCause is a concise Markdown-formatted description of the identified - root cause (e.g., "OOMKilled due to memory limit of 256Mi"). - Maximum 1024 characters. - maxLength: 1024 - minLength: 1 - type: string - summary: - description: |- - summary is a Markdown-formatted diagnosis summary explaining the - problem, its symptoms, and the agent's findings. Maximum 8192 characters. - maxLength: 8192 - minLength: 1 - type: string - required: - - confidence - - rootCause - - summary - type: object - proposal: - description: proposal contains the remediation plan for this - option. - properties: - actions: - description: |- - actions is the ordered list of discrete actions the agent proposes. - Maximum 50 items. - items: - description: |- - ProposedAction describes a single discrete action the analysis agent - recommends as part of its remediation plan. Actions are displayed to - the user after analysis for review before approval. - properties: - description: - description: |- - description is a Markdown-formatted explanation of what this action - will do (e.g., "Increase memory limit from 256Mi to 512Mi"). - Maximum 4096 characters. - maxLength: 4096 - minLength: 1 - type: string - type: - description: |- - type is the action category (e.g., "patch", "scale", "restart", - "create", "delete", "rollout"). Free-form string to allow agents - to express domain-specific action types. Must be 1-256 characters. - maxLength: 256 - minLength: 1 - type: string - required: - - description - - type - type: object - maxItems: 50 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - description: - description: |- - description is a Markdown-formatted summary of the overall remediation - approach. Maximum 8192 characters. - maxLength: 8192 - minLength: 1 - type: string - estimatedImpact: - description: |- - estimatedImpact is a Markdown-formatted description of the expected - impact of the remediation on the system - (e.g., "Brief pod restart, ~30s downtime"). - Maximum 1024 characters. - maxLength: 1024 - minLength: 1 - type: string - reversible: - description: |- - reversible indicates whether the remediation can be rolled back - if something goes wrong. See rollbackPlan for details. - Must be one of: Reversible, Irreversible, Partial. - enum: - - Reversible - - Irreversible - - Partial - type: string - risk: - description: |- - risk is the agent's assessment of how risky the remediation is. - Critical-risk proposals typically require explicit human review. - enum: - - Low - - Medium - - High - - Critical - type: string - rollbackPlan: - description: |- - rollbackPlan describes how to undo the remediation if execution fails - or causes unexpected issues. Only the execution step mutates cluster - state, so rollback lives here alongside the actions it would undo. - properties: - command: - description: |- - command is the rollback command or steps to execute. - Maximum 4096 characters. - maxLength: 4096 - minLength: 1 - type: string - description: - description: |- - description is a Markdown-formatted explanation of the rollback strategy. - Must be 1-4096 characters. - maxLength: 4096 - minLength: 1 - type: string - required: - - description - type: object - required: - - actions - - description - - estimatedImpact - - risk - type: object - rbac: - description: |- - rbac contains the RBAC permissions the execution agent will need. - The operator's policy engine validates these before creating the - actual Kubernetes RBAC resources. Omitted for advisory-only options. - minProperties: 1 - properties: - clusterScoped: - description: |- - clusterScoped are rules that will be applied via ClusterRole + - ClusterRoleBinding. Used when the agent needs cross-namespace or - non-namespaced resource access (e.g., reading nodes, CRDs). - Maximum 50 items. - items: - description: |- - RBACRule describes a single RBAC permission that the analysis agent - requests for the execution step. The operator's policy engine validates - these requests against a 6-layer defense model before creating the - actual Role/ClusterRole bindings. Each rule must include a justification - so that users and policy can audit why the permission is needed. - properties: - apiGroups: - description: |- - apiGroups are the API groups for this rule (e.g., "", "apps", "batch"). - The empty string "" represents the core API group (pods, services, etc.). - Maximum 20 items, each up to 253 characters. - items: - maxLength: 253 - type: string - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - justification: - description: |- - justification is a Markdown-formatted explanation of why this - permission is needed for the remediation - (e.g., "Need to patch deployment to increase memory limit"). - Required for audit and policy enforcement. Maximum 1024 characters. - maxLength: 1024 - minLength: 1 - type: string - namespace: - description: |- - namespace is the target namespace for namespace-scoped rules. - Must match one of the proposal's targetNamespaces. Ignored for - cluster-scoped rules. Validation is deferred to the operator's - policy engine at runtime. Must be a valid RFC 1123 DNS label. - maxLength: 63 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS label: lowercase alphanumeric - characters and hyphens, starting with an alphabetic - character and ending with an alphanumeric character' - rule: '!format.dns1123Label().validate(self).hasValue()' - resourceNames: - description: |- - resourceNames restricts the rule to specific named resources. - When empty, the rule applies to all resources of the given type. - Maximum 50 items. - items: - maxLength: 253 - minLength: 1 - type: string - maxItems: 50 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - resources: - description: |- - resources are the resource types (e.g., "pods", "deployments"). - Maximum 20 items. - items: - maxLength: 253 - minLength: 1 - type: string - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - verbs: - description: |- - verbs are the allowed operations (e.g., "get", "patch", "delete"). - Maximum 10 items. - items: - maxLength: 63 - minLength: 1 - type: string - maxItems: 10 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - required: - - apiGroups - - justification - - resources - - verbs - type: object - maxItems: 50 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - namespaceScoped: - description: |- - namespaceScoped are rules that will be applied via Role + RoleBinding - in the proposal's target namespaces. These are the most common rules. - Maximum 50 items. - items: - description: |- - RBACRule describes a single RBAC permission that the analysis agent - requests for the execution step. The operator's policy engine validates - these requests against a 6-layer defense model before creating the - actual Role/ClusterRole bindings. Each rule must include a justification - so that users and policy can audit why the permission is needed. - properties: - apiGroups: - description: |- - apiGroups are the API groups for this rule (e.g., "", "apps", "batch"). - The empty string "" represents the core API group (pods, services, etc.). - Maximum 20 items, each up to 253 characters. - items: - maxLength: 253 - type: string - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - justification: - description: |- - justification is a Markdown-formatted explanation of why this - permission is needed for the remediation - (e.g., "Need to patch deployment to increase memory limit"). - Required for audit and policy enforcement. Maximum 1024 characters. - maxLength: 1024 - minLength: 1 - type: string - namespace: - description: |- - namespace is the target namespace for namespace-scoped rules. - Must match one of the proposal's targetNamespaces. Ignored for - cluster-scoped rules. Validation is deferred to the operator's - policy engine at runtime. Must be a valid RFC 1123 DNS label. - maxLength: 63 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS label: lowercase alphanumeric - characters and hyphens, starting with an alphabetic - character and ending with an alphanumeric character' - rule: '!format.dns1123Label().validate(self).hasValue()' - resourceNames: - description: |- - resourceNames restricts the rule to specific named resources. - When empty, the rule applies to all resources of the given type. - Maximum 50 items. - items: - maxLength: 253 - minLength: 1 - type: string - maxItems: 50 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - resources: - description: |- - resources are the resource types (e.g., "pods", "deployments"). - Maximum 20 items. - items: - maxLength: 253 - minLength: 1 - type: string - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - verbs: - description: |- - verbs are the allowed operations (e.g., "get", "patch", "delete"). - Maximum 10 items. - items: - maxLength: 63 - minLength: 1 - type: string - maxItems: 10 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - required: - - apiGroups - - justification - - resources - - verbs - type: object - maxItems: 50 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - type: object - summary: - description: |- - summary is an optional Markdown-formatted one-line summary for - collapsed views in the console UI. Maximum 1024 characters. - maxLength: 1024 - minLength: 1 - type: string - title: - description: |- - title is a short Markdown-formatted name for this option - (e.g., "Increase memory limit", "Restart with backoff"). - Must be 1-256 characters. - maxLength: 256 - minLength: 1 - type: string - verification: - description: |- - verification contains the verification plan. Omitted when - verification is skipped in the workflow. - properties: - description: - description: |- - description is a Markdown-formatted summary of the verification approach. - Maximum 4096 characters. - maxLength: 4096 - minLength: 1 - type: string - steps: - description: |- - steps is the ordered list of verification checks to run. - Maximum 20 items. - items: - description: |- - VerificationStep describes a single verification check that the - verification agent should run after execution. Populated by the - analysis agent as part of the RemediationOption. - properties: - command: - description: |- - command is the command or API call to run for this check - (e.g., "oc get pod -n production -l app=web -o jsonpath='{.items[0].status.phase}'"). - Maximum 4096 characters. - maxLength: 4096 - minLength: 1 - type: string - expected: - description: |- - expected is the expected output or condition - (e.g., "Running", "ready=true"). Maximum 1024 characters. - maxLength: 1024 - minLength: 1 - type: string - name: - description: |- - name is a short identifier for this check (e.g., "pod-running"). - Must be 1-253 characters. - maxLength: 253 - minLength: 1 - type: string - type: - description: |- - type categorizes the check (e.g., "command", "metric", "condition"). - Must be 1-256 characters. - maxLength: 256 - minLength: 1 - type: string - required: - - name - - type - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - required: - - description - type: object - required: - - diagnosis - - proposal - - title - type: object - maxItems: 10 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - sandbox: - description: sandbox tracks the sandbox pod used for this analysis. - properties: - claimName: - description: |- - claimName is the name of the SandboxClaim resource that owns the - sandbox pod. Maximum 253 characters. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - namespace is the namespace where the SandboxClaim and its pod live. - Must be a valid RFC 1123 DNS label. - maxLength: 63 - minLength: 1 - type: string - x-kubernetes-validations: - - message: 'must be a valid DNS label: lowercase alphanumeric - characters and hyphens, starting with an alphabetic character - and ending with an alphanumeric character' - rule: '!format.dns1123Label().validate(self).hasValue()' - required: - - claimName - - namespace - type: object - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} diff --git a/install/0000_00_cluster-version-operator_47_lightspeed-crd-llmproviders.yaml b/install/0000_00_cluster-version-operator_47_lightspeed-crd-llmproviders.yaml deleted file mode 100644 index 63836253c..000000000 --- a/install/0000_00_cluster-version-operator_47_lightspeed-crd-llmproviders.yaml +++ /dev/null @@ -1,317 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - include.release.openshift.io/self-managed-high-availability: "true" - release.openshift.io/feature-set: TechPreviewNoUpgrade - controller-gen.kubebuilder.io/version: v0.20.1 - name: llmproviders.agentic.openshift.io -spec: - group: agentic.openshift.io - names: - kind: LLMProvider - listKind: LLMProviderList - plural: llmproviders - singular: llmprovider - scope: Cluster - versions: - - additionalPrinterColumns: - - jsonPath: .spec.type - name: Type - type: string - - jsonPath: .spec.model - name: Model - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: "LLMProvider defines an LLM provider configuration. It is the - first link in\nthe CRD chain (LLMProvider -> Agent -> Workflow -> Proposal) - and is\nreferenced by Agent resources via spec.llmProvider.\n\nLLMProvider - is cluster-scoped — the cluster admin manages LLM infrastructure\ncentrally. - The operator uses the credentials and model to configure the LLM\nclient - inside agent sandbox pods.\n\nTypically you create a small number of providers - representing different\ncapability/cost tiers (e.g., \"smart\" for complex - analysis, \"fast\" for\nroutine execution) and then reference them from - multiple Agent resources.\n\nExample — a high-capability provider for analysis - tasks:\n\n\tapiVersion: agentic.openshift.io/v1alpha1\n\tkind: LLMProvider\n\tmetadata:\n\t - \ name: smart\n\tspec:\n\t type: GoogleCloudVertex\n\t model: claude-opus-4-6\n\t - \ googleCloudVertex:\n\t credentialsSecret:\n\t name: llm-credentials\n\t - \ namespace: openshift-lightspeed\n\t project: my-gcp-project\n\t - \ region: us-central1\n\nExample — a fast, cost-efficient provider for - execution tasks:\n\n\tapiVersion: agentic.openshift.io/v1alpha1\n\tkind: - LLMProvider\n\tmetadata:\n\t name: fast\n\tspec:\n\t type: GoogleCloudVertex\n\t - \ model: claude-haiku-4-5\n\t googleCloudVertex:\n\t credentialsSecret:\n\t - \ name: llm-credentials\n\t namespace: openshift-lightspeed\n\t - \ project: my-gcp-project\n\t region: us-central1" - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More 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. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: spec defines the desired state of LLMProvider. - properties: - anthropic: - description: |- - anthropic contains Anthropic-specific configuration. - Required when type is "Anthropic". - properties: - credentialsSecret: - description: |- - credentialsSecret references a Secret containing an ANTHROPIC_API_KEY. - Since LLMProvider is cluster-scoped, both name and namespace are required. - properties: - name: - description: name of the Secret. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: namespace of the Secret. - maxLength: 63 - minLength: 1 - type: string - required: - - name - - namespace - type: object - required: - - credentialsSecret - type: object - awsBedrock: - description: |- - awsBedrock contains AWS Bedrock-specific configuration. - Required when type is "AWSBedrock". - properties: - credentialsSecret: - description: |- - credentialsSecret references a Secret containing AWS_ACCESS_KEY_ID - and AWS_SECRET_ACCESS_KEY. Since LLMProvider is cluster-scoped, - both name and namespace are required. - properties: - name: - description: name of the Secret. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: namespace of the Secret. - maxLength: 63 - minLength: 1 - type: string - required: - - name - - namespace - type: object - region: - description: region is the AWS region for the Bedrock endpoint - (e.g., "us-east-1"). - maxLength: 63 - minLength: 1 - type: string - required: - - credentialsSecret - - region - type: object - azureOpenAI: - description: |- - azureOpenAI contains Azure OpenAI Service-specific configuration. - Required when type is "AzureOpenAI". - properties: - apiVersion: - description: |- - apiVersion is the Azure OpenAI API version (e.g., "2024-02-01"). - When omitted, the SDK default is used. - maxLength: 32 - type: string - credentialsSecret: - description: |- - credentialsSecret references a Secret containing an AZURE_OPENAI_API_KEY. - Since LLMProvider is cluster-scoped, both name and namespace are required. - properties: - name: - description: name of the Secret. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: namespace of the Secret. - maxLength: 63 - minLength: 1 - type: string - required: - - name - - namespace - type: object - endpoint: - description: |- - endpoint is the Azure OpenAI resource endpoint - (e.g., "https://my-resource.openai.azure.com"). - maxLength: 2048 - minLength: 1 - type: string - x-kubernetes-validations: - - message: endpoint must be a valid HTTP or HTTPS URL - rule: isURL(self) && url(self).getScheme() in ['http', 'https'] - required: - - credentialsSecret - - endpoint - type: object - googleCloudVertex: - description: |- - googleCloudVertex contains Google Cloud Vertex AI-specific configuration. - Required when type is "GoogleCloudVertex". - properties: - credentialsSecret: - description: |- - credentialsSecret references a Secret containing a service account JSON - key (stored under the key GOOGLE_APPLICATION_CREDENTIALS). Since - LLMProvider is cluster-scoped, both name and namespace are required. - properties: - name: - description: name of the Secret. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: namespace of the Secret. - maxLength: 63 - minLength: 1 - type: string - required: - - name - - namespace - type: object - project: - description: project is the GCP project ID where Vertex AI is - enabled. - maxLength: 63 - minLength: 1 - type: string - region: - description: region is the GCP region for the Vertex AI endpoint - (e.g., "us-central1"). - maxLength: 63 - minLength: 1 - type: string - required: - - credentialsSecret - - project - - region - type: object - model: - description: |- - model is the LLM model identifier as recognized by the provider - (e.g., "claude-opus-4-6", "claude-haiku-4-5", "gpt-4o"). - Different agents can reference different LLMProviders to use different - models for different tasks (e.g., a capable model for analysis, - a fast model for execution). Must be 1-256 characters, starting with - an alphanumeric character and containing only alphanumerics, dots, - hyphens, underscores, slashes, colons, and at-signs. - maxLength: 256 - minLength: 1 - type: string - x-kubernetes-validations: - - message: model must start with an alphanumeric character and contain - only alphanumerics, dots, hyphens, underscores, slashes, colons, - and at-signs - rule: self.matches('^[a-zA-Z0-9][a-zA-Z0-9._\\-/:@]*$') - openAI: - description: |- - openAI contains OpenAI-specific configuration. - Required when type is "OpenAI". - properties: - credentialsSecret: - description: |- - credentialsSecret references a Secret containing an OPENAI_API_KEY. - Since LLMProvider is cluster-scoped, both name and namespace are required. - properties: - name: - description: name of the Secret. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: namespace of the Secret. - maxLength: 63 - minLength: 1 - type: string - required: - - name - - namespace - type: object - required: - - credentialsSecret - type: object - type: - description: |- - type is the LLM provider backend. Determines which per-provider - configuration field must be set. Allowed values: "Anthropic", - "GoogleCloudVertex", "OpenAI", "AzureOpenAI", "AWSBedrock". - enum: - - Anthropic - - GoogleCloudVertex - - OpenAI - - AzureOpenAI - - AWSBedrock - type: string - url: - description: |- - url is an optional override for the provider API endpoint. - Most providers have well-known endpoints that the operator resolves - automatically, so this is only needed for custom deployments or - API proxies. This is not related to the cluster-wide egress proxy - (config.openshift.io/v1 Proxy). The operator honors the cluster - proxy configuration (HTTP_PROXY, HTTPS_PROXY, NO_PROXY) independently - when making requests to the provider endpoint. Must be an HTTP or - HTTPS URL, maximum 2048 characters. - maxLength: 2048 - minLength: 1 - type: string - x-kubernetes-validations: - - message: url must be a valid HTTP or HTTPS URL - rule: isURL(self) && url(self).getScheme() in ['http', 'https'] - required: - - model - - type - type: object - x-kubernetes-validations: - - message: anthropic is required when type is Anthropic, and forbidden - otherwise - rule: 'self.type == ''Anthropic'' ? has(self.anthropic) : !has(self.anthropic)' - - message: googleCloudVertex is required when type is GoogleCloudVertex, - and forbidden otherwise - rule: 'self.type == ''GoogleCloudVertex'' ? has(self.googleCloudVertex) - : !has(self.googleCloudVertex)' - - message: openAI is required when type is OpenAI, and forbidden otherwise - rule: 'self.type == ''OpenAI'' ? has(self.openAI) : !has(self.openAI)' - - message: azureOpenAI is required when type is AzureOpenAI, and forbidden - otherwise - rule: 'self.type == ''AzureOpenAI'' ? has(self.azureOpenAI) : !has(self.azureOpenAI)' - - message: awsBedrock is required when type is AWSBedrock, and forbidden - otherwise - rule: 'self.type == ''AWSBedrock'' ? has(self.awsBedrock) : !has(self.awsBedrock)' - required: - - spec - type: object - served: true - storage: true - subresources: {} diff --git a/test/cvo/proposal.go b/test/cvo/proposal.go index d46fc8c59..aed7cf312 100644 --- a/test/cvo/proposal.go +++ b/test/cvo/proposal.go @@ -11,8 +11,6 @@ import ( ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/config" - apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" - kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" @@ -39,12 +37,11 @@ func init() { var _ = g.Describe(`[Jira:"Cluster Version Operator"] cluster-version-operator`, func() { var ( - c *rest.Config - kubeClient kubernetes.Interface - configClient *configv1client.ConfigV1Client - apiExtensionsClient apiextensionsclientset.Interface - rtClient ctrlruntimeclient.Client - err error + c *rest.Config + kubeClient kubernetes.Interface + configClient *configv1client.ConfigV1Client + rtClient ctrlruntimeclient.Client + err error ctx = context.Background() needRecover bool @@ -63,9 +60,6 @@ var _ = g.Describe(`[Jira:"Cluster Version Operator"] cluster-version-operator`, configClient, err = configv1client.NewForConfig(c) o.Expect(err).To(o.BeNil()) - apiExtensionsClient, err = apiextensionsclientset.NewForConfig(c) - o.Expect(err).To(o.BeNil()) - rtClient, err = ctrlruntimeclient.New(config.GetConfigOrDie(), ctrlruntimeclient.Options{}) o.Expect(err).To(o.BeNil()) @@ -88,20 +82,10 @@ var _ = g.Describe(`[Jira:"Cluster Version Operator"] cluster-version-operator`, } }) - g.It("should install light speed CRDs correctly", func() { - for _, name := range []string{"proposals.agentic.openshift.io", "agents.agentic.openshift.io", "analysisresults.agentic.openshift.io", "llmproviders.agentic.openshift.io"} { - _, err := apiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, name, metav1.GetOptions{}) - if util.IsTechPreviewNoUpgrade(ctx, c) { - o.Expect(err).To(o.BeNil()) - } else { - o.Expect(kerrors.IsNotFound(err)).To(o.BeTrue()) - } - } - }) - g.It("should create proposals", g.Label("OTA-1966"), g.Label("Serial"), oteginkgo.Informing(), func() { o.Expect(util.SkipIfNetworkRestricted(ctx, c, util.FauxinnatiAPIURL)).To(o.BeNil()) util.SkipIfNotTechPreviewNoUpgrade(ctx, c) + util.SkipIfNoAPI(ctx, c, "proposals.agentic.openshift.io") cv, err := configClient.ClusterVersions().Get(ctx, external.DefaultClusterVersionName, metav1.GetOptions{}) o.Expect(err).NotTo(o.HaveOccurred()) diff --git a/test/util/util.go b/test/util/util.go index ede032406..d72bd069f 100644 --- a/test/util/util.go +++ b/test/util/util.go @@ -17,6 +17,7 @@ import ( authenticationv1 "k8s.io/api/authentication/v1" corev1 "k8s.io/api/core/v1" + apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -149,6 +150,19 @@ func SkipIfNotTechPreviewNoUpgrade(ctx context.Context, restConfig *rest.Config) } } +// SkipIfNoAPI skips the test if the cluster does not have a particular CustomResourceDefinition. +func SkipIfNoAPI(ctx context.Context, restConfig *rest.Config, apiName string) { + apiExtensionsClient, err := apiextensionsclientset.NewForConfig(restConfig) + o.Expect(err).To(o.BeNil()) + + _, err = apiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, apiName, metav1.GetOptions{}) + if apierrors.IsNotFound(err) { + g.Skip(fmt.Sprintf("This test is skipped because the %s CustomResourceDefinition is not installed", apiName)) + } else { + o.Expect(err).To(o.BeNil()) + } +} + const ( // fauxinnati mocks Cincinnati Update Graph Server for OpenShift FauxinnatiAPIURL = "https://fauxinnati-fauxinnati.apps.ota-stage.q2z4.p1.openshiftapps.com/api/upgrades_info/graph"