From f4425b419fbba95f1c37cddb09a773d436c63abd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 12:55:53 +0000 Subject: [PATCH 1/7] Initial plan From a4347d97106fcb1fde444075131c771bfc404779 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 13:04:11 +0000 Subject: [PATCH 2/7] Add ToolGateway and ToolGatewayClass CRDs with webhook and tests Co-authored-by: g3force <779094+g3force@users.noreply.github.com> --- .golangci.yml | 3 + PROJECT | 19 + api/v1alpha1/toolgateway_types.go | 116 ++++++ api/v1alpha1/zz_generated.deepcopy.go | 216 ++++++++++++ cmd/main.go | 4 + ...e.agentic-layer.ai_toolgatewayclasses.yaml | 122 +++++++ ...runtime.agentic-layer.ai_toolgateways.yaml | 332 ++++++++++++++++++ .../samples/runtime_v1alpha1_toolgateway.yaml | 17 + config/webhook/manifests.yaml | 20 ++ .../webhook/v1alpha1/toolgateway_webhook.go | 86 +++++ .../v1alpha1/toolgateway_webhook_test.go | 123 +++++++ 11 files changed, 1058 insertions(+) create mode 100644 api/v1alpha1/toolgateway_types.go create mode 100644 config/crd/bases/runtime.agentic-layer.ai_toolgatewayclasses.yaml create mode 100644 config/crd/bases/runtime.agentic-layer.ai_toolgateways.yaml create mode 100644 config/samples/runtime_v1alpha1_toolgateway.yaml create mode 100644 internal/webhook/v1alpha1/toolgateway_webhook.go create mode 100644 internal/webhook/v1alpha1/toolgateway_webhook_test.go diff --git a/.golangci.yml b/.golangci.yml index e5b21b0..8e56be0 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -32,6 +32,9 @@ linters: - linters: - lll path: api/* + - linters: + - dupl + path: api/v1alpha1/ - linters: - dupl - lll diff --git a/PROJECT b/PROJECT index aa73029..cd6a2fb 100644 --- a/PROJECT +++ b/PROJECT @@ -58,4 +58,23 @@ resources: kind: AiGatewayClass path: github.com/agentic-layer/agent-runtime-operator/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + domain: agentic-layer.ai + group: runtime + kind: ToolGateway + path: github.com/agentic-layer/agent-runtime-operator/api/v1alpha1 + version: v1alpha1 + webhooks: + defaulting: true + webhookVersion: v1 +- api: + crdVersion: v1 + namespaced: false + domain: agentic-layer.ai + group: runtime + kind: ToolGatewayClass + path: github.com/agentic-layer/agent-runtime-operator/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/api/v1alpha1/toolgateway_types.go b/api/v1alpha1/toolgateway_types.go new file mode 100644 index 0000000..ffc91dc --- /dev/null +++ b/api/v1alpha1/toolgateway_types.go @@ -0,0 +1,116 @@ +/* +Copyright 2025 Agentic Layer. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ToolGatewaySpec defines the desired state of ToolGateway +type ToolGatewaySpec struct { + // ToolGatewayClassName specifies which ToolGatewayClass to use for this gateway instance. + // This is only needed if multiple gateway classes are defined in the cluster. + ToolGatewayClassName string `json:"toolGatewayClassName,omitempty"` + + // Replicas is the number of gateway replicas + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:default=1 + Replicas *int32 `json:"replicas,omitempty"` + + // Timeout specifies the gateway timeout for requests + // +kubebuilder:default="360s" + Timeout *metav1.Duration `json:"timeout,omitempty"` + + // Environment variables to pass to the ToolGateway container. + // These can include configuration values, credentials, or feature flags. + // +optional + Env []corev1.EnvVar `json:"env,omitempty"` + + // List of sources to populate environment variables in the ToolGateway container. + // This allows loading variables from ConfigMaps and Secrets. + // +optional + EnvFrom []corev1.EnvFromSource `json:"envFrom,omitempty"` +} + +// ToolGatewayStatus defines the observed state of ToolGateway +type ToolGatewayStatus struct { + // Conditions represent the latest available observations of the gateway's state + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// ToolGateway is the Schema for the toolgateways API +type ToolGateway struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ToolGatewaySpec `json:"spec,omitempty"` + Status ToolGatewayStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ToolGatewayList contains a list of ToolGateway +type ToolGatewayList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ToolGateway `json:"items"` +} + +// ToolGatewayClassSpec defines the desired state of ToolGatewayClass +type ToolGatewayClassSpec struct { + // Controller is the name of the controller that should handle this gateway class + // +kubebuilder:validation:Required + Controller string `json:"controller"` +} + +// ToolGatewayClassStatus defines the observed state of ToolGatewayClass +type ToolGatewayClassStatus struct { + // Conditions represent the latest available observations of the gateway class's state + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster +// +kubebuilder:printcolumn:name="Controller",type="string",JSONPath=".spec.controller" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" + +// ToolGatewayClass is the Schema for the toolgatewayclasses API +type ToolGatewayClass struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ToolGatewayClassSpec `json:"spec,omitempty"` + Status ToolGatewayClassStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ToolGatewayClassList contains a list of ToolGatewayClass +type ToolGatewayClassList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ToolGatewayClass `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ToolGateway{}, &ToolGatewayList{}, &ToolGatewayClass{}, &ToolGatewayClassList{}) +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 3f0b3a0..37c4977 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -808,6 +808,222 @@ func (in *SubAgent) DeepCopy() *SubAgent { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ToolGateway) DeepCopyInto(out *ToolGateway) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ToolGateway. +func (in *ToolGateway) DeepCopy() *ToolGateway { + if in == nil { + return nil + } + out := new(ToolGateway) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ToolGateway) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ToolGatewayClass) DeepCopyInto(out *ToolGatewayClass) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ToolGatewayClass. +func (in *ToolGatewayClass) DeepCopy() *ToolGatewayClass { + if in == nil { + return nil + } + out := new(ToolGatewayClass) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ToolGatewayClass) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ToolGatewayClassList) DeepCopyInto(out *ToolGatewayClassList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ToolGatewayClass, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ToolGatewayClassList. +func (in *ToolGatewayClassList) DeepCopy() *ToolGatewayClassList { + if in == nil { + return nil + } + out := new(ToolGatewayClassList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ToolGatewayClassList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ToolGatewayClassSpec) DeepCopyInto(out *ToolGatewayClassSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ToolGatewayClassSpec. +func (in *ToolGatewayClassSpec) DeepCopy() *ToolGatewayClassSpec { + if in == nil { + return nil + } + out := new(ToolGatewayClassSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ToolGatewayClassStatus) DeepCopyInto(out *ToolGatewayClassStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ToolGatewayClassStatus. +func (in *ToolGatewayClassStatus) DeepCopy() *ToolGatewayClassStatus { + if in == nil { + return nil + } + out := new(ToolGatewayClassStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ToolGatewayList) DeepCopyInto(out *ToolGatewayList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ToolGateway, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ToolGatewayList. +func (in *ToolGatewayList) DeepCopy() *ToolGatewayList { + if in == nil { + return nil + } + out := new(ToolGatewayList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ToolGatewayList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ToolGatewaySpec) DeepCopyInto(out *ToolGatewaySpec) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(metav1.Duration) + **out = **in + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.EnvFrom != nil { + in, out := &in.EnvFrom, &out.EnvFrom + *out = make([]v1.EnvFromSource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ToolGatewaySpec. +func (in *ToolGatewaySpec) DeepCopy() *ToolGatewaySpec { + if in == nil { + return nil + } + out := new(ToolGatewaySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ToolGatewayStatus) DeepCopyInto(out *ToolGatewayStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ToolGatewayStatus. +func (in *ToolGatewayStatus) DeepCopy() *ToolGatewayStatus { + if in == nil { + return nil + } + out := new(ToolGatewayStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ToolServer) DeepCopyInto(out *ToolServer) { *out = *in diff --git a/cmd/main.go b/cmd/main.go index b8e7f69..76989f3 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -225,6 +225,10 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "AgentGateway") os.Exit(1) } + if err := webhookv1alpha1.SetupToolGatewayWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ToolGateway") + os.Exit(1) + } } if err := (&controller.AgenticWorkforceReconciler{ Client: mgr.GetClient(), diff --git a/config/crd/bases/runtime.agentic-layer.ai_toolgatewayclasses.yaml b/config/crd/bases/runtime.agentic-layer.ai_toolgatewayclasses.yaml new file mode 100644 index 0000000..1a36c33 --- /dev/null +++ b/config/crd/bases/runtime.agentic-layer.ai_toolgatewayclasses.yaml @@ -0,0 +1,122 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: toolgatewayclasses.runtime.agentic-layer.ai +spec: + group: runtime.agentic-layer.ai + names: + kind: ToolGatewayClass + listKind: ToolGatewayClassList + plural: toolgatewayclasses + singular: toolgatewayclass + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .spec.controller + name: Controller + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ToolGatewayClass is the Schema for the toolgatewayclasses API + 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: ToolGatewayClassSpec defines the desired state of ToolGatewayClass + properties: + controller: + description: Controller is the name of the controller that should + handle this gateway class + type: string + required: + - controller + type: object + status: + description: ToolGatewayClassStatus defines the observed state of ToolGatewayClass + properties: + conditions: + description: Conditions represent the latest available observations + of the gateway class's state + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + 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 + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/runtime.agentic-layer.ai_toolgateways.yaml b/config/crd/bases/runtime.agentic-layer.ai_toolgateways.yaml new file mode 100644 index 0000000..be3fd9c --- /dev/null +++ b/config/crd/bases/runtime.agentic-layer.ai_toolgateways.yaml @@ -0,0 +1,332 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: toolgateways.runtime.agentic-layer.ai +spec: + group: runtime.agentic-layer.ai + names: + kind: ToolGateway + listKind: ToolGatewayList + plural: toolgateways + singular: toolgateway + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ToolGateway is the Schema for the toolgateways API + 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: ToolGatewaySpec defines the desired state of ToolGateway + properties: + env: + description: |- + Environment variables to pass to the ToolGateway container. + These can include configuration values, credentials, or feature flags. + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing + the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + description: |- + List of sources to populate environment variables in the ToolGateway container. + This allows loading variables from ConfigMaps and Secrets. + items: + description: EnvFromSource represents the source of a set of ConfigMaps + or Secrets + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: |- + Optional text to prepend to the name of each environment variable. + May consist of any printable ASCII characters except '='. + type: string + secretRef: + description: The Secret to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + replicas: + default: 1 + description: Replicas is the number of gateway replicas + format: int32 + minimum: 1 + type: integer + timeout: + default: 360s + description: Timeout specifies the gateway timeout for requests + type: string + toolGatewayClassName: + description: |- + ToolGatewayClassName specifies which ToolGatewayClass to use for this gateway instance. + This is only needed if multiple gateway classes are defined in the cluster. + type: string + type: object + status: + description: ToolGatewayStatus defines the observed state of ToolGateway + properties: + conditions: + description: Conditions represent the latest available observations + of the gateway's state + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + 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 + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/samples/runtime_v1alpha1_toolgateway.yaml b/config/samples/runtime_v1alpha1_toolgateway.yaml new file mode 100644 index 0000000..a0be41c --- /dev/null +++ b/config/samples/runtime_v1alpha1_toolgateway.yaml @@ -0,0 +1,17 @@ +apiVersion: runtime.agentic-layer.ai/v1alpha1 +kind: ToolGateway +metadata: + labels: + app.kubernetes.io/name: agent-runtime-operator + app.kubernetes.io/managed-by: kustomize + name: toolgateway-sample +spec: + # toolGatewayClassName: krakend # Optional: specify a gateway class + replicas: 1 + timeout: 360s + # env: # Optional: environment variables + # - name: LOG_LEVEL + # value: info + # envFrom: # Optional: environment variables from ConfigMaps or Secrets + # - configMapRef: + # name: toolgateway-config diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index ba4b561..6df2af0 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -44,6 +44,26 @@ webhooks: resources: - agentgateways sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-runtime-agentic-layer-ai-v1alpha1-toolgateway + failurePolicy: Fail + name: mtoolgateway-v1alpha1.kb.io + rules: + - apiGroups: + - runtime.agentic-layer.ai + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - toolgateways + sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/internal/webhook/v1alpha1/toolgateway_webhook.go b/internal/webhook/v1alpha1/toolgateway_webhook.go new file mode 100644 index 0000000..41ea4d1 --- /dev/null +++ b/internal/webhook/v1alpha1/toolgateway_webhook.go @@ -0,0 +1,86 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "context" + "fmt" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + runtimev1alpha1 "github.com/agentic-layer/agent-runtime-operator/api/v1alpha1" +) + +var toolgatewaylog = logf.Log.WithName("toolgateway-resource") + +// SetupToolGatewayWebhookWithManager registers the webhook for ToolGateway in the manager. +func SetupToolGatewayWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr).For(&runtimev1alpha1.ToolGateway{}). + WithDefaulter(&ToolGatewayCustomDefaulter{ + DefaultReplicas: 1, + Recorder: mgr.GetEventRecorderFor("toolgateway-defaulter-webhook"), + }). + Complete() +} + +// +kubebuilder:webhook:path=/mutate-runtime-agentic-layer-ai-v1alpha1-toolgateway,mutating=true,failurePolicy=fail,sideEffects=None,groups=runtime.agentic-layer.ai,resources=toolgateways,verbs=create;update,versions=v1alpha1,name=mtoolgateway-v1alpha1.kb.io,admissionReviewVersions=v1 + +// ToolGatewayCustomDefaulter struct is responsible for setting default values on the custom resource of the +// Kind ToolGateway when those are created or updated. +// +// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, +// as it is used only for temporary operations and does not need to be deeply copied. +type ToolGatewayCustomDefaulter struct { + DefaultReplicas int32 + Recorder record.EventRecorder +} + +var _ webhook.CustomDefaulter = &ToolGatewayCustomDefaulter{} + +// Default implements webhook.CustomDefaulter so a webhook will be registered for the Kind ToolGateway. +func (d *ToolGatewayCustomDefaulter) Default(_ context.Context, obj runtime.Object) error { + toolgateway, ok := obj.(*runtimev1alpha1.ToolGateway) + + if !ok { + return fmt.Errorf("expected a ToolGateway object but got %T", obj) + } + toolgatewaylog.Info("Defaulting for ToolGateway", "name", toolgateway.GetName()) + + d.applyDefaults(toolgateway) + + return nil +} + +// applyDefaults applies default values to the ToolGateway. +func (d *ToolGatewayCustomDefaulter) applyDefaults(toolgateway *runtimev1alpha1.ToolGateway) { + // Set default replicas if not specified + if toolgateway.Spec.Replicas == nil { + toolgateway.Spec.Replicas = new(int32) + *toolgateway.Spec.Replicas = d.DefaultReplicas + } + + // Set default timeout if not specified + if toolgateway.Spec.Timeout == nil { + toolgateway.Spec.Timeout = &metav1.Duration{Duration: 360 * time.Second} + } +} diff --git a/internal/webhook/v1alpha1/toolgateway_webhook_test.go b/internal/webhook/v1alpha1/toolgateway_webhook_test.go new file mode 100644 index 0000000..618893f --- /dev/null +++ b/internal/webhook/v1alpha1/toolgateway_webhook_test.go @@ -0,0 +1,123 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "context" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/record" + + runtimev1alpha1 "github.com/agentic-layer/agent-runtime-operator/api/v1alpha1" +) + +var _ = Describe("ToolGateway Webhook", func() { + var defaulter *ToolGatewayCustomDefaulter + + BeforeEach(func() { + defaulter = &ToolGatewayCustomDefaulter{ + DefaultReplicas: 1, + Recorder: &record.FakeRecorder{}, + } + }) + + Context("When applying defaults", func() { + It("Should set default replicas when not specified", func() { + gateway := &runtimev1alpha1.ToolGateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-gateway", + }, + Spec: runtimev1alpha1.ToolGatewaySpec{}, + } + + err := defaulter.Default(context.Background(), gateway) + Expect(err).NotTo(HaveOccurred()) + Expect(gateway.Spec.Replicas).NotTo(BeNil()) + Expect(*gateway.Spec.Replicas).To(Equal(int32(1))) + }) + + It("Should set default timeout when not specified", func() { + gateway := &runtimev1alpha1.ToolGateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-gateway", + }, + Spec: runtimev1alpha1.ToolGatewaySpec{}, + } + + err := defaulter.Default(context.Background(), gateway) + Expect(err).NotTo(HaveOccurred()) + Expect(gateway.Spec.Timeout).NotTo(BeNil()) + Expect(gateway.Spec.Timeout.Duration).To(Equal(360 * time.Second)) + }) + + It("Should not set default for ToolGatewayClassName", func() { + gateway := &runtimev1alpha1.ToolGateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-gateway", + }, + Spec: runtimev1alpha1.ToolGatewaySpec{}, + } + + err := defaulter.Default(context.Background(), gateway) + Expect(err).NotTo(HaveOccurred()) + Expect(gateway.Spec.ToolGatewayClassName).To(BeEmpty()) + }) + + It("Should not override existing values", func() { + replicas := int32(3) + timeout, _ := time.ParseDuration("120s") + gateway := &runtimev1alpha1.ToolGateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-gateway", + }, + Spec: runtimev1alpha1.ToolGatewaySpec{ + ToolGatewayClassName: "custom", + Replicas: &replicas, + Timeout: &metav1.Duration{Duration: timeout}, + }, + } + + err := defaulter.Default(context.Background(), gateway) + Expect(err).NotTo(HaveOccurred()) + Expect(gateway.Spec.ToolGatewayClassName).To(Equal("custom")) + Expect(*gateway.Spec.Replicas).To(Equal(int32(3))) + Expect(gateway.Spec.Timeout.Duration).To(Equal(120 * time.Second)) + }) + + It("Should handle partial configuration correctly", func() { + gateway := &runtimev1alpha1.ToolGateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-gateway", + }, + Spec: runtimev1alpha1.ToolGatewaySpec{ + ToolGatewayClassName: "krakend", + }, + } + + err := defaulter.Default(context.Background(), gateway) + Expect(err).NotTo(HaveOccurred()) + Expect(gateway.Spec.ToolGatewayClassName).To(Equal("krakend")) + Expect(gateway.Spec.Replicas).NotTo(BeNil()) + Expect(*gateway.Spec.Replicas).To(Equal(int32(1))) + Expect(gateway.Spec.Timeout).NotTo(BeNil()) + Expect(gateway.Spec.Timeout.Duration).To(Equal(360 * time.Second)) + }) + }) +}) From 30da766a31349c497a9f4ba39000a562244585b3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 13:05:46 +0000 Subject: [PATCH 3/7] Simplify golangci.yml exclusions to avoid redundancy Co-authored-by: g3force <779094+g3force@users.noreply.github.com> --- .golangci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 8e56be0..e50a816 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -30,11 +30,9 @@ linters: generated: lax rules: - linters: + - dupl - lll path: api/* - - linters: - - dupl - path: api/v1alpha1/ - linters: - dupl - lll From 1d74acd5c789746b0733f27d32941a7834ab2791 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 13:11:01 +0000 Subject: [PATCH 4/7] Add ToolGateway CRD Co-authored-by: g3force <779094+g3force@users.noreply.github.com> --- go.sum | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/go.sum b/go.sum index 9ca9d8b..503279a 100644 --- a/go.sum +++ b/go.sum @@ -71,8 +71,6 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= -github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc= github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -120,24 +118,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= -github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= -github.com/onsi/ginkgo/v2 v2.27.3 h1:ICsZJ8JoYafeXFFlFAG75a7CxMsJHwgKwtO+82SE9L8= -github.com/onsi/ginkgo/v2 v2.27.3/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= -github.com/onsi/ginkgo/v2 v2.27.4 h1:fcEcQW/A++6aZAZQNUmNjvA9PSOzefMJBerHJ4t8v8Y= -github.com/onsi/ginkgo/v2 v2.27.4/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= -github.com/onsi/ginkgo/v2 v2.27.5 h1:ZeVgZMx2PDMdJm/+w5fE/OyG6ILo1Y3e+QX4zSR0zTE= -github.com/onsi/ginkgo/v2 v2.27.5/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= -github.com/onsi/ginkgo/v2 v2.28.0 h1:Rrf+lVLmtlBIKv6KrIGJCjyY8N36vDVcutbGJkyqjJc= -github.com/onsi/ginkgo/v2 v2.28.0/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/ginkgo/v2 v2.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI= github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE= -github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= -github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= -github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM= -github.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4= -github.com/onsi/gomega v1.39.0 h1:y2ROC3hKFmQZJNFeGAMeHZKkjBL65mIZcvrLQBF9k6Q= -github.com/onsi/gomega v1.39.0/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4= github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28= github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -221,16 +203,12 @@ golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= @@ -238,25 +216,17 @@ golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= @@ -265,8 +235,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -293,20 +261,14 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= -k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4= k8s.io/api v0.34.3/go.mod h1:PyVQBF886Q5RSQZOim7DybQjAbVs8g7gwJNhGtY5MBk= k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= -k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= -k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE= k8s.io/apimachinery v0.34.3/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA= k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0= -k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= -k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A= k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM= k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A= From 11dd39b4ae4c49f363bef8fe3b04a80bcb2eb5ba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 13:29:41 +0000 Subject: [PATCH 5/7] Remove Replicas and Timeout fields from ToolGateway CRD Based on PR feedback, removed the Replicas and Timeout fields from ToolGatewaySpec to match the AiGateway pattern more closely rather than following AgentGateway's pattern. Updated webhook, tests, and sample YAML accordingly. Co-authored-by: g3force <779094+g3force@users.noreply.github.com> --- api/v1alpha1/toolgateway_types.go | 9 --- api/v1alpha1/zz_generated.deepcopy.go | 10 ---- ...runtime.agentic-layer.ai_toolgateways.yaml | 10 ---- .../samples/runtime_v1alpha1_toolgateway.yaml | 2 - .../webhook/v1alpha1/toolgateway_webhook.go | 25 +------- .../v1alpha1/toolgateway_webhook_test.go | 59 +------------------ 6 files changed, 5 insertions(+), 110 deletions(-) diff --git a/api/v1alpha1/toolgateway_types.go b/api/v1alpha1/toolgateway_types.go index ffc91dc..6bab7a4 100644 --- a/api/v1alpha1/toolgateway_types.go +++ b/api/v1alpha1/toolgateway_types.go @@ -27,15 +27,6 @@ type ToolGatewaySpec struct { // This is only needed if multiple gateway classes are defined in the cluster. ToolGatewayClassName string `json:"toolGatewayClassName,omitempty"` - // Replicas is the number of gateway replicas - // +kubebuilder:validation:Minimum=1 - // +kubebuilder:default=1 - Replicas *int32 `json:"replicas,omitempty"` - - // Timeout specifies the gateway timeout for requests - // +kubebuilder:default="360s" - Timeout *metav1.Duration `json:"timeout,omitempty"` - // Environment variables to pass to the ToolGateway container. // These can include configuration values, credentials, or feature flags. // +optional diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 37c4977..69d9cec 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -966,16 +966,6 @@ func (in *ToolGatewayList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ToolGatewaySpec) DeepCopyInto(out *ToolGatewaySpec) { *out = *in - if in.Replicas != nil { - in, out := &in.Replicas, &out.Replicas - *out = new(int32) - **out = **in - } - if in.Timeout != nil { - in, out := &in.Timeout, &out.Timeout - *out = new(metav1.Duration) - **out = **in - } if in.Env != nil { in, out := &in.Env, &out.Env *out = make([]v1.EnvVar, len(*in)) diff --git a/config/crd/bases/runtime.agentic-layer.ai_toolgateways.yaml b/config/crd/bases/runtime.agentic-layer.ai_toolgateways.yaml index be3fd9c..927d217 100644 --- a/config/crd/bases/runtime.agentic-layer.ai_toolgateways.yaml +++ b/config/crd/bases/runtime.agentic-layer.ai_toolgateways.yaml @@ -247,16 +247,6 @@ spec: x-kubernetes-map-type: atomic type: object type: array - replicas: - default: 1 - description: Replicas is the number of gateway replicas - format: int32 - minimum: 1 - type: integer - timeout: - default: 360s - description: Timeout specifies the gateway timeout for requests - type: string toolGatewayClassName: description: |- ToolGatewayClassName specifies which ToolGatewayClass to use for this gateway instance. diff --git a/config/samples/runtime_v1alpha1_toolgateway.yaml b/config/samples/runtime_v1alpha1_toolgateway.yaml index a0be41c..8bd51ec 100644 --- a/config/samples/runtime_v1alpha1_toolgateway.yaml +++ b/config/samples/runtime_v1alpha1_toolgateway.yaml @@ -7,8 +7,6 @@ metadata: name: toolgateway-sample spec: # toolGatewayClassName: krakend # Optional: specify a gateway class - replicas: 1 - timeout: 360s # env: # Optional: environment variables # - name: LOG_LEVEL # value: info diff --git a/internal/webhook/v1alpha1/toolgateway_webhook.go b/internal/webhook/v1alpha1/toolgateway_webhook.go index 41ea4d1..bc4e068 100644 --- a/internal/webhook/v1alpha1/toolgateway_webhook.go +++ b/internal/webhook/v1alpha1/toolgateway_webhook.go @@ -19,9 +19,7 @@ package v1alpha1 import ( "context" "fmt" - "time" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" @@ -37,8 +35,7 @@ var toolgatewaylog = logf.Log.WithName("toolgateway-resource") func SetupToolGatewayWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr).For(&runtimev1alpha1.ToolGateway{}). WithDefaulter(&ToolGatewayCustomDefaulter{ - DefaultReplicas: 1, - Recorder: mgr.GetEventRecorderFor("toolgateway-defaulter-webhook"), + Recorder: mgr.GetEventRecorderFor("toolgateway-defaulter-webhook"), }). Complete() } @@ -51,8 +48,7 @@ func SetupToolGatewayWebhookWithManager(mgr ctrl.Manager) error { // NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, // as it is used only for temporary operations and does not need to be deeply copied. type ToolGatewayCustomDefaulter struct { - DefaultReplicas int32 - Recorder record.EventRecorder + Recorder record.EventRecorder } var _ webhook.CustomDefaulter = &ToolGatewayCustomDefaulter{} @@ -66,21 +62,6 @@ func (d *ToolGatewayCustomDefaulter) Default(_ context.Context, obj runtime.Obje } toolgatewaylog.Info("Defaulting for ToolGateway", "name", toolgateway.GetName()) - d.applyDefaults(toolgateway) - + // No defaults to apply currently return nil } - -// applyDefaults applies default values to the ToolGateway. -func (d *ToolGatewayCustomDefaulter) applyDefaults(toolgateway *runtimev1alpha1.ToolGateway) { - // Set default replicas if not specified - if toolgateway.Spec.Replicas == nil { - toolgateway.Spec.Replicas = new(int32) - *toolgateway.Spec.Replicas = d.DefaultReplicas - } - - // Set default timeout if not specified - if toolgateway.Spec.Timeout == nil { - toolgateway.Spec.Timeout = &metav1.Duration{Duration: 360 * time.Second} - } -} diff --git a/internal/webhook/v1alpha1/toolgateway_webhook_test.go b/internal/webhook/v1alpha1/toolgateway_webhook_test.go index 618893f..835143b 100644 --- a/internal/webhook/v1alpha1/toolgateway_webhook_test.go +++ b/internal/webhook/v1alpha1/toolgateway_webhook_test.go @@ -18,7 +18,6 @@ package v1alpha1 import ( "context" - "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -33,41 +32,12 @@ var _ = Describe("ToolGateway Webhook", func() { BeforeEach(func() { defaulter = &ToolGatewayCustomDefaulter{ - DefaultReplicas: 1, - Recorder: &record.FakeRecorder{}, + Recorder: &record.FakeRecorder{}, } }) Context("When applying defaults", func() { - It("Should set default replicas when not specified", func() { - gateway := &runtimev1alpha1.ToolGateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-gateway", - }, - Spec: runtimev1alpha1.ToolGatewaySpec{}, - } - - err := defaulter.Default(context.Background(), gateway) - Expect(err).NotTo(HaveOccurred()) - Expect(gateway.Spec.Replicas).NotTo(BeNil()) - Expect(*gateway.Spec.Replicas).To(Equal(int32(1))) - }) - - It("Should set default timeout when not specified", func() { - gateway := &runtimev1alpha1.ToolGateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-gateway", - }, - Spec: runtimev1alpha1.ToolGatewaySpec{}, - } - - err := defaulter.Default(context.Background(), gateway) - Expect(err).NotTo(HaveOccurred()) - Expect(gateway.Spec.Timeout).NotTo(BeNil()) - Expect(gateway.Spec.Timeout.Duration).To(Equal(360 * time.Second)) - }) - - It("Should not set default for ToolGatewayClassName", func() { + It("Should not modify ToolGatewayClassName when not specified", func() { gateway := &runtimev1alpha1.ToolGateway{ ObjectMeta: metav1.ObjectMeta{ Name: "test-gateway", @@ -81,43 +51,18 @@ var _ = Describe("ToolGateway Webhook", func() { }) It("Should not override existing values", func() { - replicas := int32(3) - timeout, _ := time.ParseDuration("120s") gateway := &runtimev1alpha1.ToolGateway{ ObjectMeta: metav1.ObjectMeta{ Name: "test-gateway", }, Spec: runtimev1alpha1.ToolGatewaySpec{ ToolGatewayClassName: "custom", - Replicas: &replicas, - Timeout: &metav1.Duration{Duration: timeout}, }, } err := defaulter.Default(context.Background(), gateway) Expect(err).NotTo(HaveOccurred()) Expect(gateway.Spec.ToolGatewayClassName).To(Equal("custom")) - Expect(*gateway.Spec.Replicas).To(Equal(int32(3))) - Expect(gateway.Spec.Timeout.Duration).To(Equal(120 * time.Second)) - }) - - It("Should handle partial configuration correctly", func() { - gateway := &runtimev1alpha1.ToolGateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-gateway", - }, - Spec: runtimev1alpha1.ToolGatewaySpec{ - ToolGatewayClassName: "krakend", - }, - } - - err := defaulter.Default(context.Background(), gateway) - Expect(err).NotTo(HaveOccurred()) - Expect(gateway.Spec.ToolGatewayClassName).To(Equal("krakend")) - Expect(gateway.Spec.Replicas).NotTo(BeNil()) - Expect(*gateway.Spec.Replicas).To(Equal(int32(1))) - Expect(gateway.Spec.Timeout).NotTo(BeNil()) - Expect(gateway.Spec.Timeout.Duration).To(Equal(360 * time.Second)) }) }) }) From 0eeec5474131c94c10502aeaa78b3aaa78593fd4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 13:48:00 +0000 Subject: [PATCH 6/7] Remove ToolGateway webhook and all related changes Based on reviewer feedback, removed the webhook for ToolGateway as it is not needed. Deleted webhook files, removed webhook registration from cmd/main.go, updated PROJECT file, and regenerated manifests. Co-authored-by: g3force <779094+g3force@users.noreply.github.com> --- PROJECT | 3 - cmd/main.go | 4 -- config/webhook/manifests.yaml | 20 ------ .../webhook/v1alpha1/toolgateway_webhook.go | 67 ------------------ .../v1alpha1/toolgateway_webhook_test.go | 68 ------------------- 5 files changed, 162 deletions(-) delete mode 100644 internal/webhook/v1alpha1/toolgateway_webhook.go delete mode 100644 internal/webhook/v1alpha1/toolgateway_webhook_test.go diff --git a/PROJECT b/PROJECT index cd6a2fb..d0be159 100644 --- a/PROJECT +++ b/PROJECT @@ -66,9 +66,6 @@ resources: kind: ToolGateway path: github.com/agentic-layer/agent-runtime-operator/api/v1alpha1 version: v1alpha1 - webhooks: - defaulting: true - webhookVersion: v1 - api: crdVersion: v1 namespaced: false diff --git a/cmd/main.go b/cmd/main.go index 76989f3..b8e7f69 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -225,10 +225,6 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "AgentGateway") os.Exit(1) } - if err := webhookv1alpha1.SetupToolGatewayWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "ToolGateway") - os.Exit(1) - } } if err := (&controller.AgenticWorkforceReconciler{ Client: mgr.GetClient(), diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 6df2af0..ba4b561 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -44,26 +44,6 @@ webhooks: resources: - agentgateways sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-runtime-agentic-layer-ai-v1alpha1-toolgateway - failurePolicy: Fail - name: mtoolgateway-v1alpha1.kb.io - rules: - - apiGroups: - - runtime.agentic-layer.ai - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - toolgateways - sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/internal/webhook/v1alpha1/toolgateway_webhook.go b/internal/webhook/v1alpha1/toolgateway_webhook.go deleted file mode 100644 index bc4e068..0000000 --- a/internal/webhook/v1alpha1/toolgateway_webhook.go +++ /dev/null @@ -1,67 +0,0 @@ -/* -Copyright 2025. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "context" - "fmt" - - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/tools/record" - ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" - - runtimev1alpha1 "github.com/agentic-layer/agent-runtime-operator/api/v1alpha1" -) - -var toolgatewaylog = logf.Log.WithName("toolgateway-resource") - -// SetupToolGatewayWebhookWithManager registers the webhook for ToolGateway in the manager. -func SetupToolGatewayWebhookWithManager(mgr ctrl.Manager) error { - return ctrl.NewWebhookManagedBy(mgr).For(&runtimev1alpha1.ToolGateway{}). - WithDefaulter(&ToolGatewayCustomDefaulter{ - Recorder: mgr.GetEventRecorderFor("toolgateway-defaulter-webhook"), - }). - Complete() -} - -// +kubebuilder:webhook:path=/mutate-runtime-agentic-layer-ai-v1alpha1-toolgateway,mutating=true,failurePolicy=fail,sideEffects=None,groups=runtime.agentic-layer.ai,resources=toolgateways,verbs=create;update,versions=v1alpha1,name=mtoolgateway-v1alpha1.kb.io,admissionReviewVersions=v1 - -// ToolGatewayCustomDefaulter struct is responsible for setting default values on the custom resource of the -// Kind ToolGateway when those are created or updated. -// -// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, -// as it is used only for temporary operations and does not need to be deeply copied. -type ToolGatewayCustomDefaulter struct { - Recorder record.EventRecorder -} - -var _ webhook.CustomDefaulter = &ToolGatewayCustomDefaulter{} - -// Default implements webhook.CustomDefaulter so a webhook will be registered for the Kind ToolGateway. -func (d *ToolGatewayCustomDefaulter) Default(_ context.Context, obj runtime.Object) error { - toolgateway, ok := obj.(*runtimev1alpha1.ToolGateway) - - if !ok { - return fmt.Errorf("expected a ToolGateway object but got %T", obj) - } - toolgatewaylog.Info("Defaulting for ToolGateway", "name", toolgateway.GetName()) - - // No defaults to apply currently - return nil -} diff --git a/internal/webhook/v1alpha1/toolgateway_webhook_test.go b/internal/webhook/v1alpha1/toolgateway_webhook_test.go deleted file mode 100644 index 835143b..0000000 --- a/internal/webhook/v1alpha1/toolgateway_webhook_test.go +++ /dev/null @@ -1,68 +0,0 @@ -/* -Copyright 2025. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "context" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/record" - - runtimev1alpha1 "github.com/agentic-layer/agent-runtime-operator/api/v1alpha1" -) - -var _ = Describe("ToolGateway Webhook", func() { - var defaulter *ToolGatewayCustomDefaulter - - BeforeEach(func() { - defaulter = &ToolGatewayCustomDefaulter{ - Recorder: &record.FakeRecorder{}, - } - }) - - Context("When applying defaults", func() { - It("Should not modify ToolGatewayClassName when not specified", func() { - gateway := &runtimev1alpha1.ToolGateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-gateway", - }, - Spec: runtimev1alpha1.ToolGatewaySpec{}, - } - - err := defaulter.Default(context.Background(), gateway) - Expect(err).NotTo(HaveOccurred()) - Expect(gateway.Spec.ToolGatewayClassName).To(BeEmpty()) - }) - - It("Should not override existing values", func() { - gateway := &runtimev1alpha1.ToolGateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-gateway", - }, - Spec: runtimev1alpha1.ToolGatewaySpec{ - ToolGatewayClassName: "custom", - }, - } - - err := defaulter.Default(context.Background(), gateway) - Expect(err).NotTo(HaveOccurred()) - Expect(gateway.Spec.ToolGatewayClassName).To(Equal("custom")) - }) - }) -}) From 5d0559fa2327ca4c73ff9493fc5731a5b8097ad0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 14:04:11 +0000 Subject: [PATCH 7/7] Revert golangci.yml changes and remove sample YAML Reverted .golangci.yml to original state as duplication exclusion is no longer needed after removing Replicas and Timeout fields. Removed config/samples/runtime_v1alpha1_toolgateway.yaml as other gateways don't have sample files either. Co-authored-by: g3force <779094+g3force@users.noreply.github.com> --- .golangci.yml | 1 - config/samples/runtime_v1alpha1_toolgateway.yaml | 15 --------------- 2 files changed, 16 deletions(-) delete mode 100644 config/samples/runtime_v1alpha1_toolgateway.yaml diff --git a/.golangci.yml b/.golangci.yml index e50a816..e5b21b0 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -30,7 +30,6 @@ linters: generated: lax rules: - linters: - - dupl - lll path: api/* - linters: diff --git a/config/samples/runtime_v1alpha1_toolgateway.yaml b/config/samples/runtime_v1alpha1_toolgateway.yaml deleted file mode 100644 index 8bd51ec..0000000 --- a/config/samples/runtime_v1alpha1_toolgateway.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: runtime.agentic-layer.ai/v1alpha1 -kind: ToolGateway -metadata: - labels: - app.kubernetes.io/name: agent-runtime-operator - app.kubernetes.io/managed-by: kustomize - name: toolgateway-sample -spec: - # toolGatewayClassName: krakend # Optional: specify a gateway class - # env: # Optional: environment variables - # - name: LOG_LEVEL - # value: info - # envFrom: # Optional: environment variables from ConfigMaps or Secrets - # - configMapRef: - # name: toolgateway-config