compliance/indextemplate.go (100 lines of code) (raw):

// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one // or more contributor license agreements. Licensed under the Elastic License; // you may not use this file except in compliance with the Elastic License. package main import ( "encoding/json" "errors" "fmt" "strconv" "strings" ) // IndexTemplate contains the result of getting an index template, decoded // from json into a map. type IndexTemplate struct { IndexPatterns []string `json:"index_patterns"` } // SimulatedIndexTemplate contains the result of simulating an index template, // with the component templates resolved. type SimulatedIndexTemplate struct { Settings struct { Index struct { Mapping struct { Source struct { Mode string `json:"mode"` } `json:"source"` } `json:"mapping"` } `json:"index"` } `json:"settings"` Mappings struct { Source struct { Mode string `json:"mode"` } `json:"_source"` Runtime map[string]MappingProperty `json:"runtime"` Properties map[string]MappingProperty `json:"properties"` } `json:"mappings"` } // MappingProperty is the definition of a property in an index template. type MappingProperty map[string]any // CheckCondition checks if a property satisfies a condition. Conditions are in the // form key:value, where the key and the value are compared with attributes of the // property. func (p MappingProperty) CheckCondition(condition string) bool { key, value, ok := strings.Cut(condition, ":") if !ok { panic("cannot understand condition " + condition) } v, ok := p[strings.TrimSpace(key)] if !ok { return false } switch v := v.(type) { case string: return strings.TrimSpace(value) == strings.TrimSpace(v) case bool: expected, err := strconv.ParseBool(value) return err != nil || expected == v case json.Number: expected, err := strconv.ParseInt(value, 10, 64) if err != nil { return false } n, err := v.Int64() if err != nil { return false } return expected == n } return false } // Properties returns the child properties of this property. func (p MappingProperty) Properties() (map[string]MappingProperty, error) { properties, ok := p["properties"] if !ok { return nil, nil } mapProperties, ok := properties.(map[string]any) if !ok { return nil, errors.New("not a map") } result := make(map[string]MappingProperty) for k, v := range mapProperties { anyMap, ok := v.(map[string]any) if !ok { return nil, errors.New("not a map") } result[k] = MappingProperty(anyMap) } return result, nil } // FieldMapping looks for the definition of a field in the simulated index template. func (t *SimulatedIndexTemplate) FieldMapping(name string) (MappingProperty, error) { if runtimeField, isRuntime := t.Mappings.Runtime[name]; isRuntime { // TODO: Look for some solution to don't need to modify the properties. runtimeField["runtime"] = true return runtimeField, nil } parts := strings.Split(name, ".") properties := t.Mappings.Properties for i, part := range parts { property, found := properties[part] if !found { return nil, fmt.Errorf("property %q not found in index template", name) } if i+1 == len(parts) { return property, nil } nextProperties, err := property.Properties() if err != nil { return nil, fmt.Errorf("property %q not found in index template: %w", name, err) } properties = nextProperties } return nil, fmt.Errorf("property %q not found in index template", name) }