Skip to content

Commit

Permalink
Merge branch 'main' into flaky-test-168750
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Nov 2, 2023
2 parents 7de2a75 + 0879f2e commit 576897a
Show file tree
Hide file tree
Showing 21 changed files with 238 additions and 208 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { toMountPoint } from '@kbn/react-kibana-mount';
import type { CoreStart } from '@kbn/core/public';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import type { EmbeddableSloProps, SloEmbeddableInput } from './types';
import type { SloEmbeddableInput, EmbeddableSloProps } from './types';

import { ObservabilityPublicPluginsStart } from '../../..';
import { SloConfiguration } from './slo_configuration';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { SloSelector } from './slo_selector';

import type { EmbeddableSloProps } from './types';

interface SloConfigurationProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,19 @@ export class SLOEmbeddable extends AbstractEmbeddable<SloEmbeddableInput, Embedd
this.updateInput({ title });
}

public reportsEmbeddableLoad() {
return true;
}

public onRenderComplete() {
this.renderComplete.dispatchComplete();
}

public render(node: HTMLElement) {
super.render(node);
this.node = node;
// required for the export feature to work
this.node.setAttribute('data-shared-item', '');
this.setTitle(
this.input.title ||
i18n.translate('xpack.observability.sloEmbeddable.displayTitle', {
Expand All @@ -69,6 +80,7 @@ export class SLOEmbeddable extends AbstractEmbeddable<SloEmbeddableInput, Embedd
<KibanaContextProvider services={this.deps}>
<QueryClientProvider client={queryClient}>
<SloOverview
onRenderComplete={() => this.onRenderComplete()}
sloId={sloId}
sloInstanceId={sloInstanceId}
lastReloadRequestTime={this.input.lastReloadRequestTime}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ import { paths } from '../../../../common/locators/paths';

import { EmbeddableSloProps } from './types';

export function SloOverview({ sloId, sloInstanceId, lastReloadRequestTime }: EmbeddableSloProps) {
export function SloOverview({
sloId,
sloInstanceId,
lastReloadRequestTime,
onRenderComplete,
}: EmbeddableSloProps) {
const {
uiSettings,
application: { navigateToUrl },
Expand All @@ -39,6 +44,13 @@ export function SloOverview({ sloId, sloInstanceId, lastReloadRequestTime }: Emb
useEffect(() => {
refetch();
}, [lastReloadRequestTime, refetch]);
useEffect(() => {
if (!onRenderComplete) return;

if (!isLoading) {
onRenderComplete();
}
}, [isLoading, onRenderComplete]);

const percentFormat = uiSettings.get('format:percent:defaultPattern');
const isSloNotFound = !isLoading && slo === undefined;
Expand Down Expand Up @@ -97,6 +109,7 @@ export function SloOverview({ sloId, sloInstanceId, lastReloadRequestTime }: Emb
</LoadingContainer>
);
}

const TargetCopy = i18n.translate('xpack.observability.sloEmbeddable.overview.sloTargetLabel', {
defaultMessage: 'Target',
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface EmbeddableSloProps {
sloId: string | undefined;
sloInstanceId: string | undefined;
lastReloadRequestTime?: number | undefined;
onRenderComplete?: () => void;
}

export type SloEmbeddableInput = EmbeddableInput & EmbeddableSloProps;
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,5 @@ export const useFetchSloList = (): UseFetchSloListResponse => {
isError: false,
isSuccess: true,
data: sloList,
refetch: function () {} as UseFetchSloListResponse['refetch'],
};
};
105 changes: 46 additions & 59 deletions x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,10 @@
* 2.0.
*/

import { useState } from 'react';
import {
QueryObserverResult,
RefetchOptions,
RefetchQueryFilters,
useQuery,
useQueryClient,
} from '@tanstack/react-query';
import { i18n } from '@kbn/i18n';
import { FindSLOResponse } from '@kbn/slo-schema';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useState } from 'react';

import { useKibana } from '../../utils/kibana_react';
import { sloKeys } from './query_key_factory';
Expand All @@ -34,9 +28,6 @@ export interface UseFetchSloListResponse {
isSuccess: boolean;
isError: boolean;
data: FindSLOResponse | undefined;
refetch: <TPageData>(
options?: (RefetchOptions & RefetchQueryFilters<TPageData>) | undefined
) => Promise<QueryObserverResult<FindSLOResponse | undefined, unknown>>;
}

const SHORT_REFETCH_INTERVAL = 1000 * 5; // 5 seconds
Expand All @@ -56,56 +47,53 @@ export function useFetchSloList({
const queryClient = useQueryClient();
const [stateRefetchInterval, setStateRefetchInterval] = useState<number>(SHORT_REFETCH_INTERVAL);

const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data, refetch } = useQuery(
{
queryKey: sloKeys.list({ kqlQuery, page, sortBy, sortDirection }),
queryFn: async ({ signal }) => {
const response = await http.get<FindSLOResponse>(`/api/observability/slos`, {
query: {
...(kqlQuery && { kqlQuery }),
...(sortBy && { sortBy }),
...(sortDirection && { sortDirection }),
...(page && { page }),
},
signal,
});
const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data } = useQuery({
queryKey: sloKeys.list({ kqlQuery, page, sortBy, sortDirection }),
queryFn: async ({ signal }) => {
const response = await http.get<FindSLOResponse>(`/api/observability/slos`, {
query: {
...(kqlQuery && { kqlQuery }),
...(sortBy && { sortBy }),
...(sortDirection && { sortDirection }),
...(page && { page }),
},
signal,
});

return response;
},
keepPreviousData: true,
refetchOnWindowFocus: false,
refetchInterval: shouldRefetch ? stateRefetchInterval : undefined,
staleTime: 1000,
retry: (failureCount, error) => {
if (String(error) === 'Error: Forbidden') {
return false;
}
return failureCount < 4;
},
onSuccess: ({ results }: FindSLOResponse) => {
queryClient.invalidateQueries({ queryKey: sloKeys.historicalSummaries(), exact: false });
queryClient.invalidateQueries({ queryKey: sloKeys.activeAlerts(), exact: false });
queryClient.invalidateQueries({ queryKey: sloKeys.rules(), exact: false });
return response;
},
cacheTime: 0,
refetchOnWindowFocus: false,
refetchInterval: shouldRefetch ? stateRefetchInterval : undefined,
retry: (failureCount, error) => {
if (String(error) === 'Error: Forbidden') {
return false;
}
return failureCount < 4;
},
onSuccess: ({ results }: FindSLOResponse) => {
queryClient.invalidateQueries({ queryKey: sloKeys.historicalSummaries(), exact: false });
queryClient.invalidateQueries({ queryKey: sloKeys.activeAlerts(), exact: false });
queryClient.invalidateQueries({ queryKey: sloKeys.rules(), exact: false });

if (!shouldRefetch) {
return;
}
if (!shouldRefetch) {
return;
}

if (results.find((slo) => slo.summary.status === 'NO_DATA' || !slo.summary)) {
setStateRefetchInterval(SHORT_REFETCH_INTERVAL);
} else {
setStateRefetchInterval(LONG_REFETCH_INTERVAL);
}
},
onError: (error: Error) => {
toasts.addError(error, {
title: i18n.translate('xpack.observability.slo.list.errorNotification', {
defaultMessage: 'Something went wrong while fetching SLOs',
}),
});
},
}
);
if (results.find((slo) => slo.summary.status === 'NO_DATA' || !slo.summary)) {
setStateRefetchInterval(SHORT_REFETCH_INTERVAL);
} else {
setStateRefetchInterval(LONG_REFETCH_INTERVAL);
}
},
onError: (error: Error) => {
toasts.addError(error, {
title: i18n.translate('xpack.observability.slo.list.errorNotification', {
defaultMessage: 'Something went wrong while fetching SLOs',
}),
});
},
});

return {
data,
Expand All @@ -114,6 +102,5 @@ export function useFetchSloList({
isRefetching,
isSuccess,
isError,
refetch,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { PolicyData } from '../../../../../common/endpoint/types';
import type { CreateAndEnrollEndpointHostResponse } from '../../../../../scripts/endpoint/common/endpoint_host_services';
import {
openResponseConsoleFromEndpointList,
performCommandInputChecks,
submitCommand,
waitForEndpointListPageToBeLoaded,
} from '../../tasks/response_console';
import type { IndexedFleetEndpointPolicyResponse } from '../../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy';
import { createAgentPolicyTask, getEndpointIntegrationVersion } from '../../tasks/fleet';
import { checkEndpointListForOnlyUnIsolatedHosts } from '../../tasks/isolate';

import { login } from '../../tasks/login';
import { enableAllPolicyProtections } from '../../tasks/endpoint_policy';
import { createEndpointHost } from '../../tasks/create_endpoint_host';
import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data';

describe('Document signing:', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
let policy: PolicyData;
let createdHost: CreateAndEnrollEndpointHostResponse;

before(() => {
getEndpointIntegrationVersion().then((version) =>
createAgentPolicyTask(version).then((data) => {
indexedPolicy = data;
policy = indexedPolicy.integrationPolicies[0];

return enableAllPolicyProtections(policy.id).then(() => {
// Create and enroll a new Endpoint host
return createEndpointHost(policy.policy_id).then((host) => {
createdHost = host as CreateAndEnrollEndpointHostResponse;
});
});
})
);
});

after(() => {
if (createdHost) {
cy.task('destroyEndpointHost', createdHost);
}

if (indexedPolicy) {
cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy);
}

if (createdHost) {
deleteAllLoadedEndpointData({ endpointAgentIds: [createdHost.agentId] });
}
});

beforeEach(() => {
login();
});

it('should fail if data tampered', () => {
waitForEndpointListPageToBeLoaded(createdHost.hostname);
checkEndpointListForOnlyUnIsolatedHosts();
openResponseConsoleFromEndpointList();
performCommandInputChecks('isolate');

// stop host so that we ensure tamper happens before endpoint processes the action
cy.task('stopEndpointHost', createdHost.hostname);
// get action doc before we submit command, so we know when the new action doc is indexed
cy.task('getLatestActionDoc').then((previousActionDoc) => {
submitCommand();
cy.task('tamperActionDoc', previousActionDoc);
});
cy.task('startEndpointHost', createdHost.hostname);

const actionValidationErrorMsg =
'Fleet action response error: Failed to validate action signature; check Endpoint logs for details';
// wait for 3 minutes for the response to be indexed
cy.contains(actionValidationErrorMsg, { timeout: 180000 }).should('exist');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe('Response console', { tags: ['@ess', '@serverless'] }, () => {
login();
});

describe('Execute operations: execute', () => {
describe('Execute operations:', () => {
const homeFilePath = process.env.CI || true ? '/home/vagrant' : `/home/ubuntu`;

let indexedPolicy: IndexedFleetEndpointPolicyResponse;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe('Response console', { tags: ['@ess', '@serverless'] }, () => {
login();
});

describe('File operations: get-file and upload', () => {
describe('File operations:', () => {
const homeFilePath = process.env.CI || true ? '/home/vagrant' : `/home/ubuntu`;

const fileContent = 'This is a test file for the get-file command.';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { createAgentPolicyTask, getEndpointIntegrationVersion } from '../../../t
import {
checkEndpointListForOnlyIsolatedHosts,
checkEndpointListForOnlyUnIsolatedHosts,
isolateHostFromEndpointList,
} from '../../../tasks/isolate';

import { login } from '../../../tasks/login';
Expand All @@ -31,7 +32,7 @@ describe('Response console', { tags: ['@ess', '@serverless'] }, () => {
login();
});

describe('Host Isolation: isolate and release an endpoint', () => {
describe('Host Isolation:', () => {
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
let policy: PolicyData;
let createdHost: CreateAndEnrollEndpointHostResponse;
Expand Down Expand Up @@ -66,28 +67,30 @@ describe('Response console', { tags: ['@ess', '@serverless'] }, () => {
}
});

it('should isolate host from response console', () => {
const command = 'isolate';
it('should release an isolated host from response console', () => {
const command = 'release';
waitForEndpointListPageToBeLoaded(createdHost.hostname);
checkEndpointListForOnlyUnIsolatedHosts();
// isolate the host first
isolateHostFromEndpointList();
checkEndpointListForOnlyIsolatedHosts();
openResponseConsoleFromEndpointList();
performCommandInputChecks(command);
submitCommand();
waitForCommandToBeExecuted(command);
waitForEndpointListPageToBeLoaded(createdHost.hostname);
checkEndpointListForOnlyIsolatedHosts();
checkEndpointListForOnlyUnIsolatedHosts();
});

it('should release host from response console', () => {
const command = 'release';
it('should isolate a host from response console', () => {
const command = 'isolate';
waitForEndpointListPageToBeLoaded(createdHost.hostname);
checkEndpointListForOnlyIsolatedHosts();
checkEndpointListForOnlyUnIsolatedHosts();
openResponseConsoleFromEndpointList();
performCommandInputChecks(command);
submitCommand();
waitForCommandToBeExecuted(command);
waitForEndpointListPageToBeLoaded(createdHost.hostname);
checkEndpointListForOnlyUnIsolatedHosts();
checkEndpointListForOnlyIsolatedHosts();
});
});
});
Loading

0 comments on commit 576897a

Please sign in to comment.