From c32034e34b201ab082ef826abcae9c76744d147e Mon Sep 17 00:00:00 2001 From: Scot Wells Date: Thu, 22 Jan 2026 17:06:16 -0600 Subject: [PATCH] feat: initialize search service --- .github/workflows/build-apiserver.yaml | 6 +- Dockerfile | 22 ++-- README.md | 117 ++---------------- Taskfile.yaml | 29 ++--- cmd/{example-service => search}/main.go | 58 ++++----- config/README.md | 46 +++---- config/base/deployment.yaml | 22 ++-- config/base/kustomization.yaml | 14 +-- config/base/rbac-auth-reader.yaml | 4 +- config/base/rbac-cluster.yaml | 8 +- config/base/secret.yaml | 2 +- config/base/service.yaml | 4 +- config/base/serviceaccount.yaml | 2 +- .../api-registration/apiservice.yaml | 9 +- .../cert-manager-ca/ca-certificate.yaml | 8 +- config/components/cert-manager-ca/issuer.yaml | 4 +- .../cert-manager-ca/kustomization.yaml | 4 +- config/components/namespace/namespace.yaml | 2 +- .../observability/kustomization.yaml | 2 +- ...l => search-apiserver-servicemonitor.yaml} | 10 +- .../components/tracing/deployment-patch.yaml | 2 +- config/components/tracing/kustomization.yaml | 2 +- config/milo/iam/resources/kustomization.yaml | 2 +- ...ampleresources.yaml => searchqueries.yaml} | 10 +- .../iam/roles/example-resource-editor.yaml | 15 --- config/milo/iam/roles/kustomization.yaml | 2 +- config/milo/iam/roles/searcher.yaml | 9 ++ config/overlays/dev/kustomization.yaml | 4 +- go.mod | 9 +- go.sum | 18 +++ internal/apiserver/apiserver.go | 39 ++---- internal/metrics/metrics.go | 12 +- internal/version/version.go | 14 +-- pkg/apis/example-service/v1alpha1/types.go | 89 ------------- .../install/install.go | 2 +- .../v1alpha1/doc.go | 2 +- .../v1alpha1/register.go | 7 +- pkg/apis/search/v1alpha1/types.go | 82 ++++++++++++ .../v1alpha1/zz_generated.deepcopy.go | 47 ++++--- 39 files changed, 308 insertions(+), 432 deletions(-) rename cmd/{example-service => search}/main.go (71%) rename config/components/observability/servicemonitors/{example-service-apiserver-servicemonitor.yaml => search-apiserver-servicemonitor.yaml} (81%) rename config/milo/iam/resources/{exampleresources.yaml => searchqueries.yaml} (73%) delete mode 100644 config/milo/iam/roles/example-resource-editor.yaml create mode 100644 config/milo/iam/roles/searcher.yaml delete mode 100644 pkg/apis/example-service/v1alpha1/types.go rename pkg/apis/{example-service => search}/install/install.go (81%) rename pkg/apis/{example-service => search}/v1alpha1/doc.go (78%) rename pkg/apis/{example-service => search}/v1alpha1/register.go (85%) create mode 100644 pkg/apis/search/v1alpha1/types.go rename pkg/apis/{example-service => search}/v1alpha1/zz_generated.deepcopy.go (62%) diff --git a/.github/workflows/build-apiserver.yaml b/.github/workflows/build-apiserver.yaml index be9c4c1..dce37c4 100644 --- a/.github/workflows/build-apiserver.yaml +++ b/.github/workflows/build-apiserver.yaml @@ -28,7 +28,7 @@ jobs: attestations: write uses: datum-cloud/actions/.github/workflows/publish-docker.yaml@v1.9.0 with: - image-name: example-service # TEMPLATE: Change to your service name + image-name: search secrets: inherit publish-kustomize-bundles: @@ -44,8 +44,8 @@ jobs: packages: write uses: datum-cloud/actions/.github/workflows/publish-kustomize-bundle.yaml@v1.9.0 with: - bundle-name: ghcr.io/example-org/example-service-kustomize # TEMPLATE: Update org/name + bundle-name: ghcr.io/datum-cloud/search-kustomize bundle-path: config image-overlays: config/base - image-name: ghcr.io/example-org/example-service # TEMPLATE: Update org/name + image-name: ghcr.io/datum-cloud/search secrets: inherit diff --git a/Dockerfile b/Dockerfile index e078b7b..00ba1d1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,25 +21,19 @@ COPY cmd/ cmd/ COPY pkg/ pkg/ COPY internal/ internal/ -# TEMPLATE NOTE: Update the ldflags module path and binary name -# Change 'github.com/example-org/example-service' to your module path -# Change 'example-service' output name to your service name -# Change './cmd/example-service' to your cmd directory -# Build the binary with version information +# Build the binary with Search-specific version information RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ - -ldflags="-X 'github.com/example-org/example-service/internal/version.Version=${VERSION}' \ - -X 'github.com/example-org/example-service/internal/version.GitCommit=${GIT_COMMIT}' \ - -X 'github.com/example-org/example-service/internal/version.GitTreeState=${GIT_TREE_STATE}' \ - -X 'github.com/example-org/example-service/internal/version.BuildDate=${BUILD_DATE}'" \ - -a -o example-service ./cmd/example-service + -ldflags="-X 'go.datum.net/search/internal/version.Version=${VERSION}' \ + -X 'go.datum.net/search/internal/version.GitCommit=${GIT_COMMIT}' \ + -X 'go.datum.net/search/internal/version.GitTreeState=${GIT_TREE_STATE}' \ + -X 'go.datum.net/search/internal/version.BuildDate=${BUILD_DATE}'" \ + -a -o search ./cmd/search # Runtime stage FROM gcr.io/distroless/static:nonroot WORKDIR / -# TEMPLATE NOTE: Update binary name to match your service -COPY --from=builder /workspace/example-service . +COPY --from=builder /workspace/search . USER 65532:65532 -# TEMPLATE NOTE: Update entrypoint to match your binary name -ENTRYPOINT ["/example-service"] +ENTRYPOINT ["/search"] diff --git a/README.md b/README.md index 7b66bed..4c94e10 100644 --- a/README.md +++ b/README.md @@ -1,109 +1,8 @@ -# Kubernetes API Server Template - -> **This is a GitHub template repository.** Click "Use this template" to create a new Kubernetes aggregated API server for your own custom resources. - -A production-ready template for building Kubernetes aggregated API servers. This provides all the scaffolding and boilerplate needed to create custom Kubernetes APIs that integrate seamlessly with `kubectl` and the Kubernetes ecosystem. - -## What's Included - -- **Full API server scaffolding**: Production-ready Kubernetes aggregated API server setup -- **Example custom resource**: `ExampleResource` showing best practices -- **OpenAPI/Swagger integration**: Automatic API documentation generation -- **Metrics & monitoring**: Prometheus metrics built-in -- **Version management**: Git-based version injection -- **Development tooling**: Task-based build system, Docker support -- **Code generation**: Kubernetes code-gen integration for deepcopy and clients - -The template includes a working `ExampleResource` as a concrete example. You'll customize this to create your own resources like `DataProcessing`, `BackupJob`, `AnalyticsQuery`, etc. - -## Using This Template - -### 1. Create Repository -Click "Use this template" on GitHub to create your new repository. - -### 2. Find & Replace - -Use global find-and-replace (case-sensitive) across all files: - -| Find | Replace With | Example | -|------|-------------|---------| -| `github.com/example-org/example-service` | Your module path | `github.com/myorg/myservice` | -| `example.example-org.io` | Your API group | `myresource.mycompany.io` | -| `ghcr.io/example-org/example-service` | Your container registry | `ghcr.io/myorg/myservice` | -| `example-service` | Your service name | `myservice` | -| `ExampleService` | Your service name | `MyService` | -| `ExampleResource` | Your resource type | `DataProcessing` | - -### 3. Rename Directories - -```bash -mv cmd/example-service cmd/myservice -mv pkg/apis/example-service pkg/apis/myservice -``` - -### 4. Customize API Types - -Edit `pkg/apis/myservice/v1alpha1/types.go`: -- Rename `ExampleResource` to your resource type -- Update `ExampleResourceSpec` fields for your use case -- Update `ExampleResourceStatus` fields for your status -- Review `genclient` directives for your needs (namespaced vs cluster-scoped, allowed verbs) - -### 5. Build & Generate - -```bash -go mod tidy -task generate -task build -task test -``` - -### 6. Implement Storage Backend - -The main TODO is in `internal/apiserver/apiserver.go` - look for the `TEMPLATE NOTE` comment. You need to: -- Create a REST storage implementation -- Connect to your backend (database, API, cache, etc.) -- Implement CRUD operations -- Register storage in the apiserver - -See the [Kubernetes sample-apiserver](https://github.com/kubernetes/sample-apiserver/tree/master/pkg/registry) for examples. - -### 7. Deploy (Optional) - -Test deployment to a Kubernetes cluster: - -```bash -# Review deployment manifests -cat config/README.md - -# Generate and review manifests -kubectl kustomize config/base - -# Deploy to cluster (requires TLS certs) -./config/deploy-dev.sh -``` - -See [config/README.md](config/README.md) for detailed deployment instructions. - -### 8. Clean Up - -Search for `TEMPLATE NOTE` comments throughout the codebase and remove them once you've addressed each customization point. - -## Prerequisites - -**For users:** -- Kubernetes 1.34+ cluster -- kubectl configured to access your cluster - -**For developers:** -- Go 1.25.0 or later -- [Task](https://taskfile.dev) for development workflows -- Docker for building container images - -## License - -See [LICENSE](LICENSE) for details. - ---- - -**Questions or feedback?** Open an issue—we're here to help! +# Search + +Search is a Kubernetes-native aggregated API server that provides advanced +resource discovery through field filtering and full-text search. It indexes +cluster resources in real-time via audit log events consumed from NATS +JetStream, with declarative indexing policies controlling what gets indexed +using CEL-based filtering. The service integrates natively with kubectl/RBAC and +targets Meilisearch as the search backend. diff --git a/Taskfile.yaml b/Taskfile.yaml index f2b6eb4..b372cc4 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -1,11 +1,8 @@ version: '3' -# TEMPLATE NOTE: Update these variables for your service: -# - IMAGE_NAME: Change to your container registry and image name -# - Update all references to "example-service" throughout this file vars: TOOL_DIR: "{{.USER_WORKING_DIR}}/bin" - IMAGE_NAME: "ghcr.io/example-org/example-service" # TEMPLATE: Change to your registry/image + IMAGE_NAME: "ghcr.io/datum-cloud/search" IMAGE_TAG: "dev" tasks: @@ -18,11 +15,10 @@ tasks: # Build tasks build: desc: Build the search binary - # TEMPLATE NOTE: Update the ldflags to use your module path cmds: - | set -e - echo "Building example-service..." + echo "Building search..." mkdir -p {{.TOOL_DIR}} # Get git information for version injection @@ -36,14 +32,13 @@ tasks: echo "Version: ${VERSION}, Commit: ${GIT_COMMIT:0:7}, Tree: ${GIT_TREE_STATE}" - # TEMPLATE: Update module path and binary name go build \ - -ldflags="-X 'github.com/example-org/example-service/internal/version.Version=${VERSION}' \ - -X 'github.com/example-org/example-service/internal/version.GitCommit=${GIT_COMMIT}' \ - -X 'github.com/example-org/example-service/internal/version.GitTreeState=${GIT_TREE_STATE}' \ - -X 'github.com/example-org/example-service/internal/version.BuildDate=${BUILD_DATE}'" \ - -o {{.TOOL_DIR}}/example-service ./cmd/example-service - echo "✅ Binary built: {{.TOOL_DIR}}/example-service" + -ldflags="-X 'go.datum.net/search/internal/version.Version=${VERSION}' \ + -X 'go.datum.net/search/internal/version.GitCommit=${GIT_COMMIT}' \ + -X 'go.datum.net/search/internal/version.GitTreeState=${GIT_TREE_STATE}' \ + -X 'go.datum.net/search/internal/version.BuildDate=${BUILD_DATE}'" \ + -o {{.TOOL_DIR}}/search ./cmd/search + echo "✅ Binary built: {{.TOOL_DIR}}/search" silent: true # Development tasks @@ -77,19 +72,17 @@ tasks: # Code generation tasks generate: desc: Generate deepcopy and client code - # TEMPLATE NOTE: Update the package path to match your module and API group cmds: - | set -e echo "Generating code..." - # Run deepcopy code generator - # TEMPLATE: Update module path and API package name + # Run code generators go run k8s.io/code-generator/cmd/deepcopy-gen \ --go-header-file hack/boilerplate.go.txt \ --output-file zz_generated.deepcopy.go \ - --bounding-dirs github.com/example-org/example-service/pkg/apis \ - github.com/example-org/example-service/pkg/apis/example-service/v1alpha1 + --bounding-dirs go.datum.net/search/pkg/apis \ + go.datum.net/search/pkg/apis/search/v1alpha1 echo "✅ Code generation complete" silent: true diff --git a/cmd/example-service/main.go b/cmd/search/main.go similarity index 71% rename from cmd/example-service/main.go rename to cmd/search/main.go index 108a4d2..8ea7069 100644 --- a/cmd/example-service/main.go +++ b/cmd/search/main.go @@ -7,9 +7,9 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" - searchapiserver "github.com/example-org/example-service/internal/apiserver" - "github.com/example-org/example-service/internal/version" - "github.com/example-org/example-service/pkg/generated/openapi" + searchapiserver "go.datum.net/search/internal/apiserver" + "go.datum.net/search/internal/version" + "go.datum.net/search/pkg/generated/openapi" utilruntime "k8s.io/apimachinery/pkg/util/runtime" apiopenapi "k8s.io/apiserver/pkg/endpoints/openapi" genericapiserver "k8s.io/apiserver/pkg/server" @@ -32,22 +32,20 @@ func init() { } func main() { - cmd := NewExampleServiceServerCommand() + cmd := NewSearchServerCommand() code := cli.Run(cmd) os.Exit(code) } -// NewExampleServiceServerCommand creates the root command with subcommands for the search server. -// -// TEMPLATE NOTE: Rename this function and update the cobra.Command to match your service name. -func NewExampleServiceServerCommand() *cobra.Command { +// NewSearchServerCommand creates the root command with subcommands for the search server. +func NewSearchServerCommand() *cobra.Command { cmd := &cobra.Command{ Use: "search", - Short: "ExampleService - generic aggregated API server", - Long: `ExampleService is a generic Kubernetes aggregated API server that can be extended + Short: "Search - generic aggregated API server", + Long: `Search is a generic Kubernetes aggregated API server that can be extended with custom search implementations. -Exposes ExampleResource resources accessible through kubectl or any Kubernetes client.`, +Exposes SearchQuery resources accessible through kubectl or any Kubernetes client.`, } cmd.AddCommand(NewServeCommand()) @@ -58,14 +56,14 @@ Exposes ExampleResource resources accessible through kubectl or any Kubernetes c // NewServeCommand creates the serve subcommand that starts the API server. func NewServeCommand() *cobra.Command { - options := NewExampleServiceServerOptions() + options := NewSearchServerOptions() cmd := &cobra.Command{ Use: "serve", Short: "Start the API server", Long: `Start the API server and begin serving requests. -Exposes ExampleResource resources through kubectl.`, +Exposes SearchQuery resources through kubectl.`, RunE: func(cmd *cobra.Command, args []string) error { if err := options.Complete(); err != nil { return err @@ -94,7 +92,7 @@ func NewVersionCommand() *cobra.Command { Long: `Show the version, git commit, and build details.`, Run: func(cmd *cobra.Command, args []string) { info := version.Get() - fmt.Printf("ExampleService Server\n") + fmt.Printf("Search Server\n") fmt.Printf(" Version: %s\n", info.Version) fmt.Printf(" Git Commit: %s\n", info.GitCommit) fmt.Printf(" Git Tree: %s\n", info.GitTreeState) @@ -108,21 +106,17 @@ func NewVersionCommand() *cobra.Command { return cmd } -// ExampleServiceServerOptions contains configuration for the search server. -// -// TEMPLATE NOTE: Rename this type to match your service. -// Add custom configuration fields here as needed. -type ExampleServiceServerOptions struct { +// SearchServerOptions contains configuration for the search server. +type SearchServerOptions struct { RecommendedOptions *options.RecommendedOptions Logs *logsapi.LoggingConfiguration - // Add your custom options here } -// NewExampleServiceServerOptions creates options with default values. -func NewExampleServiceServerOptions() *ExampleServiceServerOptions { - o := &ExampleServiceServerOptions{ +// NewSearchServerOptions creates options with default values. +func NewSearchServerOptions() *SearchServerOptions { + o := &SearchServerOptions{ RecommendedOptions: options.NewRecommendedOptions( - "/registry/example.example-org.io", // TEMPLATE NOTE: Change this to your API group + "/registry/search.miloapis.com", searchapiserver.Codecs.LegacyCodec(searchapiserver.Scheme.PrioritizedVersionsAllGroups()...), ), Logs: logsapi.NewLoggingConfiguration(), @@ -137,22 +131,22 @@ func NewExampleServiceServerOptions() *ExampleServiceServerOptions { return o } -func (o *ExampleServiceServerOptions) AddFlags(fs *pflag.FlagSet) { +func (o *SearchServerOptions) AddFlags(fs *pflag.FlagSet) { o.RecommendedOptions.AddFlags(fs) } -func (o *ExampleServiceServerOptions) Complete() error { +func (o *SearchServerOptions) Complete() error { return nil } // Validate ensures required configuration is provided. -func (o *ExampleServiceServerOptions) Validate() error { +func (o *SearchServerOptions) Validate() error { // Add validation as needed return nil } // Config builds the complete server configuration from options. -func (o *ExampleServiceServerOptions) Config() (*searchapiserver.Config, error) { +func (o *SearchServerOptions) Config() (*searchapiserver.Config, error) { if err := o.RecommendedOptions.SecureServing.MaybeDefaultWithSelfSignedCerts( "localhost", nil, nil); err != nil { return nil, fmt.Errorf("error creating self-signed certificates: %v", err) @@ -165,12 +159,12 @@ func (o *ExampleServiceServerOptions) Config() (*searchapiserver.Config, error) namer := apiopenapi.NewDefinitionNamer(searchapiserver.Scheme) genericConfig.OpenAPIV3Config = genericapiserver.DefaultOpenAPIV3Config(openapi.GetOpenAPIDefinitions, namer) - genericConfig.OpenAPIV3Config.Info.Title = "ExampleService" + genericConfig.OpenAPIV3Config.Info.Title = "Search" genericConfig.OpenAPIV3Config.Info.Version = version.Version // Configure OpenAPI v2 genericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(openapi.GetOpenAPIDefinitions, namer) - genericConfig.OpenAPIConfig.Info.Title = "ExampleService" + genericConfig.OpenAPIConfig.Info.Title = "Search" genericConfig.OpenAPIConfig.Info.Version = version.Version if err := o.RecommendedOptions.ApplyTo(genericConfig); err != nil { @@ -186,7 +180,7 @@ func (o *ExampleServiceServerOptions) Config() (*searchapiserver.Config, error) } // Run initializes and starts the server. -func Run(options *ExampleServiceServerOptions, ctx context.Context) error { +func Run(options *SearchServerOptions, ctx context.Context) error { if err := logsapi.ValidateAndApply(options.Logs, utilfeature.DefaultMutableFeatureGate); err != nil { return fmt.Errorf("failed to apply logging configuration: %w", err) } @@ -203,7 +197,7 @@ func Run(options *ExampleServiceServerOptions, ctx context.Context) error { defer logs.FlushLogs() - klog.Info("Starting ExampleService server...") + klog.Info("Starting Search server...") klog.Info("Metrics available at https:///metrics") return server.Run(ctx) } diff --git a/config/README.md b/config/README.md index 7335eab..80965b1 100644 --- a/config/README.md +++ b/config/README.md @@ -1,6 +1,6 @@ # Kubernetes Deployment Configuration -This directory contains Kustomize-based Kubernetes deployment manifests for the example-service aggregated API server. +This directory contains Kustomize-based Kubernetes deployment manifests for the search aggregated API server. ## Structure @@ -36,21 +36,21 @@ config/ kubectl apply -k config/overlays/dev # Check deployment status -kubectl get pods -n example-system -kubectl get apiservice v1alpha1.example.example-org.io +kubectl get pods -n search-system +kubectl get apiservice v1alpha1.search.miloapis.com ``` ### 2. Verify API Registration ```bash # Check if the APIService is available -kubectl get apiservices | grep example +kubectl get apiservices | grep search # List API resources (once storage is implemented) -kubectl api-resources | grep example-org +kubectl api-resources | grep datum # Try creating a resource (once storage is implemented) -kubectl get exampleresources +kubectl get searchqueries ``` ## Components @@ -83,7 +83,7 @@ mkdir -p config/overlays/production apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -namespace: example-system +namespace: search-system resources: - ../../base @@ -95,7 +95,7 @@ components: - ../../components/observability images: - - name: ghcr.io/example-org/example-service + - name: ghcr.io/datum-cloud/search newTag: v1.0.0 # Production-specific patches @@ -107,7 +107,7 @@ patches: value: 3 target: kind: Deployment - name: example-service-apiserver + name: search-apiserver # Production resource limits - patch: |- @@ -119,17 +119,9 @@ patches: value: "2Gi" target: kind: Deployment - name: example-service-apiserver + name: search-apiserver ``` -### TEMPLATE NOTE: Find and Replace - -When customizing this template, replace: -- `example-system` → your namespace -- `example-service` → your service name -- `example.example-org.io` → your API group -- `ghcr.io/example-org/example-service` → your container image - ## TLS Certificates The deployment requires TLS certificates. Choose one option: @@ -150,11 +142,11 @@ components: ```bash # Generate self-signed certificate openssl req -x509 -newkey rsa:4096 -keyout tls.key -out tls.crt \ - -days 365 -nodes -subj "/CN=example-service-apiserver.example-system.svc" + -days 365 -nodes -subj "/CN=search-apiserver.search-system.svc" # Create secret -kubectl create secret tls example-service-tls \ - --cert=tls.crt --key=tls.key -n example-system +kubectl create secret tls search-tls \ + --cert=tls.crt --key=tls.key -n search-system ``` ## Observability @@ -180,10 +172,10 @@ components: ```bash # Check API server logs -kubectl logs -n example-system -l app=example-service-apiserver --tail=50 +kubectl logs -n search-system -l app=search-apiserver --tail=50 # Check APIService status -kubectl get apiservice v1alpha1.example.example-org.io -o yaml +kubectl get apiservice v1alpha1.search.miloapis.com -o yaml ``` **Common causes:** @@ -195,20 +187,20 @@ kubectl get apiservice v1alpha1.example.example-org.io -o yaml ```bash # Describe pod for detailed events -kubectl describe pod -n example-system -l app=example-service-apiserver +kubectl describe pod -n search-system -l app=search-apiserver # Check logs -kubectl logs -n example-system -l app=example-service-apiserver --previous +kubectl logs -n search-system -l app=search-apiserver --previous ``` ### Image Pull Errors ```bash # Check events -kubectl get events -n example-system --sort-by='.lastTimestamp' +kubectl get events -n search-system --sort-by='.lastTimestamp' # Verify image exists -docker pull ghcr.io/example-org/example-service:dev +docker pull ghcr.io/datum-cloud/search:dev ``` ## Production Checklist diff --git a/config/base/deployment.yaml b/config/base/deployment.yaml index df6f8e8..7be4643 100644 --- a/config/base/deployment.yaml +++ b/config/base/deployment.yaml @@ -1,18 +1,18 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: example-service-apiserver + name: search-apiserver spec: replicas: 1 selector: matchLabels: - app: example-service-apiserver + app: search-apiserver template: metadata: labels: - app: example-service-apiserver + app: search-apiserver spec: - serviceAccountName: example-service-apiserver + serviceAccountName: search-apiserver securityContext: runAsNonRoot: true runAsUser: 65532 @@ -22,7 +22,7 @@ spec: type: RuntimeDefault containers: - name: apiserver - image: ghcr.io/example-org/example-service:latest + image: ghcr.io/datum-cloud/search:latest imagePullPolicy: IfNotPresent securityContext: allowPrivilegeEscalation: false @@ -37,7 +37,7 @@ spec: name: https protocol: TCP command: - - /example-service + - /search - serve args: - --secure-port=$(SECURE_PORT) @@ -63,9 +63,9 @@ spec: - name: SECURE_PORT value: "8443" - name: TLS_CERT_FILE - value: "/var/run/example-service-apiserver/tls/tls.crt" + value: "/var/run/search-apiserver/tls/tls.crt" - name: TLS_PRIVATE_KEY_FILE - value: "/var/run/example-service-apiserver/tls/tls.key" + value: "/var/run/search-apiserver/tls/tls.key" - name: KUBECONFIG value: "" - name: AUTHENTICATION_KUBECONFIG @@ -119,7 +119,7 @@ spec: periodSeconds: 10 volumeMounts: - name: tls-certs - mountPath: /var/run/example-service-apiserver/tls + mountPath: /var/run/search-apiserver/tls readOnly: true - name: control-plane-ca mountPath: /etc/kubernetes/pki/requestheader @@ -134,8 +134,8 @@ spec: volumeAttributes: csi.cert-manager.io/issuer-name: selfsigned-cluster-issuer csi.cert-manager.io/issuer-kind: ClusterIssuer - csi.cert-manager.io/common-name: example-service-apiserver.example-system.svc - csi.cert-manager.io/dns-names: "example-service-apiserver,example-service-apiserver.example-system,example-service-apiserver.example-system.svc,example-service-apiserver.example-system.svc.cluster.local" + csi.cert-manager.io/common-name: search-apiserver.search-system.svc + csi.cert-manager.io/dns-names: "search-apiserver,search-apiserver.search-system,search-apiserver.search-system.svc,search-apiserver.search-system.svc.cluster.local" csi.cert-manager.io/duration: "8760h" csi.cert-manager.io/renew-before: "720h" csi.cert-manager.io/fs-group: "65532" diff --git a/config/base/kustomization.yaml b/config/base/kustomization.yaml index 1655464..bc3a31c 100644 --- a/config/base/kustomization.yaml +++ b/config/base/kustomization.yaml @@ -1,7 +1,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -namespace: example-system +namespace: search-system resources: - serviceaccount.yaml @@ -15,15 +15,15 @@ labels: - includeSelectors: true includeTemplates: true pairs: - app.kubernetes.io/name: example-service - app.kubernetes.io/instance: example-service-apiserver + app.kubernetes.io/name: search + app.kubernetes.io/instance: search-apiserver app.kubernetes.io/component: apiserver - app.kubernetes.io/part-of: example.example-org.io + app.kubernetes.io/part-of: search.miloapis.com app.kubernetes.io/managed-by: kustomize # Images (will be replaced by overlays for different environments) images: - - name: ghcr.io/example-org/example-service + - name: ghcr.io/datum-cloud/search newTag: latest # JSON patches to ensure rbac-auth-reader stays in kube-system @@ -31,11 +31,11 @@ images: patches: - target: kind: RoleBinding - name: example-service-apiserver-auth-reader + name: search-apiserver-auth-reader patch: |- - op: replace path: /metadata/namespace value: kube-system - op: replace path: /subjects/0/namespace - value: example-system + value: search-system diff --git a/config/base/rbac-auth-reader.yaml b/config/base/rbac-auth-reader.yaml index bdde16a..e2e589d 100644 --- a/config/base/rbac-auth-reader.yaml +++ b/config/base/rbac-auth-reader.yaml @@ -1,7 +1,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: example-service-apiserver-auth-reader + name: search-apiserver-auth-reader namespace: kube-system roleRef: apiGroup: rbac.authorization.k8s.io @@ -9,4 +9,4 @@ roleRef: name: extension-apiserver-authentication-reader subjects: - kind: ServiceAccount - name: example-service-apiserver + name: search-apiserver diff --git a/config/base/rbac-cluster.yaml b/config/base/rbac-cluster.yaml index ef4f12c..ecefe8d 100644 --- a/config/base/rbac-cluster.yaml +++ b/config/base/rbac-cluster.yaml @@ -1,7 +1,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: example-service-apiserver + name: search-apiserver rules: # Allow reading extension-apiserver-authentication configmap for authentication - apiGroups: [""] @@ -20,11 +20,11 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: example-service-apiserver + name: search-apiserver roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: example-service-apiserver + name: search-apiserver subjects: - kind: ServiceAccount - name: example-service-apiserver + name: search-apiserver diff --git a/config/base/secret.yaml b/config/base/secret.yaml index 360fefa..db1e383 100644 --- a/config/base/secret.yaml +++ b/config/base/secret.yaml @@ -5,7 +5,7 @@ metadata: labels: app.kubernetes.io/name: clickhouse app.kubernetes.io/component: database - app.kubernetes.io/part-of: example.example-org.io + app.kubernetes.io/part-of: search.miloapis.com type: Opaque stringData: password: "" diff --git a/config/base/service.yaml b/config/base/service.yaml index 722da26..c60960e 100644 --- a/config/base/service.yaml +++ b/config/base/service.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Service metadata: - name: example-service-apiserver + name: search-apiserver spec: type: ClusterIP ports: @@ -10,4 +10,4 @@ spec: targetPort: 8443 protocol: TCP selector: - app: example-service-apiserver + app: search-apiserver diff --git a/config/base/serviceaccount.yaml b/config/base/serviceaccount.yaml index 2676b3a..cedd5ce 100644 --- a/config/base/serviceaccount.yaml +++ b/config/base/serviceaccount.yaml @@ -1,4 +1,4 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: example-service-apiserver + name: search-apiserver diff --git a/config/components/api-registration/apiservice.yaml b/config/components/api-registration/apiservice.yaml index 01da715..accab79 100644 --- a/config/components/api-registration/apiservice.yaml +++ b/config/components/api-registration/apiservice.yaml @@ -1,12 +1,13 @@ apiVersion: apiregistration.k8s.io/v1 kind: APIService metadata: - name: v1alpha1.example.example-org.io + name: v1alpha1.search.miloapis.com spec: service: - name: example-service-apiserver - port: 443 - group: example.example-org.io + name: search-apiserver + namespace: search-system + port: 443 + group: search.miloapis.com version: v1alpha1 insecureSkipTLSVerify: false groupPriorityMinimum: 1000 diff --git a/config/components/cert-manager-ca/ca-certificate.yaml b/config/components/cert-manager-ca/ca-certificate.yaml index bb579a1..9dd517f 100644 --- a/config/components/cert-manager-ca/ca-certificate.yaml +++ b/config/components/cert-manager-ca/ca-certificate.yaml @@ -1,16 +1,16 @@ apiVersion: cert-manager.io/v1 kind: Certificate metadata: - name: example-service-ca + name: search-ca spec: isCA: true - secretName: example-service-ca-secret + secretName: search-ca-secret duration: 87600h # 10 years renewBefore: 8760h # 1 year before expiration subject: organizations: - - example-org # TEMPLATE NOTE: Change to your organization - commonName: Example Service Internal CA + - datum-cloud + commonName: Search Service Internal CA # Use a self-signed issuer to bootstrap the CA # This should reference an existing cluster-wide self-signed issuer issuerRef: diff --git a/config/components/cert-manager-ca/issuer.yaml b/config/components/cert-manager-ca/issuer.yaml index 38dab02..5b61aad 100644 --- a/config/components/cert-manager-ca/issuer.yaml +++ b/config/components/cert-manager-ca/issuer.yaml @@ -1,8 +1,8 @@ apiVersion: cert-manager.io/v1 kind: Issuer metadata: - name: example-service-ca-issuer + name: search-ca-issuer spec: ca: # Reference the secret created by the CA Certificate - secretName: example-service-ca-secret + secretName: search-ca-secret diff --git a/config/components/cert-manager-ca/kustomization.yaml b/config/components/cert-manager-ca/kustomization.yaml index 096aaf8..98f4037 100644 --- a/config/components/cert-manager-ca/kustomization.yaml +++ b/config/components/cert-manager-ca/kustomization.yaml @@ -9,11 +9,11 @@ resources: patches: - target: kind: Deployment - name: example-service-apiserver + name: search-apiserver patch: |- - op: replace path: /spec/template/spec/volumes/0/csi/volumeAttributes/csi.cert-manager.io~1issuer-name - value: example-service-ca-issuer + value: search-ca-issuer - op: replace path: /spec/template/spec/volumes/0/csi/volumeAttributes/csi.cert-manager.io~1issuer-kind value: Issuer diff --git a/config/components/namespace/namespace.yaml b/config/components/namespace/namespace.yaml index 26525ef..91c67ae 100644 --- a/config/components/namespace/namespace.yaml +++ b/config/components/namespace/namespace.yaml @@ -1,4 +1,4 @@ apiVersion: v1 kind: Namespace metadata: - name: example-system + name: search-system diff --git a/config/components/observability/kustomization.yaml b/config/components/observability/kustomization.yaml index d47dcd7..e2f0974 100644 --- a/config/components/observability/kustomization.yaml +++ b/config/components/observability/kustomization.yaml @@ -3,4 +3,4 @@ kind: Component resources: # ServiceMonitors for metrics collection - - servicemonitors/example-service-apiserver-servicemonitor.yaml + - servicemonitors/search-apiserver-servicemonitor.yaml diff --git a/config/components/observability/servicemonitors/example-service-apiserver-servicemonitor.yaml b/config/components/observability/servicemonitors/search-apiserver-servicemonitor.yaml similarity index 81% rename from config/components/observability/servicemonitors/example-service-apiserver-servicemonitor.yaml rename to config/components/observability/servicemonitors/search-apiserver-servicemonitor.yaml index e4461c1..61eb4f0 100644 --- a/config/components/observability/servicemonitors/example-service-apiserver-servicemonitor.yaml +++ b/config/components/observability/servicemonitors/search-apiserver-servicemonitor.yaml @@ -1,16 +1,16 @@ apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: - name: example-service-apiserver + name: search-apiserver labels: - app: example-service-apiserver - app.kubernetes.io/name: example-service-apiserver + app: search-apiserver + app.kubernetes.io/name: search-apiserver app.kubernetes.io/component: apiserver monitoring: "true" spec: selector: matchLabels: - app.kubernetes.io/name: example-service + app.kubernetes.io/name: search endpoints: - port: https path: /metrics @@ -34,4 +34,4 @@ spec: targetLabel: service namespaceSelector: matchNames: - - example-system + - search-system diff --git a/config/components/tracing/deployment-patch.yaml b/config/components/tracing/deployment-patch.yaml index 5dff9b5..59d9a98 100644 --- a/config/components/tracing/deployment-patch.yaml +++ b/config/components/tracing/deployment-patch.yaml @@ -1,7 +1,7 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: example-service-apiserver + name: search-apiserver spec: template: spec: diff --git a/config/components/tracing/kustomization.yaml b/config/components/tracing/kustomization.yaml index b23fcb2..763a6af 100644 --- a/config/components/tracing/kustomization.yaml +++ b/config/components/tracing/kustomization.yaml @@ -8,4 +8,4 @@ patches: - path: deployment-patch.yaml target: kind: Deployment - name: example-service-apiserver + name: search-apiserver diff --git a/config/milo/iam/resources/kustomization.yaml b/config/milo/iam/resources/kustomization.yaml index 128e83b..1b069a7 100644 --- a/config/milo/iam/resources/kustomization.yaml +++ b/config/milo/iam/resources/kustomization.yaml @@ -2,4 +2,4 @@ apiVersion: kustomize.config.k8s.io/v1alpha1 kind: Component resources: - - exampleresources.yaml + - searchqueries.yaml diff --git a/config/milo/iam/resources/exampleresources.yaml b/config/milo/iam/resources/searchqueries.yaml similarity index 73% rename from config/milo/iam/resources/exampleresources.yaml rename to config/milo/iam/resources/searchqueries.yaml index 8920f92..072e2d8 100644 --- a/config/milo/iam/resources/exampleresources.yaml +++ b/config/milo/iam/resources/searchqueries.yaml @@ -2,16 +2,16 @@ # Milo provides IAM capabilities for Kubernetes APIs # See https://github.com/datum-cloud/milo for more information # -# This ProtectedResource grants permissions for ExampleResource objects +# This ProtectedResource grants permissions for SearchQuery objects apiVersion: iam.miloapis.com/v1alpha1 kind: ProtectedResource metadata: - name: example.example-org.io-exampleresources + name: search.miloapis.com-searchqueries spec: serviceRef: - name: "example.example-org.io" - kind: ExampleResource - plural: exampleresources + name: "search.miloapis.com" + kind: SearchQuery + plural: searchqueries singular: exampleresource permissions: - create diff --git a/config/milo/iam/roles/example-resource-editor.yaml b/config/milo/iam/roles/example-resource-editor.yaml deleted file mode 100644 index 6f0bfdd..0000000 --- a/config/milo/iam/roles/example-resource-editor.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# TEMPLATE NOTE: This defines a Milo Role with specific permissions -# Users/ServiceAccounts can be granted this role via RoleBindings -apiVersion: iam.miloapis.com/v1alpha1 -kind: Role -metadata: - name: example.example-org.io-example-resource-editor - namespace: milo-system -spec: - launchStage: Beta # Alpha, Beta, or GA - includedPermissions: - - example.example-org.io/exampleresources.create - - example.example-org.io/exampleresources.get - - example.example-org.io/exampleresources.list - - example.example-org.io/exampleresources.update - - example.example-org.io/exampleresources.delete diff --git a/config/milo/iam/roles/kustomization.yaml b/config/milo/iam/roles/kustomization.yaml index c2628a3..4926b49 100644 --- a/config/milo/iam/roles/kustomization.yaml +++ b/config/milo/iam/roles/kustomization.yaml @@ -2,4 +2,4 @@ apiVersion: kustomize.config.k8s.io/v1alpha1 kind: Component resources: - - example-resource-editor.yaml + - searcher.yaml diff --git a/config/milo/iam/roles/searcher.yaml b/config/milo/iam/roles/searcher.yaml new file mode 100644 index 0000000..9bd08ac --- /dev/null +++ b/config/milo/iam/roles/searcher.yaml @@ -0,0 +1,9 @@ +apiVersion: iam.miloapis.com/v1alpha1 +kind: Role +metadata: + name: search.miloapis.com-searcher + namespace: milo-system +spec: + launchStage: Alpha + includedPermissions: + - search.miloapis.com/searchqueries.create diff --git a/config/overlays/dev/kustomization.yaml b/config/overlays/dev/kustomization.yaml index c7d62a5..85e6cc1 100644 --- a/config/overlays/dev/kustomization.yaml +++ b/config/overlays/dev/kustomization.yaml @@ -1,7 +1,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -namespace: example-system +namespace: search-system resources: - ../../base @@ -16,5 +16,5 @@ components: # Override image tag for dev builds images: - - name: ghcr.io/example-org/example-service + - name: ghcr.io/datum-cloud/search newTag: dev diff --git a/go.mod b/go.mod index dd0e6a9..7d5b4f0 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,9 @@ -module github.com/example-org/example-service +module go.datum.net/search go 1.25.0 require ( + github.com/go-openapi/spec v0.22.3 github.com/spf13/cobra v1.9.1 github.com/spf13/pflag v1.0.9 k8s.io/apimachinery v0.35.0 @@ -33,7 +34,13 @@ require ( github.com/go-openapi/jsonpointer v0.22.4 // indirect github.com/go-openapi/jsonreference v0.21.4 // indirect github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/swag/conv v0.25.4 // indirect github.com/go-openapi/swag/jsonname v0.25.4 // indirect + github.com/go-openapi/swag/jsonutils v0.25.4 // indirect + github.com/go-openapi/swag/loading v0.25.4 // indirect + github.com/go-openapi/swag/stringutils v0.25.4 // indirect + github.com/go-openapi/swag/typeutils v0.25.4 // indirect + github.com/go-openapi/swag/yamlutils v0.25.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.3 // indirect diff --git a/go.sum b/go.sum index d2e80e0..9feb69a 100644 --- a/go.sum +++ b/go.sum @@ -44,10 +44,28 @@ github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegO github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8= github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4= +github.com/go-openapi/spec v0.22.3 h1:qRSmj6Smz2rEBxMnLRBMeBWxbbOvuOoElvSvObIgwQc= +github.com/go-openapi/spec v0.22.3/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= +github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= +github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= +github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= +github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= +github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= +github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= +github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= +github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= +github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= +github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= diff --git a/internal/apiserver/apiserver.go b/internal/apiserver/apiserver.go index 9713042..b351181 100644 --- a/internal/apiserver/apiserver.go +++ b/internal/apiserver/apiserver.go @@ -11,9 +11,9 @@ import ( genericapiserver "k8s.io/apiserver/pkg/server" "k8s.io/klog/v2" - _ "github.com/example-org/example-service/internal/metrics" - "github.com/example-org/example-service/pkg/apis/example-service/install" - "github.com/example-org/example-service/pkg/apis/example-service/v1alpha1" + _ "go.datum.net/search/internal/metrics" + "go.datum.net/search/pkg/apis/search/install" + "go.datum.net/search/pkg/apis/search/v1alpha1" ) var ( @@ -50,8 +50,8 @@ type Config struct { ExtraConfig ExtraConfig } -// ExampleServiceServer is the search aggregated apiserver. -type ExampleServiceServer struct { +// SearchServer is the search aggregated apiserver. +type SearchServer struct { GenericAPIServer *genericapiserver.GenericAPIServer } @@ -76,37 +76,22 @@ func (cfg *Config) Complete() CompletedConfig { return CompletedConfig{&c} } -// New creates and initializes the ExampleServiceServer with storage and API groups. -func (c completedConfig) New() (*ExampleServiceServer, error) { +// New creates and initializes the SearchServer with storage and API groups. +func (c completedConfig) New() (*SearchServer, error) { genericServer, err := c.GenericConfig.New("search-apiserver", genericapiserver.NewEmptyDelegate()) if err != nil { return nil, err } - s := &ExampleServiceServer{ + s := &SearchServer{ GenericAPIServer: genericServer, } apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(v1alpha1.GroupName, Scheme, metav1.ParameterCodec, Codecs) v1alpha1Storage := map[string]rest.Storage{} - - // TEMPLATE NOTE: This is where you register your REST storage implementations. - // You need to create a storage backend that implements rest.Storage interface. - // - // Example pattern: - // storage := registry.NewYourResourceStorage(/* backend config */) - // v1alpha1Storage["yourresources"] = storage - // - // The storage implementation should handle: - // - Connecting to your backend (database, API, cache, etc.) - // - CRUD operations (Create, Get, List, Update, Delete, Watch) - // - Validation and business logic - // - // See kubernetes sample-apiserver for reference implementations: - // https://github.com/kubernetes/sample-apiserver/tree/master/pkg/registry - // - // TODO: Add your storage implementations here + // TODO: Add storage implementations here + // Example: v1alpha1Storage["searchqueries"] = search.NewQueryStorage(storage) apiGroupInfo.VersionedResourcesStorageMap["v1alpha1"] = v1alpha1Storage @@ -114,12 +99,12 @@ func (c completedConfig) New() (*ExampleServiceServer, error) { return nil, err } - klog.Info("ExampleService server initialized successfully") + klog.Info("Search server initialized successfully") return s, nil } // Run starts the server. -func (s *ExampleServiceServer) Run(ctx context.Context) error { +func (s *SearchServer) Run(ctx context.Context) error { return s.GenericAPIServer.PrepareRun().RunWithContext(ctx) } diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go index 16e4418..5603e85 100644 --- a/internal/metrics/metrics.go +++ b/internal/metrics/metrics.go @@ -10,8 +10,8 @@ const ( ) var ( - // ExampleResourceTotal tracks the total number of search queries - ExampleResourceTotal = metrics.NewCounterVec( + // SearchQueryTotal tracks the total number of search queries + SearchQueryTotal = metrics.NewCounterVec( &metrics.CounterOpts{ Namespace: namespace, Name: "query_total", @@ -21,8 +21,8 @@ var ( []string{"status"}, ) - // ExampleResourceDuration tracks the duration of search queries - ExampleResourceDuration = metrics.NewHistogramVec( + // SearchQueryDuration tracks the duration of search queries + SearchQueryDuration = metrics.NewHistogramVec( &metrics.HistogramOpts{ Namespace: namespace, Name: "query_duration_seconds", @@ -38,7 +38,7 @@ var ( // This ensures they're included in the /metrics endpoint func init() { legacyregistry.MustRegister( - ExampleResourceTotal, - ExampleResourceDuration, + SearchQueryTotal, + SearchQueryDuration, ) } diff --git a/internal/version/version.go b/internal/version/version.go index e19ab02..f560d47 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -1,4 +1,4 @@ -// Package version provides version information for the ExampleService API server. +// Package version provides version information for the Search API server. // Version information is injected at build time using ldflags. package version @@ -8,24 +8,24 @@ import ( ) var ( - // Version is the semantic version of the ExampleService API server (e.g., "v0.1.0", "v1.2.3") + // Version is the semantic version of the Search API server (e.g., "v0.1.0", "v1.2.3") // This can be set via ldflags during build: - // -ldflags="-X 'github.com/example-org/example-service/internal/version.Version=v0.1.0'" + // -ldflags="-X 'go.datum.net/search/internal/version.Version=v0.1.0'" Version = "dev" // GitCommit is the git commit hash of the build // This can be set via ldflags during build: - // -ldflags="-X 'github.com/example-org/example-service/internal/version.GitCommit=$(git rev-parse HEAD)'" + // -ldflags="-X 'go.datum.net/search/internal/version.GitCommit=$(git rev-parse HEAD)'" GitCommit = "unknown" // GitTreeState indicates whether the git tree was clean or dirty during build // This can be set via ldflags during build: - // -ldflags="-X 'github.com/example-org/example-service/internal/version.GitTreeState=clean'" + // -ldflags="-X 'go.datum.net/search/internal/version.GitTreeState=clean'" GitTreeState = "unknown" // BuildDate is the date when the binary was built (RFC3339 format) // This can be set via ldflags during build: - // -ldflags="-X 'github.com/example-org/example-service/internal/version.BuildDate=$(date -u '+%Y-%m-%dT%H:%M:%SZ')'" + // -ldflags="-X 'go.datum.net/search/internal/version.BuildDate=$(date -u '+%Y-%m-%dT%H:%M:%SZ')'" BuildDate = "unknown" ) @@ -55,6 +55,6 @@ func Get() Info { // String returns a human-readable version string func (i Info) String() string { - return fmt.Sprintf("ExampleService API Server %s (commit: %s, built: %s, go: %s, platform: %s)", + return fmt.Sprintf("Search API Server %s (commit: %s, built: %s, go: %s, platform: %s)", i.Version, i.GitCommit, i.BuildDate, i.GoVersion, i.Platform) } diff --git a/pkg/apis/example-service/v1alpha1/types.go b/pkg/apis/example-service/v1alpha1/types.go deleted file mode 100644 index 6272c49..0000000 --- a/pkg/apis/example-service/v1alpha1/types.go +++ /dev/null @@ -1,89 +0,0 @@ -// +k8s:openapi-gen=true -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// +genclient -// +genclient:nonNamespaced -// +genclient:onlyVerbs=create -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// ExampleResource represents a generic example resource resource. -// -// TEMPLATE NOTE: This is an example resource. Rename this to your custom resource type -// (e.g., DataProcessing, BackupJob, AnalyticsQuery) and customize the Spec and Status -// fields to match your use case. -// -// The genclient directives above control code generation: -// - nonNamespaced: makes this a cluster-scoped resource (remove for namespaced) -// - onlyVerbs=create: limits to POST requests (modify for full CRUD) -// -// Example usage: -// -// apiVersion: example.example-org.io/v1alpha1 -// kind: ExampleResource -// metadata: -// name: my-example -// spec: -// name: "example-1" -// count: 5 -// enabled: true -type ExampleResource struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec ExampleResourceSpec `json:"spec"` - Status ExampleResourceStatus `json:"status,omitempty"` -} - -// ExampleResourceSpec defines the desired state of ExampleResource. -// -// TEMPLATE NOTE: Customize these fields for your use case. -// This example shows common field patterns, but you can add any fields needed -// for your resource (timeouts, priorities, configuration, etc.) -type ExampleResourceSpec struct { - // Name is an example string field. - // - // +required - Name string `json:"name"` - - // Count is an example integer field. - // +optional - // +kubebuilder:default=1 - Count int32 `json:"count,omitempty"` - - // Enabled is an example boolean field. - // +optional - // +kubebuilder:default=true - Enabled bool `json:"enabled,omitempty"` -} - -// ExampleResourceStatus defines the observed state of ExampleResource. -// -// TEMPLATE NOTE: Customize the status fields to represent the state of your resource. -// Common patterns include: phase/state, conditions, observedGeneration, results, errors, etc. -type ExampleResourceStatus struct { - // Phase represents the current phase of the resource. - // +optional - Phase string `json:"phase,omitempty"` - - // Message provides human-readable details about the current state. - // +optional - Message string `json:"message,omitempty"` - - // ObservedGeneration reflects the generation most recently observed by the controller. - // +optional - ObservedGeneration int64 `json:"observedGeneration,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// ExampleResourceList is a list of ExampleResource objects -type ExampleResourceList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - - Items []ExampleResource `json:"items"` -} diff --git a/pkg/apis/example-service/install/install.go b/pkg/apis/search/install/install.go similarity index 81% rename from pkg/apis/example-service/install/install.go rename to pkg/apis/search/install/install.go index e2cc020..ed1a079 100644 --- a/pkg/apis/example-service/install/install.go +++ b/pkg/apis/search/install/install.go @@ -4,7 +4,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "github.com/example-org/example-service/pkg/apis/example-service/v1alpha1" + "go.datum.net/search/pkg/apis/search/v1alpha1" ) // Install registers the API group and adds types to a scheme diff --git a/pkg/apis/example-service/v1alpha1/doc.go b/pkg/apis/search/v1alpha1/doc.go similarity index 78% rename from pkg/apis/example-service/v1alpha1/doc.go rename to pkg/apis/search/v1alpha1/doc.go index 28a17e1..a872810 100644 --- a/pkg/apis/example-service/v1alpha1/doc.go +++ b/pkg/apis/search/v1alpha1/doc.go @@ -1,5 +1,5 @@ // +k8s:deepcopy-gen=package -// +groupName=example.example-org.io +// +groupName=search.miloapis.com // Package v1alpha1 contains API Schema definitions for the search v1alpha1 API group package v1alpha1 diff --git a/pkg/apis/example-service/v1alpha1/register.go b/pkg/apis/search/v1alpha1/register.go similarity index 85% rename from pkg/apis/example-service/v1alpha1/register.go rename to pkg/apis/search/v1alpha1/register.go index ec960c0..b510eb3 100644 --- a/pkg/apis/example-service/v1alpha1/register.go +++ b/pkg/apis/search/v1alpha1/register.go @@ -6,9 +6,8 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" ) -// TEMPLATE NOTE: Change this to your API group domain (e.g., "myresource.mycompany.io") // GroupName is the group name for the search API -const GroupName = "example.example-org.io" +const GroupName = "search.miloapis.com" // SchemeGroupVersion is group version used to register these objects var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"} @@ -28,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, - &ExampleResource{}, - &ExampleResourceList{}, + &SearchQuery{}, + &SearchQueryList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil diff --git a/pkg/apis/search/v1alpha1/types.go b/pkg/apis/search/v1alpha1/types.go new file mode 100644 index 0000000..fc658da --- /dev/null +++ b/pkg/apis/search/v1alpha1/types.go @@ -0,0 +1,82 @@ +// +k8s:openapi-gen=true +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// +genclient +// +genclient:nonNamespaced +// +genclient:onlyVerbs=create +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// SearchQuery represents a generic search query resource. +// +// This is a base type that can be extended for specific search implementations. +// +// Quick Start: +// +// apiVersion: search.miloapis.com/v1alpha1 +// kind: SearchQuery +// metadata: +// name: example-search +// spec: +// query: "your search query" +// limit: 100 +type SearchQuery struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec SearchQuerySpec `json:"spec"` + Status SearchQueryStatus `json:"status,omitempty"` +} + +// SearchQuerySpec defines the search parameters. +// +// The actual fields will depend on the specific search implementation. +type SearchQuerySpec struct { + // Query is the search query string. + // + // +required + Query string `json:"query"` + + // Limit sets the maximum number of results per page. + // Default: 10, Maximum: 100. + // + // +optional + Limit int32 `json:"limit,omitempty"` + + // Continue is the pagination cursor for fetching additional pages. + // + // Leave empty for the first page. If status.continue is non-empty after a query, + // copy that value here in a new query with identical parameters to get the next page. + // + // +optional + Continue string `json:"continue,omitempty"` +} + +// SearchQueryStatus contains the query results and pagination state. +type SearchQueryStatus struct { + // Results contains the search results as an array of Kubernetes resources. + // + // +optional + Results []runtime.RawExtension `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"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// SearchQueryList is a list of SearchQuery objects +type SearchQueryList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []SearchQuery `json:"items"` +} diff --git a/pkg/apis/example-service/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/search/v1alpha1/zz_generated.deepcopy.go similarity index 62% rename from pkg/apis/example-service/v1alpha1/zz_generated.deepcopy.go rename to pkg/apis/search/v1alpha1/zz_generated.deepcopy.go index 178513e..741b488 100644 --- a/pkg/apis/example-service/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/search/v1alpha1/zz_generated.deepcopy.go @@ -10,27 +10,27 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ExampleResource) DeepCopyInto(out *ExampleResource) { +func (in *SearchQuery) DeepCopyInto(out *SearchQuery) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExampleResource. -func (in *ExampleResource) DeepCopy() *ExampleResource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SearchQuery. +func (in *SearchQuery) DeepCopy() *SearchQuery { if in == nil { return nil } - out := new(ExampleResource) + out := new(SearchQuery) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ExampleResource) DeepCopyObject() runtime.Object { +func (in *SearchQuery) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -38,13 +38,13 @@ func (in *ExampleResource) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ExampleResourceList) DeepCopyInto(out *ExampleResourceList) { +func (in *SearchQueryList) DeepCopyInto(out *SearchQueryList) { *out = *in out.TypeMeta = in.TypeMeta in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]ExampleResource, len(*in)) + *out = make([]SearchQuery, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -52,18 +52,18 @@ func (in *ExampleResourceList) DeepCopyInto(out *ExampleResourceList) { return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExampleResourceList. -func (in *ExampleResourceList) DeepCopy() *ExampleResourceList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SearchQueryList. +func (in *SearchQueryList) DeepCopy() *SearchQueryList { if in == nil { return nil } - out := new(ExampleResourceList) + out := new(SearchQueryList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ExampleResourceList) DeepCopyObject() runtime.Object { +func (in *SearchQueryList) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -71,33 +71,40 @@ func (in *ExampleResourceList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ExampleResourceSpec) DeepCopyInto(out *ExampleResourceSpec) { +func (in *SearchQuerySpec) DeepCopyInto(out *SearchQuerySpec) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExampleResourceSpec. -func (in *ExampleResourceSpec) DeepCopy() *ExampleResourceSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SearchQuerySpec. +func (in *SearchQuerySpec) DeepCopy() *SearchQuerySpec { if in == nil { return nil } - out := new(ExampleResourceSpec) + out := new(SearchQuerySpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ExampleResourceStatus) DeepCopyInto(out *ExampleResourceStatus) { +func (in *SearchQueryStatus) DeepCopyInto(out *SearchQueryStatus) { *out = *in + if in.Results != nil { + in, out := &in.Results, &out.Results + *out = make([]runtime.RawExtension, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExampleResourceStatus. -func (in *ExampleResourceStatus) DeepCopy() *ExampleResourceStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SearchQueryStatus. +func (in *SearchQueryStatus) DeepCopy() *SearchQueryStatus { if in == nil { return nil } - out := new(ExampleResourceStatus) + out := new(SearchQueryStatus) in.DeepCopyInto(out) return out }