Skip to content

Commit

Permalink
[Logs onboarding] Removes dependency on apikey id in saved objects (#…
Browse files Browse the repository at this point in the history
…159535)

Closes #159381

Makes use of the auto-generated saved object ID to identify
observability onboarding state saved objects, so the API key id is never
persisted. In this change, the generated API key never has an explicit
association with the saved object for the onboarding flow.
  • Loading branch information
ogupte authored Jun 14, 2023
1 parent 85c5b87 commit 2bab34a
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
"ml-module": "2225cbb4bd508ea5f69db4b848be9d8a74b60198",
"ml-trained-model": "482195cefd6b04920e539d34d7356d22cb68e4f3",
"monitoring-telemetry": "5d91bf75787d9d4dd2fae954d0b3f76d33d2e559",
"observability-onboarding-state": "c2a7439293913d69cc286a8f8f9885bc2dd9682f",
"observability-onboarding-state": "55b112d6a33fedb7c1e4fec4da768d2bcc5fadc2",
"osquery-manager-usage-metric": "983bcbc3b7dda0aad29b20907db233abba709bcc",
"osquery-pack": "6ab4358ca4304a12dcfc1777c8135b75cffb4397",
"osquery-pack-asset": "b14101d3172c4b60eb5404696881ce5275c84152",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
API_KEY_ENCODED=$1
API_ENDPOINT=$2
ELASTIC_AGENT_VERSION=$3
AUTO_DOWNLOAD_CONFIG=$4
ONBOARDING_ID=$4
AUTO_DOWNLOAD_CONFIG=$5

updateStepProgress() {
local STEPNAME="$1"
local STATUS="$2" # "incomplete" | "complete" | "disabled" | "loading" | "warning" | "danger" | "current"
curl --request GET \
--url "${API_ENDPOINT}/custom_logs/step/${STEPNAME}?status=${STATUS}" \
--url "${API_ENDPOINT}/custom_logs/${ONBOARDING_ID}/step/${STEPNAME}?status=${STATUS}" \
--header "Authorization: ApiKey ${API_KEY_ENCODED}" \
--header "Content-Type: application/json" \
--header "kbn-xsrf: true" \
Expand Down Expand Up @@ -93,7 +94,7 @@ downloadElasticAgentConfig() {
echo "Downloading elastic-agent.yml"
updateStepProgress "ea-config" "loading"
curl --request GET \
--url "${API_ENDPOINT}/elastic_agent/config" \
--url "${API_ENDPOINT}/elastic_agent/config?id=${ONBOARDING_ID}" \
--header "Authorization: ApiKey ${API_KEY_ENCODED}" \
--header "Content-Type: application/json" \
--header "kbn-xsrf: true" \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function InstallElasticAgent() {
const wizardState = getState();
const [elasticAgentPlatform, setElasticAgentPlatform] =
useState<ElasticAgentPlatform>('linux-tar');
const [apiKeyId, setApiKeyId] = useState('');
const [onboardingId, setOnboardingId] = useState('');

function onInspect() {
goToStep('inspect');
Expand Down Expand Up @@ -119,20 +119,25 @@ export function InstallElasticAgent() {
headers: {
authorization: `ApiKey ${installShipperSetup?.apiKeyEncoded}`,
},
params: {
query: { id: installShipperSetup?.id ?? '' },
},
};

return callApi(
'GET /api/observability_onboarding/elastic_agent/config 2023-05-24',
installShipperSetup?.apiKeyEncoded ? options : {}
installShipperSetup?.apiKeyEncoded
? options
: { params: options.params }
);
}
},
[installShipperSetup?.apiKeyEncoded]
);

useEffect(() => {
setApiKeyId(installShipperSetup?.apiKeyId ?? '');
}, [installShipperSetup?.apiKeyId]);
setOnboardingId(installShipperSetup?.id ?? '');
}, [installShipperSetup?.id]);

