Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Get All and Explain All #149

Merged
merged 13 commits into from
Feb 11, 2021
1 change: 1 addition & 0 deletions models/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export interface Policy {
description: string;
default_state: string;
states: State[];
ism_template: any;
}

export interface State {
Expand Down
8 changes: 4 additions & 4 deletions public/pages/ChangePolicy/components/NewPolicy/NewPolicy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { EuiSpacer, EuiText, EuiRadioGroup, EuiFormRow, EuiSelect, EuiComboBox,
import { ContentPanel } from "../../../../components/ContentPanel";
import { IndexService } from "../../../../services";
import { Radio } from "../../containers/ChangePolicy/ChangePolicy";
import { Policy } from "../../../../../models/interfaces";
import { DocumentPolicy, Policy } from "../../../../../models/interfaces";
import { PolicyOption } from "../../models/interfaces";
import { getErrorMessage } from "../../../../utils/helpers";
import { DOCUMENTATION_URL } from "../../../../utils/constants";
Expand Down Expand Up @@ -60,9 +60,9 @@ export default class NewPolicy extends React.Component<NewPolicyProps, NewPolicy
try {
const searchPoliciesResponse = await indexService.searchPolicies(searchValue, true);
if (searchPoliciesResponse.ok) {
const policies = searchPoliciesResponse.response.hits.hits.map((hit: { _id: string; _source: { policy: Policy } }) => ({
label: hit._id,
value: hit._source.policy,
const policies = searchPoliciesResponse.response.policies.map((p: DocumentPolicy) => ({
label: p.id,
value: p.policy,
}));
this.setState({ policies });
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe("<ApplyPolicyModal /> spec", () => {
});

it("successfully calls search policies on mount", async () => {
httpClientMock.post = jest.fn().mockResolvedValue({ ok: true, resp: { hits: { hits: [{ _id: "test_index" }] } } });
httpClientMock.get = jest.fn().mockResolvedValue({ ok: true, resp: { hits: { hits: [{ _id: "test_index" }] } } });
const spy = jest.spyOn(browserServicesMock.indexService, "searchPolicies");
render(
<CoreServicesContext.Provider value={coreServicesMock}>
Expand All @@ -49,7 +49,7 @@ describe("<ApplyPolicyModal /> spec", () => {
});

it("adds danger toaster on safe error", async () => {
httpClientMock.post = jest.fn().mockResolvedValue({ ok: false, error: "some error" });
httpClientMock.get = jest.fn().mockResolvedValue({ ok: false, error: "some error" });
const spy = jest.spyOn(browserServicesMock.indexService, "searchPolicies");
render(
<CoreServicesContext.Provider value={coreServicesMock}>
Expand All @@ -67,7 +67,7 @@ describe("<ApplyPolicyModal /> spec", () => {
});

it("adds danger toaster on unsafe error", async () => {
httpClientMock.post = jest.fn().mockRejectedValue(new Error("testing error"));
httpClientMock.get = jest.fn().mockRejectedValue(new Error("testing error"));
const spy = jest.spyOn(browserServicesMock.indexService, "searchPolicies");
render(
<CoreServicesContext.Provider value={coreServicesMock}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
} from "@elastic/eui";
import { BrowserServices } from "../../../../models/interfaces";
import { PolicyOption } from "../../models/interfaces";
import { Policy, State } from "../../../../../models/interfaces";
import { DocumentPolicy, Policy, State } from "../../../../../models/interfaces";
import { getErrorMessage } from "../../../../utils/helpers";
import { DOCUMENTATION_URL } from "../../../../utils/constants";
import { CoreServicesContext } from "../../../../components/core_services";
Expand Down Expand Up @@ -136,9 +136,9 @@ export default class ApplyPolicyModal extends Component<ApplyPolicyModalProps, A
try {
const searchPoliciesResponse = await indexService.searchPolicies(searchValue, true);
if (searchPoliciesResponse.ok) {
const policies = searchPoliciesResponse.response.hits.hits.map((hit: { _id: string; _source: { policy: Policy } }) => ({
label: hit._id,
policy: hit._source.policy,
const policies = searchPoliciesResponse.response.policies.map((p: DocumentPolicy) => ({
label: p.id,
policy: p.policy,
}));
this.setState({ policyOptions: policies });
} else {
Expand Down
4 changes: 2 additions & 2 deletions public/pages/Policies/containers/Policies/Policies.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,15 @@ export default class Policies extends Component<PoliciesProps, PoliciesState> {
),
},
{
field: "policy.policy.description",
field: "policy.description",
name: "Description",
sortable: true,
truncateText: true,
textOnly: true,
width: "150px",
},
{
field: "policy.policy.last_updated_time",
field: "policy.last_updated_time",
name: "Last updated time",
sortable: true,
truncateText: false,
Expand Down
2 changes: 1 addition & 1 deletion public/services/IndexService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ describe("IndexService spec", () => {
const searchValue = "test";
await indexService.searchPolicies(searchValue);

expect(httpClientMock.post).toHaveBeenCalledTimes(1);
expect(httpClientMock.get).toHaveBeenCalledTimes(1);
});
});
27 changes: 11 additions & 16 deletions public/services/IndexService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@

import { HttpSetup } from "kibana/public";
import { INDEX } from "../../server/utils/constants";
import { AcknowledgedResponse, ApplyPolicyResponse, GetIndicesResponse, SearchResponse } from "../../server/models/interfaces";
import {
AcknowledgedResponse,
ApplyPolicyResponse,
GetIndicesResponse,
GetPoliciesResponse,
SearchResponse,
} from "../../server/models/interfaces";
import { ServerResponse } from "../../server/models/types";
import { NODE_API } from "../../utils/constants";

Expand Down Expand Up @@ -46,22 +52,11 @@ export default class IndexService {
return response;
};

searchPolicies = async (searchValue: string, source: boolean = false): Promise<ServerResponse<SearchResponse<any>>> => {
searchPolicies = async (searchValue: string, source: boolean = false): Promise<ServerResponse<GetPoliciesResponse>> => {
const str = searchValue.trim();
const mustQuery = {
query_string: {
default_field: "policy.policy_id",
default_operator: "AND",
query: str ? `*${str.split(" ").join("* *")}*` : "*",
},
};
const body = {
index: INDEX.OPENDISTRO_ISM_CONFIG,
size: 10,
query: { _source: source, query: { bool: { must: [mustQuery, { exists: { field: "policy" } }] } } },
};
const url = `..${NODE_API._SEARCH}`;
const response = (await this.httpClient.post(url, { body: JSON.stringify(body) })) as ServerResponse<any>;
const queryObject = { from: 0, size: 10, search: str, sortDirection: "desc", sortField: "id" };
const url = `..${NODE_API.POLICIES}`;
const response = (await this.httpClient.get(url, { query: queryObject })) as ServerResponse<GetPoliciesResponse>;
return response;
};
}
14 changes: 14 additions & 0 deletions server/clusters/ism/ismPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ export default function ismPlugin(Client: any, config: any, components: any) {
method: "GET",
});

ism.getPolicies = ca({
url: {
fmt: `${API.POLICY_BASE}`,
},
method: "GET",
});

ism.createPolicy = ca({
url: {
fmt: `${API.POLICY_BASE}/<%=policyId%>?refresh=wait_for`,
Expand Down Expand Up @@ -100,6 +107,13 @@ export default function ismPlugin(Client: any, config: any, components: any) {
method: "GET",
});

ism.explainAll = ca({
url: {
fmt: `${API.EXPLAIN_BASE}`,
},
method: "GET",
});

ism.retry = ca({
url: {
fmt: `${API.RETRY_BASE}/<%=index%>`,
Expand Down
14 changes: 10 additions & 4 deletions server/models/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ export interface ExplainResponse {
[index: string]: ExplainAPIManagedIndexMetaData | undefined;
}

export interface ExplainAllResponse {
[index: string]: ExplainAPIManagedIndexMetaData | number;
total_managed_indices: number;
}

export interface GetManagedIndicesResponse {
totalManagedIndices: number;
managedIndices: ManagedIndexItem[];
Expand Down Expand Up @@ -170,10 +175,10 @@ export interface FailedIndex {
}

export interface ExplainAPIManagedIndexMetaData {
"opendistro.index_state_management.policy_id": string | null;
index?: string;
index_uuid?: string;
policy_id?: string;
"index.opendistro.index_state_management.policy_id": string | null;
index: string;
index_uuid: string;
policy_id: string;
policy_seq_no?: number;
policy_primary_term?: number;
policy_completed?: boolean;
Expand All @@ -183,6 +188,7 @@ export interface ExplainAPIManagedIndexMetaData {
action?: { name: string; start_time: number; index: number; failed: boolean; consumed_retries: number };
retry_info?: { failed: boolean; consumed_retries: number };
info?: object;
enabled: boolean;
}

export interface IndexManagementApi {
Expand Down
79 changes: 27 additions & 52 deletions server/services/IndexService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,16 @@

import { RequestParams } from "@elastic/elasticsearch";
import { INDEX, Setting } from "../utils/constants";
import { AcknowledgedResponse, ApplyPolicyResponse, AddResponse, CatIndex, GetIndicesResponse, SearchResponse } from "../models/interfaces";
import {
AcknowledgedResponse,
ApplyPolicyResponse,
AddResponse,
CatIndex,
GetIndicesResponse,
SearchResponse,
ExplainResponse,
ExplainAPIManagedIndexMetaData,
} from "../models/interfaces";
import { ServerResponse } from "../models/types";
import { KibanaRequest, KibanaResponseFactory, IClusterClient, IKibanaResponse, RequestHandlerContext } from "../../../../src/core/server";

Expand All @@ -26,35 +35,6 @@ export default class IndexService {
this.esDriver = esDriver;
}

search = async (
context: RequestHandlerContext,
request: KibanaRequest,
response: KibanaResponseFactory
): Promise<IKibanaResponse<ServerResponse<any>>> => {
try {
const { query, index, size = 0 } = request.body as { query: object; index: string; size?: number };
const params: RequestParams.Search = { index, size, body: query };
const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request);
const results: SearchResponse<any> = await callWithRequest("search", params);
return response.custom({
statusCode: 200,
body: {
ok: true,
response: results,
},
});
} catch (err) {
console.error("Index Management - IndexService - search", err);
return response.custom({
statusCode: 200,
body: {
ok: false,
error: err.message,
},
});
}
};

getIndices = async (
context: RequestHandlerContext,
request: KibanaRequest,
Expand All @@ -81,17 +61,17 @@ export default class IndexService {
const fromNumber = parseInt(from, 10);
const sizeNumber = parseInt(size, 10);
const paginatedIndices = indicesResponse.slice(fromNumber, fromNumber + sizeNumber);
const indexUuids = paginatedIndices.map((value: CatIndex) => value.uuid);
const indexNames = paginatedIndices.map((value: CatIndex) => value.index);

const managedStatus = await this._getManagedStatus(request, indexUuids);
const managedStatus = await this._getManagedStatus(request, indexNames);

// NOTE: Cannot use response.ok due to typescript type checking
return response.custom({
statusCode: 200,
body: {
ok: true,
response: {
indices: paginatedIndices.map((catIndex: CatIndex) => ({ ...catIndex, managed: managedStatus[catIndex.uuid] || "N/A" })),
indices: paginatedIndices.map((catIndex: CatIndex) => ({ ...catIndex, managed: managedStatus[catIndex.index] || "N/A" })),
totalIndices: indicesResponse.length,
},
},
Expand Down Expand Up @@ -121,30 +101,25 @@ export default class IndexService {
}
};

// given a list of indexUuids return the managed status of each (true, false, N/A)
_getManagedStatus = async (request: KibanaRequest, indexUuids: string[]): Promise<{ [indexUuid: string]: string }> => {
_getManagedStatus = async (request: KibanaRequest, indexNames: string[]): Promise<{ [indexName: string]: string }> => {
try {
const searchParams: RequestParams.Search = {
index: INDEX.OPENDISTRO_ISM_CONFIG,
size: indexUuids.length,
body: { _source: "_id", query: { ids: { values: indexUuids } } },
};
const { callAsCurrentUser: searchCallWithRequest } = this.esDriver.asScoped(request);
const results: SearchResponse<any> = await searchCallWithRequest("search", searchParams);
const managed: { [indexUuid: string]: string } = results.hits.hits.reduce(
(accu: object, hit: { _id: string }) => ({ ...accu, [hit._id]: "Yes" }),
{}
);
return indexUuids.reduce((accu: object, value: string) => ({ ...accu, [value]: managed[value] || "No" }), {});
} catch (err) {
// If the config index does not exist then nothing is being managed
if (err.statusCode === 404 && err.body.error.type === "index_not_found_exception") {
return indexUuids.reduce((accu, value) => ({ ...accu, [value]: "No" }), {});
const explainParamas = { index: indexNames.toString() };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a typo? I am assuming that this should be explainParams

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, will fix it in today's bump version small PR

const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request);
const explainResponse: ExplainResponse = await callWithRequest("ism.explain", explainParamas);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here explaingParamas -> explainParams


const managed: { [indexName: string]: string } = {};
for (const indexName in explainResponse) {
if (indexName === "total_managed_indices") continue;
const explain = explainResponse[indexName] as ExplainAPIManagedIndexMetaData;
managed[indexName] = explain["index.opendistro.index_state_management.policy_id"] === null ? "No" : "Yes";
bowenlan-amzn marked this conversation as resolved.
Show resolved Hide resolved
}

return managed;
} catch (err) {
// otherwise it could be an unauthorized access error to config index or some other error
// in which case we will return managed status N/A
console.error("Index Management - IndexService - _getManagedStatus:", err);
return indexUuids.reduce((accu, value) => ({ ...accu, [value]: "N/A" }), {});
return indexNames.reduce((accu, value) => ({ ...accu, [value]: "N/A" }), {});
}
};

Expand Down
Loading