mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-12 01:14:22 +00:00
Document describing how to convert a kustomization file into a searchable document on appengine (will be changed to elasticsearch) soon.
170 lines
3.9 KiB
Go
170 lines
3.9 KiB
Go
package doc
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"sigs.k8s.io/yaml"
|
|
|
|
"google.golang.org/appengine/search"
|
|
)
|
|
|
|
const (
|
|
identifierStr = "identifier"
|
|
documentStr = "document"
|
|
repoURLStr = "repo_url"
|
|
filePathStr = "file_path"
|
|
creationTimeStr = "creation_time"
|
|
)
|
|
|
|
// Represents an unbreakable character stream.
|
|
type Atom = search.Atom
|
|
|
|
// Implements search.FieldLoadSaver in order to index this representation of a kustomization.yaml
|
|
// file.
|
|
type KustomizationDocument struct {
|
|
identifiers []Atom
|
|
FilePath Atom
|
|
RepositoryURL Atom
|
|
DocumentData string
|
|
CreationTime time.Time
|
|
}
|
|
|
|
// Partially implements search.FieldLoadSaver.
|
|
func (k *KustomizationDocument) Load(fields []search.Field, metadata *search.DocumentMetadata) error {
|
|
k.identifiers = make([]search.Atom, 0)
|
|
wrongTypeError := func(name string, expected interface{}, actual interface{}) error {
|
|
return fmt.Errorf("%s expects type %T, found %#v", name, expected, actual)
|
|
}
|
|
|
|
for _, f := range fields {
|
|
switch f.Name {
|
|
case identifierStr:
|
|
identifier, ok := f.Value.(search.Atom)
|
|
if !ok {
|
|
return wrongTypeError(f.Name, identifier, f.Value)
|
|
}
|
|
k.identifiers = append(k.identifiers, identifier)
|
|
|
|
case documentStr:
|
|
document, ok := f.Value.(string)
|
|
if !ok {
|
|
return wrongTypeError(f.Name, document, f.Value)
|
|
}
|
|
k.DocumentData = document
|
|
|
|
case filePathStr:
|
|
fp, ok := f.Value.(search.Atom)
|
|
if !ok {
|
|
return wrongTypeError(f.Name, fp, f.Value)
|
|
}
|
|
k.FilePath = fp
|
|
|
|
case repoURLStr:
|
|
url, ok := f.Value.(search.Atom)
|
|
if !ok {
|
|
return wrongTypeError(f.Name, url, f.Value)
|
|
}
|
|
k.RepositoryURL = url
|
|
|
|
case creationTimeStr:
|
|
time, ok := f.Value.(time.Time)
|
|
if !ok {
|
|
return wrongTypeError(f.Name, time, f.Value)
|
|
}
|
|
k.CreationTime = time
|
|
default:
|
|
return fmt.Errorf("KustomizationDocument field %s not recognized", f.Name)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Partially implements search.FieldLoadSaver.
|
|
func (k *KustomizationDocument) Save() ([]search.Field, *search.DocumentMetadata, error) {
|
|
err := k.ParseYAML()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
extraFields := []search.Field{
|
|
{Name: documentStr, Value: k.DocumentData},
|
|
{Name: filePathStr, Value: k.FilePath},
|
|
{Name: repoURLStr, Value: k.RepositoryURL},
|
|
{Name: creationTimeStr, Value: k.CreationTime},
|
|
}
|
|
|
|
fields := make([]search.Field, 0, len(k.identifiers)+len(extraFields))
|
|
for _, identifier := range k.identifiers {
|
|
fields = append(fields, search.Field{Name: identifierStr, Value: identifier})
|
|
}
|
|
fields = append(fields, extraFields...)
|
|
|
|
return fields, nil, nil
|
|
}
|
|
|
|
func (k *KustomizationDocument) ParseYAML() error {
|
|
k.identifiers = make([]Atom, 0)
|
|
|
|
var kustomization map[string]interface{}
|
|
err := yaml.Unmarshal([]byte(k.DocumentData), &kustomization)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to parse kustomization file: %s", err)
|
|
}
|
|
|
|
type Map struct {
|
|
data map[string]interface{}
|
|
prefix Atom
|
|
}
|
|
|
|
toVisit := []Map{
|
|
{
|
|
data: kustomization,
|
|
prefix: "",
|
|
},
|
|
}
|
|
|
|
atomJoin := func(vals ...interface{}) Atom {
|
|
strs := make([]string, 0, len(vals))
|
|
for _, val := range vals {
|
|
strs = append(strs, fmt.Sprint(val))
|
|
}
|
|
return Atom(strings.Trim(strings.Join(strs, " "), " "))
|
|
}
|
|
|
|
set := make(map[Atom]struct{})
|
|
|
|
for i := 0; i < len(toVisit); i++ {
|
|
visiting := toVisit[i]
|
|
for k, v := range visiting.data {
|
|
set[atomJoin(visiting.prefix, k)] = struct{}{}
|
|
switch value := v.(type) {
|
|
case map[string]interface{}:
|
|
toVisit = append(toVisit, Map{
|
|
data: value,
|
|
prefix: atomJoin(visiting.prefix, fmt.Sprint(k)),
|
|
})
|
|
case []interface{}:
|
|
for _, val := range value {
|
|
submap, ok := val.(map[string]interface{})
|
|
if !ok {
|
|
continue
|
|
}
|
|
toVisit = append(toVisit, Map{
|
|
data: submap,
|
|
prefix: atomJoin(visiting.prefix, fmt.Sprint(k)),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for key := range set {
|
|
k.identifiers = append(k.identifiers, key)
|
|
}
|
|
|
|
return nil
|
|
}
|