internal/dump/installedobjects.go (259 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 dump import ( "context" "fmt" "path/filepath" "slices" "github.com/elastic/elastic-package/internal/elasticsearch" ) const ( ComponentTemplatesDumpDir = "component_templates" ILMPoliciesDumpDir = "ilm_policies" IndexTemplatesDumpDir = "index_templates" IngestPipelinesDumpDir = "ingest_pipelines" MLModelsDumpDir = "ml_models" ) // InstalledObjectsDumper discovers and dumps objects installed in Elasticsearch for a given package. type InstalledObjectsDumper struct { packageName string client *elasticsearch.API componentTemplates []ComponentTemplate ilmPolicies []ILMPolicy indexTemplates []IndexTemplate ingestPipelines []IngestPipeline mlModels []MLModel } // NewInstalledObjectsDumper creates an InstalledObjectsDumper for a given package. func NewInstalledObjectsDumper(client *elasticsearch.API, packageName string) *InstalledObjectsDumper { return &InstalledObjectsDumper{ packageName: packageName, client: client, } } // DumpAll discovers and dumps all known resources as files in the given directory. func (e *InstalledObjectsDumper) DumpAll(ctx context.Context, dir string) (count int, err error) { n, err := e.dumpIndexTemplates(ctx, dir) if err != nil { return count, fmt.Errorf("failed to dump index templates: %w", err) } count += n n, err = e.dumpComponentTemplates(ctx, dir) if err != nil { return count, fmt.Errorf("failed to dump component templates: %w", err) } count += n n, err = e.dumpILMPolicies(ctx, dir) if err != nil { return count, fmt.Errorf("failed to dump ILM policies: %w", err) } count += n n, err = e.dumpIngestPipelines(ctx, dir) if err != nil { return count, fmt.Errorf("failed to dump ingest pipelines: %w", err) } count += n n, err = e.dumpMLModels(ctx, dir) if err != nil { return count, fmt.Errorf("failed to dumpl ML models: %w", err) } count += n return count, nil } func (e *InstalledObjectsDumper) dumpIndexTemplates(ctx context.Context, dir string) (count int, err error) { indexTemplates, err := e.getIndexTemplates(ctx) if err != nil { return count, err } dir = filepath.Join(dir, IndexTemplatesDumpDir) for i, t := range indexTemplates { err := dumpJSONResource(dir, t) if err != nil { return i, fmt.Errorf("failed to dump index template %s: %w", t.Name(), err) } } return len(indexTemplates), nil } func (e *InstalledObjectsDumper) getIndexTemplates(ctx context.Context) ([]IndexTemplate, error) { if len(e.indexTemplates) == 0 { indexTemplates, err := getIndexTemplatesForPackage(ctx, e.client, e.packageName) if err != nil { return nil, fmt.Errorf("failed to get index templates: %w", err) } e.indexTemplates = indexTemplates } return e.indexTemplates, nil } func (e *InstalledObjectsDumper) dumpComponentTemplates(ctx context.Context, dir string) (count int, err error) { componentTemplates, err := e.getComponentTemplates(ctx) if err != nil { return count, fmt.Errorf("failed to get component templates: %w", err) } dir = filepath.Join(dir, ComponentTemplatesDumpDir) for i, t := range componentTemplates { err := dumpJSONResource(dir, t) if err != nil { return i, fmt.Errorf("failed to dump component template %s: %w", t.Name(), err) } } return len(componentTemplates), nil } func (e *InstalledObjectsDumper) getComponentTemplates(ctx context.Context) ([]ComponentTemplate, error) { if len(e.componentTemplates) == 0 { indexTemplates, err := e.getIndexTemplates(ctx) if err != nil { return nil, err } names := getComponentTemplatesFromIndexTemplates(indexTemplates) componentTemplates, err := getComponentTemplates(ctx, e.client, names...) if err != nil { return nil, fmt.Errorf("failed to get component templates: %w", err) } e.componentTemplates = componentTemplates } return e.componentTemplates, nil } func getComponentTemplatesFromIndexTemplates(indexTemplates []IndexTemplate) []string { var templates []string for _, it := range indexTemplates { composedOf := it.IndexTemplate.ComposedOf if len(composedOf) == 0 { continue } for _, ct := range composedOf { if !slices.Contains(templates, ct) { templates = append(templates, ct) } } } return templates } func (e *InstalledObjectsDumper) dumpILMPolicies(ctx context.Context, dir string) (count int, err error) { ilmPolicies, err := e.getILMPolicies(ctx) if err != nil { return count, fmt.Errorf("failed to get index templates: %w", err) } dir = filepath.Join(dir, ILMPoliciesDumpDir) for i, t := range ilmPolicies { err := dumpJSONResource(dir, t) if err != nil { return i, fmt.Errorf("failed to dump ILM policy %s: %w", t.Name(), err) } } return len(ilmPolicies), nil } func (e *InstalledObjectsDumper) getILMPolicies(ctx context.Context) ([]ILMPolicy, error) { if len(e.ilmPolicies) == 0 { templates, err := e.getTemplatesWithSettings(ctx) if err != nil { return nil, err } names := getILMPoliciesFromTemplates(templates) ilmPolicies, err := getILMPolicies(ctx, e.client, names...) if err != nil { return nil, fmt.Errorf("failed to get ILM policies: %w", err) } e.ilmPolicies = ilmPolicies } return e.ilmPolicies, nil } func getILMPoliciesFromTemplates(templates []TemplateWithSettings) []string { var policies []string for _, template := range templates { name := template.TemplateSettings().Index.Lifecycle.Name if name != "" && !slices.Contains(policies, name) { policies = append(policies, name) } } return policies } func (e *InstalledObjectsDumper) dumpIngestPipelines(ctx context.Context, dir string) (count int, err error) { ingestPipelines, err := e.getIngestPipelines(ctx) if err != nil { return count, fmt.Errorf("failed to get ingest pipelines: %w", err) } dir = filepath.Join(dir, IngestPipelinesDumpDir) for i, t := range ingestPipelines { err := dumpJSONResource(dir, t) if err != nil { return i, fmt.Errorf("failed to dump ingest pipeline %s: %w", t.Name(), err) } } return len(ingestPipelines), nil } func (e *InstalledObjectsDumper) getIngestPipelines(ctx context.Context) ([]IngestPipeline, error) { if len(e.ingestPipelines) == 0 { templates, err := e.getTemplatesWithSettings(ctx) if err != nil { return nil, err } names := getIngestPipelinesFromTemplates(templates) ingestPipelines, err := getIngestPipelines(ctx, e.client, names...) if err != nil { return nil, fmt.Errorf("failed to get ingest pipelines from templates: %w", err) } e.ingestPipelines = ingestPipelines } return e.ingestPipelines, nil } func (e *InstalledObjectsDumper) getTemplatesWithSettings(ctx context.Context) ([]TemplateWithSettings, error) { var templates []TemplateWithSettings indexTemplates, err := e.getIndexTemplates(ctx) if err != nil { return nil, err } for _, template := range indexTemplates { templates = append(templates, template) } componentTemplates, err := e.getComponentTemplates(ctx) if err != nil { return nil, err } for _, template := range componentTemplates { templates = append(templates, template) } return templates, nil } type TemplateWithSettings interface { TemplateSettings() TemplateSettings } func getIngestPipelinesFromTemplates(templates []TemplateWithSettings) []string { var pipelines []string for _, template := range templates { settings := template.TemplateSettings() settingsPipelines := []string{ settings.Index.DefaultPipeline, settings.Index.FinalPipeline, } for _, pipeline := range settingsPipelines { if pipeline == "" { continue } if slices.Contains(pipelines, pipeline) { continue } pipelines = append(pipelines, pipeline) } } return pipelines } func (e *InstalledObjectsDumper) getMLModels(ctx context.Context) ([]MLModel, error) { if len(e.mlModels) == 0 { models, err := getMLModelsForPackage(ctx, e.client, e.packageName) if err != nil { return nil, fmt.Errorf("failed to get ML models: %w", err) } e.mlModels = models } return e.mlModels, nil } func (e *InstalledObjectsDumper) dumpMLModels(ctx context.Context, dir string) (count int, err error) { models, err := e.getMLModels(ctx) if err != nil { return count, fmt.Errorf("failed to get ML models: %w", err) } dir = filepath.Join(dir, MLModelsDumpDir) for i, m := range models { err := dumpJSONResource(dir, m) if err != nil { return i, fmt.Errorf("failed to dump model %s: %w", m.Name(), err) } } return len(models), nil }