From 583e6f435625fedb87561e3c3585a2a5deb42300 Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Tue, 24 Feb 2026 10:54:14 -0300 Subject: [PATCH 01/17] fix: add permissions to get/list/watch any resources This is needed as the controller-manager needs the ability to list any type of resource in the cluster --- .../controller-manager/core-control-plane/rbac/role.yaml | 8 ++++++++ internal/controllers/policy/policy_controller.go | 1 + 2 files changed, 9 insertions(+) diff --git a/config/overlays/controller-manager/core-control-plane/rbac/role.yaml b/config/overlays/controller-manager/core-control-plane/rbac/role.yaml index e591513..8b0e09a 100644 --- a/config/overlays/controller-manager/core-control-plane/rbac/role.yaml +++ b/config/overlays/controller-manager/core-control-plane/rbac/role.yaml @@ -4,6 +4,14 @@ kind: ClusterRole metadata: name: milo-controller-manager rules: +- apiGroups: + - '*' + resources: + - '*' + verbs: + - get + - list + - watch - apiGroups: - search.miloapis.com resources: diff --git a/internal/controllers/policy/policy_controller.go b/internal/controllers/policy/policy_controller.go index c0d0441..043820e 100644 --- a/internal/controllers/policy/policy_controller.go +++ b/internal/controllers/policy/policy_controller.go @@ -85,6 +85,7 @@ const ( // +kubebuilder:rbac:groups=search.miloapis.com,resources=resourceindexpolicies,verbs=get;list;watch // +kubebuilder:rbac:groups=search.miloapis.com,resources=resourceindexpolicies/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=*,resources=*,verbs=get;list;watch // Reconcile matches the state of the cluster with the desired state of a ResourceIndexPolicy. func (r *ResourceIndexPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { From 400e236a88f7f3ad0c1a16f61ed21c19bd3aacbe Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Tue, 24 Feb 2026 10:58:48 -0300 Subject: [PATCH 02/17] chore: crete pf-all command and update nats-queue to get reindex queue status --- Taskfile.yaml | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/Taskfile.yaml b/Taskfile.yaml index 1901a46..5f7b4a3 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -409,7 +409,9 @@ tasks: --metrics-bind-address=:8085 \ --health-probe-bind-address=:8086 \ --leader-elect=false \ - --meilisearch-domain="http://127.0.0.1:7700" + --meilisearch-domain="http://127.0.0.1:7700" \ + --nats-url="nats://127.0.0.1:4222" + silent: true dev:run-indexer: @@ -422,21 +424,10 @@ tasks: # Trap to kill background processes (port-forward) on exit trap 'kill $(jobs -p)' EXIT - echo "🚀 Starting NATS port-forward in background..." - task dev:pf-nats > /dev/null 2>&1 & - - echo "🚀 Starting Meilisearch port-forward in background..." - task dev:pf-meilisearch > /dev/null 2>&1 & - - echo "Waiting for port-forward to be ready..." - sleep 5 - echo "🚀 Running indexer against local NATS..." MEILISEARCH_API_KEY=search-master-key \ go run ./cmd/search indexer \ --nats-url="nats://127.0.0.1:4222" \ - --nats-subject="audit.>" \ - --nats-queue-group="search-indexer" \ --nats-audit-consumer-name="search-indexer" \ --nats-reindex-consumer-name="search-reindexer" \ --nats-stream-name="AUDIT_EVENTS" \ @@ -472,6 +463,15 @@ tasks: - echo "Port forwarding Etcd to localhost:2379..." - task test-infra:kubectl -- port-forward -n etcd-system svc/etcd 2379:2379 + dev:pf-all: + desc: Port forward all dependencies for local development + cmds: + - | + task dev:pf-meilisearch & + task dev:pf-nats & + task dev:pf-etcd & + wait + dev:run-apiserver: desc: Run the API server locally (requires dev:pf-etcd running) cmds: @@ -682,4 +682,5 @@ tasks: dev:nats-queue: desc: View the NATS queue for the indexer cmds: - - task test-infra:kubectl -- exec -n nats-system $(task test-infra:kubectl -- get pods -n nats-system -l app.kubernetes.io/component=nats-box -o jsonpath="{.items[0].metadata.name}") -- nats consumer info AUDIT_EVENTS search-indexer \ No newline at end of file + - task test-infra:kubectl -- exec -n nats-system $(task test-infra:kubectl -- get pods -n nats-system -l app.kubernetes.io/component=nats-box -o jsonpath="{.items[0].metadata.name}") -- nats consumer info AUDIT_EVENTS search-indexer + - task test-infra:kubectl -- exec -n nats-system $(task test-infra:kubectl -- get pods -n nats-system -l app.kubernetes.io/component=nats-box -o jsonpath="{.items[0].metadata.name}") -- nats consumer info REINDEX_EVENTS search-reindexer \ No newline at end of file From 1d732f3f888c89c973118184106aab8b9a7907ed Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Tue, 24 Feb 2026 11:23:09 -0300 Subject: [PATCH 03/17] feat: add RBAC to resource-indexer --- Taskfile.yaml | 1 + config/base/resource-indexer/deployment.yaml | 1 - config/overlays/ci/kustomization.yaml | 3 ++- config/overlays/dev/kustomization.yaml | 3 ++- .../core-control-plane/kustomization.yaml | 14 ++++++++++++++ .../core-control-plane/patches/deployment.yaml | 9 +++++++++ .../core-control-plane/rbac/kustomization.yaml | 6 ++++++ .../core-control-plane/rbac/role.yaml | 15 +++++++++++++++ .../core-control-plane/rbac/rolebinding.yaml | 12 ++++++++++++ .../core-control-plane/rbac/service-account.yaml | 7 +++++++ internal/indexer/policy_cache.go | 3 +++ 11 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 config/overlays/resource-indexer/core-control-plane/kustomization.yaml create mode 100644 config/overlays/resource-indexer/core-control-plane/patches/deployment.yaml create mode 100644 config/overlays/resource-indexer/core-control-plane/rbac/kustomization.yaml create mode 100644 config/overlays/resource-indexer/core-control-plane/rbac/role.yaml create mode 100644 config/overlays/resource-indexer/core-control-plane/rbac/rolebinding.yaml create mode 100644 config/overlays/resource-indexer/core-control-plane/rbac/service-account.yaml diff --git a/Taskfile.yaml b/Taskfile.yaml index 5f7b4a3..5d68616 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -122,6 +122,7 @@ tasks: # Generate RBAC rules for the controllers. - echo "Generating RBAC rules for the controllers..." - "\"{{.TOOL_DIR}}/controller-gen\" rbac:roleName=milo-controller-manager paths=\"./internal/controllers/...\" output:dir=\"./config/overlays/controller-manager/core-control-plane/rbac\"" + - "\"{{.TOOL_DIR}}/controller-gen\" rbac:roleName=milo-resource-indexer paths=\"./internal/indexer/...\" output:dir=\"./config/overlays/resource-indexer/core-control-plane/rbac\"" - task: generate:openapi silent: true diff --git a/config/base/resource-indexer/deployment.yaml b/config/base/resource-indexer/deployment.yaml index 19cba16..85c6b78 100644 --- a/config/base/resource-indexer/deployment.yaml +++ b/config/base/resource-indexer/deployment.yaml @@ -2,7 +2,6 @@ apiVersion: apps/v1 kind: Deployment metadata: name: resource-indexer - namespace: search-system labels: app.kubernetes.io/name: resource-indexer app.kubernetes.io/component: indexer diff --git a/config/overlays/ci/kustomization.yaml b/config/overlays/ci/kustomization.yaml index 534b9c4..19b8344 100644 --- a/config/overlays/ci/kustomization.yaml +++ b/config/overlays/ci/kustomization.yaml @@ -5,9 +5,10 @@ namespace: search-system resources: - ../../base/apiserver - - ../../base/resource-indexer # Controller manager with RBAC - ../controller-manager/core-control-plane + # Resource indexer with RBAC + - ../resource-indexer/core-control-plane # Include components for CI environment components: diff --git a/config/overlays/dev/kustomization.yaml b/config/overlays/dev/kustomization.yaml index 44864ef..2be128f 100644 --- a/config/overlays/dev/kustomization.yaml +++ b/config/overlays/dev/kustomization.yaml @@ -5,9 +5,10 @@ namespace: search-system resources: - ../../base/apiserver - - ../../base/resource-indexer # Controller manager with RBAC - ../controller-manager/core-control-plane + # Resource indexer with RBAC + - ../resource-indexer/core-control-plane # Include components for development environment components: diff --git a/config/overlays/resource-indexer/core-control-plane/kustomization.yaml b/config/overlays/resource-indexer/core-control-plane/kustomization.yaml new file mode 100644 index 0000000..310405f --- /dev/null +++ b/config/overlays/resource-indexer/core-control-plane/kustomization.yaml @@ -0,0 +1,14 @@ +# This overlay is used to install the resource indexer into the core control +# plane. +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + # Install the base resource indexer resources. + - ../../../base/resource-indexer + # Install the RBAC resources for the resource indexer that are specific to + # the core control plane. + - rbac + +patches: +- path: patches/deployment.yaml diff --git a/config/overlays/resource-indexer/core-control-plane/patches/deployment.yaml b/config/overlays/resource-indexer/core-control-plane/patches/deployment.yaml new file mode 100644 index 0000000..e53e127 --- /dev/null +++ b/config/overlays/resource-indexer/core-control-plane/patches/deployment.yaml @@ -0,0 +1,9 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: resource-indexer +spec: + template: + spec: + serviceAccountName: resource-indexer + automountServiceAccountToken: true diff --git a/config/overlays/resource-indexer/core-control-plane/rbac/kustomization.yaml b/config/overlays/resource-indexer/core-control-plane/rbac/kustomization.yaml new file mode 100644 index 0000000..8da1330 --- /dev/null +++ b/config/overlays/resource-indexer/core-control-plane/rbac/kustomization.yaml @@ -0,0 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- role.yaml +- rolebinding.yaml +- service-account.yaml diff --git a/config/overlays/resource-indexer/core-control-plane/rbac/role.yaml b/config/overlays/resource-indexer/core-control-plane/rbac/role.yaml new file mode 100644 index 0000000..53fbff1 --- /dev/null +++ b/config/overlays/resource-indexer/core-control-plane/rbac/role.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: milo-resource-indexer +rules: +- apiGroups: + - search.miloapis.com + resources: + - resourceindexpolicies + - resourceindexpolicies/status + verbs: + - get + - list + - watch diff --git a/config/overlays/resource-indexer/core-control-plane/rbac/rolebinding.yaml b/config/overlays/resource-indexer/core-control-plane/rbac/rolebinding.yaml new file mode 100644 index 0000000..0321428 --- /dev/null +++ b/config/overlays/resource-indexer/core-control-plane/rbac/rolebinding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: milo-resource-indexer +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: milo-resource-indexer +subjects: +- kind: ServiceAccount + name: resource-indexer + namespace: search-system diff --git a/config/overlays/resource-indexer/core-control-plane/rbac/service-account.yaml b/config/overlays/resource-indexer/core-control-plane/rbac/service-account.yaml new file mode 100644 index 0000000..a402824 --- /dev/null +++ b/config/overlays/resource-indexer/core-control-plane/rbac/service-account.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: resource-indexer + labels: + app.kubernetes.io/name: resource-indexer + app.kubernetes.io/part-of: search-control-plane diff --git a/internal/indexer/policy_cache.go b/internal/indexer/policy_cache.go index 7f694d7..98ec66b 100644 --- a/internal/indexer/policy_cache.go +++ b/internal/indexer/policy_cache.go @@ -15,6 +15,9 @@ import ( runtimecache "sigs.k8s.io/controller-runtime/pkg/cache" ) +// +kubebuilder:rbac:groups=search.miloapis.com,resources=resourceindexpolicies,verbs=get;list;watch +// +kubebuilder:rbac:groups=search.miloapis.com,resources=resourceindexpolicies/status,verbs=get;list;watch + // PolicyCache maintains a thread-safe cache of compiled ResourceIndexPolicies. // It uses a controller-runtime informer to watch the API server for changes // via a watch stream, keeping policies in-sync without polling. From 480dde61db3c83ee5a34199e51311af0781ca32f Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Tue, 24 Feb 2026 11:57:37 -0300 Subject: [PATCH 04/17] refactor: centralize Kubernetes cache startup and synchronization for indexer policies and update deployment restart logic. --- Taskfile.yaml | 7 +++++-- cmd/search/indexer/command.go | 24 +++++++++++++++++------- internal/indexer/policy_cache.go | 12 ++++-------- internal/indexer/reindex_consumer.go | 3 +++ 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/Taskfile.yaml b/Taskfile.yaml index 5d68616..b48cf89 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -377,6 +377,7 @@ tasks: echo " Etcd: task test-infra:kubectl -- logs -l app.kubernetes.io/name=etcd -n etcd-system -f" echo " Search API Server: task test-infra:kubectl -- logs -l app.kubernetes.io/name=search-apiserver -n search-system -f" echo " Search Controller: task test-infra:kubectl -- logs -l app.kubernetes.io/name=search-controller-manager -n search-system -f" + echo " Search Indexer: task test-infra:kubectl -- logs -l app.kubernetes.io/name=resource-indexer -n search-system -f" dev:generate-webhook-certs: desc: Generate all certificates for webhook server @@ -661,7 +662,6 @@ tasks: deps: - dev:build - dev:load - - dev:deploy cmds: - | set -e @@ -672,13 +672,16 @@ tasks: # Restart the deployment to pick up new image task test-infra:kubectl -- rollout restart deployment/search-apiserver -n search-system task test-infra:kubectl -- rollout restart deployment/search-controller-manager -n search-system + task test-infra:kubectl -- rollout restart deployment/resource-indexer -n search-system # Wait for rollout to complete echo "Waiting for rollout to complete..." + task test-infra:kubectl -- rollout status deployment/search-apiserver -n search-system --timeout=1000s task test-infra:kubectl -- rollout status deployment/search-controller-manager -n search-system --timeout=1000s + task test-infra:kubectl -- rollout status deployment/resource-indexer -n search-system --timeout=1000s echo "✅ Redeployment complete!" - echo "Check logs with: task test-infra:kubectl -- logs -n search-system -l app.kubernetes.io/name=search-controller-manager" + echo "Check pods with: task test-infra:kubectl -- get pods -n search-system" dev:nats-queue: desc: View the NATS queue for the indexer diff --git a/cmd/search/indexer/command.go b/cmd/search/indexer/command.go index 43ee4ba..74f4b6a 100644 --- a/cmd/search/indexer/command.go +++ b/cmd/search/indexer/command.go @@ -188,18 +188,28 @@ func Run(o *ResourceIndexerOptions, ctx context.Context) error { return fmt.Errorf("failed to create policy cache: %w", err) } - go func() { - if err := indexPolicyCache.Start(ctx); err != nil { - klog.Errorf("Index Policy cache stopped: %v", err) - } - }() + // Register handlers for both caches. They share the same underlying informer. + if err := indexPolicyCache.RegisterHandlers(ctx); err != nil { + return fmt.Errorf("failed to register index policy handlers: %w", err) + } + if err := reindexPolicyCache.RegisterHandlers(ctx); err != nil { + return fmt.Errorf("failed to register reindex policy handlers: %w", err) + } + // Start the shared cache and wait for it to be synced. go func() { - if err := reindexPolicyCache.Start(ctx); err != nil { - klog.Errorf("Reindex Policy cache stopped: %v", err) + klog.Info("Starting shared Kubernetes cache...") + if err := k8sCache.Start(ctx); err != nil { + klog.Fatalf("Kubernetes cache stopped with error: %v", err) } }() + klog.Info("Waiting for cache to sync...") + if !k8sCache.WaitForCacheSync(ctx) { + return fmt.Errorf("failed to sync Kubernetes cache") + } + klog.Info("Cache synced successfully") + // Connect to NATS klog.Infof("Connecting to NATS at %s...", o.NatsURL) nc, err := nats.Connect(o.NatsURL) diff --git a/internal/indexer/policy_cache.go b/internal/indexer/policy_cache.go index 98ec66b..5ac9daf 100644 --- a/internal/indexer/policy_cache.go +++ b/internal/indexer/policy_cache.go @@ -53,9 +53,9 @@ func NewPolicyCache(c runtimecache.Cache, requireReadyCondition bool) (*PolicyCa }, nil } -// Start registers informer event handlers for ResourceIndexPolicy objects -func (c *PolicyCache) Start(ctx context.Context) error { - klog.Info("Starting policy cache informer") +// RegisterHandlers registers informer event handlers for ResourceIndexPolicy objects. +func (c *PolicyCache) RegisterHandlers(ctx context.Context) error { + klog.Info("Registering policy cache informer handlers") informer, err := c.cache.GetInformer(ctx, &v1alpha1.ResourceIndexPolicy{}) if err != nil { @@ -95,10 +95,6 @@ func (c *PolicyCache) Start(ctx context.Context) error { return fmt.Errorf("failed to add event handler to ResourceIndexPolicy informer: %w", err) } - // Start runs all informers and blocks until ctx is cancelled. - if err := c.cache.Start(ctx); err != nil { - return fmt.Errorf("policy cache informer stopped with error: %w", err) - } return nil } @@ -112,7 +108,7 @@ func (c *PolicyCache) upsertPolicy(p *v1alpha1.ResourceIndexPolicy) { // still being initialized (e.g. index creation or initial re-indexing). if c.requireReadyCondition { if !meta.IsStatusConditionTrue(p.Status.Conditions, "Ready") { - klog.Infof("Policy %s is not yet Ready; skipping cache", key) + klog.Infof("Policy %s is not yet Ready (Ready=True condition missing); skipping cache", key) c.deletePolicy(key) return } diff --git a/internal/indexer/reindex_consumer.go b/internal/indexer/reindex_consumer.go index 3fc7047..3d6ac09 100644 --- a/internal/indexer/reindex_consumer.go +++ b/internal/indexer/reindex_consumer.go @@ -64,6 +64,7 @@ func (r *ReindexConsumer) Start(ctx context.Context) error { for _, cp := range policies { // Skip if index name is not set yet if cp.Policy.Status.IndexName == "" { + klog.V(2).Infof("ReindexConsumer: policy %s has no IndexName in status, skipping", cp.Policy.Name) continue } @@ -86,6 +87,8 @@ func (r *ReindexConsumer) Start(ctx context.Context) error { r.batcher.QueueUpsert(cp.Policy.Status.IndexName, doc, &msg) queued = true } else { + klog.V(4).Infof("ReindexConsumer: policy %s did not match resource %s/%s (id=%s), ensuring deletion", cp.Policy.Name, obj.GetNamespace(), obj.GetName(), event.ID) + // If it doesn't match this policy, we should ensure it's removed from the index // in case it was previously indexed there. r.batcher.QueueDelete(cp.Policy.Status.IndexName, resourceUID, &msg) From 52f25178e5c0366c2d1452842aa9d36878155f1d Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Wed, 25 Feb 2026 12:47:31 -0300 Subject: [PATCH 05/17] feat: introduce SearchQuery against MeiliSearch --- Taskfile.yaml | 5 +- cmd/search/indexer/command.go | 2 + cmd/search/main.go | 76 ++++- config/base/apiserver/deployment.yaml | 3 + config/overlays/ci/kustomization.yaml | 3 + config/overlays/dev/kustomization.yaml | 4 +- .../samples/search_v1alpha1_searchquery.yaml | 12 + internal/apiserver/apiserver.go | 46 ++- internal/indexer/policy_cache.go | 10 + internal/registry/searchquery/rest.go | 271 ++++++++++++++++++ pkg/apis/search/v1alpha1/policy_types.go | 6 + pkg/apis/search/v1alpha1/register.go | 4 +- pkg/apis/search/v1alpha1/types.go | 21 +- .../search/v1alpha1/zz_generated.deepcopy.go | 25 +- .../search/v1alpha1/zz_generated.openapi.go | 85 +++++- pkg/meilisearch/sdk_client.go | 33 +++ 16 files changed, 588 insertions(+), 18 deletions(-) create mode 100644 config/samples/search_v1alpha1_searchquery.yaml create mode 100644 internal/registry/searchquery/rest.go diff --git a/Taskfile.yaml b/Taskfile.yaml index b48cf89..ade3135 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -497,6 +497,7 @@ tasks: - | # Use KUBECONFIG if set, otherwise fallback to default KCFG=${KUBECONFIG:-$HOME/.kube/config} + export MEILISEARCH_API_KEY=search-master-key go run ./cmd/search serve \ --etcd-servers http://127.0.0.1:2379 \ --secure-port 9443 \ @@ -506,7 +507,9 @@ tasks: --authorization-kubeconfig="$KCFG" \ --kubeconfig="$KCFG" \ --client-ca-file="{{.CERTS_DIR}}/kind-ca.crt" \ - --authorization-always-allow-paths=/healthz,/readyz,/livez,/openapi,/openapi/v2,/openapi/v3,/apis,/api + --authorization-always-allow-paths=/healthz,/readyz,/livez,/openapi,/openapi/v2,/openapi/v3,/apis,/api \ + --meilisearch-domain="http://127.0.0.1:7700" \ + dev:undeploy: desc: Undeploy Search server from test-infra cluster diff --git a/cmd/search/indexer/command.go b/cmd/search/indexer/command.go index 74f4b6a..0ba3015 100644 --- a/cmd/search/indexer/command.go +++ b/cmd/search/indexer/command.go @@ -17,6 +17,7 @@ import ( "k8s.io/klog/v2" runtimecache "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client/config" + ctrllog "sigs.k8s.io/controller-runtime/pkg/log" ) // ResourceIndexerOptions holds the configuration for the resource indexer. @@ -159,6 +160,7 @@ func NewIndexerCommand() *cobra.Command { // Run starts the indexer consumer func Run(o *ResourceIndexerOptions, ctx context.Context) error { + ctrllog.SetLogger(klog.NewKlogr()) // Build a scheme and REST config for the controller-runtime cache. scheme := runtime.NewScheme() if err := searchv1alpha1.AddToScheme(scheme); err != nil { diff --git a/cmd/search/main.go b/cmd/search/main.go index d1272f6..d12a380 100644 --- a/cmd/search/main.go +++ b/cmd/search/main.go @@ -5,12 +5,15 @@ import ( "fmt" "os" "strings" + "time" + "github.com/google/uuid" "github.com/spf13/cobra" "github.com/spf13/pflag" searchapiserver "go.miloapis.net/search/internal/apiserver" "go.miloapis.net/search/internal/version" searchv1alpha1 "go.miloapis.net/search/pkg/apis/search/v1alpha1" + "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" apiopenapi "k8s.io/apiserver/pkg/endpoints/openapi" genericapiserver "k8s.io/apiserver/pkg/server" @@ -22,9 +25,13 @@ import ( logsapi "k8s.io/component-base/logs/api/v1" "k8s.io/klog/v2" "k8s.io/kube-openapi/pkg/common" + runtimecache "sigs.k8s.io/controller-runtime/pkg/cache" + ctrllog "sigs.k8s.io/controller-runtime/pkg/log" "go.miloapis.net/search/cmd/search/indexer" "go.miloapis.net/search/cmd/search/manager" + internalindexer "go.miloapis.net/search/internal/indexer" + "go.miloapis.net/search/pkg/meilisearch" // Register JSON logging format _ "k8s.io/component-base/logs/json/register" @@ -140,8 +147,14 @@ func NewVersionCommand() *cobra.Command { // SearchServerOptions contains configuration for the search server. type SearchServerOptions struct { - RecommendedOptions *options.RecommendedOptions - Logs *logsapi.LoggingConfiguration + RecommendedOptions *options.RecommendedOptions + Logs *logsapi.LoggingConfiguration + MeilisearchDomain string + MeilisearchHTTPTimeout time.Duration + MeilisearchTaskWaitTimeout time.Duration + MaxSearchLimit int + DefaultSearchLimit int + PagingTimeout time.Duration } // NewSearchServerOptions creates options with default values. @@ -151,7 +164,12 @@ func NewSearchServerOptions() *SearchServerOptions { "/registry/search.miloapis.com", searchapiserver.Codecs.LegacyCodec(searchapiserver.Scheme.PrioritizedVersionsAllGroups()...), ), - Logs: logsapi.NewLoggingConfiguration(), + Logs: logsapi.NewLoggingConfiguration(), + MeilisearchDomain: "http://meilisearch.meilisearch-system.svc.cluster.local:7700", + MeilisearchHTTPTimeout: 60 * time.Second, + MaxSearchLimit: 100, + DefaultSearchLimit: 10, + PagingTimeout: 24 * time.Hour, } return o @@ -159,6 +177,10 @@ func NewSearchServerOptions() *SearchServerOptions { func (o *SearchServerOptions) AddFlags(fs *pflag.FlagSet) { o.RecommendedOptions.AddFlags(fs) + fs.StringVar(&o.MeilisearchDomain, "meilisearch-domain", o.MeilisearchDomain, "Domain of the Meilisearch instance.") + fs.IntVar(&o.MaxSearchLimit, "max-search-limit", o.MaxSearchLimit, "The maximum number of results a SearchQuery can return in a single request.") + fs.IntVar(&o.DefaultSearchLimit, "default-search-limit", o.DefaultSearchLimit, "The default number of results a SearchQuery returns when no limit is specified.") + fs.DurationVar(&o.PagingTimeout, "paging-timeout", o.PagingTimeout, "The duration for which a paging (continue) token is valid.") } func (o *SearchServerOptions) Complete() error { @@ -197,9 +219,53 @@ func (o *SearchServerOptions) Config() (*searchapiserver.Config, error) { return nil, fmt.Errorf("failed to apply recommended options: %w", err) } + meiliClient, err := meilisearch.NewSDKClient(meilisearch.SDKConfig{ + Domain: o.MeilisearchDomain, + APIKey: os.Getenv("MEILISEARCH_API_KEY"), + WaitTimeout: o.MeilisearchTaskWaitTimeout, + HTTPTimeout: o.MeilisearchHTTPTimeout, + }) + if err != nil { + return nil, fmt.Errorf("failed to initialize meilisearch client: %w", err) + } + + // Create a dedicated scheme for the policy cache that only contains versioned types. + // This avoids ambiguity when the main scheme registers the same type for both + // v1alpha1 and __internal versions. + policyScheme := runtime.NewScheme() + if err := searchv1alpha1.AddToScheme(policyScheme); err != nil { + return nil, fmt.Errorf("failed to add v1alpha1 to policy scheme: %w", err) + } + + // Create a controller-runtime cache that uses a watch stream (informer) + // to keep ResourceIndexPolicies in-sync. + k8sCache, err := runtimecache.New(genericConfig.LoopbackClientConfig, runtimecache.Options{Scheme: policyScheme}) + if err != nil { + return nil, fmt.Errorf("failed to create controller-runtime cache: %w", err) + } + + // Create the policy cache (strict ready checking enabled) + indexPolicyCache, err := internalindexer.NewPolicyCache(k8sCache, true) + if err != nil { + return nil, fmt.Errorf("failed to create policy cache: %w", err) + } + + pagingSecret := []byte(os.Getenv("SEARCH_PAGING_SECRET")) + if len(pagingSecret) == 0 { + klog.Info("SEARCH_PAGING_SECRET not set, generating a random one") + pagingSecret = []byte(uuid.New().String()) + } + serverConfig := &searchapiserver.Config{ GenericConfig: genericConfig, - ExtraConfig: searchapiserver.ExtraConfig{}, + ExtraConfig: searchapiserver.ExtraConfig{ + MeiliClient: meiliClient, + PolicyCache: indexPolicyCache, + MaxSearchLimit: o.MaxSearchLimit, + DefaultSearchLimit: o.DefaultSearchLimit, + PagingSecret: pagingSecret, + PagingTimeout: o.PagingTimeout, + }, } return serverConfig, nil @@ -211,6 +277,8 @@ func Run(options *SearchServerOptions, ctx context.Context) error { return fmt.Errorf("failed to apply logging configuration: %w", err) } + ctrllog.SetLogger(klog.NewKlogr()) + config, err := options.Config() if err != nil { return err diff --git a/config/base/apiserver/deployment.yaml b/config/base/apiserver/deployment.yaml index a00ee93..a55ef64 100644 --- a/config/base/apiserver/deployment.yaml +++ b/config/base/apiserver/deployment.yaml @@ -97,6 +97,9 @@ spec: value: "4" - name: TRACING_CONFIG_FILE value: "" + envFrom: + - secretRef: + name: search-apiserver # TEMPLATE NOTE: Add your service-specific environment variables here # Example: database credentials, API keys, configuration values resources: diff --git a/config/overlays/ci/kustomization.yaml b/config/overlays/ci/kustomization.yaml index 19b8344..1fd8002 100644 --- a/config/overlays/ci/kustomization.yaml +++ b/config/overlays/ci/kustomization.yaml @@ -62,3 +62,6 @@ secretGenerator: - name: search-indexer literals: - MEILISEARCH_API_KEY=search-master-key +- name: search-apiserver + literals: + - MEILISEARCH_API_KEY=search-master-key diff --git a/config/overlays/dev/kustomization.yaml b/config/overlays/dev/kustomization.yaml index 2be128f..bb0690c 100644 --- a/config/overlays/dev/kustomization.yaml +++ b/config/overlays/dev/kustomization.yaml @@ -52,4 +52,6 @@ secretGenerator: - name: search-indexer literals: - MEILISEARCH_API_KEY=search-master-key - +- name: search-apiserver + literals: + - MEILISEARCH_API_KEY=search-master-key diff --git a/config/samples/search_v1alpha1_searchquery.yaml b/config/samples/search_v1alpha1_searchquery.yaml new file mode 100644 index 0000000..653702f --- /dev/null +++ b/config/samples/search_v1alpha1_searchquery.yaml @@ -0,0 +1,12 @@ +apiVersion: search.miloapis.com/v1alpha1 +kind: SearchQuery +metadata: + name: configmap-search-sample +spec: + targetResources: + - group: "" + version: v1 + kind: ConfigMap + query: "" # Empty query returns everything, or put a specific string + limit: 20 + offset: 0 diff --git a/internal/apiserver/apiserver.go b/internal/apiserver/apiserver.go index 02e8346..3f24b9a 100644 --- a/internal/apiserver/apiserver.go +++ b/internal/apiserver/apiserver.go @@ -2,6 +2,8 @@ package apiserver import ( "context" + "fmt" + "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -11,11 +13,13 @@ import ( genericapiserver "k8s.io/apiserver/pkg/server" "k8s.io/klog/v2" + "go.miloapis.net/search/internal/indexer" _ "go.miloapis.net/search/internal/metrics" "go.miloapis.net/search/internal/registry/policy/resourceindexpolicy" + "go.miloapis.net/search/internal/registry/searchquery" "go.miloapis.net/search/pkg/apis/search/install" - searchinstall "go.miloapis.net/search/pkg/apis/search/install" searchv1alpha1 "go.miloapis.net/search/pkg/apis/search/v1alpha1" + "go.miloapis.net/search/pkg/meilisearch" ) var ( @@ -27,7 +31,6 @@ var ( func init() { install.Install(Scheme) - searchinstall.Install(Scheme) metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) @@ -36,6 +39,8 @@ func init() { Scheme.AddKnownTypes(schema.GroupVersion{Group: searchv1alpha1.GroupName, Version: runtime.APIVersionInternal}, &searchv1alpha1.ResourceIndexPolicy{}, &searchv1alpha1.ResourceIndexPolicyList{}, + &searchv1alpha1.SearchQuery{}, + &searchv1alpha1.SearchQueryList{}, ) // Register unversioned meta types required by the API machinery. @@ -51,7 +56,12 @@ func init() { // ExtraConfig extends the generic apiserver configuration with search-specific settings. type ExtraConfig struct { - // Add custom configuration here as needed + MeiliClient *meilisearch.SDKClient + PolicyCache *indexer.PolicyCache + MaxSearchLimit int + DefaultSearchLimit int + PagingSecret []byte + PagingTimeout time.Duration } // Config combines generic and search-specific configuration. @@ -110,12 +120,42 @@ func (c completedConfig) New() (*SearchServer, error) { searchV1alpha1Storage["resourceindexpolicies"] = policyStorage.Store searchV1alpha1Storage["resourceindexpolicies/status"] = policyStorage.Status + // Add searchquery resources + searchqueryStorage := searchquery.NewREST( + c.ExtraConfig.MeiliClient, + c.ExtraConfig.PolicyCache, + c.ExtraConfig.MaxSearchLimit, + c.ExtraConfig.DefaultSearchLimit, + c.ExtraConfig.PagingSecret, + c.ExtraConfig.PagingTimeout, + ) + searchV1alpha1Storage["searchqueries"] = searchqueryStorage + searchAPIGroupInfo.VersionedResourcesStorageMap["v1alpha1"] = searchV1alpha1Storage if err := s.GenericAPIServer.InstallAPIGroup(&searchAPIGroupInfo); err != nil { return nil, err } + // Add PostStartHook to start policy cache + s.GenericAPIServer.AddPostStartHookOrDie("search-policy-cache", func(ctx genericapiserver.PostStartHookContext) error { + if err := c.ExtraConfig.PolicyCache.RegisterHandlers(ctx.Context); err != nil { + return fmt.Errorf("failed to register policy cache handlers: %w", err) + } + + go func() { + klog.Info("Starting Search policy cache...") + if err := c.ExtraConfig.PolicyCache.Start(ctx.Context); err != nil { + klog.Errorf("Policy cache stopped with error: %v", err) + } + }() + + if !c.ExtraConfig.PolicyCache.WaitForCacheSync(ctx.Context) { + return fmt.Errorf("failed to wait for policy cache sync") + } + return nil + }) + klog.Info("Search server initialized successfully") return s, nil diff --git a/internal/indexer/policy_cache.go b/internal/indexer/policy_cache.go index 5ac9daf..c3654be 100644 --- a/internal/indexer/policy_cache.go +++ b/internal/indexer/policy_cache.go @@ -155,6 +155,16 @@ func (c *PolicyCache) deletePolicy(name string) { klog.Infof("Policy %s removed from cache", name) } +// Start starts the underlying cache. +func (c *PolicyCache) Start(ctx context.Context) error { + return c.cache.Start(ctx) +} + +// WaitForCacheSync waits for the underlying cache to sync. +func (c *PolicyCache) WaitForCacheSync(ctx context.Context) bool { + return c.cache.WaitForCacheSync(ctx) +} + // GetPolicies returns a snapshot of all cached policies. func (c *PolicyCache) GetPolicies() []*policyevaluation.CachedPolicy { c.mu.RLock() diff --git a/internal/registry/searchquery/rest.go b/internal/registry/searchquery/rest.go new file mode 100644 index 0000000..4e4091f --- /dev/null +++ b/internal/registry/searchquery/rest.go @@ -0,0 +1,271 @@ +package searchquery + +import ( + "context" + "encoding/json" + "fmt" + "math" + "reflect" + "time" + + "github.com/golang-jwt/jwt/v5" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" + "k8s.io/klog/v2" + + "go.miloapis.net/search/internal/indexer" + searchv1alpha1 "go.miloapis.net/search/pkg/apis/search/v1alpha1" + "go.miloapis.net/search/pkg/meilisearch" +) + +// REST implements a RESTStorage for SearchQuery API. +type REST struct { + meiliClient *meilisearch.SDKClient + policyCache *indexer.PolicyCache + maxSearchLimit int + defaultSearchLimit int + secretKey []byte + pagingTimeout time.Duration +} + +// Ensure REST implements required interfaces +var _ rest.Storage = &REST{} +var _ rest.Creater = &REST{} +var _ rest.Scoper = &REST{} +var _ rest.SingularNameProvider = &REST{} + +// NewREST returns a RESTStorage object that will work against SearchQuery. +func NewREST(meiliClient *meilisearch.SDKClient, policyCache *indexer.PolicyCache, maxSearchLimit int, defaultSearchLimit int, pagingSecret []byte, pagingTimeout time.Duration) *REST { + return &REST{ + meiliClient: meiliClient, + policyCache: policyCache, + maxSearchLimit: maxSearchLimit, + defaultSearchLimit: defaultSearchLimit, + secretKey: pagingSecret, + pagingTimeout: pagingTimeout, + } +} + +// New returns an empty object that can be used with Create. +func (r *REST) New() runtime.Object { + return &searchv1alpha1.SearchQuery{} +} + +// Destroy cleans up its resources on shutdown. +func (r *REST) Destroy() {} + +// NamespaceScoped returns true if the storage is namespaced +func (r *REST) NamespaceScoped() bool { + return false +} + +// GetSingularName implements the rest.SingularNameProvider interface +func (r *REST) GetSingularName() string { + return "searchquery" +} + +// Create creates a new version of a resource. +func (r *REST) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) { + query, ok := obj.(*searchv1alpha1.SearchQuery) + if !ok { + return nil, fmt.Errorf("not a SearchQuery: %#v", obj) + } + + if createValidation != nil { + if err := createValidation(ctx, obj); err != nil { + return nil, err + } + } + + limit, offset, err := r.validateAndGetPagination(query) + if err != nil { + return nil, err + } + + indexUIDs, err := r.resolveIndexUIDs(query) + if err != nil { + return nil, err + } + + if len(indexUIDs) == 0 { + // No ready policies or matching indices exist yet + created := query.DeepCopy() + created.Status = searchv1alpha1.SearchQueryStatus{ + Results: []searchv1alpha1.SearchResult{}, + } + return created, nil + } + + // Perform the multi search + resp, err := r.meiliClient.MultiSearch(indexUIDs, query.Spec.Query, limit, offset) + if err != nil { + return nil, fmt.Errorf("failed to search: %w", err) + } + + // Process search results + var results []searchv1alpha1.SearchResult + + // Handle search results + for _, hit := range resp.Hits { + if res, err := formatSearchResult(hit); err == nil { + results = append(results, res) + } + } + + // Populate response status + created := query.DeepCopy() + created.Status = searchv1alpha1.SearchQueryStatus{ + Results: results, + Continue: r.calculateNextContinueToken(offset, limit, len(resp.Hits), query), + } + + return created, nil +} + +type PagingClaims struct { + Offset int64 `json:"offset"` + Limit int32 `json:"limit"` + Query string `json:"query"` + TargetResources []searchv1alpha1.TargetResource `json:"targetResources"` + jwt.RegisteredClaims +} + +// validateAndGetPagination validates the limit and continue token from the query and returns +// their effective values. +func (r *REST) validateAndGetPagination(query *searchv1alpha1.SearchQuery) (int64, int64, error) { + limit := int64(r.defaultSearchLimit) + var offset int64 = 0 + + // If continue token is provided, it dictates the query state + if query.Spec.Continue != "" { + claims := &PagingClaims{} + token, err := jwt.ParseWithClaims(query.Spec.Continue, claims, func(token *jwt.Token) (interface{}, error) { + return r.secretKey, nil + }) + + if err != nil || !token.Valid { + return 0, 0, apierrors.NewBadRequest("invalid continue token") + } + + // Verify that the query has not changed + if query.Spec.Query != claims.Query { + return 0, 0, apierrors.NewBadRequest("query string cannot be changed when using a continue token") + } + if int32(limit) != claims.Limit && query.Spec.Limit != 0 { + // If limit was specified and is different from the token, error. + // (Note: limit in query.Spec might be 0 if not provided, in which case we use the defaulted limit from the token) + if query.Spec.Limit != claims.Limit { + return 0, 0, apierrors.NewBadRequest("limit cannot be changed when using a continue token") + } + } + if !reflect.DeepEqual(query.Spec.TargetResources, claims.TargetResources) { + return 0, 0, apierrors.NewBadRequest("targetResources cannot be changed when using a continue token") + } + + // Ensure we use the correct limit/offset from the token + limit = int64(claims.Limit) + offset = claims.Offset + return limit, offset, nil + } + + // For new queries, validate limit + if query.Spec.Limit < 0 { + return 0, 0, apierrors.NewBadRequest("limit cannot be negative") + } + if query.Spec.Limit > 0 { + if int(query.Spec.Limit) > r.maxSearchLimit { + return 0, 0, apierrors.NewBadRequest(fmt.Sprintf("limit %d exceeds the maximum search limit of %d", query.Spec.Limit, r.maxSearchLimit)) + } + limit = int64(query.Spec.Limit) + } + + return limit, offset, nil +} + +// resolveIndexUIDs retrieves the Meilisearch index UIDs for the targeted resources +// in the query. If no target resources are specified, it returns all indices from +// all ready policies. +func (r *REST) resolveIndexUIDs(query *searchv1alpha1.SearchQuery) ([]string, error) { + var indexUIDs []string + policies := r.policyCache.GetPolicies() + + if len(query.Spec.TargetResources) > 0 { + for _, tr := range query.Spec.TargetResources { + found := false + for _, cp := range policies { + p := cp.Policy + if p.Spec.TargetResource.Group == tr.Group && + p.Spec.TargetResource.Version == tr.Version && + p.Spec.TargetResource.Kind == tr.Kind { + if p.Status.IndexName != "" { + indexUIDs = append(indexUIDs, p.Status.IndexName) + found = true + } + } + } + if !found { + return nil, apierrors.NewBadRequest(fmt.Sprintf("target resource %s/%s %s is not currently indexed or policy is not ready", tr.Group, tr.Version, tr.Kind)) + } + } + } else { + for _, cp := range policies { + if cp.Policy.Status.IndexName != "" { + indexUIDs = append(indexUIDs, cp.Policy.Status.IndexName) + } + } + } + return indexUIDs, nil +} + +// calculateNextContinueToken determines the next continue token for pagination. +// If the number of hits on the current page is equal to or greater than the limit, +// it assumes there are more results and returns a signed JWT containing the next offset +// and the original query parameters to ensure consistency. +func (r *REST) calculateNextContinueToken(currentOffset, limit int64, totalHitsOnPage int, query *searchv1alpha1.SearchQuery) string { + if totalHitsOnPage > 0 && int64(totalHitsOnPage) >= limit { + claims := PagingClaims{ + Offset: currentOffset + limit, + Limit: int32(limit), + Query: query.Spec.Query, + TargetResources: query.Spec.TargetResources, + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(r.pagingTimeout)), + IssuedAt: jwt.NewNumericDate(time.Now()), + }, + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + ss, err := token.SignedString(r.secretKey) + if err != nil { + klog.Errorf("Failed to sign paging token: %v", err) + return "" + } + return ss + } + return "" +} + +// formatSearchResult converts a Meilisearch hit into a SearchResult API object. +func formatSearchResult(hit map[string]json.RawMessage) (searchv1alpha1.SearchResult, error) { + var score float64 + if s, found := hit["_rankingScore"]; found { + _ = json.Unmarshal(s, &score) + score = math.Round(score*10000) / 10000 + } + + // Remove meilisearch internal fields + delete(hit, "_rankingScore") + delete(hit, "_federation") + + b, err := json.Marshal(hit) + if err != nil { + return searchv1alpha1.SearchResult{}, err + } + + return searchv1alpha1.SearchResult{ + Resource: runtime.RawExtension{Raw: b}, + RelevanceScore: score, + }, nil +} diff --git a/pkg/apis/search/v1alpha1/policy_types.go b/pkg/apis/search/v1alpha1/policy_types.go index b479632..75f999c 100644 --- a/pkg/apis/search/v1alpha1/policy_types.go +++ b/pkg/apis/search/v1alpha1/policy_types.go @@ -49,11 +49,15 @@ type ResourceIndexPolicySpec struct { // - Ternary: condition ? trueValue : falseValue // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=10 + // +listType=map + // +listMapKey=name Conditions []PolicyCondition `json:"conditions"` // Fields defines which fields from the resource are indexed. // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=10 + // +listType=map + // +listMapKey=path Fields []FieldPolicy `json:"fields"` } @@ -110,6 +114,8 @@ type ResourceIndexPolicyStatus struct { // Conditions represents the latest available observations of the policy's state. // +kubebuilder:default={{type: "Ready", status: "Unknown", reason: "Unknown", message: "Waiting for control plane to reconcile", lastTransitionTime: "1970-01-01T00:00:00Z"}} // +optional + // +listType=map + // +listMapKey=type Conditions []metav1.Condition `json:"conditions,omitempty"` // IndexName is the name of the search index created for this policy. diff --git a/pkg/apis/search/v1alpha1/register.go b/pkg/apis/search/v1alpha1/register.go index 4f0d4ae..5a5155e 100644 --- a/pkg/apis/search/v1alpha1/register.go +++ b/pkg/apis/search/v1alpha1/register.go @@ -27,8 +27,8 @@ func Resource(resource string) schema.GroupResource { // addKnownTypes adds the set of types defined in this package to the supplied scheme func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, - // &SearchQuery{}, - // &SearchQueryList{}, + &SearchQuery{}, + &SearchQueryList{}, &ResourceIndexPolicy{}, &ResourceIndexPolicyList{}, ) diff --git a/pkg/apis/search/v1alpha1/types.go b/pkg/apis/search/v1alpha1/types.go index fc658da..f308343 100644 --- a/pkg/apis/search/v1alpha1/types.go +++ b/pkg/apis/search/v1alpha1/types.go @@ -36,6 +36,11 @@ type SearchQuery struct { // // The actual fields will depend on the specific search implementation. type SearchQuerySpec struct { + // TargetResources limits the search to specific resource types. + // +optional + // +listType=atomic + TargetResources []TargetResource `json:"targetResources,omitempty"` + // Query is the search query string. // // +required @@ -58,19 +63,29 @@ type SearchQuerySpec struct { // SearchQueryStatus contains the query results and pagination state. type SearchQueryStatus struct { - // Results contains the search results as an array of Kubernetes resources. + // Results contains the search results. // // +optional - Results []runtime.RawExtension `json:"results,omitempty"` + // +listType=atomic + Results []SearchResult `json:"results,omitempty"` // Continue is the pagination cursor. // Non-empty means more results are available - copy this to spec.continue for the next page. // Empty means you have all results. - // // +optional Continue string `json:"continue,omitempty"` } +// SearchResult represents a single search result with its relevance score. +type SearchResult struct { + // Resource contains the actual Kubernetes resource. + Resource runtime.RawExtension `json:"resource"` + + // RelevanceScore is the relevance score from Meilisearch. + // +optional + RelevanceScore float64 `json:"relevanceScore,omitempty"` +} + // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // SearchQueryList is a list of SearchQuery objects diff --git a/pkg/apis/search/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/search/v1alpha1/zz_generated.deepcopy.go index 971235c..ffd77d0 100644 --- a/pkg/apis/search/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/search/v1alpha1/zz_generated.deepcopy.go @@ -151,7 +151,7 @@ func (in *SearchQuery) DeepCopyInto(out *SearchQuery) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) } @@ -208,6 +208,11 @@ func (in *SearchQueryList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SearchQuerySpec) DeepCopyInto(out *SearchQuerySpec) { *out = *in + if in.TargetResources != nil { + in, out := &in.TargetResources, &out.TargetResources + *out = make([]TargetResource, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SearchQuerySpec. @@ -225,7 +230,7 @@ func (in *SearchQueryStatus) DeepCopyInto(out *SearchQueryStatus) { *out = *in if in.Results != nil { in, out := &in.Results, &out.Results - *out = make([]runtime.RawExtension, len(*in)) + *out = make([]SearchResult, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -242,6 +247,22 @@ func (in *SearchQueryStatus) DeepCopy() *SearchQueryStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SearchResult) DeepCopyInto(out *SearchResult) { + *out = *in + in.Resource.DeepCopyInto(&out.Resource) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SearchResult. +func (in *SearchResult) DeepCopy() *SearchResult { + if in == nil { + return nil + } + out := new(SearchResult) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetResource) DeepCopyInto(out *TargetResource) { *out = *in diff --git a/pkg/apis/search/v1alpha1/zz_generated.openapi.go b/pkg/apis/search/v1alpha1/zz_generated.openapi.go index 350f3e5..0833849 100644 --- a/pkg/apis/search/v1alpha1/zz_generated.openapi.go +++ b/pkg/apis/search/v1alpha1/zz_generated.openapi.go @@ -25,6 +25,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "go.miloapis.net/search/pkg/apis/search/v1alpha1.SearchQueryList": schema_pkg_apis_search_v1alpha1_SearchQueryList(ref), "go.miloapis.net/search/pkg/apis/search/v1alpha1.SearchQuerySpec": schema_pkg_apis_search_v1alpha1_SearchQuerySpec(ref), "go.miloapis.net/search/pkg/apis/search/v1alpha1.SearchQueryStatus": schema_pkg_apis_search_v1alpha1_SearchQueryStatus(ref), + "go.miloapis.net/search/pkg/apis/search/v1alpha1.SearchResult": schema_pkg_apis_search_v1alpha1_SearchResult(ref), "go.miloapis.net/search/pkg/apis/search/v1alpha1.TargetResource": schema_pkg_apis_search_v1alpha1_TargetResource(ref), "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref), "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref), @@ -253,6 +254,14 @@ func schema_pkg_apis_search_v1alpha1_ResourceIndexPolicySpec(ref common.Referenc }, }, "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "name", + }, + "x-kubernetes-list-type": "map", + }, + }, SchemaProps: spec.SchemaProps{ Description: "Conditions filter which resources are indexed using CEL expressions. Multiple conditions can be specified and are evaluated with OR semantics - a resource is indexed if it satisfies ANY condition. Use && within a single expression to require multiple criteria together.\n\nEach condition has: - name: A unique identifier for the condition, used in status reporting\n and debugging to identify which condition(s) matched a resource.\n- expression: A CEL expression that must evaluate to a boolean. The\n resource is available as the root object in the expression context.\n\nAvailable CEL operations: - Field access: spec.replicas, metadata.name, status.phase - Map access: metadata.labels[\"app\"], metadata.annotations[\"key\"] - Comparisons: ==, !=, <, <=, >, >= - Logical operators: &&, ||, ! - String functions: contains(), startsWith(), endsWith(), matches() - List functions: exists(), all(), size(), map(), filter() - Membership: \"value\" in list, \"key\" in map - Ternary: condition ? trueValue : falseValue", Type: []string{"array"}, @@ -267,6 +276,14 @@ func schema_pkg_apis_search_v1alpha1_ResourceIndexPolicySpec(ref common.Referenc }, }, "fields": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "path", + }, + "x-kubernetes-list-type": "map", + }, + }, SchemaProps: spec.SchemaProps{ Description: "Fields defines which fields from the resource are indexed.", Type: []string{"array"}, @@ -297,6 +314,14 @@ func schema_pkg_apis_search_v1alpha1_ResourceIndexPolicyStatus(ref common.Refere Type: []string{"object"}, Properties: map[string]spec.Schema{ "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "type", + }, + "x-kubernetes-list-type": "map", + }, + }, SchemaProps: spec.SchemaProps{ Description: "Conditions represents the latest available observations of the policy's state.", Type: []string{"array"}, @@ -436,6 +461,25 @@ func schema_pkg_apis_search_v1alpha1_SearchQuerySpec(ref common.ReferenceCallbac Description: "SearchQuerySpec defines the search parameters.\n\nThe actual fields will depend on the specific search implementation.", Type: []string{"object"}, Properties: map[string]spec.Schema{ + "targetResources": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "TargetResources limits the search to specific resource types.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("go.miloapis.net/search/pkg/apis/search/v1alpha1.TargetResource"), + }, + }, + }, + }, + }, "query": { SchemaProps: spec.SchemaProps{ Description: "Query is the search query string.", @@ -462,6 +506,8 @@ func schema_pkg_apis_search_v1alpha1_SearchQuerySpec(ref common.ReferenceCallbac Required: []string{"query"}, }, }, + Dependencies: []string{ + "go.miloapis.net/search/pkg/apis/search/v1alpha1.TargetResource"}, } } @@ -473,14 +519,19 @@ func schema_pkg_apis_search_v1alpha1_SearchQueryStatus(ref common.ReferenceCallb Type: []string{"object"}, Properties: map[string]spec.Schema{ "results": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, SchemaProps: spec.SchemaProps{ - Description: "Results contains the search results as an array of Kubernetes resources.", + Description: "Results contains the search results.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + Ref: ref("go.miloapis.net/search/pkg/apis/search/v1alpha1.SearchResult"), }, }, }, @@ -496,6 +547,36 @@ func schema_pkg_apis_search_v1alpha1_SearchQueryStatus(ref common.ReferenceCallb }, }, }, + Dependencies: []string{ + "go.miloapis.net/search/pkg/apis/search/v1alpha1.SearchResult"}, + } +} + +func schema_pkg_apis_search_v1alpha1_SearchResult(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SearchResult represents a single search result with its relevance score.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "resource": { + SchemaProps: spec.SchemaProps{ + Description: "Resource contains the actual Kubernetes resource.", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, + "relevanceScore": { + SchemaProps: spec.SchemaProps{ + Description: "RelevanceScore is the relevance score from Meilisearch.", + Type: []string{"number"}, + Format: "double", + }, + }, + }, + Required: []string{"resource"}, + }, + }, Dependencies: []string{ "k8s.io/apimachinery/pkg/runtime.RawExtension"}, } diff --git a/pkg/meilisearch/sdk_client.go b/pkg/meilisearch/sdk_client.go index c8758df..6c84e62 100644 --- a/pkg/meilisearch/sdk_client.go +++ b/pkg/meilisearch/sdk_client.go @@ -353,3 +353,36 @@ func (s *SDKClient) GetSettingsUpdateTask(indexUID string) (*meilisearch.Task, e // Return the most recent task return &resp.Results[0], nil } + +// MultiSearch performs a search query across multiple indices. +func (s *SDKClient) MultiSearch(indexUIDs []string, query string, limit int64, offset int64) (*meilisearch.MultiSearchResponse, error) { + if len(indexUIDs) == 0 { + return nil, fmt.Errorf("no index UIDs provided") + } + + var queries []*meilisearch.SearchRequest + for _, uid := range indexUIDs { + queries = append(queries, &meilisearch.SearchRequest{ + IndexUID: uid, + Query: query, + ShowRankingScore: true, + }) + } + + req := &meilisearch.MultiSearchRequest{ + Queries: queries, + Federation: &meilisearch.MultiSearchFederation{ + Limit: limit, + Offset: offset, + }, + } + + klog.V(4).Infof("MultiSearch across indices %v with query %q, limit %d, offset %d", indexUIDs, query, limit, offset) + resp, err := s.client.MultiSearch(req) + if err != nil { + klog.Errorf("MultiSearch failed across indices %v: %v", indexUIDs, err) + return nil, fmt.Errorf("multi-search failed: %w", err) + } + + return resp, nil +} From c05ce9625254495252f9f0299a9ba4b495f056ed Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Fri, 27 Feb 2026 13:05:04 -0300 Subject: [PATCH 06/17] feat: add NATS TLS configuration options and apply them to the NATS connection. --- cmd/search/manager/command.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/cmd/search/manager/command.go b/cmd/search/manager/command.go index e1a245a..d67e1aa 100644 --- a/cmd/search/manager/command.go +++ b/cmd/search/manager/command.go @@ -55,6 +55,9 @@ type ControllerManagerOptions struct { // NATS settings for publishing per-resource re-index messages. NatsURL string NatsReindexSubject string + NatsTLSCA string + NatsTLSCert string + NatsTLSKey string } // NewControllerManagerOptions creates a new ControllerManagerOptions with default values @@ -97,6 +100,9 @@ func (o *ControllerManagerOptions) AddFlags(fs *pflag.FlagSet) { // NATS fs.StringVar(&o.NatsURL, "nats-url", o.NatsURL, "The URL of the NATS server used to publish re-index messages.") fs.StringVar(&o.NatsReindexSubject, "nats-reindex-subject", o.NatsReindexSubject, "The NATS subject to publish per-resource re-index messages to.") + fs.StringVar(&o.NatsTLSCA, "nats-tls-ca", o.NatsTLSCA, "The path to the NATS TLS CA file.") + fs.StringVar(&o.NatsTLSCert, "nats-tls-cert", o.NatsTLSCert, "The path to the NATS TLS certificate file.") + fs.StringVar(&o.NatsTLSKey, "nats-tls-key", o.NatsTLSKey, "The path to the NATS TLS key file.") } // Validate validates the options @@ -205,7 +211,19 @@ func Run(o *ControllerManagerOptions, ctx context.Context) error { // Connect to NATS and set up the re-index stream + publisher. setupLog.Info("Connecting to NATS for re-index publishing", "url", o.NatsURL) - nc, err := nats.Connect(o.NatsURL) + + var natsOpts []nats.Option + if o.NatsTLSCert != "" && o.NatsTLSKey != "" { + if o.NatsTLSCA != "" { + setupLog.Info("Using NATS TLS CA", "ca", o.NatsTLSCA) + natsOpts = append(natsOpts, nats.RootCAs(o.NatsTLSCA)) + } + setupLog.Info("Using NATS TLS cert", "cert", o.NatsTLSCert) + setupLog.Info("Using NATS TLS key", "key", o.NatsTLSKey) + natsOpts = append(natsOpts, nats.ClientCert(o.NatsTLSCert, o.NatsTLSKey)) + } + + nc, err := nats.Connect(o.NatsURL, natsOpts...) if err != nil { setupLog.Error(err, "unable to connect to NATS") os.Exit(1) From 714b498ed9b3bc8e6fffab70ad4376e1e533d9ba Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Fri, 27 Feb 2026 13:27:08 -0300 Subject: [PATCH 07/17] feat: Add `LeaderElectionNamespace` option and flag for controller manager configuration. --- cmd/search/manager/command.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/cmd/search/manager/command.go b/cmd/search/manager/command.go index d67e1aa..47fe05c 100644 --- a/cmd/search/manager/command.go +++ b/cmd/search/manager/command.go @@ -43,6 +43,7 @@ func init() { type ControllerManagerOptions struct { MetricsAddr string EnableLeaderElection bool + LeaderElectionNamespace string ProbeAddr string SecureMetrics bool EnableHTTP2 bool @@ -66,6 +67,7 @@ func NewControllerManagerOptions() *ControllerManagerOptions { MetricsAddr: ":8080", ProbeAddr: ":8081", EnableLeaderElection: true, + LeaderElectionNamespace: "", SecureMetrics: false, EnableHTTP2: false, MaxCELDepth: 50, @@ -85,6 +87,8 @@ func (o *ControllerManagerOptions) AddFlags(fs *pflag.FlagSet) { fs.BoolVar(&o.EnableLeaderElection, "leader-elect", o.EnableLeaderElection, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") + fs.StringVar(&o.LeaderElectionNamespace, "leader-elect-resource-namespace", o.LeaderElectionNamespace, + "The namespace in which the leader election resource will be created.") fs.BoolVar(&o.SecureMetrics, "metrics-secure", o.SecureMetrics, "If set the metrics endpoint is served securely") fs.BoolVar(&o.EnableHTTP2, "enable-http2", o.EnableHTTP2, @@ -173,11 +177,12 @@ func Run(o *ControllerManagerOptions, ctx context.Context) error { cfg := ctrl.GetConfigOrDie() mgr, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: scheme, - Metrics: metricsserver.Options{BindAddress: o.MetricsAddr, SecureServing: o.SecureMetrics, TLSOpts: tlsOpts}, - HealthProbeBindAddress: o.ProbeAddr, - LeaderElection: o.EnableLeaderElection, - LeaderElectionID: "controller.search.miloapis.com", + Scheme: scheme, + Metrics: metricsserver.Options{BindAddress: o.MetricsAddr, SecureServing: o.SecureMetrics, TLSOpts: tlsOpts}, + HealthProbeBindAddress: o.ProbeAddr, + LeaderElection: o.EnableLeaderElection, + LeaderElectionID: "controller.search.miloapis.com", + LeaderElectionNamespace: o.LeaderElectionNamespace, }) if err != nil { setupLog.Error(err, "unable to start manager") From 8cda70b0e234c31adaae3e8027920252a4ea1775 Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Fri, 27 Feb 2026 16:41:58 -0300 Subject: [PATCH 08/17] feat: Add NATS TLS CA, certificate, and key configuration for secure connections. --- cmd/search/indexer/command.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/cmd/search/indexer/command.go b/cmd/search/indexer/command.go index 0ba3015..a0142a9 100644 --- a/cmd/search/indexer/command.go +++ b/cmd/search/indexer/command.go @@ -26,6 +26,9 @@ type ResourceIndexerOptions struct { NatsURL string NatsAuditConsumerName string NatsStreamName string + NatsTLSCA string + NatsTLSCert string + NatsTLSKey string // NATS re-index consumer settings (separate REINDEX_EVENTS stream) NatsReindexStream string @@ -73,6 +76,9 @@ func (o *ResourceIndexerOptions) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&o.NatsReindexStream, "nats-reindex-stream", o.NatsReindexStream, "The JetStream stream name for re-index messages.") fs.StringVar(&o.NatsReindexConsumerName, "nats-reindex-consumer-name", o.NatsReindexConsumerName, "The name of the re-index JetStream consumer (must match the manifest).") + fs.StringVar(&o.NatsTLSCA, "nats-tls-ca", o.NatsTLSCA, "The path to the NATS TLS CA file.") + fs.StringVar(&o.NatsTLSCert, "nats-tls-cert", o.NatsTLSCert, "The path to the NATS TLS certificate file.") + fs.StringVar(&o.NatsTLSKey, "nats-tls-key", o.NatsTLSKey, "The path to the NATS TLS key file.") fs.StringVar(&o.MeilisearchDomain, "meilisearch-domain", o.MeilisearchDomain, "Domain of the Meilisearch instance.") fs.DurationVar(&o.MeilisearchTaskWaitTimeout, "meilisearch-task-wait-timeout", o.MeilisearchTaskWaitTimeout, "Timeout for waiting for Meilisearch tasks to complete.") @@ -214,7 +220,18 @@ func Run(o *ResourceIndexerOptions, ctx context.Context) error { // Connect to NATS klog.Infof("Connecting to NATS at %s...", o.NatsURL) - nc, err := nats.Connect(o.NatsURL) + + var natsOpts []nats.Option + if o.NatsTLSCert != "" && o.NatsTLSKey != "" { + if o.NatsTLSCA != "" { + klog.Infof("Using NATS TLS CA from %s", o.NatsTLSCA) + natsOpts = append(natsOpts, nats.RootCAs(o.NatsTLSCA)) + } + klog.Infof("Using NATS TLS cert from %s and key from %s", o.NatsTLSCert, o.NatsTLSKey) + natsOpts = append(natsOpts, nats.ClientCert(o.NatsTLSCert, o.NatsTLSKey)) + } + + nc, err := nats.Connect(o.NatsURL, natsOpts...) if err != nil { return fmt.Errorf("failed to connect to NATS: %w", err) } From d21cc0bf467560d119f123fbeab629c5f6825c1c Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Fri, 27 Feb 2026 19:57:54 -0300 Subject: [PATCH 09/17] feat: Add resource GVK to policy evaluation results and transformed documents --- internal/policy/evaluation/evaluator.go | 22 ++++- internal/policy/evaluation/transform_test.go | 99 +++++++++++++++++--- 2 files changed, 107 insertions(+), 14 deletions(-) diff --git a/internal/policy/evaluation/evaluator.go b/internal/policy/evaluation/evaluator.go index dfb7608..9ff49b2 100644 --- a/internal/policy/evaluation/evaluator.go +++ b/internal/policy/evaluation/evaluator.go @@ -16,6 +16,12 @@ type EvalResult struct { Matched bool // Fields contains the extracted field values from the resource, keyed by path. Fields map[string]any + // Group is the API group of the matching policy. + Group string + // Version is the API version of the matching policy. + Version string + // Kind is the kind of the matching policy. + Kind string } // PolicyEvaluator evaluates whether a Kubernetes resource matches a policy @@ -33,7 +39,12 @@ type CachedPolicy struct { // Evaluate checks if the resource matches the policy's target GVK, conditions, // and extracts the configured fields from matching resources. func (cp *CachedPolicy) Evaluate(u *unstructured.Unstructured) (*EvalResult, error) { - result := &EvalResult{Fields: map[string]any{}} + result := &EvalResult{ + Fields: map[string]any{}, + Group: cp.Policy.Spec.TargetResource.Group, + Version: cp.Policy.Spec.TargetResource.Version, + Kind: cp.Policy.Spec.TargetResource.Kind, + } // 1. Check GVK match gvk := u.GroupVersionKind() @@ -155,6 +166,15 @@ func (r *EvalResult) Transform() map[string]any { leaf := segments[len(segments)-1] current[leaf] = value } + + // Add policy GVK metadata to the document + if r.Group != "" { + doc["apiVersion"] = r.Group + "/" + r.Version + } else { + doc["apiVersion"] = r.Version + } + doc["kind"] = r.Kind + return doc } diff --git a/internal/policy/evaluation/transform_test.go b/internal/policy/evaluation/transform_test.go index ce8c452..682bfd1 100644 --- a/internal/policy/evaluation/transform_test.go +++ b/internal/policy/evaluation/transform_test.go @@ -11,17 +11,33 @@ func TestEvalResult_Transform(t *testing.T) { tests := []struct { name string fields map[string]any + group string + version string + kind string expected map[string]any }{ { - name: "empty fields", - fields: map[string]any{}, - expected: map[string]any{}, + name: "empty fields", + fields: map[string]any{}, + group: "search.miloapis.com", + version: "v1alpha1", + kind: "ResourceIndexPolicy", + expected: map[string]any{ + "apiVersion": "search.miloapis.com/v1alpha1", + "kind": "ResourceIndexPolicy", + }, }, { - name: "single field", - fields: map[string]any{".metadata.name": "test-cm"}, - expected: map[string]any{"metadata": map[string]any{"name": "test-cm"}}, + name: "single field", + fields: map[string]any{".metadata.name": "test-cm"}, + group: "", + version: "v1", + kind: "ConfigMap", + expected: map[string]any{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]any{"name": "test-cm"}, + }, }, { name: "multiple fields", @@ -30,8 +46,13 @@ func TestEvalResult_Transform(t *testing.T) { ".spec.replicas": int64(3), ".spec.selector.matchLabels.app": "foo", }, + group: "apps", + version: "v1", + kind: "Deployment", expected: map[string]any{ - "metadata": map[string]any{"name": "test-cm"}, + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]any{"name": "test-cm"}, "spec": map[string]any{ "replicas": int64(3), "selector": map[string]any{ @@ -41,10 +62,15 @@ func TestEvalResult_Transform(t *testing.T) { }, }, { - name: "nested brackets", - fields: map[string]any{".data['config.yaml']": "content"}, + name: "nested brackets", + fields: map[string]any{".data['config.yaml']": "content"}, + group: "", + version: "v1", + kind: "ConfigMap", expected: map[string]any{ - "data": map[string]any{"config.yaml": "content"}, + "apiVersion": "v1", + "kind": "ConfigMap", + "data": map[string]any{"config.yaml": "content"}, }, }, { @@ -53,7 +79,12 @@ func TestEvalResult_Transform(t *testing.T) { ".metadata.name": "test", ".metadata.namespace": "default", }, + group: "", + version: "v1", + kind: "Pod", expected: map[string]any{ + "apiVersion": "v1", + "kind": "Pod", "metadata": map[string]any{ "name": "test", "namespace": "default", @@ -66,7 +97,12 @@ func TestEvalResult_Transform(t *testing.T) { ".spec.selector['app']": "backend", ".spec.selector.component": "core", }, + group: "apps", + version: "v1", + kind: "Deployment", expected: map[string]any{ + "apiVersion": "apps/v1", + "kind": "Deployment", "spec": map[string]any{ "selector": map[string]any{ "app": "backend", @@ -85,7 +121,12 @@ func TestEvalResult_Transform(t *testing.T) { ".spec.ports[1].targetPort": int64(8443), ".spec.ports[1].name": "https", }, + group: "apps", + version: "v1", + kind: "StatefulSet", expected: map[string]any{ + "apiVersion": "apps/v1", + "kind": "StatefulSet", "spec": map[string]any{ "ports": map[string]any{ "0": map[string]any{ @@ -110,7 +151,12 @@ func TestEvalResult_Transform(t *testing.T) { ".status.containerStatuses[0].name": "main", ".status.hostIP": "10.0.0.1", }, + group: "", + version: "v1", + kind: "Pod", expected: map[string]any{ + "apiVersion": "v1", + "kind": "Pod", "status": map[string]any{ "conditions": map[string]any{ "0": map[string]any{ @@ -133,7 +179,12 @@ func TestEvalResult_Transform(t *testing.T) { ".a.b.c.d.e": "deep", ".a.b.c.f": "shallow", }, + group: "example.com", + version: "v1", + kind: "CustomResource", expected: map[string]any{ + "apiVersion": "example.com/v1", + "kind": "CustomResource", "a": map[string]any{ "b": map[string]any{ "c": map[string]any{ @@ -153,7 +204,12 @@ func TestEvalResult_Transform(t *testing.T) { ".metadata.labels['app.kubernetes.io/name']": "myapp", ".data['config.json']": "{}", }, + group: "", + version: "v1", + kind: "ConfigMap", expected: map[string]any{ + "apiVersion": "v1", + "kind": "ConfigMap", "metadata": map[string]any{ "annotations": map[string]any{ "example.com/managed-by": "controller", @@ -173,9 +229,12 @@ func TestEvalResult_Transform(t *testing.T) { "kind": "Service", "apiVersion": "v1", }, + group: "", + version: "v2", // Policy version should overwrite the one from fields + kind: "ServiceOverride", expected: map[string]any{ - "kind": "Service", - "apiVersion": "v1", + "kind": "ServiceOverride", + "apiVersion": "v2", }, }, { @@ -184,7 +243,12 @@ func TestEvalResult_Transform(t *testing.T) { ".spec['selector']['app']": "foo", ".data['key']['subkey']": "bar", }, + group: "", + version: "v1", + kind: "ConfigMap", expected: map[string]any{ + "apiVersion": "v1", + "kind": "ConfigMap", "spec": map[string]any{ "selector": map[string]any{ "app": "foo", @@ -203,6 +267,9 @@ func TestEvalResult_Transform(t *testing.T) { ".a": "scalar-value", // This sets "a" = "scalar-value" ".a.b": "nested-value", // This requires "a" to be a map. Logic should overwrite "a" with map {"b": "nested-value"} }, + group: "x", + version: "v1", + kind: "Y", expected: nil, // skip }, } @@ -212,7 +279,13 @@ func TestEvalResult_Transform(t *testing.T) { continue } t.Run(tt.name, func(t *testing.T) { - r := &EvalResult{Matched: true, Fields: tt.fields} + r := &EvalResult{ + Matched: true, + Fields: tt.fields, + Group: tt.group, + Version: tt.version, + Kind: tt.kind, + } doc := r.Transform() // Use assert.Equal which compares map structure values deeply From 0163b33a2ee0cdc31bf7a4bd484854317d24adfb Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Mon, 2 Mar 2026 18:03:45 -0300 Subject: [PATCH 10/17] fix: Enable OpenAPI schema generation and update code generation tooling and dependencies. --- Taskfile.yaml | 28 +- cmd/search/main.go | 45 +-- go.mod | 17 +- go.sum | 61 +--- hack/update-codegen.sh | 48 +++ pkg/apis/search/v1alpha1/policy_types.go | 1 + pkg/apis/search/v1alpha1/types.go | 1 + .../search/v1alpha1/zz_generated.deepcopy.go | 19 +- .../openapi}/zz_generated.openapi.go | 287 ++++++++++------- zz_generated.openapi.go | 300 ++++++++++++++++++ 10 files changed, 571 insertions(+), 236 deletions(-) create mode 100755 hack/update-codegen.sh rename pkg/{apis/search/v1alpha1 => generated/openapi}/zz_generated.openapi.go (91%) create mode 100644 zz_generated.openapi.go diff --git a/Taskfile.yaml b/Taskfile.yaml index ade3135..4fd02fe 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -127,33 +127,13 @@ tasks: silent: true generate:openapi: - desc: Generate OpenAPI definitions for search API types - deps: - - task: install-go-tool - vars: - NAME: openapi-gen - PACKAGE: k8s.io/code-generator/cmd/openapi-gen - VERSION: v0.23.0 + desc: Generate Kubernetes OpenAPI definitions cmds: - - echo "Generating OpenAPI definitions..." - | set -e - # Packages to generate OpenAPI for - PACKAGES=( - "pkg/apis/search/v1alpha1" - ) - - for REL_DIR in "${PACKAGES[@]}"; do - PKG="go.miloapis.net/search/$REL_DIR" - echo "Generating OpenAPI for $PKG..." - - "{{.TOOL_DIR}}/openapi-gen" \ - --input-dirs "$PKG,k8s.io/apimachinery/pkg/apis/meta/v1,k8s.io/apimachinery/pkg/runtime,k8s.io/apimachinery/pkg/version" \ - --output-package "$REL_DIR" \ - --output-base "." \ - --output-file-base "zz_generated.openapi" \ - --go-header-file "hack/boilerplate.go.txt" - done + echo "🔄 Generating Kubernetes OpenAPI definitions..." + ./hack/update-codegen.sh + echo "✅ OpenAPI generation complete" silent: true # Test tasks diff --git a/cmd/search/main.go b/cmd/search/main.go index d12a380..0cfb8e2 100644 --- a/cmd/search/main.go +++ b/cmd/search/main.go @@ -4,15 +4,16 @@ import ( "context" "fmt" "os" - "strings" "time" "github.com/google/uuid" "github.com/spf13/cobra" "github.com/spf13/pflag" + searchapiserver "go.miloapis.net/search/internal/apiserver" "go.miloapis.net/search/internal/version" searchv1alpha1 "go.miloapis.net/search/pkg/apis/search/v1alpha1" + "go.miloapis.net/search/pkg/generated/openapi" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" apiopenapi "k8s.io/apiserver/pkg/endpoints/openapi" @@ -24,7 +25,8 @@ import ( "k8s.io/component-base/logs" logsapi "k8s.io/component-base/logs/api/v1" "k8s.io/klog/v2" - "k8s.io/kube-openapi/pkg/common" + openapiutil "k8s.io/kube-openapi/pkg/util" + "k8s.io/kube-openapi/pkg/validation/spec" runtimecache "sigs.k8s.io/controller-runtime/pkg/cache" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" @@ -43,31 +45,6 @@ func init() { utilfeature.DefaultMutableFeatureGate.Set("RemoteRequestHeaderUID=true") } -func GetOpenAPIDefinitions(cb common.ReferenceCallback) map[string]common.OpenAPIDefinition { - defs := make(map[string]common.OpenAPIDefinition) - - merge := func(pkgDefs map[string]common.OpenAPIDefinition) { - for k, v := range pkgDefs { - // For k8s.io types, store both the original key and the transformed key - // because the namer behavior is inconsistent across different types - if strings.HasPrefix(k, "k8s.io/") { - // Store original key (with slashes) - defs[k] = v - // Also store transformed key (io.k8s with dots) - newK := "io.k8s." + k[7:] - newK = strings.ReplaceAll(newK, "/", ".") - defs[newK] = v - } else { - // For non-k8s.io types, keep as-is - defs[k] = v - } - } - } - - merge(searchv1alpha1.GetOpenAPIDefinitions(cb)) - return defs -} - func main() { cmd := NewSearchServerCommand() code := cli.Run(cmd) @@ -203,17 +180,25 @@ func (o *SearchServerOptions) Config() (*searchapiserver.Config, error) { genericConfig := genericapiserver.NewRecommendedConfig(searchapiserver.Codecs) // Set effective version to match the Kubernetes version we're built against. - genericConfig.EffectiveVersion = basecompatibility.NewEffectiveVersionFromString("1.34", "", "") + genericConfig.EffectiveVersion = basecompatibility.NewEffectiveVersionFromString("1.35", "", "") namer := apiopenapi.NewDefinitionNamer(searchapiserver.Scheme) - genericConfig.OpenAPIV3Config = genericapiserver.DefaultOpenAPIV3Config(GetOpenAPIDefinitions, namer) + genericConfig.OpenAPIV3Config = genericapiserver.DefaultOpenAPIV3Config(openapi.GetOpenAPIDefinitions, namer) genericConfig.OpenAPIV3Config.Info.Title = "Search" genericConfig.OpenAPIV3Config.Info.Version = version.Version + genericConfig.OpenAPIV3Config.GetDefinitionName = func(name string) (string, spec.Extensions) { + friendlyName, extensions := namer.GetDefinitionName(name) + return openapiutil.ToRESTFriendlyName(friendlyName), extensions + } // Configure OpenAPI v2 - genericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(GetOpenAPIDefinitions, namer) + genericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(openapi.GetOpenAPIDefinitions, namer) genericConfig.OpenAPIConfig.Info.Title = "Search" genericConfig.OpenAPIConfig.Info.Version = version.Version + genericConfig.OpenAPIConfig.GetDefinitionName = func(name string) (string, spec.Extensions) { + friendlyName, extensions := namer.GetDefinitionName(name) + return openapiutil.ToRESTFriendlyName(friendlyName), extensions + } if err := o.RecommendedOptions.ApplyTo(genericConfig); err != nil { return nil, fmt.Errorf("failed to apply recommended options: %w", err) diff --git a/go.mod b/go.mod index 026420c..6f978ca 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.25.0 require ( github.com/go-logr/logr v1.4.3 + github.com/golang-jwt/jwt/v5 v5.3.1 github.com/google/cel-go v0.27.0 github.com/google/uuid v1.6.0 github.com/meilisearch/meilisearch-go v0.36.1 @@ -11,11 +12,11 @@ require ( github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/stretchr/testify v1.11.1 - k8s.io/api v0.35.1 - k8s.io/apimachinery v0.35.1 - k8s.io/apiserver v0.35.1 - k8s.io/client-go v0.35.1 - k8s.io/component-base v0.35.1 + k8s.io/api v0.35.2 + k8s.io/apimachinery v0.35.2 + k8s.io/apiserver v0.35.2 + k8s.io/client-go v0.35.2 + k8s.io/component-base v0.35.2 k8s.io/klog/v2 v2.130.1 k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 sigs.k8s.io/controller-runtime v0.23.1 @@ -45,7 +46,6 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/go-openapi/swag/jsonname v0.25.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/gnostic-models v0.7.0 // indirect @@ -68,7 +68,6 @@ require ( github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.16.1 // indirect - github.com/stoewer/go-strcase v1.3.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/x448/float16 v0.8.4 // indirect go.etcd.io/etcd/api/v3 v3.6.5 // indirect @@ -109,9 +108,9 @@ require ( gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.35.0 // indirect - k8s.io/code-generator v0.35.1 // indirect + k8s.io/code-generator v0.35.2 // indirect k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b // indirect - k8s.io/kms v0.35.1 // indirect + k8s.io/kms v0.35.2 // indirect k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect diff --git a/go.sum b/go.sum index ac59a2f..558a651 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= -cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= @@ -8,8 +6,6 @@ github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cq github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= -github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= -github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -65,16 +61,12 @@ github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= -github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI= -github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo= github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw= github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= @@ -118,8 +110,6 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/meilisearch/meilisearch-go v0.36.0 h1:N1etykTektXt5KPcSbhBO0d5Xx5NaKj4pJWEM7WA5dI= -github.com/meilisearch/meilisearch-go v0.36.0/go.mod h1:HBfHzKMxcSbTOvqdfuRA/yf6Vk9IivcwKocWRuW7W78= github.com/meilisearch/meilisearch-go v0.36.1 h1:mJTCJE5g7tRvaqKco6DfqOuJEjX+rRltDEnkEC02Y0M= github.com/meilisearch/meilisearch-go v0.36.1/go.mod h1:hWcR0MuWLSzHfbz9GGzIr3s9rnXLm1jqkmHkJPbUSvM= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -164,17 +154,10 @@ github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiT github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= -github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= @@ -238,8 +221,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -307,42 +288,28 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -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.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY= -k8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA= -k8s.io/api v0.35.1 h1:0PO/1FhlK/EQNVK5+txc4FuhQibV25VLSdLMmGpDE/Q= -k8s.io/api v0.35.1/go.mod h1:28uR9xlXWml9eT0uaGo6y71xK86JBELShLy4wR1XtxM= +k8s.io/api v0.35.2 h1:tW7mWc2RpxW7HS4CoRXhtYHSzme1PN1UjGHJ1bdrtdw= +k8s.io/api v0.35.2/go.mod h1:7AJfqGoAZcwSFhOjcGM7WV05QxMMgUaChNfLTXDRE60= k8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4= k8s.io/apiextensions-apiserver v0.35.0/go.mod h1:E1Ahk9SADaLQ4qtzYFkwUqusXTcaV2uw3l14aqpL2LU= -k8s.io/apimachinery v0.35.0 h1:Z2L3IHvPVv/MJ7xRxHEtk6GoJElaAqDCCU0S6ncYok8= -k8s.io/apimachinery v0.35.0/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= -k8s.io/apimachinery v0.35.1 h1:yxO6gV555P1YV0SANtnTjXYfiivaTPvCTKX6w6qdDsU= -k8s.io/apimachinery v0.35.1/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= -k8s.io/apiserver v0.35.0 h1:CUGo5o+7hW9GcAEF3x3usT3fX4f9r8xmgQeCBDaOgX4= -k8s.io/apiserver v0.35.0/go.mod h1:QUy1U4+PrzbJaM3XGu2tQ7U9A4udRRo5cyxkFX0GEds= -k8s.io/apiserver v0.35.1 h1:potxdhhTL4i6AYAa2QCwtlhtB1eCdWQFvJV6fXgJzxs= -k8s.io/apiserver v0.35.1/go.mod h1:BiL6Dd3A2I/0lBnteXfWmCFobHM39vt5+hJQd7Lbpi4= -k8s.io/client-go v0.35.0 h1:IAW0ifFbfQQwQmga0UdoH0yvdqrbwMdq9vIFEhRpxBE= -k8s.io/client-go v0.35.0/go.mod h1:q2E5AAyqcbeLGPdoRB+Nxe3KYTfPce1Dnu1myQdqz9o= -k8s.io/client-go v0.35.1 h1:+eSfZHwuo/I19PaSxqumjqZ9l5XiTEKbIaJ+j1wLcLM= -k8s.io/client-go v0.35.1/go.mod h1:1p1KxDt3a0ruRfc/pG4qT/3oHmUj1AhSHEcxNSGg+OA= -k8s.io/code-generator v0.35.0 h1:TvrtfKYZTm9oDF2z+veFKSCcgZE3Igv0svY+ehCmjHQ= -k8s.io/code-generator v0.35.0/go.mod h1:iS1gvVf3c/T71N5DOGYO+Gt3PdJ6B9LYSvIyQ4FHzgc= -k8s.io/code-generator v0.35.1/go.mod h1:F2Fhm7aA69tC/VkMXLDokdovltXEF026Tb9yfQXQWKg= -k8s.io/component-base v0.35.0 h1:+yBrOhzri2S1BVqyVSvcM3PtPyx5GUxCK2tinZz1G94= -k8s.io/component-base v0.35.0/go.mod h1:85SCX4UCa6SCFt6p3IKAPej7jSnF3L8EbfSyMZayJR0= -k8s.io/component-base v0.35.1 h1:XgvpRf4srp037QWfGBLFsYMUQJkE5yMa94UsJU7pmcE= -k8s.io/component-base v0.35.1/go.mod h1:HI/6jXlwkiOL5zL9bqA3en1Ygv60F03oEpnuU1G56Bs= +k8s.io/apimachinery v0.35.2 h1:NqsM/mmZA7sHW02JZ9RTtk3wInRgbVxL8MPfzSANAK8= +k8s.io/apimachinery v0.35.2/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= +k8s.io/apiserver v0.35.2 h1:rb52v0CZGEL0FkhjS+I6jHflAp7fZ4MIaKcEHX7wmDk= +k8s.io/apiserver v0.35.2/go.mod h1:CROJUAu0tfjZLyYgSeBsBan2T7LUJGh0ucWwTCSSk7g= +k8s.io/client-go v0.35.2 h1:YUfPefdGJA4aljDdayAXkc98DnPkIetMl4PrKX97W9o= +k8s.io/client-go v0.35.2/go.mod h1:4QqEwh4oQpeK8AaefZ0jwTFJw/9kIjdQi0jpKeYvz7g= +k8s.io/code-generator v0.35.2 h1:3874swbO2c26VWTf6lKD4NWGyHIfyBeTCk7caCG3TuU= +k8s.io/code-generator v0.35.2/go.mod h1:id4XLCm0yAQq5nlvyfAKibMOKnMjzlesAwGw6kM3Adc= +k8s.io/component-base v0.35.2 h1:btgR+qNrpWuRSuvWSnQYsZy88yf5gVwemvz0yw79pGc= +k8s.io/component-base v0.35.2/go.mod h1:B1iBJjooe6xIJYUucAxb26RwhAjzx0gHnqO9htWIX+0= k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b h1:gMplByicHV/TJBizHd9aVEsTYoJBnnUAT5MHlTkbjhQ= k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b/go.mod h1:CgujABENc3KuTrcsdpGmrrASjtQsWCT7R99mEV4U/fM= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kms v0.35.0 h1:/x87FED2kDSo66csKtcYCEHsxF/DBlNl7LfJ1fVQs1o= -k8s.io/kms v0.35.0/go.mod h1:VT+4ekZAdrZDMgShK37vvlyHUVhwI9t/9tvh0AyCWmQ= -k8s.io/kms v0.35.1 h1:kjv2r9g1mY7uL+l1RhyAZvWVZIA/4qIfBHXyjFGLRhU= -k8s.io/kms v0.35.1/go.mod h1:VT+4ekZAdrZDMgShK37vvlyHUVhwI9t/9tvh0AyCWmQ= +k8s.io/kms v0.35.2 h1:XPlj7QmLBfzm8gGQnc3+Y95hZLiJs3DjA0IyFOV5Z7g= +k8s.io/kms v0.35.2/go.mod h1:VT+4ekZAdrZDMgShK37vvlyHUVhwI9t/9tvh0AyCWmQ= k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 h1:HhDfevmPS+OalTjQRKbTHppRIz01AWi8s45TMXStgYY= k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh new file mode 100755 index 0000000..fcf5849 --- /dev/null +++ b/hack/update-codegen.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -o errexit +set -o nounset +set -o pipefail + +SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +MODULE_NAME="go.miloapis.net/search" + +# Find code-generator +CODEGEN_PKG=$(go list -m -f '{{.Dir}}' k8s.io/code-generator 2>/dev/null) + +if [ -z "${CODEGEN_PKG}" ]; then + echo "ERROR: k8s.io/code-generator not found in go.mod" + echo "Run: go get k8s.io/code-generator" + exit 1 +fi + +echo "Using code-generator from: ${CODEGEN_PKG}" + +# Source the code generation helper +source "${CODEGEN_PKG}/kube_codegen.sh" + +# Generate deepcopy +echo "Generating deepcopy..." +kube::codegen::gen_helpers \ + --boilerplate "${SCRIPT_ROOT}/hack/boilerplate.go.txt" \ + "${SCRIPT_ROOT}/pkg/apis" + +# Generate OpenAPI definitions +echo "Generating OpenAPI definitions..." +go run k8s.io/kube-openapi/cmd/openapi-gen \ + --go-header-file "${SCRIPT_ROOT}/hack/boilerplate.go.txt" \ + --output-dir "${SCRIPT_ROOT}/pkg/generated/openapi" \ + --output-pkg "${MODULE_NAME}/pkg/generated/openapi" \ + --output-file zz_generated.openapi.go \ + --report-filename /dev/null \ + "${MODULE_NAME}/pkg/apis/search/v1alpha1" \ + "k8s.io/apimachinery/pkg/apis/meta/v1" \ + "k8s.io/apimachinery/pkg/api/resource" \ + "k8s.io/apimachinery/pkg/runtime" \ + "k8s.io/apimachinery/pkg/version" + +echo "" +echo "Code generation complete!" +echo "" +echo "Generated:" +echo " - Deepcopy functions: pkg/apis/search/v1alpha1/zz_generated.deepcopy.go" +echo " - OpenAPI: pkg/generated/openapi/" diff --git a/pkg/apis/search/v1alpha1/policy_types.go b/pkg/apis/search/v1alpha1/policy_types.go index 75f999c..dae272c 100644 --- a/pkg/apis/search/v1alpha1/policy_types.go +++ b/pkg/apis/search/v1alpha1/policy_types.go @@ -5,6 +5,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +// +k8s:openapi-gen=true // +kubebuilder:object:root=true // +kubebuilder:subresource:status // +kubebuilder:resource:scope=Cluster diff --git a/pkg/apis/search/v1alpha1/types.go b/pkg/apis/search/v1alpha1/types.go index f308343..9028883 100644 --- a/pkg/apis/search/v1alpha1/types.go +++ b/pkg/apis/search/v1alpha1/types.go @@ -6,6 +6,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" ) +// +k8s:openapi-gen=true // +genclient // +genclient:nonNamespaced // +genclient:onlyVerbs=create diff --git a/pkg/apis/search/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/search/v1alpha1/zz_generated.deepcopy.go index ffd77d0..76ad395 100644 --- a/pkg/apis/search/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/search/v1alpha1/zz_generated.deepcopy.go @@ -1,17 +1,19 @@ //go:build !ignore_autogenerated +// +build !ignore_autogenerated -// Code generated by controller-gen. DO NOT EDIT. +// Code generated by deepcopy-gen. DO NOT EDIT. package v1alpha1 import ( - "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FieldPolicy) DeepCopyInto(out *FieldPolicy) { *out = *in + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FieldPolicy. @@ -27,6 +29,7 @@ func (in *FieldPolicy) DeepCopy() *FieldPolicy { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PolicyCondition) DeepCopyInto(out *PolicyCondition) { *out = *in + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyCondition. @@ -46,6 +49,7 @@ func (in *ResourceIndexPolicy) DeepCopyInto(out *ResourceIndexPolicy) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceIndexPolicy. @@ -78,6 +82,7 @@ func (in *ResourceIndexPolicyList) DeepCopyInto(out *ResourceIndexPolicyList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceIndexPolicyList. @@ -112,6 +117,7 @@ func (in *ResourceIndexPolicySpec) DeepCopyInto(out *ResourceIndexPolicySpec) { *out = make([]FieldPolicy, len(*in)) copy(*out, *in) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceIndexPolicySpec. @@ -134,6 +140,7 @@ func (in *ResourceIndexPolicyStatus) DeepCopyInto(out *ResourceIndexPolicyStatus (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceIndexPolicyStatus. @@ -153,6 +160,7 @@ func (in *SearchQuery) DeepCopyInto(out *SearchQuery) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SearchQuery. @@ -185,6 +193,7 @@ func (in *SearchQueryList) DeepCopyInto(out *SearchQueryList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SearchQueryList. @@ -213,6 +222,7 @@ func (in *SearchQuerySpec) DeepCopyInto(out *SearchQuerySpec) { *out = make([]TargetResource, len(*in)) copy(*out, *in) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SearchQuerySpec. @@ -235,6 +245,7 @@ func (in *SearchQueryStatus) DeepCopyInto(out *SearchQueryStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SearchQueryStatus. @@ -251,6 +262,7 @@ func (in *SearchQueryStatus) DeepCopy() *SearchQueryStatus { func (in *SearchResult) DeepCopyInto(out *SearchResult) { *out = *in in.Resource.DeepCopyInto(&out.Resource) + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SearchResult. @@ -266,6 +278,7 @@ func (in *SearchResult) DeepCopy() *SearchResult { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetResource) DeepCopyInto(out *TargetResource) { *out = *in + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetResource. diff --git a/pkg/apis/search/v1alpha1/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go similarity index 91% rename from pkg/apis/search/v1alpha1/zz_generated.openapi.go rename to pkg/generated/openapi/zz_generated.openapi.go index 0833849..7691282 100644 --- a/pkg/apis/search/v1alpha1/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -3,12 +3,13 @@ // Code generated by openapi-gen. DO NOT EDIT. -// This file was autogenerated by openapi-gen. Do not edit it manually! - -package v1alpha1 +package openapi import ( + resource "k8s.io/apimachinery/pkg/api/resource" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + version "k8s.io/apimachinery/pkg/version" common "k8s.io/kube-openapi/pkg/common" spec "k8s.io/kube-openapi/pkg/validation/spec" ) @@ -27,59 +28,60 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "go.miloapis.net/search/pkg/apis/search/v1alpha1.SearchQueryStatus": schema_pkg_apis_search_v1alpha1_SearchQueryStatus(ref), "go.miloapis.net/search/pkg/apis/search/v1alpha1.SearchResult": schema_pkg_apis_search_v1alpha1_SearchResult(ref), "go.miloapis.net/search/pkg/apis/search/v1alpha1.TargetResource": schema_pkg_apis_search_v1alpha1_TargetResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIResource": schema_pkg_apis_meta_v1_APIResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIResourceList": schema_pkg_apis_meta_v1_APIResourceList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIVersions": schema_pkg_apis_meta_v1_APIVersions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ApplyOptions": schema_pkg_apis_meta_v1_ApplyOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Condition": schema_pkg_apis_meta_v1_Condition(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.CreateOptions": schema_pkg_apis_meta_v1_CreateOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.DeleteOptions": schema_pkg_apis_meta_v1_DeleteOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Duration": schema_pkg_apis_meta_v1_Duration(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.FieldSelectorRequirement": schema_pkg_apis_meta_v1_FieldSelectorRequirement(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1": schema_pkg_apis_meta_v1_FieldsV1(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GetOptions": schema_pkg_apis_meta_v1_GetOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupKind": schema_pkg_apis_meta_v1_GroupKind(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupResource": schema_pkg_apis_meta_v1_GroupResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersion": schema_pkg_apis_meta_v1_GroupVersion(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery": schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionKind": schema_pkg_apis_meta_v1_GroupVersionKind(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionResource": schema_pkg_apis_meta_v1_GroupVersionResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.InternalEvent": schema_pkg_apis_meta_v1_InternalEvent(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector": schema_pkg_apis_meta_v1_LabelSelector(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement": schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.List": schema_pkg_apis_meta_v1_List(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta": schema_pkg_apis_meta_v1_ListMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ListOptions": schema_pkg_apis_meta_v1_ListOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry": schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime": schema_pkg_apis_meta_v1_MicroTime(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta": schema_pkg_apis_meta_v1_ObjectMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference": schema_pkg_apis_meta_v1_OwnerReference(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata": schema_pkg_apis_meta_v1_PartialObjectMetadata(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadataList": schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Patch": schema_pkg_apis_meta_v1_Patch(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PatchOptions": schema_pkg_apis_meta_v1_PatchOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions": schema_pkg_apis_meta_v1_Preconditions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.RootPaths": schema_pkg_apis_meta_v1_RootPaths(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR": schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Status": schema_pkg_apis_meta_v1_Status(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause": schema_pkg_apis_meta_v1_StatusCause(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails": schema_pkg_apis_meta_v1_StatusDetails(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Table": schema_pkg_apis_meta_v1_Table(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition": schema_pkg_apis_meta_v1_TableColumnDefinition(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableOptions": schema_pkg_apis_meta_v1_TableOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableRow": schema_pkg_apis_meta_v1_TableRow(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition": schema_pkg_apis_meta_v1_TableRowCondition(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Time": schema_pkg_apis_meta_v1_Time(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Timestamp": schema_pkg_apis_meta_v1_Timestamp(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta": schema_pkg_apis_meta_v1_TypeMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.UpdateOptions": schema_pkg_apis_meta_v1_UpdateOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.WatchEvent": schema_pkg_apis_meta_v1_WatchEvent(ref), - "k8s.io/apimachinery/pkg/runtime.RawExtension": schema_k8sio_apimachinery_pkg_runtime_RawExtension(ref), - "k8s.io/apimachinery/pkg/runtime.TypeMeta": schema_k8sio_apimachinery_pkg_runtime_TypeMeta(ref), - "k8s.io/apimachinery/pkg/runtime.Unknown": schema_k8sio_apimachinery_pkg_runtime_Unknown(ref), - "k8s.io/apimachinery/pkg/version.Info": schema_k8sio_apimachinery_pkg_version_Info(ref), + resource.Quantity{}.OpenAPIModelName(): schema_apimachinery_pkg_api_resource_Quantity(ref), + v1.APIGroup{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_APIGroup(ref), + v1.APIGroupList{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_APIGroupList(ref), + v1.APIResource{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_APIResource(ref), + v1.APIResourceList{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_APIResourceList(ref), + v1.APIVersions{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_APIVersions(ref), + v1.ApplyOptions{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_ApplyOptions(ref), + v1.Condition{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_Condition(ref), + v1.CreateOptions{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_CreateOptions(ref), + v1.DeleteOptions{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_DeleteOptions(ref), + v1.Duration{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_Duration(ref), + v1.FieldSelectorRequirement{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_FieldSelectorRequirement(ref), + v1.FieldsV1{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_FieldsV1(ref), + v1.GetOptions{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_GetOptions(ref), + v1.GroupKind{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_GroupKind(ref), + v1.GroupResource{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_GroupResource(ref), + v1.GroupVersion{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_GroupVersion(ref), + v1.GroupVersionForDiscovery{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref), + v1.GroupVersionKind{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_GroupVersionKind(ref), + v1.GroupVersionResource{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_GroupVersionResource(ref), + v1.InternalEvent{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_InternalEvent(ref), + v1.LabelSelector{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_LabelSelector(ref), + v1.LabelSelectorRequirement{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref), + v1.List{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_List(ref), + v1.ListMeta{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_ListMeta(ref), + v1.ListOptions{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_ListOptions(ref), + v1.ManagedFieldsEntry{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref), + v1.MicroTime{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_MicroTime(ref), + v1.ObjectMeta{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_ObjectMeta(ref), + v1.OwnerReference{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_OwnerReference(ref), + v1.PartialObjectMetadata{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_PartialObjectMetadata(ref), + v1.PartialObjectMetadataList{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref), + v1.Patch{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_Patch(ref), + v1.PatchOptions{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_PatchOptions(ref), + v1.Preconditions{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_Preconditions(ref), + v1.RootPaths{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_RootPaths(ref), + v1.ServerAddressByClientCIDR{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref), + v1.Status{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_Status(ref), + v1.StatusCause{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_StatusCause(ref), + v1.StatusDetails{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_StatusDetails(ref), + v1.Table{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_Table(ref), + v1.TableColumnDefinition{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_TableColumnDefinition(ref), + v1.TableOptions{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_TableOptions(ref), + v1.TableRow{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_TableRow(ref), + v1.TableRowCondition{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_TableRowCondition(ref), + v1.Time{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_Time(ref), + v1.Timestamp{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_Timestamp(ref), + v1.TypeMeta{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_TypeMeta(ref), + v1.UpdateOptions{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_UpdateOptions(ref), + v1.WatchEvent{}.OpenAPIModelName(): schema_pkg_apis_meta_v1_WatchEvent(ref), + runtime.RawExtension{}.OpenAPIModelName(): schema_k8sio_apimachinery_pkg_runtime_RawExtension(ref), + runtime.TypeMeta{}.OpenAPIModelName(): schema_k8sio_apimachinery_pkg_runtime_TypeMeta(ref), + runtime.Unknown{}.OpenAPIModelName(): schema_k8sio_apimachinery_pkg_runtime_Unknown(ref), + version.Info{}.OpenAPIModelName(): schema_k8sio_apimachinery_pkg_version_Info(ref), } } @@ -166,7 +168,7 @@ func schema_pkg_apis_search_v1alpha1_ResourceIndexPolicy(ref common.ReferenceCal "metadata": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + Ref: ref(v1.ObjectMeta{}.OpenAPIModelName()), }, }, "spec": { @@ -186,7 +188,7 @@ func schema_pkg_apis_search_v1alpha1_ResourceIndexPolicy(ref common.ReferenceCal }, }, Dependencies: []string{ - "go.miloapis.net/search/pkg/apis/search/v1alpha1.ResourceIndexPolicySpec", "go.miloapis.net/search/pkg/apis/search/v1alpha1.ResourceIndexPolicyStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "go.miloapis.net/search/pkg/apis/search/v1alpha1.ResourceIndexPolicySpec", "go.miloapis.net/search/pkg/apis/search/v1alpha1.ResourceIndexPolicyStatus", v1.ObjectMeta{}.OpenAPIModelName()}, } } @@ -214,7 +216,7 @@ func schema_pkg_apis_search_v1alpha1_ResourceIndexPolicyList(ref common.Referenc "metadata": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + Ref: ref(v1.ListMeta{}.OpenAPIModelName()), }, }, "items": { @@ -235,7 +237,7 @@ func schema_pkg_apis_search_v1alpha1_ResourceIndexPolicyList(ref common.Referenc }, }, Dependencies: []string{ - "go.miloapis.net/search/pkg/apis/search/v1alpha1.ResourceIndexPolicy", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + "go.miloapis.net/search/pkg/apis/search/v1alpha1.ResourceIndexPolicy", v1.ListMeta{}.OpenAPIModelName()}, } } @@ -329,7 +331,7 @@ func schema_pkg_apis_search_v1alpha1_ResourceIndexPolicyStatus(ref common.Refere Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"), + Ref: ref(v1.Condition{}.OpenAPIModelName()), }, }, }, @@ -353,7 +355,7 @@ func schema_pkg_apis_search_v1alpha1_ResourceIndexPolicyStatus(ref common.Refere }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.Condition"}, + v1.Condition{}.OpenAPIModelName()}, } } @@ -381,7 +383,7 @@ func schema_pkg_apis_search_v1alpha1_SearchQuery(ref common.ReferenceCallback) c "metadata": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + Ref: ref(v1.ObjectMeta{}.OpenAPIModelName()), }, }, "spec": { @@ -401,7 +403,7 @@ func schema_pkg_apis_search_v1alpha1_SearchQuery(ref common.ReferenceCallback) c }, }, Dependencies: []string{ - "go.miloapis.net/search/pkg/apis/search/v1alpha1.SearchQuerySpec", "go.miloapis.net/search/pkg/apis/search/v1alpha1.SearchQueryStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "go.miloapis.net/search/pkg/apis/search/v1alpha1.SearchQuerySpec", "go.miloapis.net/search/pkg/apis/search/v1alpha1.SearchQueryStatus", v1.ObjectMeta{}.OpenAPIModelName()}, } } @@ -429,7 +431,7 @@ func schema_pkg_apis_search_v1alpha1_SearchQueryList(ref common.ReferenceCallbac "metadata": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + Ref: ref(v1.ListMeta{}.OpenAPIModelName()), }, }, "items": { @@ -450,7 +452,7 @@ func schema_pkg_apis_search_v1alpha1_SearchQueryList(ref common.ReferenceCallbac }, }, Dependencies: []string{ - "go.miloapis.net/search/pkg/apis/search/v1alpha1.SearchQuery", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + "go.miloapis.net/search/pkg/apis/search/v1alpha1.SearchQuery", v1.ListMeta{}.OpenAPIModelName()}, } } @@ -562,8 +564,7 @@ func schema_pkg_apis_search_v1alpha1_SearchResult(ref common.ReferenceCallback) "resource": { SchemaProps: spec.SchemaProps{ Description: "Resource contains the actual Kubernetes resource.", - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + Ref: ref(runtime.RawExtension{}.OpenAPIModelName()), }, }, "relevanceScore": { @@ -578,7 +579,7 @@ func schema_pkg_apis_search_v1alpha1_SearchResult(ref common.ReferenceCallback) }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/runtime.RawExtension"}, + runtime.RawExtension{}.OpenAPIModelName()}, } } @@ -620,6 +621,54 @@ func schema_pkg_apis_search_v1alpha1_TargetResource(ref common.ReferenceCallback } } +func schema_apimachinery_pkg_api_resource_Quantity(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.EmbedOpenAPIDefinitionIntoV2Extension(common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation.", + OneOf: common.GenerateOpenAPIV3OneOfSchema(resource.Quantity{}.OpenAPIV3OneOfTypes()), + Format: resource.Quantity{}.OpenAPISchemaFormat(), + }, + }, + }, common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation.", + Type: resource.Quantity{}.OpenAPISchemaType(), + Format: resource.Quantity{}.OpenAPISchemaFormat(), + }, + }, + }) +} + +func schema_apimachinery_pkg_api_resource_int64Amount(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "int64Amount represents a fixed precision numerator and arbitrary scale exponent. It is faster than operations on inf.Dec for values that can be represented as int64.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "value": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + "scale": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"value", "scale"}, + }, + }, + } +} + func schema_pkg_apis_meta_v1_APIGroup(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -662,7 +711,7 @@ func schema_pkg_apis_meta_v1_APIGroup(ref common.ReferenceCallback) common.OpenA Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery"), + Ref: ref(v1.GroupVersionForDiscovery{}.OpenAPIModelName()), }, }, }, @@ -672,7 +721,7 @@ func schema_pkg_apis_meta_v1_APIGroup(ref common.ReferenceCallback) common.OpenA SchemaProps: spec.SchemaProps{ Description: "preferredVersion is the version preferred by the API server, which probably is the storage version.", Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery"), + Ref: ref(v1.GroupVersionForDiscovery{}.OpenAPIModelName()), }, }, "serverAddressByClientCIDRs": { @@ -688,7 +737,7 @@ func schema_pkg_apis_meta_v1_APIGroup(ref common.ReferenceCallback) common.OpenA Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR"), + Ref: ref(v1.ServerAddressByClientCIDR{}.OpenAPIModelName()), }, }, }, @@ -699,7 +748,7 @@ func schema_pkg_apis_meta_v1_APIGroup(ref common.ReferenceCallback) common.OpenA }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery", "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR"}, + v1.GroupVersionForDiscovery{}.OpenAPIModelName(), v1.ServerAddressByClientCIDR{}.OpenAPIModelName()}, } } @@ -737,7 +786,7 @@ func schema_pkg_apis_meta_v1_APIGroupList(ref common.ReferenceCallback) common.O Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup"), + Ref: ref(v1.APIGroup{}.OpenAPIModelName()), }, }, }, @@ -748,7 +797,7 @@ func schema_pkg_apis_meta_v1_APIGroupList(ref common.ReferenceCallback) common.O }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup"}, + v1.APIGroup{}.OpenAPIModelName()}, } } @@ -916,7 +965,7 @@ func schema_pkg_apis_meta_v1_APIResourceList(ref common.ReferenceCallback) commo Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.APIResource"), + Ref: ref(v1.APIResource{}.OpenAPIModelName()), }, }, }, @@ -927,7 +976,7 @@ func schema_pkg_apis_meta_v1_APIResourceList(ref common.ReferenceCallback) commo }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.APIResource"}, + v1.APIResource{}.OpenAPIModelName()}, } } @@ -985,7 +1034,7 @@ func schema_pkg_apis_meta_v1_APIVersions(ref common.ReferenceCallback) common.Op Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR"), + Ref: ref(v1.ServerAddressByClientCIDR{}.OpenAPIModelName()), }, }, }, @@ -996,7 +1045,7 @@ func schema_pkg_apis_meta_v1_APIVersions(ref common.ReferenceCallback) common.Op }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR"}, + v1.ServerAddressByClientCIDR{}.OpenAPIModelName()}, } } @@ -1097,8 +1146,7 @@ func schema_pkg_apis_meta_v1_Condition(ref common.ReferenceCallback) common.Open "lastTransitionTime": { SchemaProps: spec.SchemaProps{ 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.", - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + Ref: ref(v1.Time{}.OpenAPIModelName()), }, }, "reason": { @@ -1122,7 +1170,7 @@ func schema_pkg_apis_meta_v1_Condition(ref common.ReferenceCallback) common.Open }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + v1.Time{}.OpenAPIModelName()}, } } @@ -1218,7 +1266,7 @@ func schema_pkg_apis_meta_v1_DeleteOptions(ref common.ReferenceCallback) common. "preconditions": { SchemaProps: spec.SchemaProps{ Description: "Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions"), + Ref: ref(v1.Preconditions{}.OpenAPIModelName()), }, }, "orphanDependents": { @@ -1266,7 +1314,7 @@ func schema_pkg_apis_meta_v1_DeleteOptions(ref common.ReferenceCallback) common. }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions"}, + v1.Preconditions{}.OpenAPIModelName()}, } } @@ -1578,15 +1626,12 @@ func schema_pkg_apis_meta_v1_InternalEvent(ref common.ReferenceCallback) common. "Object": { SchemaProps: spec.SchemaProps{ Description: "Object is:\n * If Type is Added or Modified: the new state of the object.\n * If Type is Deleted: the state of the object immediately before deletion.\n * If Type is Bookmark: the object (instance of a type being watched) where\n only ResourceVersion field is set. On successful restart of watch from a\n bookmark resourceVersion, client is guaranteed to not get repeat event\n nor miss any events.\n * If Type is Error: *api.Status is recommended; other types may make sense\n depending on context.", - Ref: ref("k8s.io/apimachinery/pkg/runtime.Object"), }, }, }, Required: []string{"Type", "Object"}, }, }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/runtime.Object"}, } } @@ -1626,7 +1671,7 @@ func schema_pkg_apis_meta_v1_LabelSelector(ref common.ReferenceCallback) common. Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement"), + Ref: ref(v1.LabelSelectorRequirement{}.OpenAPIModelName()), }, }, }, @@ -1641,7 +1686,7 @@ func schema_pkg_apis_meta_v1_LabelSelector(ref common.ReferenceCallback) common. }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement"}, + v1.LabelSelectorRequirement{}.OpenAPIModelName()}, } } @@ -1720,7 +1765,7 @@ func schema_pkg_apis_meta_v1_List(ref common.ReferenceCallback) common.OpenAPIDe SchemaProps: spec.SchemaProps{ Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + Ref: ref(v1.ListMeta{}.OpenAPIModelName()), }, }, "items": { @@ -1730,8 +1775,7 @@ func schema_pkg_apis_meta_v1_List(ref common.ReferenceCallback) common.OpenAPIDe Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + Ref: ref(runtime.RawExtension{}.OpenAPIModelName()), }, }, }, @@ -1742,7 +1786,7 @@ func schema_pkg_apis_meta_v1_List(ref common.ReferenceCallback) common.OpenAPIDe }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, + v1.ListMeta{}.OpenAPIModelName(), runtime.RawExtension{}.OpenAPIModelName()}, } } @@ -1915,7 +1959,7 @@ func schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref common.ReferenceCallback) co "time": { SchemaProps: spec.SchemaProps{ Description: "Time is the timestamp of when the ManagedFields entry was added. The timestamp will also be updated if a field is added, the manager changes any of the owned fields value or removes a field. The timestamp does not update when a field is removed from the entry because another manager took it over.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + Ref: ref(v1.Time{}.OpenAPIModelName()), }, }, "fieldsType": { @@ -1928,7 +1972,7 @@ func schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref common.ReferenceCallback) co "fieldsV1": { SchemaProps: spec.SchemaProps{ Description: "FieldsV1 holds the first JSON version format as described in the \"FieldsV1\" type.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1"), + Ref: ref(v1.FieldsV1{}.OpenAPIModelName()), }, }, "subresource": { @@ -1942,7 +1986,7 @@ func schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref common.ReferenceCallback) co }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + v1.FieldsV1{}.OpenAPIModelName(), v1.Time{}.OpenAPIModelName()}, } } @@ -2017,14 +2061,13 @@ func schema_pkg_apis_meta_v1_ObjectMeta(ref common.ReferenceCallback) common.Ope "creationTimestamp": { SchemaProps: spec.SchemaProps{ Description: "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + Ref: ref(v1.Time{}.OpenAPIModelName()), }, }, "deletionTimestamp": { SchemaProps: spec.SchemaProps{ Description: "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + Ref: ref(v1.Time{}.OpenAPIModelName()), }, }, "deletionGracePeriodSeconds": { @@ -2084,7 +2127,7 @@ func schema_pkg_apis_meta_v1_ObjectMeta(ref common.ReferenceCallback) common.Ope Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference"), + Ref: ref(v1.OwnerReference{}.OpenAPIModelName()), }, }, }, @@ -2124,7 +2167,7 @@ func schema_pkg_apis_meta_v1_ObjectMeta(ref common.ReferenceCallback) common.Ope Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry"), + Ref: ref(v1.ManagedFieldsEntry{}.OpenAPIModelName()), }, }, }, @@ -2134,7 +2177,7 @@ func schema_pkg_apis_meta_v1_ObjectMeta(ref common.ReferenceCallback) common.Ope }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry", "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + v1.ManagedFieldsEntry{}.OpenAPIModelName(), v1.OwnerReference{}.OpenAPIModelName(), v1.Time{}.OpenAPIModelName()}, } } @@ -2228,14 +2271,14 @@ func schema_pkg_apis_meta_v1_PartialObjectMetadata(ref common.ReferenceCallback) SchemaProps: spec.SchemaProps{ Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + Ref: ref(v1.ObjectMeta{}.OpenAPIModelName()), }, }, }, }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + v1.ObjectMeta{}.OpenAPIModelName()}, } } @@ -2264,7 +2307,7 @@ func schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref common.ReferenceCallb SchemaProps: spec.SchemaProps{ Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + Ref: ref(v1.ListMeta{}.OpenAPIModelName()), }, }, "items": { @@ -2275,7 +2318,7 @@ func schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref common.ReferenceCallb Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata"), + Ref: ref(v1.PartialObjectMetadata{}.OpenAPIModelName()), }, }, }, @@ -2286,7 +2329,7 @@ func schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref common.ReferenceCallb }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata"}, + v1.ListMeta{}.OpenAPIModelName(), v1.PartialObjectMetadata{}.OpenAPIModelName()}, } } @@ -2485,7 +2528,7 @@ func schema_pkg_apis_meta_v1_Status(ref common.ReferenceCallback) common.OpenAPI SchemaProps: spec.SchemaProps{ Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + Ref: ref(v1.ListMeta{}.OpenAPIModelName()), }, }, "status": { @@ -2512,7 +2555,7 @@ func schema_pkg_apis_meta_v1_Status(ref common.ReferenceCallback) common.OpenAPI "details": { SchemaProps: spec.SchemaProps{ Description: "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails"), + Ref: ref(v1.StatusDetails{}.OpenAPIModelName()), }, }, "code": { @@ -2526,7 +2569,7 @@ func schema_pkg_apis_meta_v1_Status(ref common.ReferenceCallback) common.OpenAPI }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails"}, + v1.ListMeta{}.OpenAPIModelName(), v1.StatusDetails{}.OpenAPIModelName()}, } } @@ -2612,7 +2655,7 @@ func schema_pkg_apis_meta_v1_StatusDetails(ref common.ReferenceCallback) common. Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause"), + Ref: ref(v1.StatusCause{}.OpenAPIModelName()), }, }, }, @@ -2629,7 +2672,7 @@ func schema_pkg_apis_meta_v1_StatusDetails(ref common.ReferenceCallback) common. }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause"}, + v1.StatusCause{}.OpenAPIModelName()}, } } @@ -2658,7 +2701,7 @@ func schema_pkg_apis_meta_v1_Table(ref common.ReferenceCallback) common.OpenAPID SchemaProps: spec.SchemaProps{ Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + Ref: ref(v1.ListMeta{}.OpenAPIModelName()), }, }, "columnDefinitions": { @@ -2674,7 +2717,7 @@ func schema_pkg_apis_meta_v1_Table(ref common.ReferenceCallback) common.OpenAPID Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition"), + Ref: ref(v1.TableColumnDefinition{}.OpenAPIModelName()), }, }, }, @@ -2693,7 +2736,7 @@ func schema_pkg_apis_meta_v1_Table(ref common.ReferenceCallback) common.OpenAPID Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.TableRow"), + Ref: ref(v1.TableRow{}.OpenAPIModelName()), }, }, }, @@ -2704,7 +2747,7 @@ func schema_pkg_apis_meta_v1_Table(ref common.ReferenceCallback) common.OpenAPID }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition", "k8s.io/apimachinery/pkg/apis/meta/v1.TableRow"}, + v1.ListMeta{}.OpenAPIModelName(), v1.TableColumnDefinition{}.OpenAPIModelName(), v1.TableRow{}.OpenAPIModelName()}, } } @@ -2835,7 +2878,7 @@ func schema_pkg_apis_meta_v1_TableRow(ref common.ReferenceCallback) common.OpenA Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition"), + Ref: ref(v1.TableRowCondition{}.OpenAPIModelName()), }, }, }, @@ -2844,8 +2887,7 @@ func schema_pkg_apis_meta_v1_TableRow(ref common.ReferenceCallback) common.OpenA "object": { SchemaProps: spec.SchemaProps{ Description: "This field contains the requested additional information about each object based on the includeObject policy when requesting the Table. If \"None\", this field is empty, if \"Object\" this will be the default serialization of the object for the current API version, and if \"Metadata\" (the default) will contain the object metadata. Check the returned kind and apiVersion of the object before parsing. The media type of the object will always match the enclosing list - if this as a JSON table, these will be JSON encoded objects.", - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + Ref: ref(runtime.RawExtension{}.OpenAPIModelName()), }, }, }, @@ -2853,7 +2895,7 @@ func schema_pkg_apis_meta_v1_TableRow(ref common.ReferenceCallback) common.OpenA }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, + v1.TableRowCondition{}.OpenAPIModelName(), runtime.RawExtension{}.OpenAPIModelName()}, } } @@ -3048,8 +3090,7 @@ func schema_pkg_apis_meta_v1_WatchEvent(ref common.ReferenceCallback) common.Ope "object": { SchemaProps: spec.SchemaProps{ Description: "Object is:\n * If Type is Added or Modified: the new state of the object.\n * If Type is Deleted: the state of the object immediately before deletion.\n * If Type is Error: *Status is recommended; other types may make sense\n depending on context.", - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + Ref: ref(runtime.RawExtension{}.OpenAPIModelName()), }, }, }, @@ -3057,7 +3098,7 @@ func schema_pkg_apis_meta_v1_WatchEvent(ref common.ReferenceCallback) common.Ope }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/runtime.RawExtension"}, + runtime.RawExtension{}.OpenAPIModelName()}, } } diff --git a/zz_generated.openapi.go b/zz_generated.openapi.go new file mode 100644 index 0000000..253a22d --- /dev/null +++ b/zz_generated.openapi.go @@ -0,0 +1,300 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by openapi-gen. DO NOT EDIT. + +// This file was autogenerated by openapi-gen. Do not edit it manually! + +package v1alpha1 + +import ( + common "k8s.io/kube-openapi/pkg/common" + spec "k8s.io/kube-openapi/pkg/validation/spec" +) + +func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { + return map[string]common.OpenAPIDefinition{ + "go.miloapis.com/milo/pkg/apis/identity/v1alpha1.Session": schema_pkg_apis_identity_v1alpha1_Session(ref), + "go.miloapis.com/milo/pkg/apis/identity/v1alpha1.SessionList": schema_pkg_apis_identity_v1alpha1_SessionList(ref), + "go.miloapis.com/milo/pkg/apis/identity/v1alpha1.SessionStatus": schema_pkg_apis_identity_v1alpha1_SessionStatus(ref), + "go.miloapis.com/milo/pkg/apis/identity/v1alpha1.UserIdentity": schema_pkg_apis_identity_v1alpha1_UserIdentity(ref), + "go.miloapis.com/milo/pkg/apis/identity/v1alpha1.UserIdentityList": schema_pkg_apis_identity_v1alpha1_UserIdentityList(ref), + "go.miloapis.com/milo/pkg/apis/identity/v1alpha1.UserIdentityStatus": schema_pkg_apis_identity_v1alpha1_UserIdentityStatus(ref), + } +} + +func schema_pkg_apis_identity_v1alpha1_Session(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("go.miloapis.com/milo/pkg/apis/identity/v1alpha1.SessionStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "go.miloapis.com/milo/pkg/apis/identity/v1alpha1.SessionStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_identity_v1alpha1_SessionList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("go.miloapis.com/milo/pkg/apis/identity/v1alpha1.Session"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "go.miloapis.com/milo/pkg/apis/identity/v1alpha1.Session", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_identity_v1alpha1_SessionStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "userUID": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "provider": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "ip": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "fingerprintID": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "createdAt": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "expiresAt": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + }, + Required: []string{"userUID", "provider", "createdAt"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_pkg_apis_identity_v1alpha1_UserIdentity(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "UserIdentity represents a user's linked identity within an external identity provider.\n\nThis resource describes the connection between a Milo user and their account in an external authentication provider (e.g., GitHub, Google, Microsoft). It is NOT the identity provider itself, but rather the user's specific identity within that provider.\n\nUse cases:\n - Display all authentication methods linked to a user account in the UI\n - Show which external accounts a user has connected\n - Provide visibility into federated identity mappings\n\nImportant notes:\n - This is a read-only resource for display purposes only\n - Identity management (linking/unlinking providers) is handled by the external\n authentication provider (e.g., Zitadel), not through this API\n - No sensitive credentials or tokens are exposed through this resource", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("go.miloapis.com/milo/pkg/apis/identity/v1alpha1.UserIdentityStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "go.miloapis.com/milo/pkg/apis/identity/v1alpha1.UserIdentityStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_identity_v1alpha1_UserIdentityList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "UserIdentityList is a list of UserIdentity resources.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("go.miloapis.com/milo/pkg/apis/identity/v1alpha1.UserIdentity"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "go.miloapis.com/milo/pkg/apis/identity/v1alpha1.UserIdentity", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_identity_v1alpha1_UserIdentityStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "UserIdentityStatus contains the details of a user's identity within an external provider. All fields are read-only and populated by the authentication provider.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "userUID": { + SchemaProps: spec.SchemaProps{ + Description: "UserUID is the unique identifier of the Milo user who owns this identity.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "providerID": { + SchemaProps: spec.SchemaProps{ + Description: "ProviderID is the unique identifier of the external identity provider instance. This is typically an internal ID from the authentication system.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "providerName": { + SchemaProps: spec.SchemaProps{ + Description: "ProviderName is the human-readable name of the identity provider. Examples: \"GitHub\", \"Google\", \"Microsoft\", \"GitLab\"", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "username": { + SchemaProps: spec.SchemaProps{ + Description: "Username is the user's username or identifier within the external identity provider. This is the name the user is known by in the external system (e.g., GitHub username).", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"userUID", "providerID", "providerName", "username"}, + }, + }, + } +} From f5b032d3ee1b7f7352e86f63ac4621a1d9fad244 Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Tue, 3 Mar 2026 11:57:19 -0300 Subject: [PATCH 11/17] chore: consolidate kustomize components into core-control-plane overlay --- .github/workflows/build-apiserver.yaml | 2 +- config/overlays/core-control-plane/kustomization.yaml | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 config/overlays/core-control-plane/kustomization.yaml diff --git a/.github/workflows/build-apiserver.yaml b/.github/workflows/build-apiserver.yaml index dce37c4..d6328d6 100644 --- a/.github/workflows/build-apiserver.yaml +++ b/.github/workflows/build-apiserver.yaml @@ -46,6 +46,6 @@ jobs: with: bundle-name: ghcr.io/datum-cloud/search-kustomize bundle-path: config - image-overlays: config/base + image-overlays: config/overlays/core-control-plane image-name: ghcr.io/datum-cloud/search secrets: inherit diff --git a/config/overlays/core-control-plane/kustomization.yaml b/config/overlays/core-control-plane/kustomization.yaml new file mode 100644 index 0000000..cdb380d --- /dev/null +++ b/config/overlays/core-control-plane/kustomization.yaml @@ -0,0 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - ../../base/apiserver + - ../../base/resource-indexer + - ../controller-manager/core-control-plane + +images: + - name: ghcr.io/datum-cloud/search + newTag: latest From 0ce40b9b72a2a89a9f2387394dc5269ae6b786ef Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Tue, 3 Mar 2026 13:31:48 -0300 Subject: [PATCH 12/17] feat: Introduce new IAM roles (viewer, editor, admin) and define the ResourceIndexPolicy protected resource. --- config/milo/iam/resources/kustomization.yaml | 1 + .../iam/resources/resourceindexpolicies.yaml | 18 ++++++++++++++++++ config/milo/iam/resources/searchqueries.yaml | 18 +----------------- config/milo/iam/roles/admin.yaml | 15 +++++++++++++++ config/milo/iam/roles/editor.yaml | 17 +++++++++++++++++ config/milo/iam/roles/kustomization.yaml | 3 +++ config/milo/iam/roles/searcher.yaml | 5 ++++- config/milo/iam/roles/viewer.yaml | 17 +++++++++++++++++ 8 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 config/milo/iam/resources/resourceindexpolicies.yaml create mode 100644 config/milo/iam/roles/admin.yaml create mode 100644 config/milo/iam/roles/editor.yaml create mode 100644 config/milo/iam/roles/viewer.yaml diff --git a/config/milo/iam/resources/kustomization.yaml b/config/milo/iam/resources/kustomization.yaml index 1b069a7..2302dfc 100644 --- a/config/milo/iam/resources/kustomization.yaml +++ b/config/milo/iam/resources/kustomization.yaml @@ -3,3 +3,4 @@ kind: Component resources: - searchqueries.yaml + - resourceindexpolicies.yaml diff --git a/config/milo/iam/resources/resourceindexpolicies.yaml b/config/milo/iam/resources/resourceindexpolicies.yaml new file mode 100644 index 0000000..2bc9e51 --- /dev/null +++ b/config/milo/iam/resources/resourceindexpolicies.yaml @@ -0,0 +1,18 @@ +apiVersion: iam.miloapis.com/v1alpha1 +kind: ProtectedResource +metadata: + name: search.miloapis.com-resourceindexpolicies +spec: + serviceRef: + name: "search.miloapis.com" + kind: ResourceIndexPolicy + plural: resourceindexpolicies + singular: resourceindexpolicy + permissions: + - list + - get + - create + - update + - delete + - patch + - watch diff --git a/config/milo/iam/resources/searchqueries.yaml b/config/milo/iam/resources/searchqueries.yaml index 072e2d8..160422c 100644 --- a/config/milo/iam/resources/searchqueries.yaml +++ b/config/milo/iam/resources/searchqueries.yaml @@ -1,8 +1,3 @@ -# TEMPLATE NOTE: This is an example of integrating with Milo IAM -# Milo provides IAM capabilities for Kubernetes APIs -# See https://github.com/datum-cloud/milo for more information -# -# This ProtectedResource grants permissions for SearchQuery objects apiVersion: iam.miloapis.com/v1alpha1 kind: ProtectedResource metadata: @@ -12,17 +7,6 @@ spec: name: "search.miloapis.com" kind: SearchQuery plural: searchqueries - singular: exampleresource + singular: searchquery permissions: - create - - get - - list - - update - - delete - parentResources: - - apiGroup: resourcemanager.miloapis.com - kind: Organization - - apiGroup: resourcemanager.miloapis.com - kind: Project - - apiGroup: iam.miloapis.com - kind: User diff --git a/config/milo/iam/roles/admin.yaml b/config/milo/iam/roles/admin.yaml new file mode 100644 index 0000000..5736aca --- /dev/null +++ b/config/milo/iam/roles/admin.yaml @@ -0,0 +1,15 @@ +apiVersion: iam.miloapis.com/v1alpha1 +kind: Role +metadata: + name: search.miloapis.com-admin + namespace: milo-system + labels: + app.kubernetes.io/name: admin + app.kubernetes.io/part-of: search.miloapis.com +spec: + launchStage: Beta + inheritedRoles: + - name: search.miloapis.com-editor + namespace: milo-system + includedPermissions: + - search.miloapis.com/resourceindexpolicies.delete diff --git a/config/milo/iam/roles/editor.yaml b/config/milo/iam/roles/editor.yaml new file mode 100644 index 0000000..5c1f20c --- /dev/null +++ b/config/milo/iam/roles/editor.yaml @@ -0,0 +1,17 @@ +apiVersion: iam.miloapis.com/v1alpha1 +kind: Role +metadata: + name: search.miloapis.com-editor + namespace: milo-system + labels: + app.kubernetes.io/name: editor + app.kubernetes.io/part-of: search.miloapis.com +spec: + launchStage: Beta + inheritedRoles: + - name: search.miloapis.com-viewer + namespace: milo-system + includedPermissions: + - search.miloapis.com/resourceindexpolicies.create + - search.miloapis.com/resourceindexpolicies.update + - search.miloapis.com/resourceindexpolicies.patch diff --git a/config/milo/iam/roles/kustomization.yaml b/config/milo/iam/roles/kustomization.yaml index 4926b49..8428c3b 100644 --- a/config/milo/iam/roles/kustomization.yaml +++ b/config/milo/iam/roles/kustomization.yaml @@ -3,3 +3,6 @@ kind: Component resources: - searcher.yaml + - viewer.yaml + - editor.yaml + - admin.yaml diff --git a/config/milo/iam/roles/searcher.yaml b/config/milo/iam/roles/searcher.yaml index 9bd08ac..e6c20e0 100644 --- a/config/milo/iam/roles/searcher.yaml +++ b/config/milo/iam/roles/searcher.yaml @@ -3,7 +3,10 @@ kind: Role metadata: name: search.miloapis.com-searcher namespace: milo-system + labels: + app.kubernetes.io/name: searcher + app.kubernetes.io/part-of: search.miloapis.com spec: - launchStage: Alpha + launchStage: Beta includedPermissions: - search.miloapis.com/searchqueries.create diff --git a/config/milo/iam/roles/viewer.yaml b/config/milo/iam/roles/viewer.yaml new file mode 100644 index 0000000..37f9202 --- /dev/null +++ b/config/milo/iam/roles/viewer.yaml @@ -0,0 +1,17 @@ +apiVersion: iam.miloapis.com/v1alpha1 +kind: Role +metadata: + name: search.miloapis.com-viewer + namespace: milo-system + labels: + app.kubernetes.io/name: viewer + app.kubernetes.io/part-of: search.miloapis.com +spec: + launchStage: Beta + inheritedRoles: + - name: search.miloapis.com-searcher + namespace: milo-system + includedPermissions: + - search.miloapis.com/resourceindexpolicies.get + - search.miloapis.com/resourceindexpolicies.list + - search.miloapis.com/resourceindexpolicies.watch From 68cc4607f6a02422eb50f18417584d25bd2c9f7d Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Tue, 3 Mar 2026 14:27:04 -0300 Subject: [PATCH 13/17] feat: Add NATS URL, TLS, and leader election configuration to controller-manager and resource-indexer deployments. This is made in order to use the env var pattern to patch the layer easily. --- config/base/controller-manager/deployment.yaml | 15 +++++++++++++++ config/base/resource-indexer/deployment.yaml | 11 +++++++++++ 2 files changed, 26 insertions(+) diff --git a/config/base/controller-manager/deployment.yaml b/config/base/controller-manager/deployment.yaml index bbf9c06..b2db27d 100644 --- a/config/base/controller-manager/deployment.yaml +++ b/config/base/controller-manager/deployment.yaml @@ -43,6 +43,11 @@ spec: - --meilisearch-domain=$(MEILISEARCH_DOMAIN) - --meilisearch-task-wait-timeout=$(MEILISEARCH_TASK_WAIT_TIMEOUT) - --max-cel-depth=$(MAX_CEL_DEPTH) + - --nats-url=$(NATS_URL) + - --nats-tls-ca=$(NATS_TLS_CA) + - --nats-tls-cert=$(NATS_TLS_CERT) + - --nats-tls-key=$(NATS_TLS_KEY) + - --leader-elect-resource-namespace=$(LEADER_ELECT_RESOURCE_NAMESPACE) env: - name: POD_NAMESPACE valueFrom: @@ -62,6 +67,16 @@ spec: value: "30s" - name: MAX_CEL_DEPTH value: "10" + - name: NATS_URL + value: "" + - name: NATS_TLS_CA + value: "" + - name: NATS_TLS_CERT + value: "" + - name: NATS_TLS_KEY + value: "" + - name: LEADER_ELECT_RESOURCE_NAMESPACE + value: "" - name: MEILISEARCH_API_KEY valueFrom: secretKeyRef: diff --git a/config/base/resource-indexer/deployment.yaml b/config/base/resource-indexer/deployment.yaml index 85c6b78..7e36f03 100644 --- a/config/base/resource-indexer/deployment.yaml +++ b/config/base/resource-indexer/deployment.yaml @@ -22,9 +22,20 @@ spec: command: - /search - indexer + args: + - --nats-url=$(NATS_URL) + - --nats-tls-ca=$(NATS_TLS_CA) + - --nats-tls-cert=$(NATS_TLS_CERT) + - --nats-tls-key=$(NATS_TLS_KEY) env: - name: NATS_URL value: "nats://nats.nats-system.svc.cluster.local:4222" + - name: NATS_TLS_CA + value: "" + - name: NATS_TLS_CERT + value: "" + - name: NATS_TLS_KEY + value: "" - name: NATS_Subject value: "audit.>" - name: NATS_QUEUE_GROUP From d0f2eae83222d85e121b1e7f7501c15c91a32cd6 Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Tue, 3 Mar 2026 15:27:26 -0300 Subject: [PATCH 14/17] feat: Add Meilisearch domain argument and environment variable to the resource indexer deployment. --- config/base/resource-indexer/deployment.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/base/resource-indexer/deployment.yaml b/config/base/resource-indexer/deployment.yaml index 7e36f03..e7bef33 100644 --- a/config/base/resource-indexer/deployment.yaml +++ b/config/base/resource-indexer/deployment.yaml @@ -27,6 +27,7 @@ spec: - --nats-tls-ca=$(NATS_TLS_CA) - --nats-tls-cert=$(NATS_TLS_CERT) - --nats-tls-key=$(NATS_TLS_KEY) + - --meilisearch-domain=$(MEILISEARCH_DOMAIN) env: - name: NATS_URL value: "nats://nats.nats-system.svc.cluster.local:4222" @@ -44,6 +45,8 @@ spec: value: "search-indexer" - name: NATS_STREAM_NAME value: "AUDIT_EVENTS" + - name: MEILISEARCH_DOMAIN + value: "http://meilisearch.meilisearch-system.svc.cluster.local:7700" envFrom: - secretRef: name: search-indexer From 1c2618370e0af24a96238bd9a33f3a6a947ee2d1 Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Tue, 3 Mar 2026 15:34:05 -0300 Subject: [PATCH 15/17] feat: add meilisearch-domain as env pattern --- config/base/apiserver/deployment.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/config/base/apiserver/deployment.yaml b/config/base/apiserver/deployment.yaml index a55ef64..b8ebe46 100644 --- a/config/base/apiserver/deployment.yaml +++ b/config/base/apiserver/deployment.yaml @@ -58,8 +58,9 @@ spec: - --requestheader-extra-headers-prefix=$(REQUESTHEADER_EXTRA_HEADERS_PREFIX) - --logging-format=$(LOGGING_FORMAT) - --tracing-config-file=$(TRACING_CONFIG_FILE) - - -v=$(LOG_LEVEL) + - --v=$(LOG_LEVEL) - --etcd-servers=$(ETCD_SERVERS) + - --meilisearch-domain=$(MEILISEARCH_DOMAIN) env: - name: ETCD_SERVERS value: "http://etcd.etcd-system.svc.cluster.local:2379" @@ -97,6 +98,8 @@ spec: value: "4" - name: TRACING_CONFIG_FILE value: "" + - name: MEILISEARCH_DOMAIN + value: "http://meilisearch.meilisearch-system.svc.cluster.local:7700" envFrom: - secretRef: name: search-apiserver From a74d6926439477c36a84c24ccba4f445dcd7750d Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Tue, 3 Mar 2026 17:35:32 -0300 Subject: [PATCH 16/17] chore: update NATS URL in controller-manager deployment and Meilisearch Helm chart to v0.26.0 --- config/base/controller-manager/deployment.yaml | 2 +- config/dependencies/meilisearch/helmrelease.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/base/controller-manager/deployment.yaml b/config/base/controller-manager/deployment.yaml index b2db27d..95b5eee 100644 --- a/config/base/controller-manager/deployment.yaml +++ b/config/base/controller-manager/deployment.yaml @@ -68,7 +68,7 @@ spec: - name: MAX_CEL_DEPTH value: "10" - name: NATS_URL - value: "" + value: "nats://nats.nats-system.svc.cluster.local:4222" - name: NATS_TLS_CA value: "" - name: NATS_TLS_CERT diff --git a/config/dependencies/meilisearch/helmrelease.yaml b/config/dependencies/meilisearch/helmrelease.yaml index 2482c09..def63de 100644 --- a/config/dependencies/meilisearch/helmrelease.yaml +++ b/config/dependencies/meilisearch/helmrelease.yaml @@ -10,7 +10,7 @@ spec: chart: spec: chart: meilisearch - version: "0.24.0" + version: "0.26.0" sourceRef: kind: HelmRepository name: meilisearch From 0e089eb3efc7165675e1e40a824b46aac61aa018 Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Tue, 3 Mar 2026 17:39:52 -0300 Subject: [PATCH 17/17] chore: update diagrams with latest plantuml renderer --- docs/diagrams/ResourceIndexerScaling.png | Bin 25410 -> 25410 bytes docs/diagrams/SearchServiceContainers.png | Bin 49900 -> 49900 bytes docs/diagrams/SearchServiceContext.png | Bin 27523 -> 27523 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/diagrams/ResourceIndexerScaling.png b/docs/diagrams/ResourceIndexerScaling.png index 6f96d332f71390cf251b0a56e2a0ac943464cf1a..8e6355f15b3905d3051d041b4d5c0ac4c307422e 100644 GIT binary patch delta 25 hcmX?fjPcMh#to}@xc=}PnlAosQMg-s^A?`IFaVj_3v>Vg delta 25 hcmX?fjPcMh#to}@xPI^(nk@cyRdw;4&0Bc-!T_GD3>W|a diff --git a/docs/diagrams/SearchServiceContainers.png b/docs/diagrams/SearchServiceContainers.png index c038821a99eda7d96023c6bd92adbd197a0e08ac..f2900b7dc9a983b160fc2555caed6ec0b6bb5d77 100644 GIT binary patch delta 25 hcmaFU%KWC4d4nz=*I)KVE!Cqu@Ax)tHs{;A1ptXB3X%W- delta 25 hcmaFU%KWC4d4nz=*FW|~P1Pg+tv_AfY|giJ3jmW93)TPt diff --git a/docs/diagrams/SearchServiceContext.png b/docs/diagrams/SearchServiceContext.png index 9636bc40f65fed9ebdf84036df8debaf8f3df958..0e6f48f3ce201daad52e382b31982de7c2181614 100644 GIT binary patch delta 26 icmZp^&e(jNaYH{hcg_d-yLJ_?cQD7~Y@WsaG9Cb-e+(!9 delta 26 icmZp^&e(jNaYH{h_nQCmcWo