Skip to content

Commit d328087

Browse files
committed
feat: initialize search service
1 parent 75bfbab commit d328087

39 files changed

+314
-432
lines changed

.github/workflows/build-apiserver.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
attestations: write
2929
uses: datum-cloud/actions/.github/workflows/publish-docker.yaml@v1.9.0
3030
with:
31-
image-name: example-service # TEMPLATE: Change to your service name
31+
image-name: search
3232
secrets: inherit
3333

3434
publish-kustomize-bundles:
@@ -44,8 +44,8 @@ jobs:
4444
packages: write
4545
uses: datum-cloud/actions/.github/workflows/publish-kustomize-bundle.yaml@v1.9.0
4646
with:
47-
bundle-name: ghcr.io/example-org/example-service-kustomize # TEMPLATE: Update org/name
47+
bundle-name: ghcr.io/datum-cloud/search-kustomize
4848
bundle-path: config
4949
image-overlays: config/base
50-
image-name: ghcr.io/example-org/example-service # TEMPLATE: Update org/name
50+
image-name: ghcr.io/datum-cloud/search
5151
secrets: inherit

Dockerfile

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,19 @@ COPY cmd/ cmd/
2121
COPY pkg/ pkg/
2222
COPY internal/ internal/
2323

24-
# TEMPLATE NOTE: Update the ldflags module path and binary name
25-
# Change 'github.com/example-org/example-service' to your module path
26-
# Change 'example-service' output name to your service name
27-
# Change './cmd/example-service' to your cmd directory
28-
# Build the binary with version information
24+
# Build the binary with Search-specific version information
2925
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
30-
-ldflags="-X 'github.com/example-org/example-service/internal/version.Version=${VERSION}' \
31-
-X 'github.com/example-org/example-service/internal/version.GitCommit=${GIT_COMMIT}' \
32-
-X 'github.com/example-org/example-service/internal/version.GitTreeState=${GIT_TREE_STATE}' \
33-
-X 'github.com/example-org/example-service/internal/version.BuildDate=${BUILD_DATE}'" \
34-
-a -o example-service ./cmd/example-service
26+
-ldflags="-X 'go.datum.net/search/internal/version.Version=${VERSION}' \
27+
-X 'go.datum.net/search/internal/version.GitCommit=${GIT_COMMIT}' \
28+
-X 'go.datum.net/search/internal/version.GitTreeState=${GIT_TREE_STATE}' \
29+
-X 'go.datum.net/search/internal/version.BuildDate=${BUILD_DATE}'" \
30+
-a -o search ./cmd/search
3531

3632
# Runtime stage
3733
FROM gcr.io/distroless/static:nonroot
3834

3935
WORKDIR /
40-
# TEMPLATE NOTE: Update binary name to match your service
41-
COPY --from=builder /workspace/example-service .
36+
COPY --from=builder /workspace/search .
4237
USER 65532:65532
4338

44-
# TEMPLATE NOTE: Update entrypoint to match your binary name
45-
ENTRYPOINT ["/example-service"]
39+
ENTRYPOINT ["/search"]

README.md

