internal/export/dashboards.go (92 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 export
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"github.com/Masterminds/semver/v3"
"github.com/elastic/elastic-package/internal/common"
"github.com/elastic/elastic-package/internal/kibana"
"github.com/elastic/elastic-package/internal/logger"
"github.com/elastic/elastic-package/internal/packages"
)
// Dashboards method exports selected dashboards with references objects. All Kibana objects are saved to local files
// in appropriate directories.
func Dashboards(ctx context.Context, kibanaClient *kibana.Client, dashboardsIDs []string) error {
packageRoot, err := packages.MustFindPackageRoot()
if err != nil {
return fmt.Errorf("locating package root failed: %w", err)
}
logger.Debugf("Package root found: %s", packageRoot)
m, err := packages.ReadPackageManifestFromPackageRoot(packageRoot)
if err != nil {
return fmt.Errorf("reading package manifest failed (path: %s): %w", packageRoot, err)
}
versionInfo, err := kibanaClient.Version()
if err != nil {
return fmt.Errorf("getting Kibana version information: %w", err)
}
if err := checkKibanaVersion(versionInfo); err != nil {
return fmt.Errorf("cannot import from this Kibana version: %w", err)
}
objects, err := kibanaClient.Export(ctx, dashboardsIDs)
if err != nil {
return fmt.Errorf("exporting dashboards using Kibana client failed: %w", err)
}
transformContext := &transformationContext{
packageName: m.Name,
}
objects, err = applyTransformations(transformContext, objects)
if err != nil {
return fmt.Errorf("can't transform Kibana objects: %w", err)
}
err = saveObjectsToFiles(packageRoot, objects)
if err != nil {
return fmt.Errorf("can't save Kibana objects: %w", err)
}
return nil
}
func checkKibanaVersion(info kibana.VersionInfo) error {
version, err := semver.NewVersion(info.Number)
if err != nil {
return fmt.Errorf("cannot parse version %s: %w", info.Number, err)
}
minVersion := semver.MustParse("8.8.0")
maxVersion := semver.MustParse("8.10.0")
if version.LessThan(maxVersion) && !version.LessThan(minVersion) {
// See:
// - https://github.com/elastic/elastic-package/issues/1354
// - https://github.com/elastic/kibana/pull/161969
return fmt.Errorf("packages with dashboards exported since Kibana %s may not be installed till %s, please export the dashboard/s from a different version", minVersion, maxVersion)
}
return nil
}
func applyTransformations(ctx *transformationContext, objects []common.MapStr) ([]common.MapStr, error) {
return newObjectTransformer().
withContext(ctx).
withTransforms(filterUnsupportedTypes,
decodeObject,
stripObjectProperties,
standardizeObjectProperties,
removeFleetManagedTags,
standardizeObjectID).
transform(objects)
}
func saveObjectsToFiles(packageRoot string, objects []common.MapStr) error {
logger.Debug("Save Kibana objects to files")
for _, object := range objects {
id, _ := object.GetValue("id")
aType, _ := object.GetValue("type")
// Marshal object to byte content
b, err := json.MarshalIndent(&object, "", " ")
if err != nil {
return fmt.Errorf("marshalling Kibana object failed (ID: %s): %w", id.(string), err)
}
// Create target directory
targetDir := filepath.Join(packageRoot, "kibana", aType.(string))
err = os.MkdirAll(targetDir, 0755)
if err != nil {
return fmt.Errorf("creating target directory failed (path: %s): %w", targetDir, err)
}
// Save object to file
objectPath := filepath.Join(targetDir, id.(string)+".json")
err = os.WriteFile(objectPath, b, 0644)
if err != nil {
return fmt.Errorf("writing to file failed: %w", err)
}
}
return nil
}