Skip to content

Commit

Permalink
[FTR] support "deployment agnostic" api-integration tests (#189853)
Browse files Browse the repository at this point in the history
## Summary

### This PR introduces a new type of API integration tests in FTR:
deployment-agnostic

![8zcgq0
(1)](https://github.com/user-attachments/assets/17c6d4ee-7848-4a4c-a006-7ae54e523243)

#### Test suite is considered deployment-agnostic when it fulfils the
following criteria:

**Functionality**: It tests Kibana APIs that are **logically identical
in both stateful and serverless environments** for the same SAML roles.

**Design**: The test design is **clean and does not require additional
logic** to execute in either stateful or serverless environments.

### How It Works
Most existing stateful tests use basic authentication for API testing.
In contrast, serverless tests use SAML authentication with
project-specific role mapping.

Since stateful deployments also support SAML, deployment-agnostic tests
**configure Elasticsearch and Kibana with SAML authentication in both
cases**. For roles, stateful deployments define 'viewer', 'editor', and
'admin' roles with serverless-alike privileges.

New `samlAuth` service has `AuthProvider` interface with 2 different
implementations: depending on environment context (serverless or
stateful) appropriate implementation is used. But it remains on service
level and hidden in test suite.

test example
```
export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
  const samlAuth = getService('samlAuth');
  const supertestWithoutAuth = getService('supertestWithoutAuth');
  let roleAuthc: RoleCredentials;
  let internalHeaders: InternalRequestHeader;

  describe('GET /api/console/api_server', () => {
    before(async () => {
      roleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('admin');
      internalHeaders = samlAuth.getInternalRequestHeader();
    });
    after(async () => {
      await samlAuth.invalidateM2mApiKeyWithRoleScope(roleAuthc);
    });
    it('returns autocomplete definitions', async () => {
      const { body } = await supertestWithoutAuth
        .get('/api/console/api_server')
        .set(roleAuthc.apiKeyHeader)
        .set(internalHeaders)
        .set('kbn-xsrf', 'true')
        .expect(200);
      expect(body.es).to.be.ok();
      const {
        es: { name, globals, endpoints },
      } = body;
      expect(name).to.be.ok();
      expect(Object.keys(globals).length).to.be.above(0);
      expect(Object.keys(endpoints).length).to.be.above(0);
    });
  });
}
```

Please read
[readme](https://github.com/elastic/kibana/blob/966822ac872c71284258faf61682176251bcf2c2/x-pack/test/api_integration/deployment_agnostic/README.md)
for more details and step-by-step guide. It should help migrating
existing serverless tests to deployment-agnostic, assuming requirements
are met.

### Examples

Deployment-agnostic tests:

```
x-pack/test/api_integration/deployment_agnostic/apis/console/spec_definitions.ts

x-pack/test/api_integration/deployment_agnostic/apis/core/compression.ts

x-pack/test/api_integration/deployment_agnostic/apis/painless_lab/painless_lab.ts
```

Configs to run it:

```
node scripts/functional_tests --config x-pack/test/api_integration/deployment_agnostic/oblt.serverless.config.ts
node scripts/functional_tests --config x-pack/test/api_integration/deployment_agnostic/search.serverless.config.ts
node scripts/functional_tests --config x-pack/test/api_integration/deployment_agnostic/security.serverless.config.ts

node scripts/functional_tests --config x-pack/test/api_integration/deployment_agnostic/stateful.config.ts
```





PR is a compact version of #188737 with reduced changes in existing
serverless tests.

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: elena-shostak <165678770+elena-shostak@users.noreply.github.com>
Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com>
  • Loading branch information
4 people authored Aug 7, 2024
1 parent f3aeb81 commit 7df01e9
Show file tree
Hide file tree
Showing 72 changed files with 1,756 additions and 420 deletions.
2 changes: 2 additions & 0 deletions .buildkite/ftr_base_serverless_configs.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
disabled:
# Base config files, only necessary to inform config finding script

# Serverless deployment-agnostic default config for api-integration tests
- x-pack/test/api_integration/deployment_agnostic/default_configs/serverless.config.base.ts
# Serverless base config files
- x-pack/test_serverless/api_integration/config.base.ts
- x-pack/test_serverless/functional/config.base.ts
Expand Down
2 changes: 2 additions & 0 deletions .buildkite/ftr_oblt_serverless_configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ enabled:
- x-pack/test_serverless/functional/test_suites/observability/common_configs/config.group5.ts
- x-pack/test_serverless/functional/test_suites/observability/common_configs/config.group6.ts
- x-pack/test_serverless/functional/test_suites/observability/config.screenshots.ts
# serverless config files that run deployment-agnostic tests
- x-pack/test/api_integration/deployment_agnostic/oblt.serverless.config.ts
5 changes: 4 additions & 1 deletion .buildkite/ftr_platform_stateful_configs.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
disabled:
# Stateful base config for deployment-agnostic tests
- x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts
# Base config files, only necessary to inform config finding script
- test/functional/config.base.js
- test/functional/firefox/config.base.ts
Expand Down Expand Up @@ -155,7 +157,6 @@ enabled:
- x-pack/test/api_integration/apis/monitoring/config.ts
- x-pack/test/api_integration/apis/monitoring_collection/config.ts
- x-pack/test/api_integration/apis/osquery/config.ts
- x-pack/test/api_integration/apis/painless_lab/config.ts
- x-pack/test/api_integration/apis/search/config.ts
- x-pack/test/api_integration/apis/searchprofiler/config.ts
- x-pack/test/api_integration/apis/security/config.ts
Expand Down Expand Up @@ -359,3 +360,5 @@ enabled:
- x-pack/performance/journeys_e2e/apm_service_inventory.ts
- x-pack/performance/journeys_e2e/infra_hosts_view.ts
- x-pack/test/custom_branding/config.ts
# stateful config files that run deployment-agnostic tests
- x-pack/test/api_integration/deployment_agnostic/stateful.config.ts
2 changes: 2 additions & 0 deletions .buildkite/ftr_search_serverless_configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ enabled:
- x-pack/test_serverless/functional/test_suites/search/common_configs/config.group4.ts
- x-pack/test_serverless/functional/test_suites/search/common_configs/config.group5.ts
- x-pack/test_serverless/functional/test_suites/search/common_configs/config.group6.ts
# serverless config files that run deployment-agnostic tests
- x-pack/test/api_integration/deployment_agnostic/search.serverless.config.ts
2 changes: 2 additions & 0 deletions .buildkite/ftr_security_serverless_configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,5 @@ enabled:
- x-pack/test/security_solution_api_integration/test_suites/edr_workflows/response_actions/trial_license_complete_tier/configs/serverless.config.ts
- x-pack/test/security_solution_endpoint/configs/serverless.endpoint.config.ts
- x-pack/test/security_solution_endpoint/configs/serverless.integrations.config.ts
# serverless config files that run deployment-agnostic tests
- x-pack/test/api_integration/deployment_agnostic/security.serverless.config.ts
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ module.exports = {
'test/*/*.config.ts',
'test/*/{tests,test_suites,apis,apps}/**/*',
'test/server_integration/**/*.ts',
'x-pack/test/*/{tests,test_suites,apis,apps}/**/*',
'x-pack/test/*/{tests,test_suites,apis,apps,deployment_agnostic}/**/*',
'x-pack/test/*/*config.*ts',
'x-pack/test/saved_object_api_integration/*/apis/**/*',
'x-pack/test/ui_capabilities/*/tests/**/*',
Expand Down
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,7 @@ x-pack/test/observability_ai_assistant_functional @elastic/obs-ai-assistant
/x-pack/test_serverless/functional/test_suites/security/ftr/ @elastic/appex-qa
/x-pack/test_serverless/functional/test_suites/common/home_page/ @elastic/appex-qa
/x-pack/test_serverless/**/services/ @elastic/appex-qa
/packages/kbn-es/src/stateful_resources/roles.yml @elastic/appex-qa

# Core
/config/ @elastic/kibana-core
Expand Down
2 changes: 1 addition & 1 deletion packages/kbn-es/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ export {
readRolesDescriptorsFromResource,
} from './src/utils';
export type { ArtifactLicense } from './src/artifact';
export { SERVERLESS_ROLES_ROOT_PATH } from './src/paths';
export { SERVERLESS_ROLES_ROOT_PATH, STATEFUL_ROLES_ROOT_PATH } from './src/paths';
2 changes: 2 additions & 0 deletions packages/kbn-es/src/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export const ES_CONFIG = 'config/elasticsearch.yml';

export const ES_KEYSTORE_BIN = maybeUseBat('./bin/elasticsearch-keystore');

export const STATEFUL_ROLES_ROOT_PATH = resolve(__dirname, './stateful_resources');

export const SERVERLESS_OPERATOR_USERS_PATH = resolve(
__dirname,
'./serverless_resources/operator_users.yml'
Expand Down
130 changes: 130 additions & 0 deletions packages/kbn-es/src/stateful_resources/roles.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# -----
# This file is for information purpose only. 'viewer' and 'editor' roles are defined in stateful Elasticsearch by default
# Source: https://github.com/elastic/elasticsearch/blob/4272164530807787d4d8b991e3095a6e79176dbf/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java#L861-L952
# Note: inconsistency between these roles definition and the same roles of serverless project may break FTR deployment-agnostic tests
# -----
viewer:
cluster: []
indices:
- names:
- '.alerts*'
- '.preview.alerts*'
privileges:
- 'read'
- 'view_index_metadata'
allow_restricted_indices: false
- names:
- '.items-*'
- '.lists-*'
- '.siem-signals*'
privileges:
- 'read'
- 'view_index_metadata'
allow_restricted_indices: false
- names:
- '/~(([.]|ilm-history-).*)/'
privileges:
- 'read'
- 'view_index_metadata'
allow_restricted_indices: false
- names:
- '.profiling-*'
- 'profiling-*'
privileges:
- 'read'
- 'view_index_metadata'
allow_restricted_indices: false
applications:
- application: 'kibana-.kibana'
privileges:
- 'read'
resources:
- '*'
run_as: []

editor:
cluster: []
indices:
- names:
- 'observability-annotations'
privileges:
- 'read'
- 'view_index_metadata'
- 'write'
allow_restricted_indices: false
- names:
- '.items-*'
- '.lists-*'
- '.siem-signals*'
privileges:
- 'maintenance'
- 'read'
- 'view_index_metadata'
- 'write'
allow_restricted_indices: false
- names:
- '/~(([.]|ilm-history-).*)/'
privileges:
- 'read'
- 'view_index_metadata'
allow_restricted_indices: false
- names:
- '.profiling-*'
- 'profiling-*'
privileges:
- 'read'
- 'view_index_metadata'
allow_restricted_indices: false
- names:
- '.alerts*'
- '.internal.alerts*'
- '.internal.preview.alerts*'
- '.preview.alerts*'
privileges:
- 'maintenance'
- 'read'
- 'view_index_metadata'
- 'write'
allow_restricted_indices: false
applications:
- application: 'kibana-.kibana'
privileges:
- 'all'
resources:
- '*'
run_as: []

# Admin role without 'remote_indices' access definition
# There is no such built-in role in stateful, and it's a role "similar" to the built-in 'admin' role in serverless
admin:
# TODO: 'all' should be replaced with explicit list both here and serverless for deployment-agnostic tests with 'admin' role to be compatible
cluster: ['all']
indices:
- names: ['*']
privileges: ['all']
allow_restricted_indices: false
- names: ['*']
privileges:
- 'monitor'
- 'read'
- 'read_cross_cluster'
- 'view_index_metadata'
allow_restricted_indices: true
applications:
- application: '*'
privileges: ['*']
resources: ['*']
run_as: ['*']

# temporarily added for testing purpose
system_indices_superuser:
cluster: ['all']
indices:
- names: ['*']
privileges: ['all']
allow_restricted_indices: true
applications:
- application: '*'
privileges: ['*']
resources: ['*']
run_as: ['*']
2 changes: 2 additions & 0 deletions packages/kbn-ftr-common-functional-services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ export type Es = ProvidedType<typeof EsProvider>;
import { SupertestWithoutAuthProvider } from './services/supertest_without_auth';
export type SupertestWithoutAuthProviderType = ProvidedType<typeof SupertestWithoutAuthProvider>;

export type { InternalRequestHeader, RoleCredentials } from './services/saml_auth';

export type { FtrProviderContext } from './services/ftr_provider_context';
2 changes: 2 additions & 0 deletions packages/kbn-ftr-common-functional-services/services/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ import { EsProvider } from './es';
import { KibanaServerProvider } from './kibana_server';
import { RetryService } from './retry';
import { SupertestWithoutAuthProvider } from './supertest_without_auth';
import { SamlAuthProvider } from './saml_auth';

export const services = {
es: EsProvider,
kibanaServer: KibanaServerProvider,
esArchiver: EsArchiverProvider,
retry: RetryService,
supertestWithoutAuth: SupertestWithoutAuthProvider,
samlAuth: SamlAuthProvider,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export const COMMON_REQUEST_HEADERS = {
'kbn-xsrf': 'some-xsrf-token',
};

// possible change in 9.0 to match serverless
const STATEFUL_INTERNAL_REQUEST_HEADERS = {
...COMMON_REQUEST_HEADERS,
};

const SERVERLESS_INTERNAL_REQUEST_HEADERS = {
...COMMON_REQUEST_HEADERS,
'x-elastic-internal-origin': 'kibana',
};

export type InternalRequestHeader =
| typeof STATEFUL_INTERNAL_REQUEST_HEADERS
| typeof SERVERLESS_INTERNAL_REQUEST_HEADERS;

export const getServerlessInternalRequestHeaders = (): InternalRequestHeader => {
return SERVERLESS_INTERNAL_REQUEST_HEADERS;
};

export const getStatefulInternalRequestHeaders = (): InternalRequestHeader => {
return STATEFUL_INTERNAL_REQUEST_HEADERS;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import fs from 'fs';
import { type Config } from '@kbn/test';
import { ToolingLog } from '@kbn/tooling-log';
import { MOCK_IDP_REALM_NAME } from '@kbn/mock-idp-utils';
import { KibanaServer } from '../..';

import { ServerlessAuthProvider } from './serverless/auth_provider';
import { StatefulAuthProvider } from './stateful/auth_provider';
import { createRole, createRoleMapping } from './stateful/create_role_mapping';

const STATEFUL_ADMIN_ROLE_MAPPING_PATH = './stateful/admin_mapping';

export interface AuthProvider {
getSupportedRoleDescriptors(): any;
getDefaultRole(): string;
getRolesDefinitionPath(): string;
getCommonRequestHeader(): { [key: string]: string };
getInternalRequestHeader(): { [key: string]: string };
}

export interface AuthProviderProps {
config: Config;
kibanaServer: KibanaServer;
log: ToolingLog;
}

export const getAuthProvider = async (props: AuthProviderProps) => {
const { config, log, kibanaServer } = props;
const isServerless = !!props.config.get('serverless');
if (isServerless) {
return new ServerlessAuthProvider(config);
}

const provider = new StatefulAuthProvider();
// TODO: Move it to @kbn-es package, so that roles and its mapping are created before FTR services loading starts.
// 'viewer' and 'editor' roles are available by default, but we have to create 'admin' role
const adminRoleMapping = JSON.parse(
fs.readFileSync(require.resolve(STATEFUL_ADMIN_ROLE_MAPPING_PATH), 'utf8')
);
await createRole({ roleName: 'admin', roleMapping: adminRoleMapping, kibanaServer, log });
const roles = Object.keys(provider.getSupportedRoleDescriptors());
// Creating roles mapping for mock-idp
await createRoleMapping({ name: MOCK_IDP_REALM_NAME, roles, config, log });
return provider;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export { SamlAuthProvider } from './saml_auth_provider';
export type { RoleCredentials } from './saml_auth_provider';
export type { InternalRequestHeader } from './default_request_headers';
Loading

0 comments on commit 7df01e9

Please sign in to comment.