[Fleet][AI4DSOC] Adding new config to enable prerelease integrations by default (#218489)

## Summary

Introduces a new fleet config variable to be able to set the default the
fleet setting for `prerelease_integrations_enabled`.

This is to be used in the new search_ai_lake tier for the ai4dsoc
project as we want to enable pre-release versions by default.

## How to test

1. Set `xpack.fleet.prereleaseEnabledByDefault: true` in your
`kibana.dev.yml`
2. Start up elasticsearch and kibana
3. Navigate to the integrations page and the toggle to enable should be
on
<img width="750" alt="Screenshot 2025-04-16 at 3 25 50 PM"
src="https://github.com/user-attachments/assets/17d14630-94f5-4f2a-ab32-d733d0b36d48"
/>

OR

1. Add the following to `serverless.security.dev.yml`:
```
xpack.securitySolutionServerless.productTypes:
[
  { product_line: 'ai_soc', product_tier: 'search_ai_lake' },
]
```
2. Restart Kibana serverless for security
3. Navigate to the Configurations -> Integrations page
4. Click on the 'Splunk' integration and verify it loads the page
<img width="750" alt="Screenshot 2025-04-16 at 5 15 28 PM"
src="https://github.com/user-attachments/assets/ba4bf986-1b47-4703-9f33-9a0a7a437539"
/>

___ 
Relates: https://github.com/elastic/security-team/issues/11789
This commit is contained in:
Kylie Meli 2025-04-18 12:18:32 -04:00 committed by GitHub
parent 2a97766b9d
commit bb38af57f7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 147 additions and 5 deletions

View file

@ -13,7 +13,7 @@ xpack.features.overrides:
siemV2.description: null
securitySolutionSiemMigrations.hidden: true
## Agentless deployment by default
# Custom integrations/fleet settings
xpack.fleet.agentless.isDefault: true
# Override integrations home
xpack.fleet.integrationsHomeOverride: '/app/security/configurations/integrations'
xpack.fleet.prereleaseEnabledByDefault: true

View file

@ -267,6 +267,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
'xpack.fleet.internal.onlyAllowAgentUpgradeToKnownVersions (boolean?)',
'xpack.fleet.developer.maxAgentPoliciesWithInactivityTimeout (number?)',
'xpack.fleet.integrationsHomeOverride (string?)',
'xpack.fleet.prereleaseEnabledByDefault (boolean?)',
'xpack.global_search.search_timeout (duration?)',
'xpack.global_search_bar.input_max_limit (number?)',
'xpack.graph.canEditDrillDownUrls (boolean?)',

View file

@ -89,6 +89,7 @@ export interface FleetConfigType {
retryDelays?: string[];
};
integrationsHomeOverride?: string;
prereleaseEnabledByDefault?: boolean;
}
// Calling Object.entries(PackagesGroupedByStatus) gave `status: string`

View file

