mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
[asset_manager] Add /assets/diff endpoint (#153730)
This PR adds a `/diff` endpoint that given two time ranges will return
which assets exist only in either time range and which assets exist in
both time ranges.
### How to test
Start up a local ES and Kibana instance and run these commands to setup
the test data:
```curl
curl -X POST http://localhost:5601/ftw/api/asset-manager/assets/sample \
-u 'elastic:changeme' \
-H 'kbn-xsrf: xxx' \
-H 'Content-Type: application/json' \
-d '{"baseDateTime":"2022-02-07T00:00:00.000Z", "excludeEans": ["k8s.pod:pod-200wwc3","k8s.pod:pod-200naq4","k8s.pod:pod-200ohr5","k8s.pod:pod-200yyx6","k8s.pod:pod-200psd7","k8s.pod:pod-200wmc8","k8s.pod:pod-200ugg9"]}'
```
```curl
curl -X POST http://localhost:5601/ftw/api/asset-manager/assets/sample \
-u 'elastic:changeme' \
-H 'kbn-xsrf: xxx' \
-H 'Content-Type: application/json' \
-d '{"baseDateTime":"2022-02-07T01:30:00.000Z", "excludeEans": ["k8s.pod:pod-200wwc3","k8s.pod:pod-200naq4", "k8s.pod:pod-200xrg1","k8s.pod:pod-200dfp2"]}'
```
```curl
curl -X POST http://localhost:5601/ftw/api/asset-manager/assets/sample \
-u 'elastic:changeme' \
-H 'kbn-xsrf: xxx' \
-H 'Content-Type: application/json' \
-d '{"baseDateTime":"2022-02-07T03:00:00.000Z", "excludeEans": ["k8s.cluster:cluster-001","k8s.cluster:cluster-002","k8s.node:node-101","k8s.node:node-102","k8s.node:node-103","k8s.pod:pod-200xrg1","k8s.pod:pod-200dfp2"]}'
```
From there you can test based on the requests described in the
[documentation](063b730c7a/x-pack/plugins/asset_manager/docs/index.md (get-assetsdiff)
).
Closes #153489
This commit is contained in:
parent
b2812f3278
commit
49d8e0e8ff
3 changed files with 841 additions and 0 deletions
|
@ -406,6 +406,638 @@ GET /assets?from=2023-03-25T17:44:44.000Z&to=2023-03-25T18:44:44.000Z&ean=k8s.no
|
|||
|
||||
<a name="sample-data" id="sample-data"></a>
|
||||
|
||||
### GET /assets/diff
|
||||
|
||||
Returns assets found in the two time ranges, split by what occurs in only either or in both.
|
||||
|
||||
#### Request
|
||||
|
||||
| Option | Type | Required? | Default | Description |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| aFrom | RangeDate | Yes | N/A | Starting point for baseline date range to search for assets within |
|
||||
| aTo | RangeDate | Yes | N/A | End point for baseline date range to search for assets within |
|
||||
| bFrom | RangeDate | Yes | N/A | Starting point for comparison date range |
|
||||
| bTo | RangeDate | Yes | N/A | End point for comparison date range |
|
||||
| type | AssetType[] | No | all | Restrict results to one or more asset.type value |
|
||||
|
||||
#### Responses
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Request where comparison range is missing assets that are found in the baseline range</summary>
|
||||
|
||||
```curl
|
||||
GET /assets/diff?aFrom=2022-02-07T00:00:00.000Z&aTo=2022-02-07T01:30:00.000Z&bFrom=2022-02-07T01:00:00.000Z&bTo=2022-02-07T02:00:00.000Z
|
||||
|
||||
{
|
||||
"onlyInA": [
|
||||
{
|
||||
"@timestamp": "2022-02-07T00:00:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200xrg1",
|
||||
"asset.name": "k8s-pod-200xrg1-aws",
|
||||
"asset.ean": "k8s.pod:pod-200xrg1",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-101"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T00:00:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200dfp2",
|
||||
"asset.name": "k8s-pod-200dfp2-aws",
|
||||
"asset.ean": "k8s.pod:pod-200dfp2",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-101"
|
||||
]
|
||||
}
|
||||
],
|
||||
"onlyInB": [],
|
||||
"inBoth": [
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.cluster",
|
||||
"asset.id": "cluster-001",
|
||||
"asset.name": "Cluster 001 (AWS EKS)",
|
||||
"asset.ean": "k8s.cluster:cluster-001",
|
||||
"orchestrator.type": "kubernetes",
|
||||
"orchestrator.cluster.name": "Cluster 001 (AWS EKS)",
|
||||
"orchestrator.cluster.id": "cluster-001",
|
||||
"cloud.provider": "aws",
|
||||
"cloud.region": "us-east-1",
|
||||
"cloud.service.name": "eks"
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.cluster",
|
||||
"asset.id": "cluster-002",
|
||||
"asset.name": "Cluster 002 (Azure AKS)",
|
||||
"asset.ean": "k8s.cluster:cluster-002",
|
||||
"orchestrator.type": "kubernetes",
|
||||
"orchestrator.cluster.name": "Cluster 002 (Azure AKS)",
|
||||
"orchestrator.cluster.id": "cluster-002",
|
||||
"cloud.provider": "azure",
|
||||
"cloud.region": "eu-west",
|
||||
"cloud.service.name": "aks"
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.node",
|
||||
"asset.id": "node-101",
|
||||
"asset.name": "k8s-node-101-aws",
|
||||
"asset.ean": "k8s.node:node-101",
|
||||
"asset.parents": [
|
||||
"k8s.cluster:cluster-001"
|
||||
],
|
||||
"orchestrator.type": "kubernetes",
|
||||
"orchestrator.cluster.name": "Cluster 001 (AWS EKS)",
|
||||
"orchestrator.cluster.id": "cluster-001",
|
||||
"cloud.provider": "aws",
|
||||
"cloud.region": "us-east-1",
|
||||
"cloud.service.name": "eks"
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.node",
|
||||
"asset.id": "node-102",
|
||||
"asset.name": "k8s-node-102-aws",
|
||||
"asset.ean": "k8s.node:node-102",
|
||||
"asset.parents": [
|
||||
"k8s.cluster:cluster-001"
|
||||
],
|
||||
"orchestrator.type": "kubernetes",
|
||||
"orchestrator.cluster.name": "Cluster 001 (AWS EKS)",
|
||||
"orchestrator.cluster.id": "cluster-001",
|
||||
"cloud.provider": "aws",
|
||||
"cloud.region": "us-east-1",
|
||||
"cloud.service.name": "eks"
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.node",
|
||||
"asset.id": "node-103",
|
||||
"asset.name": "k8s-node-103-aws",
|
||||
"asset.ean": "k8s.node:node-103",
|
||||
"asset.parents": [
|
||||
"k8s.cluster:cluster-001"
|
||||
],
|
||||
"orchestrator.type": "kubernetes",
|
||||
"orchestrator.cluster.name": "Cluster 001 (AWS EKS)",
|
||||
"orchestrator.cluster.id": "cluster-001",
|
||||
"cloud.provider": "aws",
|
||||
"cloud.region": "us-east-1",
|
||||
"cloud.service.name": "eks"
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200ohr5",
|
||||
"asset.name": "k8s-pod-200ohr5-aws",
|
||||
"asset.ean": "k8s.pod:pod-200ohr5",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-102"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200yyx6",
|
||||
"asset.name": "k8s-pod-200yyx6-aws",
|
||||
"asset.ean": "k8s.pod:pod-200yyx6",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-103"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200psd7",
|
||||
"asset.name": "k8s-pod-200psd7-aws",
|
||||
"asset.ean": "k8s.pod:pod-200psd7",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-103"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200wmc8",
|
||||
"asset.name": "k8s-pod-200wmc8-aws",
|
||||
"asset.ean": "k8s.pod:pod-200wmc8",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-103"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200ugg9",
|
||||
"asset.name": "k8s-pod-200ugg9-aws",
|
||||
"asset.ean": "k8s.pod:pod-200ugg9",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-103"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Request where baseline range is missing assets that are found in the comparison range</summary>
|
||||
|
||||
```curl
|
||||
GET /assets/diff?aFrom=2022-02-07T01:00:00.000Z&aTo=2022-02-07T01:30:00.000Z&bFrom=2022-02-07T01:00:00.000Z&bTo=2022-02-07T03:00:00.000Z
|
||||
|
||||
{
|
||||
"onlyInA": [],
|
||||
"onlyInB": [
|
||||
{
|
||||
"@timestamp": "2022-02-07T03:00:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200wwc3",
|
||||
"asset.name": "k8s-pod-200wwc3-aws",
|
||||
"asset.ean": "k8s.pod:pod-200wwc3",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-101"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T03:00:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200naq4",
|
||||
"asset.name": "k8s-pod-200naq4-aws",
|
||||
"asset.ean": "k8s.pod:pod-200naq4",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-102"
|
||||
]
|
||||
}
|
||||
],
|
||||
"inBoth": [
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.cluster",
|
||||
"asset.id": "cluster-001",
|
||||
"asset.name": "Cluster 001 (AWS EKS)",
|
||||
"asset.ean": "k8s.cluster:cluster-001",
|
||||
"orchestrator.type": "kubernetes",
|
||||
"orchestrator.cluster.name": "Cluster 001 (AWS EKS)",
|
||||
"orchestrator.cluster.id": "cluster-001",
|
||||
"cloud.provider": "aws",
|
||||
"cloud.region": "us-east-1",
|
||||
"cloud.service.name": "eks"
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.cluster",
|
||||
"asset.id": "cluster-002",
|
||||
"asset.name": "Cluster 002 (Azure AKS)",
|
||||
"asset.ean": "k8s.cluster:cluster-002",
|
||||
"orchestrator.type": "kubernetes",
|
||||
"orchestrator.cluster.name": "Cluster 002 (Azure AKS)",
|
||||
"orchestrator.cluster.id": "cluster-002",
|
||||
"cloud.provider": "azure",
|
||||
"cloud.region": "eu-west",
|
||||
"cloud.service.name": "aks"
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.node",
|
||||
"asset.id": "node-101",
|
||||
"asset.name": "k8s-node-101-aws",
|
||||
"asset.ean": "k8s.node:node-101",
|
||||
"asset.parents": [
|
||||
"k8s.cluster:cluster-001"
|
||||
],
|
||||
"orchestrator.type": "kubernetes",
|
||||
"orchestrator.cluster.name": "Cluster 001 (AWS EKS)",
|
||||
"orchestrator.cluster.id": "cluster-001",
|
||||
"cloud.provider": "aws",
|
||||
"cloud.region": "us-east-1",
|
||||
"cloud.service.name": "eks"
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.node",
|
||||
"asset.id": "node-102",
|
||||
"asset.name": "k8s-node-102-aws",
|
||||
"asset.ean": "k8s.node:node-102",
|
||||
"asset.parents": [
|
||||
"k8s.cluster:cluster-001"
|
||||
],
|
||||
"orchestrator.type": "kubernetes",
|
||||
"orchestrator.cluster.name": "Cluster 001 (AWS EKS)",
|
||||
"orchestrator.cluster.id": "cluster-001",
|
||||
"cloud.provider": "aws",
|
||||
"cloud.region": "us-east-1",
|
||||
"cloud.service.name": "eks"
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.node",
|
||||
"asset.id": "node-103",
|
||||
"asset.name": "k8s-node-103-aws",
|
||||
"asset.ean": "k8s.node:node-103",
|
||||
"asset.parents": [
|
||||
"k8s.cluster:cluster-001"
|
||||
],
|
||||
"orchestrator.type": "kubernetes",
|
||||
"orchestrator.cluster.name": "Cluster 001 (AWS EKS)",
|
||||
"orchestrator.cluster.id": "cluster-001",
|
||||
"cloud.provider": "aws",
|
||||
"cloud.region": "us-east-1",
|
||||
"cloud.service.name": "eks"
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200ohr5",
|
||||
"asset.name": "k8s-pod-200ohr5-aws",
|
||||
"asset.ean": "k8s.pod:pod-200ohr5",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-102"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200yyx6",
|
||||
"asset.name": "k8s-pod-200yyx6-aws",
|
||||
"asset.ean": "k8s.pod:pod-200yyx6",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-103"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200psd7",
|
||||
"asset.name": "k8s-pod-200psd7-aws",
|
||||
"asset.ean": "k8s.pod:pod-200psd7",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-103"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200wmc8",
|
||||
"asset.name": "k8s-pod-200wmc8-aws",
|
||||
"asset.ean": "k8s.pod:pod-200wmc8",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-103"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200ugg9",
|
||||
"asset.name": "k8s-pod-200ugg9-aws",
|
||||
"asset.ean": "k8s.pod:pod-200ugg9",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-103"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Request where each range is missing assets found in the other range</summary>
|
||||
|
||||
```curl
|
||||
GET /assets/diff?aFrom=2022-02-07T00:00:00.000Z&aTo=2022-02-07T01:30:00.000Z&bFrom=2022-02-07T01:00:00.000Z&bTo=2022-02-07T03:00:00.000Z
|
||||
|
||||
{
|
||||
"onlyInA": [
|
||||
{
|
||||
"@timestamp": "2022-02-07T00:00:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200xrg1",
|
||||
"asset.name": "k8s-pod-200xrg1-aws",
|
||||
"asset.ean": "k8s.pod:pod-200xrg1",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-101"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T00:00:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200dfp2",
|
||||
"asset.name": "k8s-pod-200dfp2-aws",
|
||||
"asset.ean": "k8s.pod:pod-200dfp2",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-101"
|
||||
]
|
||||
}
|
||||
],
|
||||
"onlyInB": [
|
||||
{
|
||||
"@timestamp": "2022-02-07T03:00:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200wwc3",
|
||||
"asset.name": "k8s-pod-200wwc3-aws",
|
||||
"asset.ean": "k8s.pod:pod-200wwc3",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-101"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T03:00:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200naq4",
|
||||
"asset.name": "k8s-pod-200naq4-aws",
|
||||
"asset.ean": "k8s.pod:pod-200naq4",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-102"
|
||||
]
|
||||
}
|
||||
],
|
||||
"inBoth": [
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.cluster",
|
||||
"asset.id": "cluster-001",
|
||||
"asset.name": "Cluster 001 (AWS EKS)",
|
||||
"asset.ean": "k8s.cluster:cluster-001",
|
||||
"orchestrator.type": "kubernetes",
|
||||
"orchestrator.cluster.name": "Cluster 001 (AWS EKS)",
|
||||
"orchestrator.cluster.id": "cluster-001",
|
||||
"cloud.provider": "aws",
|
||||
"cloud.region": "us-east-1",
|
||||
"cloud.service.name": "eks"
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.cluster",
|
||||
"asset.id": "cluster-002",
|
||||
"asset.name": "Cluster 002 (Azure AKS)",
|
||||
"asset.ean": "k8s.cluster:cluster-002",
|
||||
"orchestrator.type": "kubernetes",
|
||||
"orchestrator.cluster.name": "Cluster 002 (Azure AKS)",
|
||||
"orchestrator.cluster.id": "cluster-002",
|
||||
"cloud.provider": "azure",
|
||||
"cloud.region": "eu-west",
|
||||
"cloud.service.name": "aks"
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.node",
|
||||
"asset.id": "node-101",
|
||||
"asset.name": "k8s-node-101-aws",
|
||||
"asset.ean": "k8s.node:node-101",
|
||||
"asset.parents": [
|
||||
"k8s.cluster:cluster-001"
|
||||
],
|
||||
"orchestrator.type": "kubernetes",
|
||||
"orchestrator.cluster.name": "Cluster 001 (AWS EKS)",
|
||||
"orchestrator.cluster.id": "cluster-001",
|
||||
"cloud.provider": "aws",
|
||||
"cloud.region": "us-east-1",
|
||||
"cloud.service.name": "eks"
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.node",
|
||||
"asset.id": "node-102",
|
||||
"asset.name": "k8s-node-102-aws",
|
||||
"asset.ean": "k8s.node:node-102",
|
||||
"asset.parents": [
|
||||
"k8s.cluster:cluster-001"
|
||||
],
|
||||
"orchestrator.type": "kubernetes",
|
||||
"orchestrator.cluster.name": "Cluster 001 (AWS EKS)",
|
||||
"orchestrator.cluster.id": "cluster-001",
|
||||
"cloud.provider": "aws",
|
||||
"cloud.region": "us-east-1",
|
||||
"cloud.service.name": "eks"
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.node",
|
||||
"asset.id": "node-103",
|
||||
"asset.name": "k8s-node-103-aws",
|
||||
"asset.ean": "k8s.node:node-103",
|
||||
"asset.parents": [
|
||||
"k8s.cluster:cluster-001"
|
||||
],
|
||||
"orchestrator.type": "kubernetes",
|
||||
"orchestrator.cluster.name": "Cluster 001 (AWS EKS)",
|
||||
"orchestrator.cluster.id": "cluster-001",
|
||||
"cloud.provider": "aws",
|
||||
"cloud.region": "us-east-1",
|
||||
"cloud.service.name": "eks"
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200ohr5",
|
||||
"asset.name": "k8s-pod-200ohr5-aws",
|
||||
"asset.ean": "k8s.pod:pod-200ohr5",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-102"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200yyx6",
|
||||
"asset.name": "k8s-pod-200yyx6-aws",
|
||||
"asset.ean": "k8s.pod:pod-200yyx6",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-103"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200psd7",
|
||||
"asset.name": "k8s-pod-200psd7-aws",
|
||||
"asset.ean": "k8s.pod:pod-200psd7",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-103"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200wmc8",
|
||||
"asset.name": "k8s-pod-200wmc8-aws",
|
||||
"asset.ean": "k8s.pod:pod-200wmc8",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-103"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200ugg9",
|
||||
"asset.name": "k8s-pod-200ugg9-aws",
|
||||
"asset.ean": "k8s.pod:pod-200ugg9",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-103"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Request where each range is missing assets found in the other range, but restricted by type</summary>
|
||||
|
||||
```curl
|
||||
GET /assets/diff?aFrom=2022-02-07T00:00:00.000Z&aTo=2022-02-07T01:30:00.000Z&bFrom=2022-02-07T01:00:00.000Z&bTo=2022-02-07T03:00:00.000Z&type=k8s.pod
|
||||
|
||||
{
|
||||
"onlyInA": [
|
||||
{
|
||||
"@timestamp": "2022-02-07T00:00:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200xrg1",
|
||||
"asset.name": "k8s-pod-200xrg1-aws",
|
||||
"asset.ean": "k8s.pod:pod-200xrg1",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-101"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T00:00:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200dfp2",
|
||||
"asset.name": "k8s-pod-200dfp2-aws",
|
||||
"asset.ean": "k8s.pod:pod-200dfp2",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-101"
|
||||
]
|
||||
}
|
||||
],
|
||||
"onlyInB": [
|
||||
{
|
||||
"@timestamp": "2022-02-07T03:00:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200wwc3",
|
||||
"asset.name": "k8s-pod-200wwc3-aws",
|
||||
"asset.ean": "k8s.pod:pod-200wwc3",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-101"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T03:00:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200naq4",
|
||||
"asset.name": "k8s-pod-200naq4-aws",
|
||||
"asset.ean": "k8s.pod:pod-200naq4",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-102"
|
||||
]
|
||||
}
|
||||
],
|
||||
"inBoth": [
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200ohr5",
|
||||
"asset.name": "k8s-pod-200ohr5-aws",
|
||||
"asset.ean": "k8s.pod:pod-200ohr5",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-102"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200yyx6",
|
||||
"asset.name": "k8s-pod-200yyx6-aws",
|
||||
"asset.ean": "k8s.pod:pod-200yyx6",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-103"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200psd7",
|
||||
"asset.name": "k8s-pod-200psd7-aws",
|
||||
"asset.ean": "k8s.pod:pod-200psd7",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-103"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200wmc8",
|
||||
"asset.name": "k8s-pod-200wmc8-aws",
|
||||
"asset.ean": "k8s.pod:pod-200wmc8",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-103"
|
||||
]
|
||||
},
|
||||
{
|
||||
"@timestamp": "2022-02-07T01:30:00.000Z",
|
||||
"asset.type": "k8s.pod",
|
||||
"asset.id": "pod-200ugg9",
|
||||
"asset.name": "k8s-pod-200ugg9-aws",
|
||||
"asset.ean": "k8s.pod:pod-200ugg9",
|
||||
"asset.parents": [
|
||||
"k8s.node:node-103"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### GET /assets/sample
|
||||
|
||||
Returns the list of pre-defined sample asset documents that would be indexed
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { RequestHandlerContext } from '@kbn/core/server';
|
||||
import { differenceBy, intersectionBy } from 'lodash';
|
||||
import { debug } from '../../common/debug_log';
|
||||
import { ASSET_MANAGER_API_BASE } from '../constants';
|
||||
import { getAssets } from '../lib/get_assets';
|
||||
|
@ -56,4 +57,79 @@ export function assetsRoutes<T extends RequestHandlerContext>({ router }: SetupR
|
|||
}
|
||||
}
|
||||
);
|
||||
|
||||
// GET /assets/diff
|
||||
const assetType = schema.oneOf([
|
||||
schema.literal('k8s.pod'),
|
||||
schema.literal('k8s.cluster'),
|
||||
schema.literal('k8s.node'),
|
||||
]);
|
||||
|
||||
const getAssetsDiffQueryOptions = schema.object({
|
||||
aFrom: schema.string(),
|
||||
aTo: schema.string(),
|
||||
bFrom: schema.string(),
|
||||
bTo: schema.string(),
|
||||
type: schema.maybe(schema.oneOf([schema.arrayOf(assetType), assetType])),
|
||||
});
|
||||
router.get<unknown, typeof getAssetsDiffQueryOptions.type, unknown>(
|
||||
{
|
||||
path: `${ASSET_MANAGER_API_BASE}/assets/diff`,
|
||||
validate: {
|
||||
query: getAssetsDiffQueryOptions,
|
||||
},
|
||||
},
|
||||
async (context, req, res) => {
|
||||
const { aFrom, aTo, bFrom, bTo, type } = req.query;
|
||||
|
||||
if (new Date(aFrom) > new Date(aTo)) {
|
||||
return res.badRequest({
|
||||
body: `Time range cannot move backwards in time. "aTo" (${aTo}) is before "aFrom" (${aFrom}).`,
|
||||
});
|
||||
}
|
||||
|
||||
if (new Date(bFrom) > new Date(bTo)) {
|
||||
return res.badRequest({
|
||||
body: `Time range cannot move backwards in time. "bTo" (${bTo}) is before "bFrom" (${bFrom}).`,
|
||||
});
|
||||
}
|
||||
|
||||
const esClient = await getEsClientFromContext(context);
|
||||
|
||||
try {
|
||||
const resultsForA = await getAssets({
|
||||
esClient,
|
||||
filters: {
|
||||
from: aFrom,
|
||||
to: aTo,
|
||||
type,
|
||||
},
|
||||
});
|
||||
|
||||
const resultsForB = await getAssets({
|
||||
esClient,
|
||||
filters: {
|
||||
from: bFrom,
|
||||
to: bTo,
|
||||
type,
|
||||
},
|
||||
});
|
||||
|
||||
const onlyInA = differenceBy(resultsForA, resultsForB, 'asset.ean');
|
||||
const onlyInB = differenceBy(resultsForB, resultsForA, 'asset.ean');
|
||||
const inBoth = intersectionBy(resultsForA, resultsForB, 'asset.ean');
|
||||
|
||||
return res.ok({
|
||||
body: {
|
||||
onlyInA,
|
||||
onlyInB,
|
||||
inBoth,
|
||||
},
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
debug('error looking up asset records', error);
|
||||
return res.customError({ statusCode: 500 });
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
|
|||
import { createSampleAssets, deleteSampleAssets, viewSampleAssetDocs } from '../helpers';
|
||||
|
||||
const ASSETS_ENDPOINT = '/api/asset-manager/assets';
|
||||
const DIFF_ENDPOINT = ASSETS_ENDPOINT + '/diff';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
|
@ -179,5 +180,137 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(getResponse.body.results).to.eql(targetAssets);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /assets/diff', () => {
|
||||
it('should reject requests that do not include the two time ranges to compare', async () => {
|
||||
const timestamp = new Date().toISOString();
|
||||
|
||||
let getResponse = await supertest.get(DIFF_ENDPOINT).expect(400);
|
||||
expect(getResponse.body.message).to.equal(
|
||||
'[request query.aFrom]: expected value of type [string] but got [undefined]'
|
||||
);
|
||||
|
||||
getResponse = await supertest.get(DIFF_ENDPOINT).query({ aFrom: timestamp }).expect(400);
|
||||
expect(getResponse.body.message).to.equal(
|
||||
'[request query.aTo]: expected value of type [string] but got [undefined]'
|
||||
);
|
||||
|
||||
getResponse = await supertest
|
||||
.get(DIFF_ENDPOINT)
|
||||
.query({ aFrom: timestamp, aTo: timestamp })
|
||||
.expect(400);
|
||||
expect(getResponse.body.message).to.equal(
|
||||
'[request query.bFrom]: expected value of type [string] but got [undefined]'
|
||||
);
|
||||
|
||||
getResponse = await supertest
|
||||
.get(DIFF_ENDPOINT)
|
||||
.query({ aFrom: timestamp, aTo: timestamp, bFrom: timestamp })
|
||||
.expect(400);
|
||||
expect(getResponse.body.message).to.equal(
|
||||
'[request query.bTo]: expected value of type [string] but got [undefined]'
|
||||
);
|
||||
|
||||
await supertest
|
||||
.get(DIFF_ENDPOINT)
|
||||
.query({ aFrom: timestamp, aTo: timestamp, bFrom: timestamp, bTo: timestamp })
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it('should reject requests where either time range is moving backwards in time', async () => {
|
||||
const now = new Date();
|
||||
const isoNow = now.toISOString();
|
||||
const oneHourAgo = new Date(now.getTime() - 1000 * 60 * 60 * 1).toISOString();
|
||||
|
||||
let getResponse = await supertest
|
||||
.get(DIFF_ENDPOINT)
|
||||
.query({
|
||||
aFrom: isoNow,
|
||||
aTo: oneHourAgo,
|
||||
bFrom: isoNow,
|
||||
bTo: isoNow,
|
||||
})
|
||||
.expect(400);
|
||||
expect(getResponse.body.message).to.equal(
|
||||
`Time range cannot move backwards in time. "aTo" (${oneHourAgo}) is before "aFrom" (${isoNow}).`
|
||||
);
|
||||
|
||||
getResponse = await supertest
|
||||
.get(DIFF_ENDPOINT)
|
||||
.query({
|
||||
aFrom: isoNow,
|
||||
aTo: isoNow,
|
||||
bFrom: isoNow,
|
||||
bTo: oneHourAgo,
|
||||
})
|
||||
.expect(400);
|
||||
expect(getResponse.body.message).to.equal(
|
||||
`Time range cannot move backwards in time. "bTo" (${oneHourAgo}) is before "bFrom" (${isoNow}).`
|
||||
);
|
||||
|
||||
await supertest
|
||||
.get(DIFF_ENDPOINT)
|
||||
.query({
|
||||
aFrom: oneHourAgo,
|
||||
aTo: isoNow,
|
||||
bFrom: oneHourAgo,
|
||||
bTo: isoNow,
|
||||
})
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it('should return the difference in assets present between two time ranges', async () => {
|
||||
const onlyInA = sampleAssetDocs.slice(0, 2);
|
||||
const onlyInB = sampleAssetDocs.slice(sampleAssetDocs.length - 2);
|
||||
const inBoth = sampleAssetDocs.slice(2, sampleAssetDocs.length - 2);
|
||||
const now = new Date();
|
||||
const oneHourAgo = new Date(now.getTime() - 1000 * 60 * 60 * 1);
|
||||
const twoHoursAgo = new Date(now.getTime() - 1000 * 60 * 60 * 2);
|
||||
await createSampleAssets(supertest, {
|
||||
baseDateTime: twoHoursAgo.toISOString(),
|
||||
excludeEans: inBoth.concat(onlyInB).map((asset) => asset['asset.ean']),
|
||||
});
|
||||
await createSampleAssets(supertest, {
|
||||
baseDateTime: oneHourAgo.toISOString(),
|
||||
excludeEans: onlyInA.concat(onlyInB).map((asset) => asset['asset.ean']),
|
||||
});
|
||||
await createSampleAssets(supertest, {
|
||||
excludeEans: inBoth.concat(onlyInA).map((asset) => asset['asset.ean']),
|
||||
});
|
||||
|
||||
const twoHoursAndTenMinuesAgo = new Date(now.getTime() - 1000 * 60 * 130 * 1);
|
||||
const fiftyMinuesAgo = new Date(now.getTime() - 1000 * 60 * 50 * 1);
|
||||
const seventyMinuesAgo = new Date(now.getTime() - 1000 * 60 * 70 * 1);
|
||||
const tenMinutesAfterNow = new Date(now.getTime() + 1000 * 60 * 10);
|
||||
|
||||
const getResponse = await supertest
|
||||
.get(DIFF_ENDPOINT)
|
||||
.query({
|
||||
aFrom: twoHoursAndTenMinuesAgo,
|
||||
aTo: fiftyMinuesAgo,
|
||||
bFrom: seventyMinuesAgo,
|
||||
bTo: tenMinutesAfterNow,
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(getResponse.body).to.have.property('onlyInA');
|
||||
expect(getResponse.body).to.have.property('onlyInB');
|
||||
expect(getResponse.body).to.have.property('inBoth');
|
||||
|
||||
getResponse.body.onlyInA.forEach((asset: any) => {
|
||||
delete asset['@timestamp'];
|
||||
});
|
||||
getResponse.body.onlyInB.forEach((asset: any) => {
|
||||
delete asset['@timestamp'];
|
||||
});
|
||||
getResponse.body.inBoth.forEach((asset: any) => {
|
||||
delete asset['@timestamp'];
|
||||
});
|
||||
|
||||
expect(getResponse.body.onlyInA).to.eql(onlyInA);
|
||||
expect(getResponse.body.onlyInB).to.eql(onlyInB);
|
||||
expect(getResponse.body.inBoth).to.eql(inBoth);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue