mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-22 06:47:00 +00:00
196 lines
4.8 KiB
Go
196 lines
4.8 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gorilla/mux"
|
|
"github.com/rs/cors"
|
|
|
|
"sigs.k8s.io/kustomize/api/internal/crawl/index"
|
|
)
|
|
|
|
type kustomizeSearch struct {
|
|
ctx context.Context
|
|
// Eventually pIndex *index.PlugginIndex
|
|
idx *index.KustomizeIndex
|
|
router *mux.Router
|
|
log *log.Logger
|
|
}
|
|
|
|
// New server. Creating a server does not launch it. To launch simply:
|
|
// srv, _ := NewKustomizeSearch(context.Backgroud())
|
|
// err := srv.Serve()
|
|
// if err != nil {
|
|
// // Handle server issues.
|
|
// }
|
|
//
|
|
// The server has three enpoints, two of which are functional:
|
|
//
|
|
// /search: processes the ?q= parameter for a text query and
|
|
// returns a list of 10 resutls starting from the ?from= value provided,
|
|
// with the default being zero.
|
|
//
|
|
// /metrics: returns overall metrics about the files indexed. Returns
|
|
// timeseries data for kustomization files, and returns breakdown of file
|
|
// counts by their 'kind' fields
|
|
//
|
|
// /register: not implemented, but meant as an endpoint for adding new
|
|
// kustomization files to the corpus.
|
|
func NewKustomizeSearch(ctx context.Context) (*kustomizeSearch, error) {
|
|
idx, err := index.NewKustomizeIndex(ctx, "kustomize")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ks := &kustomizeSearch{
|
|
ctx: ctx,
|
|
idx: idx,
|
|
router: mux.NewRouter(),
|
|
log: log.New(os.Stdout, "Kustomize server: ",
|
|
log.LstdFlags|log.Llongfile|log.LUTC),
|
|
}
|
|
|
|
return ks, nil
|
|
}
|
|
|
|
// Set up common middleware and the routes for the server.
|
|
func (ks *kustomizeSearch) routes() {
|
|
|
|
// Setup middleware.
|
|
ks.router.Use(func(handler http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
handler.ServeHTTP(w, r)
|
|
})
|
|
})
|
|
|
|
ks.router.HandleFunc("/liveness", ks.liveness()).Methods(http.MethodGet)
|
|
ks.router.HandleFunc("/readiness", ks.readiness()).Methods(http.MethodGet)
|
|
ks.router.HandleFunc("/search", ks.search()).Methods(http.MethodGet)
|
|
ks.router.HandleFunc("/metrics", ks.metrics()).Methods(http.MethodGet)
|
|
ks.router.HandleFunc("/register", ks.register()).Methods(http.MethodPost)
|
|
}
|
|
|
|
// Start listening and serving on the provided port.
|
|
func (ks *kustomizeSearch) Serve(port int) error {
|
|
ks.routes()
|
|
handler := cors.Default().Handler(ks.router)
|
|
s := &http.Server{
|
|
Addr: fmt.Sprintf(":%d", port),
|
|
Handler: handler,
|
|
// Timeouts/Limits
|
|
}
|
|
|
|
return s.ListenAndServe()
|
|
}
|
|
|
|
// /liveness endpoint
|
|
func (ks *kustomizeSearch) liveness() http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
}
|
|
|
|
// /readyness endpoint
|
|
func (ks *kustomizeSearch) readiness() http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
opt := index.KustomizeSearchOptions{}
|
|
_, err := ks.idx.Search("", opt)
|
|
if err != nil {
|
|
http.Error(w,
|
|
`{ "error": "could not connect to database" }`,
|
|
http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
}
|
|
|
|
// /register endpoint.
|
|
func (ks *kustomizeSearch) register() http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
http.Error(w, "not implemented", http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
// /search endpoint.
|
|
func (ks *kustomizeSearch) search() http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
values := r.URL.Query()
|
|
|
|
queries := values["q"]
|
|
ks.log.Println("Query: ", values)
|
|
|
|
var from int
|
|
fromParam := values["from"]
|
|
if len(fromParam) > 0 {
|
|
from, _ = strconv.Atoi(fromParam[0])
|
|
if from < 0 {
|
|
from = 0
|
|
}
|
|
}
|
|
_, noKinds := values["nokinds"]
|
|
|
|
opt := index.KustomizeSearchOptions{
|
|
SearchOptions: index.SearchOptions{
|
|
Size: 10,
|
|
From: from,
|
|
},
|
|
KindAggregation: !noKinds,
|
|
}
|
|
|
|
results, err := ks.idx.Search(strings.Join(queries, " "), opt)
|
|
if err != nil {
|
|
ks.log.Println("Error: ", err)
|
|
http.Error(w, fmt.Sprintf(
|
|
`{ "error": "could not complete the query" }`),
|
|
http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
enc := json.NewEncoder(w)
|
|
setIndent(enc)
|
|
if err = enc.Encode(results); err != nil {
|
|
http.Error(w, `{ "error": "failed to send back results" }`,
|
|
http.StatusInternalServerError)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
// metrics endpoint.
|
|
func (ks *kustomizeSearch) metrics() http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
res, err := ks.idx.Search("", index.KustomizeSearchOptions{
|
|
KindAggregation: true,
|
|
TimeseriesAggregation: true,
|
|
})
|
|
if err != nil {
|
|
http.Error(w, `{ "error": "could not perform the search."}`,
|
|
http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
enc := json.NewEncoder(w)
|
|
setIndent(enc)
|
|
if err := enc.Encode(res); err != nil {
|
|
http.Error(w, `{ "error": "could not format return value" }`,
|
|
http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// make json response human readable.
|
|
func setIndent(e *json.Encoder) {
|
|
e.SetIndent("", " ")
|
|
}
|