Lines changed: 8 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,8 @@
1-
# Kubernetes API Server Template
2-
3-
> **This is a GitHub template repository.** Click "Use this template" to create a new Kubernetes aggregated API server for your own custom resources.
4-
5-
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.
6-
7-
## What's Included
8-
9-
- **Full API server scaffolding**: Production-ready Kubernetes aggregated API server setup
10-
- **Example custom resource**: `ExampleResource` showing best practices
11-
- **OpenAPI/Swagger integration**: Automatic API documentation generation
12-
- **Metrics & monitoring**: Prometheus metrics built-in
13-
- **Version management**: Git-based version injection
14-
- **Development tooling**: Task-based build system, Docker support
15-
- **Code generation**: Kubernetes code-gen integration for deepcopy and clients
16-
17-
The template includes a working `ExampleResource` as a concrete example. You'll customize this to create your own resources like `DataProcessing`, `BackupJob`, `AnalyticsQuery`, etc.
18-
19-
## Using This Template
20-
21-
### 1. Create Repository
22-
Click "Use this template" on GitHub to create your new repository.
23-
24-
### 2. Find & Replace
25-
26-
Use global find-and-replace (case-sensitive) across all files:
27-
28-
| Find | Replace With | Example |
29-
|------|-------------|---------|
30-
| `github.com/example-org/example-service` | Your module path | `github.com/myorg/myservice` |
31-
| `example.example-org.io` | Your API group | `myresource.mycompany.io` |
32-
| `ghcr.io/example-org/example-service` | Your container registry | `ghcr.io/myorg/myservice` |
33-
| `example-service` | Your service name | `myservice` |
34-
| `ExampleService` | Your service name | `MyService` |
35-
| `ExampleResource` | Your resource type | `DataProcessing` |
36-
37-
### 3. Rename Directories
38-
39-
```bash
40-
mv cmd/example-service cmd/myservice
41-
mv pkg/apis/example-service pkg/apis/myservice
42-
```
43-
44-
### 4. Customize API Types
45-
46-
Edit `pkg/apis/myservice/v1alpha1/types.go`:
47-
- Rename `ExampleResource` to your resource type
48-
- Update `ExampleResourceSpec` fields for your use case
49-
- Update `ExampleResourceStatus` fields for your status
50-
- Review `genclient` directives for your needs (namespaced vs cluster-scoped, allowed verbs)
51-
52-
### 5. Build & Generate
53-
54-
```bash
55-
go mod tidy
56-
task generate
57-
task build
58-
task test
59-
```
60-
61-
### 6. Implement Storage Backend
62-
63-
The main TODO is in `internal/apiserver/apiserver.go` - look for the `TEMPLATE NOTE` comment. You need to:
64-
- Create a REST storage implementation
65-
- Connect to your backend (database, API, cache, etc.)
66-
- Implement CRUD operations
67-
- Register storage in the apiserver
68-
69-
See the [Kubernetes sample-apiserver](https://github.com/kubernetes/sample-apiserver/tree/master/pkg/registry) for examples.
70-
71-
### 7. Deploy (Optional)
72-
73-
Test deployment to a Kubernetes cluster:
74-
75-
```bash
76-
# Review deployment manifests
77-
cat config/README.md
78-
79-
# Generate and review manifests
80-
kubectl kustomize config/base
81-
82-
# Deploy to cluster (requires TLS certs)
83-
./config/deploy-dev.sh
84-
```
85-
86-
See [config/README.md](config/README.md) for detailed deployment instructions.
87-
88-
### 8. Clean Up
89-
90-
Search for `TEMPLATE NOTE` comments throughout the codebase and remove them once you've addressed each customization point.
91-
92-
## Prerequisites
93-
94-
**For users:**
95-
- Kubernetes 1.34+ cluster
96-
- kubectl configured to access your cluster
97-
98-
**For developers:**
99-
- Go 1.25.0 or later
100-
- [Task](https://taskfile.dev) for development workflows
101-
- Docker for building container images
102-
103-
## License
104-
105-
See [LICENSE](LICENSE) for details.
106-
107-
---
108-
109-
**Questions or feedback?** Open an issue—we're here to help!
1+
# Search
2+
3+
Search is a Kubernetes-native aggregated API server that provides advanced
4+
resource discovery through field filtering and full-text search. It indexes
5+
cluster resources in real-time via audit log events consumed from NATS
6+
JetStream, with declarative indexing policies controlling what gets indexed
7+
using CEL-based filtering. The service integrates natively with kubectl/RBAC and
8+
targets Meilisearch as the search backend.

Taskfile.yaml

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
version: '3'
22

3-
# TEMPLATE NOTE: Update these variables for your service:
4-
# - IMAGE_NAME: Change to your container registry and image name
5-
# - Update all references to "example-service" throughout this file
63
vars:
74
TOOL_DIR: "{{.USER_WORKING_DIR}}/bin"
8-
IMAGE_NAME: "ghcr.io/example-org/example-service" # TEMPLATE: Change to your registry/image
5+
IMAGE_NAME: "ghcr.io/datum-cloud/search"
96
IMAGE_TAG: "dev"
107

118
tasks:
@@ -18,11 +15,10 @@ tasks:
1815
# Build tasks
1916
build:
2017
desc: Build the search binary
21-
# TEMPLATE NOTE: Update the ldflags to use your module path
2218
cmds:
2319
- |
2420
set -e
25-
echo "Building example-service..."
21+
echo "Building search..."
2622
mkdir -p {{.TOOL_DIR}}
2723
2824
# Get git information for version injection
@@ -36,14 +32,13 @@ tasks:
3632
3733
echo "Version: ${VERSION}, Commit: ${GIT_COMMIT:0:7}, Tree: ${GIT_TREE_STATE}"
3834
39-
# TEMPLATE: Update module path and binary name
4035
go build \
41-
-ldflags="-X 'github.com/example-org/example-service/internal/version.Version=${VERSION}' \
42-
-X 'github.com/example-org/example-service/internal/version.GitCommit=${GIT_COMMIT}' \
43-
-X 'github.com/example-org/example-service/internal/version.GitTreeState=${GIT_TREE_STATE}' \
44-
-X 'github.com/example-org/example-service/internal/version.BuildDate=${BUILD_DATE}'" \
45-
-o {{.TOOL_DIR}}/example-service ./cmd/example-service
46-
echo "✅ Binary built: {{.TOOL_DIR}}/example-service"
36+
-ldflags="-X 'go.datum.net/search/internal/version.Version=${VERSION}' \
37+
-X 'go.datum.net/search/internal/version.GitCommit=${GIT_COMMIT}' \
38+
-X 'go.datum.net/search/internal/version.GitTreeState=${GIT_TREE_STATE}' \
39+
-X 'go.datum.net/search/internal/version.BuildDate=${BUILD_DATE}'" \
40+
-o {{.TOOL_DIR}}/search ./cmd/search
41+
echo "✅ Binary built: {{.TOOL_DIR}}/search"
4742
silent: true
4843

4944
# Development tasks
@@ -77,19 +72,17 @@ tasks:
7772
# Code generation tasks
7873
generate:
7974
desc: Generate deepcopy and client code
80-
# TEMPLATE NOTE: Update the package path to match your module and API group
8175
cmds:
8276
- |
8377
set -e
8478
echo "Generating code..."
8579
86-
# Run deepcopy code generator
87-
# TEMPLATE: Update module path and API package name
80+
# Run code generators
8881
go run k8s.io/code-generator/cmd/deepcopy-gen \
8982
--go-header-file hack/boilerplate.go.txt \
9083
--output-file zz_generated.deepcopy.go \
91-
--bounding-dirs github.com/example-org/example-service/pkg/apis \
92-
github.com/example-org/example-service/pkg/apis/example-service/v1alpha1
84+
--bounding-dirs go.datum.net/search/pkg/apis \
85+
go.datum.net/search/pkg/apis/search/v1alpha1
9386
9487
echo "✅ Code generation complete"
9588
silent: true
Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import (
77

88
"github.com/spf13/cobra"
99
"github.com/spf13/pflag"
10-
searchapiserver "github.com/example-org/example-service/internal/apiserver"
11-
"github.com/example-org/example-service/internal/version"
12-
"github.com/example-org/example-service/pkg/generated/openapi"
10+
searchapiserver "go.datum.net/search/internal/apiserver"
11+
"go.datum.net/search/internal/version"
12+
"go.datum.net/search/pkg/generated/openapi"
1313
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
1414
apiopenapi "k8s.io/apiserver/pkg/endpoints/openapi"
1515
genericapiserver "k8s.io/apiserver/pkg/server"
@@ -32,22 +32,20 @@ func init() {
3232
}
3333

3434
func main() {
35-
cmd := NewExampleServiceServerCommand()
35+
cmd := NewSearchServerCommand()
3636
code := cli.Run(cmd)
3737
os.Exit(code)
3838
}
3939

40-
// NewExampleServiceServerCommand creates the root command with subcommands for the search server.
41-
//
42-
// TEMPLATE NOTE: Rename this function and update the cobra.Command to match your service name.
43-
func NewExampleServiceServerCommand() *cobra.Command {
40+
// NewSearchServerCommand creates the root command with subcommands for the search server.
41+
func NewSearchServerCommand() *cobra.Command {
4442
cmd := &cobra.Command{
4543
Use: "search",
46-
Short: "ExampleService - generic aggregated API server",
47-
Long: `ExampleService is a generic Kubernetes aggregated API server that can be extended
44+
Short: "Search - generic aggregated API server",
45+
Long: `Search is a generic Kubernetes aggregated API server that can be extended
4846
with custom search implementations.
4947
50-
Exposes ExampleResource resources accessible through kubectl or any Kubernetes client.`,
48+
Exposes SearchQuery resources accessible through kubectl or any Kubernetes client.`,
5149
}
5250

5351
cmd.AddCommand(NewServeCommand())
@@ -58,14 +56,14 @@ Exposes ExampleResource resources accessible through kubectl or any Kubernetes c
5856

5957
// NewServeCommand creates the serve subcommand that starts the API server.
6058
func NewServeCommand() *cobra.Command {
61-
options := NewExampleServiceServerOptions()
59+
options := NewSearchServerOptions()
6260

6361
cmd := &cobra.Command{
6462
Use: "serve",
6563
Short: "Start the API server",
6664
Long: `Start the API server and begin serving requests.
6765
68-
Exposes ExampleResource resources through kubectl.`,
66+
Exposes SearchQuery resources through kubectl.`,
6967
RunE: func(cmd *cobra.Command, args []string) error {
7068
if err := options.Complete(); err != nil {
7169
return err
@@ -94,7 +92,7 @@ func NewVersionCommand() *cobra.Command {
9492
Long: `Show the version, git commit, and build details.`,
9593
Run: func(cmd *cobra.Command, args []string) {
9694
info := version.Get()
97-
fmt.Printf("ExampleService Server\n")
95+
fmt.Printf("Search Server\n")
9896
fmt.Printf(" Version: %s\n", info.Version)
9997
fmt.Printf(" Git Commit: %s\n", info.GitCommit)
10098
fmt.Printf(" Git Tree: %s\n", info.GitTreeState)
@@ -108,21 +106,17 @@ func NewVersionCommand() *cobra.Command {
108106
return cmd
109107
}
110108

111-
// ExampleServiceServerOptions contains configuration for the search server.
112-
//
113-
// TEMPLATE NOTE: Rename this type to match your service.
114-
// Add custom configuration fields here as needed.
115-
type ExampleServiceServerOptions struct {
109+
// SearchServerOptions contains configuration for the search server.
110+
type SearchServerOptions struct {
116111
RecommendedOptions *options.RecommendedOptions
117112
Logs *logsapi.LoggingConfiguration
118-
// Add your custom options here
119113
}
120114

121-
// NewExampleServiceServerOptions creates options with default values.
122-
func NewExampleServiceServerOptions() *ExampleServiceServerOptions {
123-
o := &ExampleServiceServerOptions{
115+
// NewSearchServerOptions creates options with default values.
116+
func NewSearchServerOptions() *SearchServerOptions {
117+
o := &SearchServerOptions{
124118
RecommendedOptions: options.NewRecommendedOptions(
125-
"/registry/example.example-org.io", // TEMPLATE NOTE: Change this to your API group
119+
"/registry/search.datum.net",
126120
searchapiserver.Codecs.LegacyCodec(searchapiserver.Scheme.PrioritizedVersionsAllGroups()...),
127121
),
128122
Logs: logsapi.NewLoggingConfiguration(),
@@ -137,22 +131,22 @@ func NewExampleServiceServerOptions() *ExampleServiceServerOptions {
137131
return o
138132
}
139133

140-
func (o *ExampleServiceServerOptions) AddFlags(fs *pflag.FlagSet) {
134+
func (o *SearchServerOptions) AddFlags(fs *pflag.FlagSet) {
141135
o.RecommendedOptions.AddFlags(fs)
142136
}
143137

144-
func (o *ExampleServiceServerOptions) Complete() error {
138+
func (o *SearchServerOptions) Complete() error {
145139
return nil
146140
}
147141

148142
// Validate ensures required configuration is provided.
149-
func (o *ExampleServiceServerOptions) Validate() error {
143+
func (o *SearchServerOptions) Validate() error {
150144
// Add validation as needed
151145
return nil
152146
}
153147

154148
// Config builds the complete server configuration from options.
155-
func (o *ExampleServiceServerOptions) Config() (*searchapiserver.Config, error) {
149+
func (o *SearchServerOptions) Config() (*searchapiserver.Config, error) {
156150
if err := o.RecommendedOptions.SecureServing.MaybeDefaultWithSelfSignedCerts(
157151
"localhost", nil, nil); err != nil {
158152
return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
@@ -165,12 +159,12 @@ func (o *ExampleServiceServerOptions) Config() (*searchapiserver.Config, error)
165159

166160
namer := apiopenapi.NewDefinitionNamer(searchapiserver.Scheme)
167161
genericConfig.OpenAPIV3Config = genericapiserver.DefaultOpenAPIV3Config(openapi.GetOpenAPIDefinitions, namer)
168-
genericConfig.OpenAPIV3Config.Info.Title = "ExampleService"
162+
genericConfig.OpenAPIV3Config.Info.Title = "Search"
169163
genericConfig.OpenAPIV3Config.Info.Version = version.Version
170164

171165
// Configure OpenAPI v2
172166
genericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(openapi.GetOpenAPIDefinitions, namer)
173-
genericConfig.OpenAPIConfig.Info.Title = "ExampleService"
167+
genericConfig.OpenAPIConfig.Info.Title = "Search"
174168
genericConfig.OpenAPIConfig.Info.Version = version.Version
175169

176170
if err := o.RecommendedOptions.ApplyTo(genericConfig); err != nil {
@@ -186,7 +180,7 @@ func (o *ExampleServiceServerOptions) Config() (*searchapiserver.Config, error)
186180
}
187181

188182
// Run initializes and starts the server.
189-
func Run(options *ExampleServiceServerOptions, ctx context.Context) error {
183+
func Run(options *SearchServerOptions, ctx context.Context) error {
190184
if err := logsapi.ValidateAndApply(options.Logs, utilfeature.DefaultMutableFeatureGate); err != nil {
191185
return fmt.Errorf("failed to apply logging configuration: %w", err)
192186
}
@@ -203,7 +197,7 @@ func Run(options *ExampleServiceServerOptions, ctx context.Context) error {
203197

204198
defer logs.FlushLogs()
205199

206-
klog.Info("Starting ExampleService server...")
200+
klog.Info("Starting Search server...")
207201
klog.Info("Metrics available at https://<server-address>/metrics")
208202
return server.Run(ctx)
209203
}

0 commit comments

Comments
 (0)