@ -49,6 +49,7 @@ export const config: PluginConfigDescriptor = {
onlyAllowAgentUpgradeToKnownVersions: true,
},
integrationsHomeOverride: true,
prereleaseEnabledByDefault: true,
},
deprecations: ({ renameFromRoot, unused, unusedFromRoot }) => [
// Unused settings before Fleet server exists
@ -300,6 +301,7 @@ export const config: PluginConfigDescriptor = {
})
),
integrationsHomeOverride: schema.maybe(schema.string()),
prereleaseEnabledByDefault: schema.boolean({ defaultValue: false }),
},
{
validate: (configToValidate) => {

View file

@ -17,7 +17,7 @@ import type { Settings } from '../types';
import { DeleteUnenrolledAgentsPreconfiguredError } from '../errors';
import { appContextService } from './app_context';
import { getSettings, saveSettings, settingsSetup } from './settings';
import { createDefaultSettings, getSettings, saveSettings, settingsSetup } from './settings';
import { auditLoggingService } from './audit_logging';
import { fleetServerHostService } from './fleet_server_host';
@ -106,6 +106,86 @@ describe('settingsSetup', () => {
expect(soClientMock.create).not.toBeCalled();
});
it('should update prerelease_integrations_enabled if settings exist and prereleaseEnabledByDefault is true', async () => {
const soClientMock = savedObjectsClientMock.create();
mockedAppContextService.getConfig.mockReturnValue({
prereleaseEnabledByDefault: true,
enabled: false,
agents: {
enabled: false,
elasticsearch: {
hosts: undefined,
ca_sha256: undefined,
ca_trusted_fingerprint: undefined,
},
fleet_server: undefined,
},
});
soClientMock.find.mockResolvedValue({
total: 1,
page: 1,
per_page: 10,
saved_objects: [
{
id: GLOBAL_SETTINGS_ID,
attributes: { prerelease_integrations_enabled: false },
references: [],
type: GLOBAL_SETTINGS_SAVED_OBJECT_TYPE,
score: 0,
},
],
});
soClientMock.update.mockResolvedValueOnce({
id: GLOBAL_SETTINGS_ID,
type: GLOBAL_SETTINGS_SAVED_OBJECT_TYPE,
attributes: { prerelease_integrations_enabled: true },
references: [],
});
await settingsSetup(soClientMock);
expect(soClientMock.update).toHaveBeenCalled();
});
it('should not update settings if prereleaseEnabledByDefault is false', async () => {
const soClientMock = savedObjectsClientMock.create();
mockedAppContextService.getConfig.mockReturnValue({
prereleaseEnabledByDefault: false,
enabled: false,
agents: {
enabled: false,
elasticsearch: {
hosts: undefined,
ca_sha256: undefined,
ca_trusted_fingerprint: undefined,
},
fleet_server: undefined,
},
});
soClientMock.find.mockResolvedValueOnce({
total: 1,
page: 0,
per_page: 10,
saved_objects: [
{
id: GLOBAL_SETTINGS_ID,
attributes: { prerelease_integrations_enabled: false },
references: [],
type: GLOBAL_SETTINGS_SAVED_OBJECT_TYPE,
score: 0,
},
],
});
await settingsSetup(soClientMock);
expect(soClientMock.update).not.toHaveBeenCalled();
});
});
describe('getSettings', () => {
@ -379,3 +459,54 @@ describe('saveSettings', () => {
}
});
});
describe('createDefaultSettings', () => {
afterEach(() => {
jest.resetAllMocks();
});
it('should return default settings with prerelease_integrations_enabled set to true if config.prereleaseEnabledByDefault is true', () => {
mockedAppContextService.getConfig.mockReturnValue({
prereleaseEnabledByDefault: true,
enabled: true,
agents: {
enabled: false,
elasticsearch: {
hosts: undefined,
ca_sha256: undefined,
ca_trusted_fingerprint: undefined,
},
},
});
const result = createDefaultSettings();
expect(result).toEqual({ prerelease_integrations_enabled: true });
});
it('should return default settings with prerelease_integrations_enabled set to false if config.prereleaseEnabledByDefault is false', () => {
mockedAppContextService.getConfig.mockReturnValue({
prereleaseEnabledByDefault: false,
enabled: true,
agents: {
enabled: false,
elasticsearch: {
hosts: undefined,
ca_sha256: undefined,
ca_trusted_fingerprint: undefined,
},
},
});
const result = createDefaultSettings();
expect(result).toEqual({ prerelease_integrations_enabled: false });
});
it('should return default settings with prerelease_integrations_enabled as false if config is not defined', () => {
mockedAppContextService.getConfig.mockReturnValue(undefined);
const result = createDefaultSettings();
expect(result).toEqual({ prerelease_integrations_enabled: false });
});
});

View file

@ -72,7 +72,13 @@ export async function getSettingsOrUndefined(
export async function settingsSetup(soClient: SavedObjectsClientContract) {
try {
await getSettings(soClient);
const config = appContextService.getConfig();
const settings = await getSettings(soClient);
if (config?.prereleaseEnabledByDefault && !settings.prerelease_integrations_enabled) {
await saveSettings(soClient, {
prerelease_integrations_enabled: config?.prereleaseEnabledByDefault,
});
}
} catch (e) {
if (e.isBoom && e.output.statusCode === 404) {
const defaultSettings = createDefaultSettings();
@ -166,5 +172,6 @@ function getConfigFleetServerHosts() {
}
export function createDefaultSettings(): BaseSettings {
return { prerelease_integrations_enabled: false };
const config = appContextService.getConfig();
return { prerelease_integrations_enabled: !!config?.prereleaseEnabledByDefault };
}