@@ -18,113 +18,222 @@ state:
1818 api_key: !!str {{ api_key }}
1919 organization_id: !!str {{ organization_id }}
2020 lookbehind: !!int {{ lookbehind }}
21+ add_tags: !!bool {{ add_tags }}
22+ tag_endpoint: !!str "https://cloud.elastic.co/api/v1/deployments/_search"
2123redact:
2224 fields:
2325 - api_key
2426program: |-
25- state.with(
26- // Determine the 'from' value using the cursor or fallback to
27- // calculating it based on the lookback days.
28- state.?cursor.last_to.optMap(ts, timestamp(ts)).orValue(
29- now - duration(string(int(state.lookbehind) * 24) + "h")
30- ).format(
31- // Truncate the 'from' time to the UTC day.
32- time_layout.DateOnly
33- ).parse_time(time_layout.DateOnly).as(from,
34- // Limit the 'from' value to 2021-01-01 (this is API enforced).
35- max(from, timestamp("2021-01-01T00:00:00Z"))
36- ).as(from,
27+ (state.add_tags) ?
28+ request(
29+ "POST",
30+ state.tag_endpoint
31+ ).with(
3732 {
38- "from": from,
39- "to": from + duration("24h"),
33+ "Header": {
34+ "Authorization": ["ApiKey " + state.api_key],
35+ "Content-Type": ["application/json"]
36+ },
37+ "Body": {
38+ "query": {
39+ "bool": {
40+ "filter": [
41+ {
42+ "nested": {
43+ "path": "resources.elasticsearch",
44+ "query": { "exists": { "field": "resources.elasticsearch.id" } }
45+ }
46+ },
47+ {
48+ "nested": {
49+ "path": "resources.elasticsearch",
50+ "query": { "match": { "resources.elasticsearch.info.settings.metadata.organization_id": { "query": state.organization_id } } }
51+ }
52+ }
53+ ]
54+ }
55+ },
56+ "size": 100
57+ }.encode_json()
4058 }
41- ).as(req, (req.to > now) ?
42- // We would fetch data in the future, back off
59+ ).do_request().as(deployments_resp, (deployments_resp.StatusCode == 200) ?
60+ bytes(deployments_resp.Body).decode_json().as(deployments_body,
61+ zip(
62+ deployments_body.deployments.map(d,
63+ d.id
64+ ),
65+ deployments_body.deployments.map(d, d.metadata.collate("tags"))
66+ )
67+ ).as(tags,
68+ state.with(
69+ state.?cursor.last_to.optMap(ts, timestamp(ts)).orValue(
70+ now - duration(string(int(state.lookbehind) * 24) + "h")
71+ ).format(
72+ time_layout.DateOnly
73+ ).parse_time(time_layout.DateOnly).as(from,
74+ max(from, timestamp("2021-01-01T00:00:00Z"))
75+ ).as(from,
76+ {
77+ "from": from,
78+ "to": from + duration("24h"),
79+ }
80+ ).as(req, (req.to > now) ?
81+ {
82+ "events": [],
83+ "cursor": { "last_to": req.to },
84+ "want_more": false,
85+ }
86+ :
87+ get_request(
88+ state.url + "?" + {
89+ "from": [req.from.format(time_layout.RFC3339)],
90+ "to": [req.to.format(time_layout.RFC3339)],
91+ }.format_query()
92+ ).with(
93+ { "Header": { "Authorization": ["ApiKey " + state.api_key] } }
94+ ).do_request().as(resp, (resp.StatusCode == 200) ?
95+ bytes(resp.Body).decode_json().as(body, (has(body.instances) && size(body.instances) > 0) ?
96+ {
97+ "events": body.instances.map(instance,
98+ instance.product_line_items.map(line_item,
99+ {
100+ "ess": {
101+ "billing": line_item.with(
102+ {
103+ "deployment_name": instance.name,
104+ "deployment_id": instance.id,
105+ "deployment_type": instance.type,
106+ "deployment_tags": tags[?instance.id].orValue([]).map(t,
107+ t.key + ":" + t.value
108+ ),
109+ "organization_id": state.organization_id,
110+ "from": req.from.format(time_layout.RFC3339),
111+ "to": req.to.format(time_layout.RFC3339),
112+ "quantities": null
113+ }
114+ ),
115+ },
116+ }
117+ )
118+ ).flatten(),
119+ "cursor": { "last_to": req.to },
120+ "want_more": req.to < now - duration("24h"),
121+ }
122+ :
123+ {
124+ "events": [ { "fake": true } ],
125+ "cursor": { "last_to": req.to },
126+ "want_more": req.to < now - duration("24h"),
127+ }
128+ )
129+ :
130+ {
131+ "events": { "error": { "code": string(resp.StatusCode), "id": string(resp.Status), "message": "GET " + resp.Request.URL + ": " + ( (size(resp.Body) != 0) ? string(resp.Body) : string(resp.Status) + " (" + string(resp.StatusCode) + ")" ) } },
132+ "want_more": false,
133+ }
134+ )
135+ )
136+ )
137+ )
138+ :
43139 {
44- "events": [],
45- "cursor": {
46- // We don't change the last_to, we're just waiting
47- "last_to": req.to,
48- },
140+ "events": { "error": { "code": string(deployments_resp.StatusCode), "id": string(deployments_resp.Status), "message": "POST " + deployments_resp.Request.URL + ": " + ( (size(deployments_resp.Body) != 0) ? string(deployments_resp.Body) : string(deployments_resp.Status) + " (" + string(deployments_resp.StatusCode) + ")" ) } },
49141 "want_more": false,
50142 }
51- :
52- get_request(
53- state.url + "?" + {
54- "from": [req.from.format(time_layout.RFC3339)],
55- "to": [req.to.format(time_layout.RFC3339)],
56- }.format_query()
57- ).with(
143+ )
144+ :
145+ state.with(
146+ state.?cursor.last_to.optMap(ts, timestamp(ts)).orValue(
147+ now - duration(string(int(state.lookbehind) * 24) + "h")
148+ ).format(
149+ time_layout.DateOnly
150+ ).parse_time(time_layout.DateOnly).as(from,
151+ max(from, timestamp("2021-01-01T00:00:00Z"))
152+ ).as(from,
58153 {
59- "Header": {
60- "Authorization": ["ApiKey " + state.api_key],
154+ "from": from,
155+ "to": from + duration("24h"),
156+ }
157+ ).as(req, (req.to > now) ?
158+ {
159+ "events": [],
160+ "cursor": {
161+ "last_to": req.to,
61162 },
163+ "want_more": false,
62164 }
63- ).do_request().as(resp, (resp.StatusCode == 200) ?
64- // Response is successful, but did we get any data?
65- bytes(resp.Body).decode_json().as(body, (has(body.instances) && size(body.instances) > 0) ?
165+ :
166+ get_request(
167+ state.url + "?" + {
168+ "from": [req.from.format(time_layout.RFC3339)],
169+ "to": [req.to.format(time_layout.RFC3339)],
170+ }.format_query()
171+ ).with(
66172 {
67- "events": body.instances.map(instance,
68- instance.product_line_items.map(line_item,
69- {
70- "ess": {
71- "billing": line_item.with(
72- {
73- "deployment_name": instance.name, // Include deployment name
74- "deployment_id": instance.id, // Include deployment ID
75- "deployment_type": instance.type, // Include deployment type
76- "organization_id": state.organization_id, // Include organization ID
77- "from": req.from.format(time_layout.RFC3339),
78- "to": req.to.format(time_layout.RFC3339),
79- "quantities": null // overwrite useless quantities to avoid flatten failure
80- }
81- ),
82- },
83- }
84- )
85- ).flatten(), // Pass line_items as events, with added info
86- "cursor": {
87- "last_to": req.to,
173+ "Header": {
174+ "Authorization": ["ApiKey " + state.api_key],
88175 },
89- // Are we more than 1 day behind?
90- "want_more": req.to < now - duration("24h"),
91176 }
177+ ).do_request().as(resp, (resp.StatusCode == 200) ?
178+ bytes(resp.Body).decode_json().as(body, (has(body.instances) && size(body.instances) > 0) ?
179+ {
180+ "events": body.instances.map(instance,
181+ instance.product_line_items.map(line_item,
182+ {
183+ "ess": {
184+ "billing": line_item.with(
185+ {
186+ "deployment_name": instance.name,
187+ "deployment_id": instance.id,
188+ "deployment_type": instance.type,
189+ "organization_id": state.organization_id,
190+ "from": req.from.format(time_layout.RFC3339),
191+ "to": req.to.format(time_layout.RFC3339),
192+ "quantities": null,
193+ "deployment_tags": [],
194+ }
195+ ),
196+ },
197+ }
198+ )
199+ ).flatten(),
200+ "cursor": {
201+ "last_to": req.to,
202+ },
203+ "want_more": req.to < now - duration("24h"),
204+ }
205+ :
206+ {
207+ "events": [
208+ {
209+ "fake": true,
210+ },
211+ ],
212+ "cursor": {
213+ "last_to": req.to,
214+ },
215+ "want_more": req.to < now - duration("24h"),
216+ }
217+ )
92218 :
93- // We don't have any data, but we still need to return an event
94- // Otherwise the "want_more" logic will not work
95219 {
96- "events": [
97- {
98- "fake": true, // This will be discarded by the drop_event processor.
220+ "events": {
221+ "error": {
222+ "code": string(resp.StatusCode),
223+ "id": string(resp.Status),
224+ "message": "GET " + resp.Request.URL + ": " + (
225+ (size(resp.Body) != 0) ?
226+ string(resp.Body)
227+ :
228+ string(resp.Status) + " (" + string(resp.StatusCode) + ")"
229+ ),
99230 },
100- ],
101- "cursor": {
102- "last_to": req.to,
103231 },
104- // Are we more than 1 day behind?
105- "want_more": req.to < now - duration("24h"),
232+ "want_more": false,
106233 }
107234 )
108- :
109- // Response was not successful, return an error event
110- {
111- "events": {
112- "error": {
113- "code": string(resp.StatusCode),
114- "id": string(resp.Status),
115- "message": "GET " + resp.Request.URL + ": " + (
116- (size(resp.Body) != 0) ?
117- string(resp.Body)
118- :
119- string(resp.Status) + " (" + string(resp.StatusCode) + ")"
120- ),
121- },
122- },
123- "want_more": false,
124- }
125235 )
126236 )
127- )
128237{{ #if tags.length }}
129238tags:
130239{{ else if preserve_original_event}}
0 commit comments