Skip to content

Commit 668609f

Browse files
jsorianomrodm
andauthored
Add contract testing for policies (#1847)
Add a new policy test runner that can be used to test different sets of configuration values. With this test runner, a set of package or data stream variables can be used to create a policy, and the policy can be compared with a pre-generated one. Examples are provided for the sql_input and apache test packages. --------- Co-authored-by: Mario Rodriguez Molins <marrodmo@gmail.com>
1 parent 2c23d41 commit 668609f

File tree

23 files changed

+1176
-3
lines changed

23 files changed

+1176
-3
lines changed

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,12 @@ For details on how to run static tests for a package, see the [HOWTO guide](http
568568
#### System Tests
569569
These tests allow you to test a package's ability to ingest data end-to-end.
570570

571-
For details on how to configure amd run system tests, review the [HOWTO guide](https://github.com/elastic/elastic-package/blob/main/docs/howto/system_testing.md).
571+
For details on how to configure and run system tests, review the [HOWTO guide](https://github.com/elastic/elastic-package/blob/main/docs/howto/system_testing.md).
572+
573+
#### Policy Tests
574+
These tests allow you to test different configuration options and the policies they generate, without needing to run a full scenario.
575+
576+
For details on how to configure and run policy tests, review the [HOWTO guide](https://github.com/elastic/elastic-package/blob/main/docs/howto/policy_testing.md).
572577

573578
### `elastic-package test asset`
574579

@@ -582,6 +587,12 @@ _Context: package_
582587

583588
Run pipeline tests for the package.
584589

590+
### `elastic-package test policy`
591+
592+
_Context: package_
593+
594+
Run policy tests for the package.
595+
585596
### `elastic-package test static`
586597

587598
_Context: package_

cmd/testrunner.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,12 @@ For details on how to run static tests for a package, see the [HOWTO guide](http
5050
#### System Tests
5151
These tests allow you to test a package's ability to ingest data end-to-end.
5252
53-
For details on how to configure amd run system tests, review the [HOWTO guide](https://github.com/elastic/elastic-package/blob/main/docs/howto/system_testing.md).`
53+
For details on how to configure and run system tests, review the [HOWTO guide](https://github.com/elastic/elastic-package/blob/main/docs/howto/system_testing.md).
54+
55+
#### Policy Tests
56+
These tests allow you to test different configuration options and the policies they generate, without needing to run a full scenario.
57+
58+
For details on how to configure and run policy tests, review the [HOWTO guide](https://github.com/elastic/elastic-package/blob/main/docs/howto/policy_testing.md).`
5459

5560
func setupTestCommand() *cobraext.Command {
5661
var testTypeCmdActions []cobraext.CommandAction
@@ -314,7 +319,7 @@ func testTypeCommandActionFactory(runner testrunner.TestRunner) cobraext.Command
314319
}
315320

316321
var kibanaClient *kibana.Client
317-
if testType == "system" || testType == "asset" {
322+
if testType == "system" || testType == "asset" || testType == "policy" {
318323
// pipeline and static tests do not require a kibana client to perform their required operations
319324
kibanaClient, err = stack.NewKibanaClientFromProfile(profile)
320325
if err != nil {

docs/howto/policy_testing.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# HOWTO: Writing policy tests for a package
2+
3+
## Introduction
4+
Elastic Packages support a variety of configuration variables as defined in
5+
their manifest files. Policy tests allow to check that specific sets of
6+
variables are accepted by Fleet, and they produce an expected agent policy.
7+
8+
## Defining policy tests
9+
10+
Policy tests can be defined in data streams in integration packages, or at the
11+
package level in input packages.
12+
13+
Each test is composed of two files, one for the configuration of the policy, and
14+
another one for the expected result.
15+
16+
When defining the tests at the data stream level, they must be defined following
17+
this structure.
18+
```
19+
<package root>/
20+
data_stream/
21+
<data stream>/
22+
_dev/
23+
test/
24+
policy/
25+
test-<test name>.yml
26+
test-<test name>.expected
27+
```
28+
29+
When defining the tests at the package level, in input packages, they must be
30+
defined following this structure:
31+
```
32+
<package root>/
33+
_dev/
34+
test/
35+
policy/
36+
test-<test name>.yml
37+
test-<test name>.expected
38+
```
39+
40+
It is possible, and encouraged, to define multiple policy tests for each package
41+
or data stream.
42+
43+
44+
### Defining the configuration of the policy
45+
46+
Test configuration for the policy is defined in a YAML file prefixed with
47+
`test-`.
48+
49+
In these configuration files it is possible to define:
50+
- Values for the variables of the package manifest.
51+
- Values for the variables of the data stream manifests (only used for
52+
integration packages).
53+
- Input to use, for packages supporting multiple input types.
54+
55+
For example, the following configuration tells Fleet to create a policy using an
56+
specific input, and some variables at the package level:
57+
```
58+
input: httpjson
59+
vars:
60+
url: http://localhost:1234/api/v1/logs
61+
username: test
62+
password: test
63+
```
64+
65+
The following configuration would set a value for a variable defined at the data
66+
stream level:
67+
```
68+
data_stream:
69+
vars:
70+
paths:
71+
- "/var/logs/apache/access.log*"
72+
```
73+
74+
This configuration may look familiar to you if you are used to define system
75+
tests. The main difference is that policy tests are not going to be executed, so
76+
anything can be configured there, without expectations on having running
77+
services or reachable services. Also, no placeholders are expected to be found
78+
in policy tests.
79+
80+
81+
### Defining the expected policy
82+
83+
Once you have decided the policy settings you would like to test, you should
84+
define the expected resulting policy. In principle it is possible to define it
85+
manually given that most of the information is included in the package, but it
86+
can be quite cumbersome. `elastic-package` is able to generate this file for
87+
you, using the `--generate` flag.
88+
89+
If you run the policy tests with the `--generate` flag, `elastic-package` will
90+
write the found policy in the expected place.
91+
```
92+
$ elastic-package test policy --generate
93+
```
94+
95+
Then check that the generated content is what you would expect to have.
96+
97+
98+
## Running policy tests
99+
100+
You can run policy tests with the `elastic-package test` command. If a package
101+
includes policy tests, they will be executed if no test type is specified. You
102+
can also run the policy tests only indicating its type:
103+
```
104+
$ elastic-package test policy
105+
```
106+
107+
With integration packages, you can run the policy tests for a single data
108+
stream, for example:
109+
```
110+
$ elastic-package test policy --data-streams access
111+
```
112+
113+
Results are displayed using the usual format options. When the test fail,
114+
`elastic-package` shows the differences between the expected and found policy.

internal/kibana/policies.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ type Policy struct {
2525
DataOutputID string `json:"data_output_id,omitempty"`
2626
}
2727

28+
// DownloadedPolicy represents a policy as returned by the download policy API.
29+
type DownloadedPolicy json.RawMessage
30+
2831
// CreatePolicy persists the given Policy in Fleet.
2932
func (c *Client) CreatePolicy(ctx context.Context, p Policy) (*Policy, error) {
3033
reqBody, err := json.Marshal(p)
@@ -84,6 +87,22 @@ func (c *Client) GetPolicy(ctx context.Context, policyID string) (*Policy, error
8487
return &resp.Item, nil
8588
}
8689

90+
// DownloadPolicy fetches the agent Policy as would be downloaded by an agent.
91+
func (c *Client) DownloadPolicy(ctx context.Context, policyID string) (DownloadedPolicy, error) {
92+
statusCode, respBody, err := c.get(ctx, fmt.Sprintf("%s/agent_policies/%s/download", FleetAPI, policyID))
93+
if err != nil {
94+
return nil, fmt.Errorf("could not get policy: %w", err)
95+
}
96+
if statusCode == http.StatusNotFound {
97+
return nil, &ErrPolicyNotFound{id: policyID}
98+
}
99+
if statusCode != http.StatusOK {
100+
return nil, fmt.Errorf("could not get policy; API status code = %d; response body = %s", statusCode, respBody)
101+
}
102+
103+
return respBody, nil
104+
}
105+
87106
// GetRawPolicy fetches the given Policy with all the fields in Fleet.
88107
func (c *Client) GetRawPolicy(ctx context.Context, policyID string) (json.RawMessage, error) {
89108
statusCode, respBody, err := c.get(ctx, fmt.Sprintf("%s/agent_policies/%s", FleetAPI, policyID))

0 commit comments

Comments
 (0)