const apiKeyEncoded = installShipperSetup?.apiKeyEncoded;

Expand All @@ -142,16 +147,14 @@ export function InstallElasticAgent() {
refetch: refetchProgress,
} = useFetcher(
(callApi) => {
if (CurrentStep === InstallElasticAgent && apiKeyId) {
if (CurrentStep === InstallElasticAgent && onboardingId) {
return callApi(
'GET /internal/observability_onboarding/custom_logs/progress',
{
params: { query: { apiKeyId } },
}
'GET /internal/observability_onboarding/custom_logs/{id}/progress',
{ params: { path: { id: onboardingId } } }
);
}
},
[apiKeyId]
[onboardingId]
);

const progressSucceded = progressStatus === FETCH_STATUS.SUCCESS;
Expand Down Expand Up @@ -352,6 +355,7 @@ export function InstallElasticAgent() {
scriptDownloadUrl: setup?.scriptDownloadUrl,
elasticAgentVersion: setup?.elasticAgentVersion,
autoDownloadConfig: wizardState.autoDownloadConfig,
onboardingId,
})}
</EuiCodeBlock>
<EuiSpacer size="m" />
Expand Down Expand Up @@ -571,19 +575,21 @@ function getInstallShipperCommand({
scriptDownloadUrl = '$SCRIPT_DOWNLOAD_URL',
elasticAgentVersion = '$ELASTIC_AGENT_VERSION',
autoDownloadConfig = false,
onboardingId = '$ONBOARDING_ID',
}: {
elasticAgentPlatform: ElasticAgentPlatform;
apiKeyEncoded: string | undefined;
apiEndpoint: string | undefined;
scriptDownloadUrl: string | undefined;
elasticAgentVersion: string | undefined;
autoDownloadConfig: boolean;
onboardingId: string | undefined;
}) {
const setupScriptFilename = 'standalone_agent_setup.sh';
const PLATFORM_COMMAND: Record<ElasticAgentPlatform, string> = {
'linux-tar': oneLine`
curl ${scriptDownloadUrl} -o ${setupScriptFilename} &&
sudo bash ${setupScriptFilename} ${apiKeyEncoded} ${apiEndpoint} ${elasticAgentVersion} ${
sudo bash ${setupScriptFilename} ${apiKeyEncoded} ${apiEndpoint} ${elasticAgentVersion} ${onboardingId} ${
autoDownloadConfig ? 'autoDownloadConfig=1' : ''
}
`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ import {

export async function getObservabilityOnboardingState({
savedObjectsClient,
apiKeyId,
savedObjectId,
}: {
savedObjectsClient: SavedObjectsClientContract;
apiKeyId: string;
savedObjectId: string;
}): Promise<SavedObservabilityOnboardingState | undefined> {
try {
const result = await savedObjectsClient.get<ObservabilityOnboardingState>(
OBSERVABILITY_ONBOARDING_STATE_SAVED_OBJECT_TYPE,
apiKeyId
savedObjectId
);
const { id, updated_at: updatedAt, attributes } = result;
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

import * as t from 'io-ts';
import { getAuthenticationAPIKey } from '../../lib/get_authentication_api_key';
import { ObservabilityOnboardingState } from '../../saved_objects/observability_onboarding_status';
import { createObservabilityOnboardingServerRoute } from '../create_observability_onboarding_server_route';
import { createShipperApiKey } from './api_key/create_shipper_api_key';
Expand Down Expand Up @@ -50,7 +49,9 @@ const installShipperSetupRoute = createObservabilityOnboardingServerRoute({
const coreStart = await core.start();

const kibanaUrl =
plugins.cloud?.setup?.kibanaUrl ?? getFallbackUrls(coreStart).kibanaUrl;
core.setup.http.basePath.publicBaseUrl ?? // priority given to server.publicBaseUrl
plugins.cloud?.setup?.kibanaUrl ?? // then cloud id
getFallbackUrls(coreStart).kibanaUrl; // falls back to local network binding
const scriptDownloadUrl = `${kibanaUrl}/plugins/observabilityOnboarding/assets/standalone_agent_setup.sh`;
const apiEndpoint = `${kibanaUrl}/api/observability_onboarding`;

Expand All @@ -71,10 +72,7 @@ const createApiKeyRoute = createObservabilityOnboardingServerRoute({
state: t.record(t.string, t.unknown),
}),
}),
async handler(resources): Promise<{
apiKeyId: string;
apiKeyEncoded: string;
}> {
async handler(resources): Promise<{ apiKeyEncoded: string; id: string }> {
const {
context,
params: {
Expand All @@ -87,32 +85,29 @@ const createApiKeyRoute = createObservabilityOnboardingServerRoute({
const {
elasticsearch: { client },
} = await context.core;
const { id: apiKeyId, encoded: apiKeyEncoded } = await createShipperApiKey(
const { encoded: apiKeyEncoded } = await createShipperApiKey(
client.asCurrentUser,
name
);

const savedObjectsClient = coreStart.savedObjects.getScopedClient(request);

await saveObservabilityOnboardingState({
const { id } = await saveObservabilityOnboardingState({
savedObjectsClient,
apiKeyId,
observabilityOnboardingState: { state } as ObservabilityOnboardingState,
});

return {
apiKeyId,
apiKeyEncoded,
};
return { apiKeyEncoded, id };
},
});

const stepProgressUpdateRoute = createObservabilityOnboardingServerRoute({
endpoint:
'GET /api/observability_onboarding/custom_logs/step/{name} 2023-05-24',
'GET /api/observability_onboarding/custom_logs/{id}/step/{name} 2023-05-24',
options: { tags: [] },
params: t.type({
path: t.type({
id: t.string,
name: t.string,
}),
query: t.type({
Expand All @@ -122,21 +117,19 @@ const stepProgressUpdateRoute = createObservabilityOnboardingServerRoute({
async handler(resources): Promise<object> {
const {
params: {
path: { name },
path: { id, name },
query: { status },
},
request,
core,
} = resources;
const authApiKey = getAuthenticationAPIKey(request);
const coreStart = await core.start();
const savedObjectsClient =
coreStart.savedObjects.createInternalRepository();

const savedObservabilityOnboardingState =
await getObservabilityOnboardingState({
savedObjectsClient,
apiKeyId: authApiKey?.apiKeyId as string,
savedObjectId: id,
});

if (!savedObservabilityOnboardingState) {
Expand All @@ -146,11 +139,15 @@ const stepProgressUpdateRoute = createObservabilityOnboardingServerRoute({
};
}

const { id, updatedAt, ...observabilityOnboardingState } =
savedObservabilityOnboardingState;
const {
id: savedObjectId,
updatedAt,
...observabilityOnboardingState
} = savedObservabilityOnboardingState;

await saveObservabilityOnboardingState({
savedObjectsClient,
apiKeyId: authApiKey?.apiKeyId as string,
savedObjectId,
observabilityOnboardingState: {
...observabilityOnboardingState,
progress: {
Expand All @@ -164,17 +161,17 @@ const stepProgressUpdateRoute = createObservabilityOnboardingServerRoute({
});

const getProgressRoute = createObservabilityOnboardingServerRoute({
endpoint: 'GET /internal/observability_onboarding/custom_logs/progress',
endpoint: 'GET /internal/observability_onboarding/custom_logs/{id}/progress',
options: { tags: [] },
params: t.type({
query: t.type({
apiKeyId: t.string,
path: t.type({
id: t.string,
}),
}),
async handler(resources): Promise<{ progress: Record<string, string> }> {
const {
params: {
query: { apiKeyId },
path: { id },
},
core,
request,
Expand All @@ -185,7 +182,7 @@ const getProgressRoute = createObservabilityOnboardingServerRoute({
const savedObservabilityOnboardingState =
(await getObservabilityOnboardingState({
savedObjectsClient,
apiKeyId,
savedObjectId: id,
})) || null;
const progress = { ...savedObservabilityOnboardingState?.progress };

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { SavedObjectsClientContract } from '@kbn/core/server';
import { SavedObjectsClientContract, SavedObject } from '@kbn/core/server';
import {
OBSERVABILITY_ONBOARDING_STATE_SAVED_OBJECT_TYPE,
ObservabilityOnboardingState,
Expand All @@ -15,26 +15,33 @@ import {
interface Options {
savedObjectsClient: SavedObjectsClientContract;
observabilityOnboardingState: ObservabilityOnboardingState;
apiKeyId: string;
savedObjectId?: string;
}
export async function saveObservabilityOnboardingState({
savedObjectsClient,
observabilityOnboardingState,
apiKeyId,
savedObjectId,
}: Options): Promise<SavedObservabilityOnboardingState> {
const {
id,
attributes,
updated_at: updatedAt,
} = await savedObjectsClient.update<ObservabilityOnboardingState>(
OBSERVABILITY_ONBOARDING_STATE_SAVED_OBJECT_TYPE,
apiKeyId,
observabilityOnboardingState,
{ upsert: observabilityOnboardingState }
);
let savedObject: Omit<
SavedObject<ObservabilityOnboardingState>,
'attributes' | 'references'
>;
if (savedObjectId) {
savedObject = await savedObjectsClient.update<ObservabilityOnboardingState>(
OBSERVABILITY_ONBOARDING_STATE_SAVED_OBJECT_TYPE,
savedObjectId,
observabilityOnboardingState
);
} else {
savedObject = await savedObjectsClient.create<ObservabilityOnboardingState>(
OBSERVABILITY_ONBOARDING_STATE_SAVED_OBJECT_TYPE,
observabilityOnboardingState
);
}
const { id, updated_at: updatedAt } = savedObject;
return {
id,
...(attributes as ObservabilityOnboardingState),
...observabilityOnboardingState,
updatedAt: updatedAt ? Date.parse(updatedAt) : 0,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import * as t from 'io-ts';
import { getAuthenticationAPIKey } from '../../lib/get_authentication_api_key';
import { createObservabilityOnboardingServerRoute } from '../create_observability_onboarding_server_route';
import { getObservabilityOnboardingState } from '../custom_logs/get_observability_onboarding_state';
Expand All @@ -13,9 +14,19 @@ import { getFallbackUrls } from '../custom_logs/get_fallback_urls';

const generateConfig = createObservabilityOnboardingServerRoute({
endpoint: 'GET /api/observability_onboarding/elastic_agent/config 2023-05-24',
params: t.type({
query: t.type({ id: t.string }),
}),
options: { tags: [] },
async handler(resources): Promise<string> {
const { core, plugins, request } = resources;
const {
params: {
query: { id },
},
core,
plugins,
request,
} = resources;
const authApiKey = getAuthenticationAPIKey(request);

const coreStart = await core.start();
Expand All @@ -28,7 +39,7 @@ const generateConfig = createObservabilityOnboardingServerRoute({

const savedState = await getObservabilityOnboardingState({
savedObjectsClient,
apiKeyId: authApiKey?.apiKeyId ?? '',
savedObjectId: id,
});

const yaml = generateYml({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export interface SavedObservabilityOnboardingState
export const observabilityOnboardingState: SavedObjectsType = {
name: OBSERVABILITY_ONBOARDING_STATE_SAVED_OBJECT_TYPE,
hidden: false,
namespaceType: 'multiple',
namespaceType: 'agnostic',
mappings: {
properties: {
state: { type: 'object', dynamic: false },
Expand Down

0 comments on commit 2bab34a

Please sign in to comment.