Просмотр исходного кода

feat(kb): add ExternalKnowledgeBase api

Thomas Zhang 2 месяцев назад
Родитель
Сommit
b811d47fba

+ 8 - 0
PROJECT

@@ -57,4 +57,12 @@ resources:
   kind: ManagedTool
   path: github.com/LocoStack/loco-operator/api/v1alpha1
   version: v1alpha1
+- api:
+    crdVersion: v1
+    namespaced: true
+  controller: true
+  domain: locostack.com
+  kind: ExternalKnowledgeBase
+  path: github.com/LocoStack/loco-operator/api/v1alpha1
+  version: v1alpha1
 version: "3"

+ 92 - 0
api/v1alpha1/externalknowledgebase_types.go

@@ -0,0 +1,92 @@
+/*
+Copyright 2026 LocoStack.
+
+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 (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// EDIT THIS FILE!  THIS IS SCAFFOLDING FOR YOU TO OWN!
+// NOTE: json tags are required.  Any new fields you add must have json tags for the fields to be serialized.
+
+// ExternalKnowledgeBaseSpec defines the desired state of ExternalKnowledgeBase
+type ExternalKnowledgeBaseSpec struct {
+	// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
+	// Important: Run "make" to regenerate code after modifying this file
+	// The following markers will use OpenAPI v3 schema to validate the value
+	// More info: https://book.kubebuilder.io/reference/markers/crd-validation.html
+
+	// foo is an example field of ExternalKnowledgeBase. Edit externalknowledgebase_types.go to remove/update
+	// +optional
+	Foo *string `json:"foo,omitempty"`
+}
+
+// ExternalKnowledgeBaseStatus defines the observed state of ExternalKnowledgeBase.
+type ExternalKnowledgeBaseStatus struct {
+	// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
+	// Important: Run "make" to regenerate code after modifying this file
+
+	// For Kubernetes API conventions, see:
+	// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties
+
+	// conditions represent the current state of the ExternalKnowledgeBase resource.
+	// Each condition has a unique type and reflects the status of a specific aspect of the resource.
+	//
+	// Standard condition types include:
+	// - "Available": the resource is fully functional
+	// - "Progressing": the resource is being created or updated
+	// - "Degraded": the resource failed to reach or maintain its desired state
+	//
+	// The status of each condition is one of True, False, or Unknown.
+	// +listType=map
+	// +listMapKey=type
+	// +optional
+	Conditions []metav1.Condition `json:"conditions,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+// +kubebuilder:subresource:status
+
+// ExternalKnowledgeBase is the Schema for the externalknowledgebases API
+type ExternalKnowledgeBase struct {
+	metav1.TypeMeta `json:",inline"`
+
+	// metadata is a standard object metadata
+	// +optional
+	metav1.ObjectMeta `json:"metadata,omitzero"`
+
+	// spec defines the desired state of ExternalKnowledgeBase
+	// +required
+	Spec ExternalKnowledgeBaseSpec `json:"spec"`
+
+	// status defines the observed state of ExternalKnowledgeBase
+	// +optional
+	Status ExternalKnowledgeBaseStatus `json:"status,omitzero"`
+}
+
+// +kubebuilder:object:root=true
+
+// ExternalKnowledgeBaseList contains a list of ExternalKnowledgeBase
+type ExternalKnowledgeBaseList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitzero"`
+	Items           []ExternalKnowledgeBase `json:"items"`
+}
+
+func init() {
+	SchemeBuilder.Register(&ExternalKnowledgeBase{}, &ExternalKnowledgeBaseList{})
+}

+ 101 - 0
api/v1alpha1/zz_generated.deepcopy.go

@@ -260,6 +260,107 @@ func (in *ConditionalArgs) DeepCopy() *ConditionalArgs {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ExternalKnowledgeBase) DeepCopyInto(out *ExternalKnowledgeBase) {
+	*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 ExternalKnowledgeBase.
+func (in *ExternalKnowledgeBase) DeepCopy() *ExternalKnowledgeBase {
+	if in == nil {
+		return nil
+	}
+	out := new(ExternalKnowledgeBase)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ExternalKnowledgeBase) 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 *ExternalKnowledgeBaseList) DeepCopyInto(out *ExternalKnowledgeBaseList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]ExternalKnowledgeBase, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalKnowledgeBaseList.
+func (in *ExternalKnowledgeBaseList) DeepCopy() *ExternalKnowledgeBaseList {
+	if in == nil {
+		return nil
+	}
+	out := new(ExternalKnowledgeBaseList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ExternalKnowledgeBaseList) 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 *ExternalKnowledgeBaseSpec) DeepCopyInto(out *ExternalKnowledgeBaseSpec) {
+	*out = *in
+	if in.Foo != nil {
+		in, out := &in.Foo, &out.Foo
+		*out = new(string)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalKnowledgeBaseSpec.
+func (in *ExternalKnowledgeBaseSpec) DeepCopy() *ExternalKnowledgeBaseSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(ExternalKnowledgeBaseSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ExternalKnowledgeBaseStatus) DeepCopyInto(out *ExternalKnowledgeBaseStatus) {
+	*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 ExternalKnowledgeBaseStatus.
+func (in *ExternalKnowledgeBaseStatus) DeepCopy() *ExternalKnowledgeBaseStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(ExternalKnowledgeBaseStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ExternalModel) DeepCopyInto(out *ExternalModel) {
 	*out = *in

+ 7 - 0
cmd/main.go

@@ -220,6 +220,13 @@ func main() {
 		setupLog.Error(err, "Failed to create controller", "controller", "ManagedTool")
 		os.Exit(1)
 	}
+	if err := (&controller.ExternalKnowledgeBaseReconciler{
+		Client: mgr.GetClient(),
+		Scheme: mgr.GetScheme(),
+	}).SetupWithManager(mgr); err != nil {
+		setupLog.Error(err, "Failed to create controller", "controller", "ExternalKnowledgeBase")
+		os.Exit(1)
+	}
 	// +kubebuilder:scaffold:builder
 
 	if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {

+ 127 - 0
config/crd/bases/locostack.com_externalknowledgebases.yaml

@@ -0,0 +1,127 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.20.1
+  name: externalknowledgebases.locostack.com
+spec:
+  group: locostack.com
+  names:
+    kind: ExternalKnowledgeBase
+    listKind: ExternalKnowledgeBaseList
+    plural: externalknowledgebases
+    singular: externalknowledgebase
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: ExternalKnowledgeBase is the Schema for the externalknowledgebases
+          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: spec defines the desired state of ExternalKnowledgeBase
+            properties:
+              foo:
+                description: foo is an example field of ExternalKnowledgeBase. Edit
+                  externalknowledgebase_types.go to remove/update
+                type: string
+            type: object
+          status:
+            description: status defines the observed state of ExternalKnowledgeBase
+            properties:
+              conditions:
+                description: |-
+                  conditions represent the current state of the ExternalKnowledgeBase resource.
+                  Each condition has a unique type and reflects the status of a specific aspect of the resource.
+
+                  Standard condition types include:
+                  - "Available": the resource is fully functional
+                  - "Progressing": the resource is being created or updated
+                  - "Degraded": the resource failed to reach or maintain its desired state
+
+                  The status of each condition is one of True, False, or Unknown.
+                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
+                x-kubernetes-list-map-keys:
+                - type
+                x-kubernetes-list-type: map
+            type: object
+        required:
+        - spec
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}

+ 1 - 0
config/crd/kustomization.yaml

@@ -8,6 +8,7 @@ resources:
 - bases/locostack.com_managedmodels.yaml
 - bases/locostack.com_externaltools.yaml
 - bases/locostack.com_managedtools.yaml
+- bases/locostack.com_externalknowledgebases.yaml
 # +kubebuilder:scaffold:crdkustomizeresource
 
 patches:

+ 27 - 0
config/rbac/externalknowledgebase_admin_role.yaml

@@ -0,0 +1,27 @@
+# This rule is not used by the project loco-operator itself.
+# It is provided to allow the cluster admin to help manage permissions for users.
+#
+# Grants full permissions ('*') over locostack.com.
+# This role is intended for users authorized to modify roles and bindings within the cluster,
+# enabling them to delegate specific permissions to other users or groups as needed.
+
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  labels:
+    app.kubernetes.io/name: loco-operator
+    app.kubernetes.io/managed-by: kustomize
+  name: externalknowledgebase-admin-role
+rules:
+- apiGroups:
+  - locostack.com
+  resources:
+  - externalknowledgebases
+  verbs:
+  - '*'
+- apiGroups:
+  - locostack.com
+  resources:
+  - externalknowledgebases/status
+  verbs:
+  - get

+ 33 - 0
config/rbac/externalknowledgebase_editor_role.yaml

@@ -0,0 +1,33 @@
+# This rule is not used by the project loco-operator itself.
+# It is provided to allow the cluster admin to help manage permissions for users.
+#
+# Grants permissions to create, update, and delete resources within the locostack.com.
+# This role is intended for users who need to manage these resources
+# but should not control RBAC or manage permissions for others.
+
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  labels:
+    app.kubernetes.io/name: loco-operator
+    app.kubernetes.io/managed-by: kustomize
+  name: externalknowledgebase-editor-role
+rules:
+- apiGroups:
+  - locostack.com
+  resources:
+  - externalknowledgebases
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - locostack.com
+  resources:
+  - externalknowledgebases/status
+  verbs:
+  - get

+ 29 - 0
config/rbac/externalknowledgebase_viewer_role.yaml

@@ -0,0 +1,29 @@
+# This rule is not used by the project loco-operator itself.
+# It is provided to allow the cluster admin to help manage permissions for users.
+#
+# Grants read-only access to locostack.com resources.
+# This role is intended for users who need visibility into these resources
+# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing.
+
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  labels:
+    app.kubernetes.io/name: loco-operator
+    app.kubernetes.io/managed-by: kustomize
+  name: externalknowledgebase-viewer-role
+rules:
+- apiGroups:
+  - locostack.com
+  resources:
+  - externalknowledgebases
+  verbs:
+  - get
+  - list
+  - watch
+- apiGroups:
+  - locostack.com
+  resources:
+  - externalknowledgebases/status
+  verbs:
+  - get

+ 3 - 0
config/rbac/kustomization.yaml

@@ -22,6 +22,9 @@ resources:
 # default, aiding admins in cluster management. Those roles are
 # not used by the loco-operator itself. You can comment the following lines
 # if you do not want those helpers be installed with your Project.
+- externalknowledgebase_admin_role.yaml
+- externalknowledgebase_editor_role.yaml
+- externalknowledgebase_viewer_role.yaml
 - managedtool_admin_role.yaml
 - managedtool_editor_role.yaml
 - managedtool_viewer_role.yaml

+ 3 - 0
config/rbac/role.yaml

@@ -46,6 +46,7 @@ rules:
   - locostack.com
   resources:
   - components
+  - externalknowledgebases
   - externalmodels
   - externaltools
   - managedmodels
@@ -63,6 +64,7 @@ rules:
   - locostack.com
   resources:
   - components/finalizers
+  - externalknowledgebases/finalizers
   - externalmodels/finalizers
   - externaltools/finalizers
   - managedmodels/finalizers
@@ -74,6 +76,7 @@ rules:
   - locostack.com
   resources:
   - components/status
+  - externalknowledgebases/status
   - externalmodels/status
   - externaltools/status
   - managedmodels/status

+ 1 - 0
config/samples/kustomization.yaml

@@ -6,4 +6,5 @@ resources:
 - v1alpha1_managedmodel.yaml
 - v1alpha1_externaltool.yaml
 - v1alpha1_managedtool.yaml
+- v1alpha1_externalknowledgebase.yaml
 # +kubebuilder:scaffold:manifestskustomizesamples

+ 9 - 0
config/samples/v1alpha1_externalknowledgebase.yaml

@@ -0,0 +1,9 @@
+apiVersion: locostack.com/v1alpha1
+kind: ExternalKnowledgeBase
+metadata:
+  labels:
+    app.kubernetes.io/name: loco-operator
+    app.kubernetes.io/managed-by: kustomize
+  name: externalknowledgebase-sample
+spec:
+  # TODO(user): Add fields here

+ 63 - 0
internal/controller/externalknowledgebase_controller.go

@@ -0,0 +1,63 @@
+/*
+Copyright 2026 LocoStack.
+
+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 controller
+
+import (
+	"context"
+
+	"k8s.io/apimachinery/pkg/runtime"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+
+	"github.com/LocoStack/loco-operator/api/v1alpha1"
+)
+
+// ExternalKnowledgeBaseReconciler reconciles a ExternalKnowledgeBase object
+type ExternalKnowledgeBaseReconciler struct {
+	client.Client
+	Scheme *runtime.Scheme
+}
+
+// +kubebuilder:rbac:groups=locostack.com,resources=externalknowledgebases,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=locostack.com,resources=externalknowledgebases/status,verbs=get;update;patch
+// +kubebuilder:rbac:groups=locostack.com,resources=externalknowledgebases/finalizers,verbs=update
+
+// Reconcile is part of the main kubernetes reconciliation loop which aims to
+// move the current state of the cluster closer to the desired state.
+// TODO(user): Modify the Reconcile function to compare the state specified by
+// the ExternalKnowledgeBase object against the actual cluster state, and then
+// perform operations to make the cluster state reflect the state specified by
+// the user.
+//
+// For more details, check Reconcile and its Result here:
+// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.23.3/pkg/reconcile
+func (r *ExternalKnowledgeBaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+	_ = logf.FromContext(ctx)
+
+	// TODO(user): your logic here
+
+	return ctrl.Result{}, nil
+}
+
+// SetupWithManager sets up the controller with the Manager.
+func (r *ExternalKnowledgeBaseReconciler) SetupWithManager(mgr ctrl.Manager) error {
+	return ctrl.NewControllerManagedBy(mgr).
+		For(&v1alpha1.ExternalKnowledgeBase{}).
+		Named("externalknowledgebase").
+		Complete(r)
+}

+ 84 - 0
internal/controller/externalknowledgebase_controller_test.go

@@ -0,0 +1,84 @@
+/*
+Copyright 2026 LocoStack.
+
+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 controller
+
+import (
+	"context"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	"k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/types"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	"github.com/LocoStack/loco-operator/api/v1alpha1"
+)
+
+var _ = Describe("ExternalKnowledgeBase Controller", func() {
+	Context("When reconciling a resource", func() {
+		const resourceName = "test-resource"
+
+		ctx := context.Background()
+
+		typeNamespacedName := types.NamespacedName{
+			Name:      resourceName,
+			Namespace: "default", // TODO(user):Modify as needed
+		}
+		externalknowledgebase := &v1alpha1.ExternalKnowledgeBase{}
+
+		BeforeEach(func() {
+			By("creating the custom resource for the Kind ExternalKnowledgeBase")
+			err := k8sClient.Get(ctx, typeNamespacedName, externalknowledgebase)
+			if err != nil && errors.IsNotFound(err) {
+				resource := &v1alpha1.ExternalKnowledgeBase{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      resourceName,
+						Namespace: "default",
+					},
+					// TODO(user): Specify other spec details if needed.
+				}
+				Expect(k8sClient.Create(ctx, resource)).To(Succeed())
+			}
+		})
+
+		AfterEach(func() {
+			// TODO(user): Cleanup logic after each test, like removing the resource instance.
+			resource := &v1alpha1.ExternalKnowledgeBase{}
+			err := k8sClient.Get(ctx, typeNamespacedName, resource)
+			Expect(err).NotTo(HaveOccurred())
+
+			By("Cleanup the specific resource instance ExternalKnowledgeBase")
+			Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
+		})
+		It("should successfully reconcile the resource", func() {
+			By("Reconciling the created resource")
+			controllerReconciler := &ExternalKnowledgeBaseReconciler{
+				Client: k8sClient,
+				Scheme: k8sClient.Scheme(),
+			}
+
+			_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
+				NamespacedName: typeNamespacedName,
+			})
+			Expect(err).NotTo(HaveOccurred())
+			// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
+			// Example: If you expect a certain status condition after reconciliation, verify it here.
+		})
+	})
+})