compliance/elasticsearch.go (105 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 (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"github.com/elastic/go-elasticsearch/v8"
)
// Elasticsearch is an elasticsearch client.
type Elasticsearch struct {
client *elasticsearch.Client
}
// NewElasticsearchClient creates a new Elasticsearch client.
func NewElasticsearchClient() (*Elasticsearch, error) {
config := elasticsearch.Config{
Addresses: []string{
elasticPackageGetEnv("ELASTICSEARCH_HOST"),
},
Username: elasticPackageGetEnv("ELASTICSEARCH_USERNAME"),
Password: elasticPackageGetEnv("ELASTICSEARCH_PASSWORD"),
}
if caCert := elasticPackageGetEnv("CA_CERT"); caCert != "" {
certPool, err := x509.SystemCertPool()
if err != nil {
return nil, fmt.Errorf("failed to get system certificate pool: %w", err)
}
pem, err := os.ReadFile(caCert)
if err != nil {
return nil, fmt.Errorf("failed to read certificate \"%s\": %w", caCert, err)
}
if ok := certPool.AppendCertsFromPEM(pem); !ok {
return nil, fmt.Errorf("no certs were appended from \"%s\"", caCert)
}
config.Transport = &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: certPool,
},
}
}
client, err := elasticsearch.NewClient(config)
if err != nil {
return nil, err
}
return &Elasticsearch{
client: client,
}, nil
}
// IndexTemplate looks for an index template.
func (es *Elasticsearch) IndexTemplate(name string) (*IndexTemplate, error) {
resp, err := es.client.Indices.GetIndexTemplate(
es.client.Indices.GetIndexTemplate.WithContext(context.TODO()),
es.client.Indices.GetIndexTemplate.WithName(name),
)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("HTTP status code %d", resp.StatusCode)
}
var templatesResponse struct {
IndexTemplates []struct {
IndexTemplate *IndexTemplate `json:"index_template"`
} `json:"index_templates"`
}
err = newJSONDecoder(resp.Body).Decode(&templatesResponse)
if err != nil {
return nil, err
}
if n := len(templatesResponse.IndexTemplates); n != 1 {
return nil, fmt.Errorf("one index template expected, found %d", n)
}
return templatesResponse.IndexTemplates[0].IndexTemplate, nil
}
// SimulateIndexTemplate simulates the instantiation of an index template, resolving its
// component templates.
func (es *Elasticsearch) SimulateIndexTemplate(name string) (*SimulatedIndexTemplate, error) {
resp, err := es.client.Indices.SimulateTemplate(
es.client.Indices.SimulateTemplate.WithName(name),
es.client.Indices.SimulateTemplate.WithContext(context.TODO()),
)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("HTTP status code %d", resp.StatusCode)
}
var simulateResponse struct {
Template *SimulatedIndexTemplate `json:"template"`
}
err = newJSONDecoder(resp.Body).Decode(&simulateResponse)
if err != nil {
return nil, err
}
if simulateResponse.Template == nil {
return nil, errors.New("empty template simulated, something is wrong")
}
return simulateResponse.Template, nil
}
func newJSONDecoder(r io.Reader) *json.Decoder {
dec := json.NewDecoder(r)
dec.UseNumber()
return dec
}