Skip to content

Commit

Permalink
Merge branch 'master' into alerting/tls-warning
Browse files Browse the repository at this point in the history
* master:
  [Remote clustersadopt changes to remote info API (elastic#60795)
  Only run xpack siem cypress in PRs when there are siem changes (elastic#60661)
  [CI] Add error steps and help links to PR comments (elastic#60772)
  skip flaky functional test (elastic#60898)
  [Alerting] Fixes mistake in empty list assertion (elastic#60896)
  a11y tests for login and logout (elastic#60799)
  removed boom errors from AlertNavigationRegistry (elastic#60887)
  • Loading branch information
gmmorris committed Mar 23, 2020
2 parents 38f1efc + 8572e3f commit 4122b2e
Show file tree
Hide file tree
Showing 12 changed files with 182 additions and 25 deletions.
7 changes: 6 additions & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ kibanaPipeline(timeoutMinutes: 135, checkPrChanges: true) {
'xpack-ciGroup9': kibanaPipeline.xpackCiGroupProcess(9),
'xpack-ciGroup10': kibanaPipeline.xpackCiGroupProcess(10),
'xpack-accessibility': kibanaPipeline.functionalTestProcess('xpack-accessibility', './test/scripts/jenkins_xpack_accessibility.sh'),
'xpack-siemCypress': kibanaPipeline.functionalTestProcess('xpack-siemCypress', './test/scripts/jenkins_siem_cypress.sh'),
'xpack-siemCypress': { processNumber ->
whenChanged(['x-pack/legacy/plugins/siem/', 'x-pack/test/siem_cypress/']) {
kibanaPipeline.functionalTestProcess('xpack-siemCypress', './test/scripts/jenkins_siem_cypress.sh')(processNumber)
}
},

// 'xpack-visualRegression': kibanaPipeline.functionalTestProcess('xpack-visualRegression', './test/scripts/jenkins_xpack_visual_regression.sh'),
]),
])
Expand Down
19 changes: 19 additions & 0 deletions vars/githubPr.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,20 @@ def getNextCommentMessage(previousCommentInfo = [:]) {
## :broken_heart: Build Failed
* [continuous-integration/kibana-ci/pull-request](${env.BUILD_URL})
* Commit: ${getCommitHash()}
* [Pipeline Steps](${env.BUILD_URL}flowGraphTable) (look for red circles / failed steps)
* [Interpreting CI Failures](https://www.elastic.co/guide/en/kibana/current/interpreting-ci-failures.html)
"""

try {
def steps = getFailedSteps()
if (steps?.size() > 0) {
def list = steps.collect { "* [${it.displayName}](${it.logs})" }.join("\n")
messages << "### Failed CI Steps\n${list}"
}
} catch (ex) {
buildUtils.printStacktrace(ex)
print "Error retrieving failed pipeline steps for PR comment, will skip this section"
}
}

messages << getTestFailuresMessage()
Expand Down Expand Up @@ -220,3 +233,9 @@ def deleteComment(commentId) {
def getCommitHash() {
return env.ghprbActualCommit
}

def getFailedSteps() {
return jenkinsApi.getFailedSteps()?.findAll { step ->
step.displayName != 'Check out from version control'
}
}
21 changes: 21 additions & 0 deletions vars/jenkinsApi.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
def getSteps() {
def url = "${env.BUILD_URL}api/json?tree=actions[nodes[iconColor,running,displayName,id,parents]]"
def responseRaw = httpRequest([ method: "GET", url: url ])
def response = toJSON(responseRaw)

def graphAction = response?.actions?.find { it._class == "org.jenkinsci.plugins.workflow.job.views.FlowGraphAction" }

return graphAction?.nodes
}

def getFailedSteps() {
def steps = getSteps()
def failedSteps = steps?.findAll { it.iconColor == "red" && it._class == "org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode" }
failedSteps.each { step ->
step.logs = "${env.BUILD_URL}execution/node/${step.id}/log".toString()
}

return failedSteps
}

return this
11 changes: 9 additions & 2 deletions vars/prChanges.groovy
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import groovy.transform.Field

public static @Field PR_CHANGES_CACHE = null

def getSkippablePaths() {
return [
Expand Down Expand Up @@ -36,9 +39,13 @@ def areChangesSkippable() {
}

def getChanges() {
withGithubCredentials {
return githubPrs.getChanges(env.ghprbPullId)
if (!PR_CHANGES_CACHE && env.ghprbPullId) {
withGithubCredentials {
PR_CHANGES_CACHE = githubPrs.getChanges(env.ghprbPullId)
}
}

return PR_CHANGES_CACHE
}

def getChangedFiles() {
Expand Down
57 changes: 57 additions & 0 deletions vars/whenChanged.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
whenChanged('some/path') { yourCode() } can be used to execute pipeline code in PRs only when changes are detected on paths that you specify.
The specified code blocks will also always be executed during the non-PR jobs for tracked branches.
You have the option of passing in path prefixes, or regexes. Single or multiple.
Path specifications are NOT globby, they are only prefixes.
Specifying multiple will treat them as ORs.
Example Usages:
whenChanged('a/path/prefix/') { someCode() }
whenChanged(startsWith: 'a/path/prefix/') { someCode() } // Same as above
whenChanged(['prefix1/', 'prefix2/']) { someCode() }
whenChanged(regex: /\.test\.js$/) { someCode() }
whenChanged(regex: [/abc/, /xyz/]) { someCode() }
*/

def call(String startsWithString, Closure closure) {
return whenChanged([ startsWith: startsWithString ], closure)
}

def call(List<String> startsWithStrings, Closure closure) {
return whenChanged([ startsWith: startsWithStrings ], closure)
}

def call(Map params, Closure closure) {
if (!githubPr.isPr()) {
return closure()
}

def files = prChanges.getChangedFiles()
def hasMatch = false

if (params.regex) {
params.regex = [] + params.regex
print "Checking PR for changes that match: ${params.regex.join(', ')}"
hasMatch = !!files.find { file ->
params.regex.find { regex -> file =~ regex }
}
}

if (!hasMatch && params.startsWith) {
params.startsWith = [] + params.startsWith
print "Checking PR for changes that start with: ${params.startsWith.join(', ')}"
hasMatch = !!files.find { file ->
params.startsWith.find { str -> file.startsWith(str) }
}
}

if (hasMatch) {
print "Changes found, executing pipeline."
closure()
} else {
print "No changes found, skipping."
}
}

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

import Boom from 'boom';
import { i18n } from '@kbn/i18n';
import { AlertType } from '../../common';
import { AlertNavigationHandler } from './types';
Expand Down Expand Up @@ -36,7 +35,7 @@ export class AlertNavigationRegistry {

public registerDefault(consumer: string, handler: AlertNavigationHandler) {
if (this.hasDefaultHandler(consumer)) {
throw Boom.badRequest(
throw new Error(
i18n.translate('xpack.alerting.alertNavigationRegistry.register.duplicateDefaultError', {
defaultMessage: 'Default Navigation within "{consumer}" is already registered.',
values: {
Expand All @@ -54,7 +53,7 @@ export class AlertNavigationRegistry {

public register(consumer: string, alertType: AlertType, handler: AlertNavigationHandler) {
if (this.hasTypedHandler(consumer, alertType)) {
throw Boom.badRequest(
throw new Error(
i18n.translate('xpack.alerting.alertNavigationRegistry.register.duplicateNavigationError', {
defaultMessage:
'Navigation for Alert type "{alertType}" within "{consumer}" is already registered.',
Expand All @@ -78,7 +77,7 @@ export class AlertNavigationRegistry {
return (consumerHandlers.get(alertType.id) ?? consumerHandlers.get(DEFAULT_HANDLER))!;
}

throw Boom.badRequest(
throw new Error(
i18n.translate('xpack.alerting.alertNavigationRegistry.get.missingNavigationError', {
defaultMessage:
'Navigation for Alert type "{alertType}" within "{consumer}" is not registered.',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ describe('cluster_serialization', () => {
expect(() => deserializeCluster('foo', 'bar')).toThrowError();
});

it('should deserialize a complete cluster object', () => {
it('should deserialize a complete default cluster object', () => {
expect(
deserializeCluster('test_cluster', {
seeds: ['localhost:9300'],
connected: true,
mode: 'sniff',
num_nodes_connected: 1,
max_connections_per_cluster: 3,
initial_connect_timeout: '30s',
Expand All @@ -29,6 +30,7 @@ describe('cluster_serialization', () => {
})
).toEqual({
name: 'test_cluster',
mode: 'sniff',
seeds: ['localhost:9300'],
isConnected: true,
connectedNodesCount: 1,
Expand All @@ -40,6 +42,37 @@ describe('cluster_serialization', () => {
});
});

it('should deserialize a complete "proxy" mode cluster object', () => {
expect(
deserializeCluster('test_cluster', {
proxy_address: 'localhost:9300',
mode: 'proxy',
connected: true,
num_proxy_sockets_connected: 1,
max_proxy_socket_connections: 3,
initial_connect_timeout: '30s',
skip_unavailable: false,
server_name: 'my_server_name',
transport: {
ping_schedule: '-1',
compress: false,
},
})
).toEqual({
name: 'test_cluster',
mode: 'proxy',
proxyAddress: 'localhost:9300',
isConnected: true,
connectedSocketsCount: 1,
proxySocketConnections: 3,
initialConnectTimeout: '30s',
skipUnavailable: false,
transportPingSchedule: '-1',
transportCompress: false,
serverName: 'my_server_name',
});
});

it('should deserialize a cluster object without transport information', () => {
expect(
deserializeCluster('test_cluster', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ export interface ClusterEs {
ping_schedule?: string;
compress?: boolean;
};
address?: string;
max_socket_connections?: number;
num_sockets_connected?: number;
proxy_address?: string;
max_proxy_socket_connections?: number;
num_proxy_sockets_connected?: number;
server_name?: string;
}

export interface Cluster {
Expand Down Expand Up @@ -77,9 +78,10 @@ export function deserializeCluster(
initial_connect_timeout: initialConnectTimeout,
skip_unavailable: skipUnavailable,
transport,
address: proxyAddress,
max_socket_connections: proxySocketConnections,
num_sockets_connected: connectedSocketsCount,
proxy_address: proxyAddress,
max_proxy_socket_connections: proxySocketConnections,
num_proxy_sockets_connected: connectedSocketsCount,
server_name: serverName,
} = esClusterObject;

let deserializedClusterObject: Cluster = {
Expand All @@ -94,6 +96,7 @@ export function deserializeCluster(
proxyAddress,
proxySocketConnections,
connectedSocketsCount,
serverName,
};

if (transport) {
Expand Down
7 changes: 0 additions & 7 deletions x-pack/plugins/remote_clusters/server/routes/api/get_route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,9 @@ export const register = (deps: RouteDependencies): void => {
? get(clusterSettings, `persistent.cluster.remote[${clusterName}].proxy`, undefined)
: undefined;

// server_name is not available via the GET /_remote/info API, so we get it from the cluster settings
// Per https://github.com/elastic/kibana/pull/26067#issuecomment-441848124, we only look at persistent settings
const serverName = isPersistent
? get(clusterSettings, `persistent.cluster.remote[${clusterName}].server_name`, undefined)
: undefined;

return {
...deserializeCluster(clusterName, cluster, deprecatedProxyAddress),
isConfiguredByNode,
serverName,
};
});

Expand Down
21 changes: 20 additions & 1 deletion x-pack/test/accessibility/apps/login_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,33 @@ export default function({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.security.forceLogout();
});

it('meets a11y requirements', async () => {
it('login page meets a11y requirements', async () => {
await PageObjects.common.navigateToApp('login');

await retry.waitFor(
'login page visible',
async () => await testSubjects.exists('loginSubmit')
);
await a11y.testAppSnapshot();
});

it('User can login with a11y requirements', async () => {
await PageObjects.security.login();
await a11y.testAppSnapshot();
});

it('Wrong credentials message meets a11y requirements', async () => {
await PageObjects.security.loginPage.login('wrong-user', 'wrong-password', {
expectSuccess: false,
});
await PageObjects.security.loginPage.getErrorMessage();
await a11y.testAppSnapshot();
});

it('Logout message acknowledges a11y requirements', async () => {
await PageObjects.security.login();
await PageObjects.security.logout();
await testSubjects.getVisibleText('loginInfoMessage');
await a11y.testAppSnapshot();
});
});
Expand Down
3 changes: 2 additions & 1 deletion x-pack/test/functional/apps/uptime/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
const pageObjects = getPageObjects(['uptime']);
const es = getService('es');

describe('uptime settings page', () => {
// Flaky https://github.com/elastic/kibana/issues/60866
describe.skip('uptime settings page', () => {
const settingsPage = () => pageObjects.uptime.settings;
beforeEach('navigate to clean app root', async () => {
// make 10 checks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,11 @@ export function TriggersActionsPageProvider({ getService }: FtrProviderContext)
const table = await find.byCssSelector('[data-test-subj="alertsList"] table');
const $ = await table.parseDomContent();
const rows = $.findTestSubjects('alert-row').toArray();
expect(rows.length).not.to.eql(0);
expect(rows.length).to.eql(0);
const emptyRow = await find.byCssSelector(
'[data-test-subj="alertsList"] table .euiTableRow'
);
expect(await emptyRow.getVisibleText()).not.to.eql('No items found');
expect(await emptyRow.getVisibleText()).to.eql('No items found');
});
return true;
},
Expand Down

0 comments on commit 4122b2e

Please sign in to comment.