-
Notifications
You must be signed in to change notification settings - Fork 116
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Refactoring: extracting common code * Adding skeleton for asset loading test runner * Refactoring: defining and implementing TestRunner interface * More fleshing out * More fleshing out * Adding HOWTO * Refactoring: make ingest pipeline name method of DataStreamManifest * Adding assertions * More fleshing out * Better test result reporting * Fix issue with kibana assets being ignored * Add data stream to asset * Adding entry to README * Making language consistent * Adding godoc comments * More godoc comments * Build packages before starting up stack * Adding license headers * Using nil slices * Move settings to stack package * Refactoring: merging packages API into Kibana client * Use multierror to collect all errors * Removing IsConfigRequired * Bumping up apache version to something very large * Adding comment for large version * Fixing paths * Check errors length * Fixing typo * Initialize OLDPWD * Check for existence of flag before trying to parse it * Adding comment to explain check * Setting large versions for all test packages * Removing vestigial stack settings code * Adding lens asset
- Loading branch information
1 parent
e4b08c4
commit 2e65b64
Showing
18 changed files
with
532 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# HOWTO: Writing asset loading tests for a package | ||
|
||
## Introduction | ||
|
||
Elastic Packages define assets to be loaded into Elasticsearch and Kibana. Asset loading tests exercise installing a package to ensure that its assets are loaded into Elasticsearch and Kibana as expected. | ||
|
||
## Conceptual process | ||
|
||
Conceptually, running an asset load test involves the following steps: | ||
|
||
1. Build the package. | ||
1. Deploy Elasticsearch, Kibana, and the Package Registry (all part of the Elastic Stack). This step takes time so it should typically be done once as a pre-requisite to running asset loading tests on multiple packages. | ||
1. Install the package. | ||
1. Use various Kibana and Elasticsearch APIs to assert that the package's assets were loaded into Kibana and Elasticsearch as expected. | ||
1. Remove the package. | ||
|
||
## Defining an asset loading test | ||
|
||
As a package developer, you do not need to do any work to define an asset loading test for your package. All the necessary information is already present in the package's files. | ||
|
||
## Running an asset loading test | ||
|
||
First, you must build your package. This corresponds to step 1 as described in the [_Conceptual process_](#Conceptual-process) section. | ||
|
||
Navigate to the package's root folder (or any sub-folder under it) and run the following command. | ||
|
||
``` | ||
elastic-package build | ||
``` | ||
|
||
Next, you must deploy Elasticsearch, Kibana, and the Package Registry. This corresponds to step 2 as described in the [_Conceptual process_](#Conceptual-process) section. | ||
|
||
``` | ||
elastic-package stack up -d | ||
``` | ||
|
||
For a complete listing of options available for this command, run `elastic-package stack up -h` or `elastic-package help stack up`. | ||
|
||
Next, you must set environment variables needed for further `elastic-package` commands. | ||
|
||
``` | ||
$(elastic-package stack shellinit) | ||
``` | ||
|
||
Next, you must invoke the asset loading test runner. This corresponds to steps 3 through 5 as described in the [_Conceptual process_](#Conceptual-process) section. | ||
|
||
Navigate to the package's root folder (or any sub-folder under it) and run the following command. | ||
|
||
``` | ||
elastic-package test asset | ||
``` | ||
|
||
Finally, when you are done running all asset loading tests, bring down the Elastic Stack. This corresponds to step 4 as described in the [_Conceptual process_](#Conceptual-process) section. | ||
|
||
``` | ||
elastic-package stack down | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// 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 kibana | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
|
||
"github.com/pkg/errors" | ||
|
||
"github.com/elastic/elastic-package/internal/packages" | ||
) | ||
|
||
// InstallPackage installs the given package in Fleet. | ||
func (c *Client) InstallPackage(pkg packages.PackageManifest) ([]packages.Asset, error) { | ||
path := fmt.Sprintf("%s/epm/packages/%s-%s", FleetAPI, pkg.Name, pkg.Version) | ||
statusCode, respBody, err := c.post(path, nil) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "could not install package") | ||
} | ||
|
||
return processResults("install", statusCode, respBody) | ||
} | ||
|
||
// RemovePackage removes the given package from Fleet. | ||
func (c *Client) RemovePackage(pkg packages.PackageManifest) ([]packages.Asset, error) { | ||
path := fmt.Sprintf("%s/epm/packages/%s-%s", FleetAPI, pkg.Name, pkg.Version) | ||
statusCode, respBody, err := c.delete(path) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "could not delete package") | ||
} | ||
|
||
return processResults("remove", statusCode, respBody) | ||
} | ||
|
||
func processResults(action string, statusCode int, respBody []byte) ([]packages.Asset, error) { | ||
if statusCode != 200 { | ||
return nil, fmt.Errorf("could not %s package; API status code = %d; response body = %s", action, statusCode, respBody) | ||
} | ||
|
||
var resp struct { | ||
Assets []packages.Asset `json:"response"` | ||
} | ||
|
||
if err := json.Unmarshal(respBody, &resp); err != nil { | ||
return nil, errors.Wrapf(err, "could not convert %s package (response) to JSON", action) | ||
} | ||
|
||
return resp.Assets, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
// 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 packages | ||
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
|
||
"github.com/elastic/elastic-package/internal/multierror" | ||
) | ||
|
||
// AssetType represents the type of package asset. | ||
type AssetType string | ||
|
||
// Supported asset types. | ||
const ( | ||
AssetTypeElasticsearchIndexTemplate AssetType = "index_template" | ||
AssetTypeElasticsearchIngestPipeline AssetType = "ingest_pipeline" | ||
|
||
AssetTypeKibanaSavedSearch AssetType = "search" | ||
AssetTypeKibanaVisualization AssetType = "visualization" | ||
AssetTypeKibanaDashboard AssetType = "dashboard" | ||
AssetTypeKibanaMap AssetType = "map" | ||
AssetTypeKibanaLens AssetType = "lens" | ||
) | ||
|
||
// Asset represents a package asset to be loaded into Kibana or Elasticsearch. | ||
type Asset struct { | ||
ID string `json:"id"` | ||
Type AssetType `json:"type"` | ||
DataStream string | ||
} | ||
|
||
// LoadPackageAssets parses the package contents and returns a list of assets defined by the package. | ||
func LoadPackageAssets(pkgRootPath string) ([]Asset, error) { | ||
assets, err := loadKibanaAssets(pkgRootPath) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "could not load kibana assets") | ||
} | ||
|
||
a, err := loadElasticsearchAssets(pkgRootPath) | ||
if err != nil { | ||
return a, errors.Wrap(err, "could not load elasticsearch assets") | ||
} | ||
assets = append(assets, a...) | ||
|
||
return assets, nil | ||
} | ||
|
||
func loadKibanaAssets(pkgRootPath string) ([]Asset, error) { | ||
kibanaAssetsFolderPath := filepath.Join(pkgRootPath, "kibana") | ||
|
||
var ( | ||
errs multierror.Error | ||
|
||
assetTypes = []AssetType{ | ||
AssetTypeKibanaDashboard, | ||
AssetTypeKibanaVisualization, | ||
AssetTypeKibanaSavedSearch, | ||
AssetTypeKibanaMap, | ||
AssetTypeKibanaLens, | ||
} | ||
|
||
assets []Asset | ||
) | ||
|
||
for _, assetType := range assetTypes { | ||
a, err := loadFileBasedAssets(kibanaAssetsFolderPath, assetType) | ||
if err != nil { | ||
errs = append(errs, errors.Wrapf(err, "could not load kibana %s assets", assetType)) | ||
continue | ||
} | ||
|
||
assets = append(assets, a...) | ||
} | ||
|
||
if len(errs) > 0 { | ||
return nil, errs | ||
} | ||
|
||
return assets, nil | ||
} | ||
|
||
func loadElasticsearchAssets(pkgRootPath string) ([]Asset, error) { | ||
packageManifestPath := filepath.Join(pkgRootPath, PackageManifestFile) | ||
pkgManifest, err := ReadPackageManifest(packageManifestPath) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "reading package manifest file failed") | ||
} | ||
|
||
dataStreamManifestPaths, err := filepath.Glob(filepath.Join(pkgRootPath, "data_stream", "*", DataStreamManifestFile)) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "could not read data stream manifest file paths") | ||
} | ||
|
||
var assets []Asset | ||
for _, dsManifestPath := range dataStreamManifestPaths { | ||
dsManifest, err := ReadDataStreamManifest(dsManifestPath) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "reading data stream manifest failed") | ||
} | ||
|
||
indexTemplateName := fmt.Sprintf("%s-%s.%s", dsManifest.Type, pkgManifest.Name, dsManifest.Name) | ||
asset := Asset{ | ||
ID: indexTemplateName, | ||
Type: AssetTypeElasticsearchIndexTemplate, | ||
DataStream: dsManifest.Name, | ||
} | ||
assets = append(assets, asset) | ||
|
||
if dsManifest.Type == dataStreamTypeLogs { | ||
ingestPipelineName := dsManifest.GetPipelineNameOrDefault() | ||
if ingestPipelineName == defaultPipelineName { | ||
ingestPipelineName = fmt.Sprintf("%s-%s.%s-%s", dsManifest.Type, pkgManifest.Name, dsManifest.Name, pkgManifest.Version) | ||
} | ||
asset = Asset{ | ||
ID: ingestPipelineName, | ||
Type: AssetTypeElasticsearchIngestPipeline, | ||
DataStream: dsManifest.Name, | ||
} | ||
assets = append(assets, asset) | ||
} | ||
} | ||
|
||
return assets, nil | ||
} | ||
|
||
func loadFileBasedAssets(kibanaAssetsFolderPath string, assetType AssetType) ([]Asset, error) { | ||
assetsFolderPath := filepath.Join(kibanaAssetsFolderPath, string(assetType)) | ||
_, err := os.Stat(assetsFolderPath) | ||
if err != nil && os.IsNotExist(err) { | ||
// No assets folder defined; nothing to load | ||
return nil, nil | ||
} | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "error finding kibana %s assets folder", assetType) | ||
} | ||
|
||
files, err := ioutil.ReadDir(assetsFolderPath) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "could not read %s files", assetType) | ||
} | ||
|
||
var assets []Asset | ||
for _, f := range files { | ||
if f.IsDir() { | ||
continue | ||
} | ||
|
||
name := f.Name() | ||
id := strings.TrimSuffix(name, ".json") | ||
|
||
asset := Asset{ | ||
ID: id, | ||
Type: assetType, | ||
} | ||
assets = append(assets, asset) | ||
} | ||
|
||
return assets, nil | ||
} |
Oops, something went wrong.