Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport 2.0] Custom healthcheck with filters #2244

Merged
merged 1 commit into from
Sep 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion config/opensearch_dashboards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@
# This node attribute should assign all nodes of the same cluster an integer value that increments with each new cluster that is spun up
# e.g. in opensearch.yml file you would set the value to a setting using node.attr.cluster_id:
# Should only be enabled if there is a corresponding node attribute created in your OpenSearch config that matches the value here
#opensearch.optimizedHealthcheckId: "cluster_id"
#opensearch.optimizedHealthcheck.id: "cluster_id"
#opensearch.optimizedHealthcheck.filters: {
# attribute_key: "attribute_value",
#}

# If your OpenSearch is protected with basic authentication, these settings provide
# the username and password that the OpenSearch Dashboards server uses to perform maintenance on the OpenSearch Dashboards
Expand Down
15 changes: 12 additions & 3 deletions src/core/server/opensearch/opensearch_config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ test('set correct defaults', () => {
"enabled": false,
"maxPercentage": 1,
},
"optimizedHealthcheckId": undefined,
"optimizedHealthcheck": undefined,
"password": undefined,
"pingTimeout": "PT30S",
"requestHeadersWhitelist": Array [
Expand Down Expand Up @@ -491,11 +491,20 @@ describe('deprecations', () => {
`);
});

it('logs a warning if elasticsearch.optimizedHealthcheckId is set and opensearch.optimizedHealthcheckId is not', () => {
it('logs a warning if elasticsearch.optimizedHealthcheckId is set and opensearch.optimizedHealthcheck.id is not', () => {
const { messages } = applyLegacyDeprecations({ optimizedHealthcheckId: '' });
expect(messages).toMatchInlineSnapshot(`
Array [
"\\"elasticsearch.optimizedHealthcheckId\\" is deprecated and has been replaced by \\"opensearch.optimizedHealthcheckId\\"",
"\\"elasticsearch.optimizedHealthcheckId\\" is deprecated and has been replaced by \\"opensearch.optimizedHealthcheck.id\\"",
]
`);
});

it('logs a warning if opensearch.optimizedHealthcheckId is set and opensearch.optimizedHealthcheck.id is not', () => {
const { messages } = applyOpenSearchDeprecations({ optimizedHealthcheckId: '' });
expect(messages).toMatchInlineSnapshot(`
Array [
"\\"opensearch.optimizedHealthcheckId\\" is deprecated and has been replaced by \\"opensearch.optimizedHealthcheck.id\\"",
]
`);
});
Expand Down
16 changes: 12 additions & 4 deletions src/core/server/opensearch/opensearch_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,14 @@ export const configSchema = schema.object({
requestTimeout: schema.duration({ defaultValue: '30s' }),
pingTimeout: schema.duration({ defaultValue: schema.siblingRef('requestTimeout') }),
logQueries: schema.boolean({ defaultValue: false }),
optimizedHealthcheckId: schema.maybe(schema.string()),
optimizedHealthcheck: schema.maybe(
schema.object({
id: schema.string(),
filters: schema.maybe(
schema.recordOf(schema.string(), schema.string(), { defaultValue: {} })
),
})
),
ssl: schema.object(
{
verificationMode: schema.oneOf(
Expand Down Expand Up @@ -154,7 +161,8 @@ const deprecations: ConfigDeprecationProvider = ({ renameFromRoot, renameFromRoo
renameFromRoot('elasticsearch.requestTimeout', 'opensearch.requestTimeout'),
renameFromRoot('elasticsearch.pingTimeout', 'opensearch.pingTimeout'),
renameFromRoot('elasticsearch.logQueries', 'opensearch.logQueries'),
renameFromRoot('elasticsearch.optimizedHealthcheckId', 'opensearch.optimizedHealthcheckId'),
renameFromRoot('elasticsearch.optimizedHealthcheckId', 'opensearch.optimizedHealthcheck.id'),
renameFromRoot('opensearch.optimizedHealthcheckId', 'opensearch.optimizedHealthcheck.id'),
renameFromRoot('elasticsearch.ssl', 'opensearch.ssl'),
renameFromRoot('elasticsearch.apiVersion', 'opensearch.apiVersion'),
renameFromRoot('elasticsearch.healthCheck', 'opensearch.healthCheck'),
Expand Down Expand Up @@ -222,7 +230,7 @@ export class OpenSearchConfig {
* Specifies whether Dashboards should only query the local OpenSearch node when
* all nodes in the cluster have the same node attribute value
*/
public readonly optimizedHealthcheckId?: string;
public readonly optimizedHealthcheck?: OpenSearchConfigType['optimizedHealthcheck'];

/**
* Hosts that the client will connect to. If sniffing is enabled, this list will
Expand Down Expand Up @@ -310,7 +318,7 @@ export class OpenSearchConfig {
this.ignoreVersionMismatch = rawConfig.ignoreVersionMismatch;
this.apiVersion = rawConfig.apiVersion;
this.logQueries = rawConfig.logQueries;
this.optimizedHealthcheckId = rawConfig.optimizedHealthcheckId;
this.optimizedHealthcheck = rawConfig.optimizedHealthcheck;
this.hosts = Array.isArray(rawConfig.hosts) ? rawConfig.hosts : [rawConfig.hosts];
this.requestHeadersWhitelist = Array.isArray(rawConfig.requestHeadersWhitelist)
? rawConfig.requestHeadersWhitelist
Expand Down
2 changes: 1 addition & 1 deletion src/core/server/opensearch/opensearch_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class OpenSearchService

const opensearchNodesCompatibility$ = pollOpenSearchNodesVersion({
internalClient: this.client.asInternalUser,
optimizedHealthcheckId: config.optimizedHealthcheckId,
optimizedHealthcheck: config.optimizedHealthcheck,
log: this.log,
ignoreVersionMismatch: config.ignoreVersionMismatch,
opensearchVersionCheckInterval: config.healthCheckDelay.asMilliseconds(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,35 @@ function createNodes(...versions: string[]): NodesInfo {
return { nodes };
}

function createNodesWithAttribute(
targetId: string,
filterId: string,
targetAttributeValue: string,
filterAttributeValue: string,
...versions: string[]
): NodesInfo {
const nodes = {} as any;
versions
.map((version, i) => {
return {
version,
http: {
publish_address: 'http_address',
},
ip: 'ip',
attributes: {
cluster_id: i % 2 === 0 ? targetId : filterId,
custom_attribute: i % 2 === 0 ? targetAttributeValue : filterAttributeValue,
},
};
})
.forEach((node, i) => {
nodes[`node-${i}`] = node;
});

return { nodes };
}

describe('mapNodesVersionCompatibility', () => {
function createNodesInfoWithoutHTTP(version: string): NodesInfo {
return { nodes: { 'node-without-http': { version, ip: 'ip' } } } as any;
Expand Down Expand Up @@ -179,7 +208,7 @@ describe('pollOpenSearchNodesVersion', () => {

pollOpenSearchNodesVersion({
internalClient,
optimizedHealthcheckId: 'cluster_id',
optimizedHealthcheck: { id: 'cluster_id' },
opensearchVersionCheckInterval: 1,
ignoreVersionMismatch: false,
opensearchDashboardsVersion: OPENSEARCH_DASHBOARDS_VERSION,
Expand All @@ -203,7 +232,104 @@ describe('pollOpenSearchNodesVersion', () => {

pollOpenSearchNodesVersion({
internalClient,
optimizedHealthcheckId: 'cluster_id',
optimizedHealthcheck: { id: 'cluster_id' },
opensearchVersionCheckInterval: 1,
ignoreVersionMismatch: false,
opensearchDashboardsVersion: OPENSEARCH_DASHBOARDS_VERSION,
log: mockLogger,
})
.pipe(take(1))
.subscribe({
next: (result) => {
expect(result).toEqual(
mapNodesVersionCompatibility(nodes, OPENSEARCH_DASHBOARDS_VERSION, false)
);
},
complete: done,
error: done,
});
});

it('returns compatibility results and isCompatible=true with filters', (done) => {
expect.assertions(2);
const target = {
id: '0',
attribute: 'foo',
};
const filter = {
id: '1',
attribute: 'bar',
};

// will filter out every odd index
const nodes = createNodesWithAttribute(
target.id,
filter.id,
target.attribute,
filter.attribute,
'5.1.0',
'6.2.0',
'5.1.0',
'5.1.1-Beta1'
);

// @ts-expect-error we need to return an incompatible type to use the testScheduler here
internalClient.cluster.state.mockReturnValueOnce({ body: nodes });

nodeInfosSuccessOnce(nodes);

pollOpenSearchNodesVersion({
internalClient,
optimizedHealthcheck: { id: target.id, filters: { custom_attribute: filter.attribute } },
opensearchVersionCheckInterval: 1,
ignoreVersionMismatch: false,
opensearchDashboardsVersion: OPENSEARCH_DASHBOARDS_VERSION,
log: mockLogger,
})
.pipe(take(1))
.subscribe({
next: (result) => {
expect(result).toEqual(
mapNodesVersionCompatibility(nodes, OPENSEARCH_DASHBOARDS_VERSION, false)
);
expect(result.isCompatible).toBe(true);
},
complete: done,
error: done,
});
});

it('returns compatibility results and isCompatible=false with filters', (done) => {
expect.assertions(2);
const target = {
id: '0',
attribute: 'foo',
};
const filter = {
id: '1',
attribute: 'bar',
};

// will filter out every odd index
const nodes = createNodesWithAttribute(
target.id,
filter.id,
target.attribute,
filter.attribute,
'5.1.0',
'5.1.0',
'6.2.0',
'5.1.1-Beta1'
);

// @ts-expect-error we need to return an incompatible type to use the testScheduler here
internalClient.cluster.state.mockReturnValueOnce({ body: nodes });

nodeInfosSuccessOnce(nodes);

pollOpenSearchNodesVersion({
internalClient,
optimizedHealthcheck: { id: target.id, filters: { custom_attribute: filter.attribute } },
opensearchVersionCheckInterval: 1,
ignoreVersionMismatch: false,
opensearchDashboardsVersion: OPENSEARCH_DASHBOARDS_VERSION,
Expand All @@ -215,6 +341,7 @@ describe('pollOpenSearchNodesVersion', () => {
expect(result).toEqual(
mapNodesVersionCompatibility(nodes, OPENSEARCH_DASHBOARDS_VERSION, false)
);
expect(result.isCompatible).toBe(false);
},
complete: done,
error: done,
Expand All @@ -232,7 +359,7 @@ describe('pollOpenSearchNodesVersion', () => {

pollOpenSearchNodesVersion({
internalClient,
optimizedHealthcheckId: 'cluster_id',
optimizedHealthcheck: { id: 'cluster_id' },
opensearchVersionCheckInterval: 1,
ignoreVersionMismatch: false,
opensearchDashboardsVersion: OPENSEARCH_DASHBOARDS_VERSION,
Expand Down Expand Up @@ -268,7 +395,7 @@ describe('pollOpenSearchNodesVersion', () => {

const opensearchNodesCompatibility$ = pollOpenSearchNodesVersion({
internalClient,
optimizedHealthcheckId: 'cluster_id',
optimizedHealthcheck: { id: 'cluster_id' },
opensearchVersionCheckInterval: 100,
ignoreVersionMismatch: false,
opensearchDashboardsVersion: OPENSEARCH_DASHBOARDS_VERSION,
Expand Down Expand Up @@ -308,7 +435,7 @@ describe('pollOpenSearchNodesVersion', () => {

const opensearchNodesCompatibility$ = pollOpenSearchNodesVersion({
internalClient,
optimizedHealthcheckId: 'cluster_id',
optimizedHealthcheck: { id: 'cluster_id' },
opensearchVersionCheckInterval: 10,
ignoreVersionMismatch: false,
opensearchDashboardsVersion: OPENSEARCH_DASHBOARDS_VERSION,
Expand Down
Loading