pkg/genlib/fields/load.go (204 lines of code) (raw):
package fields
import (
"archive/zip"
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"github.com/elastic/go-ucfg/yaml"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path"
"path/filepath"
"strings"
)
var ErrNotFound = errors.New("not found")
const (
fieldsSlug = "fields"
packageSlug = "package"
dataStreamSlug = "data_stream"
searchSlug = "search"
kibanaVersionSlug = "kibana.version"
manifestSlug = "manifest.yml"
)
type yamlManifest struct {
Type string `config:"type"`
}
func LoadFields(ctx context.Context, baseURL, integration, dataStream, version string) (Fields, string, error) {
fieldsContent, dataStreamType, err := getFieldsFilesAndDataStreamType(ctx, baseURL, integration, dataStream, version)
if err != nil {
return nil, dataStreamType, err
}
if len(fieldsContent) == 0 {
return nil, dataStreamType, ErrNotFound
}
fieldsFromYaml, err := loadFieldsFromYaml(fieldsContent)
if err != nil {
return nil, dataStreamType, err
}
fields := collectFields(fieldsFromYaml, "")
fields, err = normaliseFields(fields)
return fields, dataStreamType, err
}
func LoadFieldsWithTemplateFromString(ctx context.Context, fieldsContent string) (Fields, error) {
if len(fieldsContent) == 0 {
return nil, ErrNotFound
}
fieldsYaml := []byte("- key: key\n fields:\n")
for _, line := range strings.Split(fieldsContent, "\n") {
fieldsYaml = append(fieldsYaml, []byte(` `+line+"\n")...)
}
fieldsFromYaml, err := loadFieldsFromYaml(fieldsYaml)
if err != nil {
return nil, err
}
fields := collectFields(fieldsFromYaml, "")
return normaliseFields(fields)
}
func LoadFieldsWithTemplate(ctx context.Context, fieldYamlPath string) (Fields, error) {
fieldsFileContent, err := os.ReadFile(fieldYamlPath)
if err != nil {
return nil, err
}
var fieldsContent string
key := strings.TrimSuffix(filepath.Base(fieldYamlPath), filepath.Ext(fieldYamlPath))
keyEntry := fmt.Sprintf("- key: %s\n fields:\n", key)
for _, line := range strings.Split(string(fieldsFileContent), "\n") {
keyEntry += ` ` + line + "\n"
}
fieldsContent += keyEntry
if len(fieldsContent) == 0 {
return nil, ErrNotFound
}
fieldsFromYaml, err := loadFieldsFromYaml([]byte(fieldsContent))
if err != nil {
return nil, err
}
fields := collectFields(fieldsFromYaml, "")
return normaliseFields(fields)
}
func makePackageURL(baseURL, integration, version string) (*url.URL, error) {
u, err := url.Parse(baseURL)
if err != nil {
return nil, err
}
u.Path = path.Join(u.Path, packageSlug, integration, version)
return u, nil
}
func makeDownloadURL(baseURL, donwloadPath string) (*url.URL, error) {
u, err := url.Parse(baseURL)
if err != nil {
return nil, err
}
u.Path = path.Join(u.Path, donwloadPath)
return u, nil
}
func getFieldsFilesAndDataStreamType(ctx context.Context, baseURL, integration, dataStream, version string) ([]byte, string, error) {
packageURL, err := makePackageURL(baseURL, integration, version)
if err != nil {
return nil, "", err
}
r, err := getFromURL(ctx, packageURL.String())
if err != nil {
return nil, "", err
}
var downloadPayload struct {
Download string `json:"download"`
}
body, err := ioutil.ReadAll(r)
if err = json.Unmarshal(body, &downloadPayload); err != nil {
return nil, "", err
}
downloadURL, err := makeDownloadURL(baseURL, downloadPayload.Download)
r, err = getFromURL(ctx, downloadURL.String())
defer func(r io.ReadCloser) {
if r != nil {
_ = r.Close()
}
}(r)
if err != nil {
return nil, "", err
}
zipContent, err := ioutil.ReadAll(r)
if err != nil {
return nil, "", err
}
archive, err := zip.NewReader(bytes.NewReader(zipContent), int64(len(zipContent)))
if err != nil {
return nil, "", err
}
prefixFieldsPath := path.Join(fmt.Sprintf("%s-%s", integration, version), dataStreamSlug, dataStream, fieldsSlug)
manifestPath := path.Join(fmt.Sprintf("%s-%s", integration, version), dataStreamSlug, dataStream, manifestSlug)
var dataStreamType string
var fieldsContent string
for _, z := range archive.File {
if z.FileInfo().IsDir() {
continue
}
if !strings.HasPrefix(z.Name, prefixFieldsPath) && !strings.HasPrefix(z.Name, manifestPath) {
continue
}
fieldsFileName := z.Name
zr, err := z.Open()
if err != nil {
if zr != nil {
_ = zr.Close()
}
return nil, "", err
}
fieldsFileContent, err := ioutil.ReadAll(zr)
if err != nil {
if zr != nil {
_ = zr.Close()
}
return nil, "", err
}
_ = zr.Close()
if strings.HasPrefix(z.Name, prefixFieldsPath) {
key := strings.TrimSuffix(filepath.Base(fieldsFileName), filepath.Ext(fieldsFileName))
keyEntry := fmt.Sprintf("- key: %s\n fields:\n", key)
for _, line := range strings.Split(string(fieldsFileContent), "\n") {
keyEntry += ` ` + line + "\n"
}
fieldsContent += keyEntry
}
if strings.HasPrefix(z.Name, manifestPath) {
var manifest yamlManifest
cfg, err := yaml.NewConfig(fieldsFileContent)
if err != nil {
return nil, "", err
}
err = cfg.Unpack(&manifest)
if err != nil {
return nil, "", err
}
dataStreamType = manifest.Type
}
}
return []byte(fieldsContent), dataStreamType, nil
}
func getFromURL(ctx context.Context, srcURL string) (io.ReadCloser, error) {
req, err := http.NewRequestWithContext(ctx, "GET", srcURL, nil)
if err != nil {
return nil, err
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
if resp.Body != nil {
_ = resp.Body.Close()
}
return nil, err
}
if resp.StatusCode != http.StatusOK {
if resp.Body != nil {
_ = resp.Body.Close()
}
return nil, ErrNotFound
}
return resp.Body, nil
}