Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into single-version-deps
Browse files Browse the repository at this point in the history
  • Loading branch information
mistic committed Oct 1, 2020
2 parents 30434fc + 5aa3fb5 commit d1c4fda
Show file tree
Hide file tree
Showing 30 changed files with 388 additions and 191 deletions.
43 changes: 38 additions & 5 deletions src/dev/code_coverage/docs/team_assignment/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,41 @@
# Team Assignment Ingestion Pipeline
# Code Coverage Team Assignments

Team assignment will occur once per ci run.
Team assignment uses an ingest pipeline.
Team assignment occurs once per ci run.

The coverage user has the coverage admin role.
The "orchestration" entry point is a [Jenkinsfile Scripted Pipeline](https://github.com/elastic/kibana/blob/f73bc48b3bbbb5ad2042c1aa267aea2150b7b742/.ci/Jenkinsfile_coverage#L21)
This Jenkinsfile runs a [shell script](https://github.com/elastic/kibana/blob/master/src/dev/code_coverage/shell_scripts/generate_team_assignments_and_ingest_coverage.sh#L33) that kicks everything off.
The end result is the data is ingested to our [Kibana Stats Cluster](https://kibana-stats.elastic.dev/app/dashboards#/view/58b8db70-62f9-11ea-8312-7f2d69b79843?_g=(filters%3A!()%2CrefreshInterval%3A(pause%3A!t%2Cvalue%3A0)%2Ctime%3A(from%3Anow-7d%2Cto%3Anow)))

This role must have the rights depicted below ![Cluster Rights](./security_privleges.png)
## Team Assignment Parsing (from .github/CODEOWNERS)
We add additional metadata to the CODEOWNERS file.
This metadata allows users to assign teams to paths, in a friendly location.
Example CODEOWNERS Block:
_notice the coverage delimiter `#CC# ...`_
```
/x-pack/test/functional/es_archives/endpoint/ @elastic/endpoint-app-team @elastic/siem
/x-pack/test/plugin_functional/plugins/resolver_test/ @elastic/endpoint-app-team @elastic/siem
/x-pack/test/plugin_functional/test_suites/resolver/ @elastic/endpoint-app-team @elastic/siem
#CC# /x-pack/legacy/plugins/siem/ @elastic/siem
#CC# /x-pack/plugins/siem/ @elastic/siem
#CC# /x-pack/plugins/security_solution/ @elastic/siem
```
The first 3 lines above fill the usual purpose of the CODEOWNERS file and cause PRs modifying files in these paths to require approval by the listed team(s).
They also attribute files in those paths for purpose of code coverage reporting.
The last 3 lines above ONLY attribute files in those paths for purpose of code coverage reporting.

## Team Assignment Data File Creation (Before Ingestion)
We create a data file containing all paths in the repo, with a team assigned.
Example Team Assignments Block:
```
x-pack/plugins/security_solution/common/constants.ts siem
x-pack/plugins/security_solution/common/detection_engine/build_exceptions_query.test.ts siem
x-pack/plugins/security_solution/common/detection_engine/build_exceptions_query.ts siem
...
```

## Team Assignment Data File Usage (During Code Coverage Ingestion)
Subsequently, we use the data file during ingestion.
We search the data file, for any given "coveredFilePath"
- Given the above assignments block, and lets say the "coveredFilePath" during ingestion is
- `x-pack/plugins/security_solution/common/constants.ts`
- The team assignment would be `siem` in our [Kibana Stats Cluster](https://kibana-stats.elastic.dev/app/dashboards#/view/58b8db70-62f9-11ea-8312-7f2d69b79843?_g=(filters%3A!()%2CrefreshInterval%3A(pause%3A!t%2Cvalue%3A0)%2Ctime%3A(from%3Anow-7d%2Cto%3Anow)))
Binary file not shown.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,20 @@ import { EuiCallOut, EuiButton } from '@elastic/eui';
import React from 'react';
import { i18n } from '@kbn/i18n';
import { useApmPluginContext } from '../../../../hooks/useApmPluginContext';
import { useMlHref } from '../../../../../../ml/public';

export function LegacyJobsCallout() {
const { core } = useApmPluginContext();
const {
core,
plugins: { ml },
} = useApmPluginContext();
const mlADLink = useMlHref(ml, core.http.basePath.get(), {
page: 'jobs',
pageState: {
jobId: 'high_mean_response_time',
},
});

return (
<EuiCallOut
title={i18n.translate(
Expand All @@ -28,11 +39,7 @@ export function LegacyJobsCallout() {
}
)}
</p>
<EuiButton
href={core.http.basePath.prepend(
'/app/ml#/jobs?mlManagement=(jobId:high_mean_response_time)'
)}
>
<EuiButton href={mlADLink}>
{i18n.translate(
'xpack.apm.settings.anomaly_detection.legacy_jobs.button',
{ defaultMessage: 'Review jobs' }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describe('MLJobLink', () => {
);

expect(href).toMatchInlineSnapshot(
`"/basepath/app/ml#/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now-4h))"`
`"/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now%2Fw,to:now-4h))&_a=(mlTimeSeriesExplorer:(),zoom:(from:now%2Fw,to:now-4h))"`
);
});
it('should produce the correct URL with jobId, serviceName, and transactionType', async () => {
Expand All @@ -41,7 +41,27 @@ describe('MLJobLink', () => {
);

expect(href).toMatchInlineSnapshot(
`"/basepath/app/ml#/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now-4h))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-test,transaction.type:request)))"`
`"/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now%2Fw,to:now-4h))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-test,transaction.type:request)),zoom:(from:now%2Fw,to:now-4h))"`
);
});

it('correctly encodes time range values', async () => {
const href = await getRenderedHref(
() => (
<MLJobLink
jobId="apm-production-485b-high_mean_transaction_duration"
serviceName="opbeans-java"
transactionType="request"
/>
),
{
search:
'?rangeFrom=2020-07-29T17:27:29.000Z&rangeTo=2020-07-29T18:45:00.000Z&refreshInterval=10000&refreshPaused=true',
} as Location
);

expect(href).toMatchInlineSnapshot(
`"/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(apm-production-485b-high_mean_transaction_duration)),refreshInterval:(pause:!t,value:10000),time:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z'))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-java,transaction.type:request)),zoom:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z'))"`
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,14 @@ import { MLLink } from './MLLink';

test('MLLink produces the correct URL', async () => {
const href = await getRenderedHref(
() => (
<MLLink path="/some/path" query={{ ml: { jobIds: ['something'] } }} />
),
() => <MLLink query={{ ml: { jobIds: ['something'] } }} />,
{
search:
'?rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0',
} as Location
);

expect(href).toMatchInlineSnapshot(
`"/basepath/app/ml#/some/path?_g=(ml:(jobIds:!(something)),refreshInterval:(pause:!t,value:0),time:(from:now-5h,to:now-2h))&mlManagement=(groupIds:!(apm))"`
`"/app/ml/jobs?mlManagement=(groupIds:!(apm),jobId:!(something))&_g=(refreshInterval:(pause:!t,value:0),time:(from:now-5h,to:now-2h))"`
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@

import { EuiLink } from '@elastic/eui';
import React from 'react';
import { useLocation } from 'react-router-dom';
import rison, { RisonValue } from 'rison-node';
import url from 'url';
import { useApmPluginContext } from '../../../../hooks/useApmPluginContext';
import { getTimepickerRisonData, TimepickerRisonData } from '../rison_helpers';
import { useMlHref, ML_PAGES } from '../../../../../../ml/public';
import { useUrlParams } from '../../../../hooks/useUrlParams';

interface MlRisonData {
ml?: {
Expand All @@ -26,28 +24,41 @@ interface Props {
}

export function MLLink({ children, path = '', query = {}, external }: Props) {
const { core } = useApmPluginContext();
const location = useLocation();
const {
core,
plugins: { ml },
} = useApmPluginContext();

const risonQuery: MlRisonData & TimepickerRisonData = getTimepickerRisonData(
location.search
);

if (query.ml) {
risonQuery.ml = query.ml;
let jobIds: string[] = [];
if (query.ml?.jobIds) {
jobIds = query.ml.jobIds;
}
const { urlParams } = useUrlParams();
const { rangeFrom, rangeTo, refreshInterval, refreshPaused } = urlParams;

const href = url.format({
pathname: core.http.basePath.prepend('/app/ml'),
hash: `${path}?_g=${rison.encode(
risonQuery as RisonValue
)}&mlManagement=${rison.encode({ groupIds: ['apm'] })}`,
// default to link to ML Anomaly Detection jobs management page
const mlADLink = useMlHref(ml, core.http.basePath.get(), {
page: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE,
pageState: {
jobId: jobIds,
groupIds: ['apm'],
globalState: {
time:
rangeFrom !== undefined && rangeTo !== undefined
? { from: rangeFrom, to: rangeTo }
: undefined,
refreshInterval:
refreshPaused !== undefined && refreshInterval !== undefined
? { pause: refreshPaused, value: refreshInterval }
: undefined,
},
},
});

return (
<EuiLink
children={children}
href={href}
href={mlADLink}
external={external}
target={external ? '_blank' : undefined}
/>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/

import querystring from 'querystring';
import { useLocation } from 'react-router-dom';
import rison from 'rison-node';
import url from 'url';
import { useApmPluginContext } from '../../../../hooks/useApmPluginContext';
import { getTimepickerRisonData } from '../rison_helpers';
import { useMlHref } from '../../../../../../ml/public';
import { useUrlParams } from '../../../../hooks/useUrlParams';

export function useTimeSeriesExplorerHref({
jobId,
Expand All @@ -20,41 +17,38 @@ export function useTimeSeriesExplorerHref({
serviceName?: string;
transactionType?: string;
}) {
const { core } = useApmPluginContext();
const location = useLocation();
const { time, refreshInterval } = getTimepickerRisonData(location.search);
// default to link to ML Anomaly Detection jobs management page
const {
core,
plugins: { ml },
} = useApmPluginContext();
const { urlParams } = useUrlParams();
const { rangeFrom, rangeTo, refreshInterval, refreshPaused } = urlParams;

const search = querystring.stringify(
{
_g: rison.encode({
ml: { jobIds: [jobId] },
time,
refreshInterval,
}),
const timeRange =
rangeFrom !== undefined && rangeTo !== undefined
? { from: rangeFrom, to: rangeTo }
: undefined;
const mlAnomalyDetectionHref = useMlHref(ml, core.http.basePath.get(), {
page: 'timeseriesexplorer',
pageState: {
jobIds: [jobId],
timeRange,
refreshInterval:
refreshPaused !== undefined && refreshInterval !== undefined
? { pause: refreshPaused, value: refreshInterval }
: undefined,
zoom: timeRange,
...(serviceName && transactionType
? {
_a: rison.encode({
mlTimeSeriesExplorer: {
entities: {
'service.name': serviceName,
'transaction.type': transactionType,
},
},
}),
entities: {
'service.name': serviceName,
'transaction.type': transactionType,
},
}
: null),
: {}),
},
undefined,
undefined,
{
encodeURIComponent(str: string) {
return str;
},
}
);

return url.format({
pathname: core.http.basePath.prepend('/app/ml'),
hash: url.format({ pathname: '/timeseriesexplorer', search }),
});

return mlAnomalyDetectionHref;
}
Loading

0 comments on commit d1c4fda

Please sign in to comment.