Skip to content

Commit e21daee

Browse files
Merge branch '7.9' into backport/7.9/pr-73122
2 parents eab2a1a + 33fa9dc commit e21daee

14 files changed

Lines changed: 348 additions & 214 deletions

File tree

.ci/pipeline-library/src/test/KibanaBasePipelineTest.groovy

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ class KibanaBasePipelineTest extends BasePipelineTest {
7878
return helper.callStack.find { it.methodName == name }
7979
}
8080

81+
def fnMocks(String name) {
82+
helper.callStack.findAll { it.methodName == name }
83+
}
84+
8185
void mockFailureBuild() {
8286
props([
8387
buildUtils: [

.ci/pipeline-library/src/test/slackNotifications.groovy

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,67 @@ class SlackNotificationsTest extends KibanaBasePipelineTest {
5959
args.blocks[2].text.text.toString()
6060
)
6161
}
62+
63+
@Test
64+
void 'sendFailedBuild() should call slackSend() with a backup message when first attempt fails'() {
65+
mockFailureBuild()
66+
def counter = 0
67+
helper.registerAllowedMethod('slackSend', [Map.class], { ++counter > 1 })
68+
slackNotifications.sendFailedBuild()
69+
70+
def args = fnMocks('slackSend')[1].args[0]
71+
72+
def expected = [
73+
channel: '#kibana-operations-alerts',
74+
username: 'Kibana Operations',
75+
iconEmoji: ':jenkins:',
76+
color: 'danger',
77+
message: ':broken_heart: elastic / kibana # master #1',
78+
]
79+
80+
expected.each {
81+
assertEquals(it.value.toString(), args[it.key].toString())
82+
}
83+
84+
assertEquals(
85+
":broken_heart: *<http://jenkins.localhost:8080/job/elastic+kibana+master/1/|elastic / kibana # master #1>*" +
86+
"\n\nFirst attempt at sending this notification failed. Please check the build.",
87+
args.blocks[0].text.text.toString()
88+
)
89+
}
90+
91+
@Test
92+
void 'getTestFailures() should truncate list of failures to 10'() {
93+
prop('testUtils', [
94+
getFailures: {
95+
return (1..12).collect {
96+
return [
97+
url: Mocks.TEST_FAILURE_URL,
98+
fullDisplayName: "Failure #${it}",
99+
]
100+
}
101+
},
102+
])
103+
104+
def message = (String) slackNotifications.getTestFailures()
105+
106+
assertTrue("Message ends with truncated indicator", message.endsWith("...and 2 more"))
107+
assertTrue("Message contains Failure #10", message.contains("Failure #10"))
108+
assertTrue("Message does not contain Failure #11", !message.contains("Failure #11"))
109+
}
110+
111+
@Test
112+
void 'shortenMessage() should truncate a long message, but leave parts that fit'() {
113+
assertEquals('Hello\nHello\n[...truncated...]', slackNotifications.shortenMessage('Hello\nHello\nthis is a long string', 29))
114+
}
115+
116+
@Test
117+
void 'shortenMessage() should not modify a short message'() {
118+
assertEquals('Hello world', slackNotifications.shortenMessage('Hello world', 11))
119+
}
120+
121+
@Test
122+
void 'shortenMessage() should truncate an entire message with only one part'() {
123+
assertEquals('[...truncated...]', slackNotifications.shortenMessage('Hello world this is a really long message', 40))
124+
}
62125
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@
458458
"jest-cli": "^25.5.4",
459459
"jest-environment-jsdom-thirteen": "^1.0.1",
460460
"jest-raw-loader": "^1.0.1",
461-
"jimp": "^0.9.6",
461+
"jimp": "^0.14.0",
462462
"json5": "^1.0.1",
463463
"karma": "5.0.2",
464464
"karma-chrome-launcher": "2.2.0",

vars/slackNotifications.groovy

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,35 @@ def dividerBlock() {
1313
return [ type: "divider" ]
1414
}
1515

16+
// If a message is longer than the limit, split it up by '\n' into parts, and return as many parts as will fit within the limit
17+
def shortenMessage(message, sizeLimit = 3000) {
18+
if (message.size() <= sizeLimit) {
19+
return message
20+
}
21+
22+
def truncatedMessage = "[...truncated...]"
23+
24+
def parts = message.split("\n")
25+
message = ""
26+
27+
for(def part in parts) {
28+
if ((message.size() + part.size() + truncatedMessage.size() + 1) > sizeLimit) {
29+
break;
30+
}
31+
message += part+"\n"
32+
}
33+
34+
message += truncatedMessage
35+
36+
return message.size() <= sizeLimit ? message : truncatedMessage
37+
}
38+
1639
def markdownBlock(message) {
1740
return [
1841
type: "section",
1942
text: [
2043
type: "mrkdwn",
21-
text: message,
44+
text: shortenMessage(message, 3000), // 3000 is max text length for `section`s only
2245
],
2346
]
2447
}
@@ -29,7 +52,7 @@ def contextBlock(message) {
2952
elements: [
3053
[
3154
type: 'mrkdwn',
32-
text: message,
55+
text: message, // Not sure what the size limit is here, I tried 10000s of characters and it still worked
3356
]
3457
]
3558
]
@@ -62,7 +85,7 @@ def getTestFailures() {
6285
def messages = []
6386
messages << "*Test Failures*"
6487

65-
def list = failures.collect {
88+
def list = failures.take(10).collect {
6689
def name = it
6790
.fullDisplayName
6891
.split(/\./, 2)[-1]
@@ -73,7 +96,9 @@ def getTestFailures() {
7396

7497
return "• <${it.url}|${name}>"
7598
}.join("\n")
76-
return "*Test Failures*\n${list}"
99+
100+
def moreText = failures.size() > 10 ? "\n• ...and ${failures.size()-10} more" : ""
101+
return "*Test Failures*\n${list}${moreText}"
77102
}
78103

79104
def getDefaultDisplayName() {
@@ -98,6 +123,10 @@ def getStatusIcon() {
98123
return ':broken_heart:'
99124
}
100125

126+
def getBackupMessage(config) {
127+
return "${getStatusIcon()} ${config.title}\n\nFirst attempt at sending this notification failed. Please check the build."
128+
}
129+
101130
def sendFailedBuild(Map params = [:]) {
102131
def config = [
103132
channel: '#kibana-operations-alerts',
@@ -117,14 +146,25 @@ def sendFailedBuild(Map params = [:]) {
117146
blocks << dividerBlock()
118147
blocks << config.context
119148

120-
slackSend(
149+
def resp = slackSend(
121150
channel: config.channel,
122151
username: config.username,
123152
iconEmoji: config.icon,
124153
color: config.color,
125154
message: message,
126155
blocks: blocks
127156
)
157+
158+
if (!resp) {
159+
slackSend(
160+
channel: config.channel,
161+
username: config.username,
162+
iconEmoji: config.icon,
163+
color: config.color,
164+
message: message,
165+
blocks: [markdownBlock(getBackupMessage(config))]
166+
)
167+
}
128168
}
129169

130170
def onFailure(Map options = [:]) {

x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
5353
onClick={() => {
5454
setIsReassignFlyoutOpen(true);
5555
}}
56+
disabled={!agent.active}
5657
key="reassignConfig"
5758
>
5859
<FormattedMessage

x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,18 +90,18 @@ const RowActions = React.memo<{ agent: Agent; onReassignClick: () => void; refre
9090
onClick={() => {
9191
onReassignClick();
9292
}}
93+
disabled={!agent.active}
9394
key="reassignConfig"
9495
>
9596
<FormattedMessage
9697
id="xpack.ingestManager.agentList.reassignActionText"
9798
defaultMessage="Assign new agent config"
9899
/>
99100
</EuiContextMenuItem>,
100-
101101
<AgentUnenrollProvider forceUnenroll={isUnenrolling}>
102102
{(unenrollAgentsPrompt) => (
103103
<EuiContextMenuItem
104-
disabled={!hasWriteCapabilites}
104+
disabled={!hasWriteCapabilites || !agent.active}
105105
icon="cross"
106106
onClick={() => {
107107
unenrollAgentsPrompt([agent.id], 1, () => {

x-pack/plugins/security_solution/cypress/integration/timeline_flyout_button.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { createNewTimeline } from '../tasks/timeline';
1313

1414
import { HOSTS_URL } from '../urls/navigation';
1515

16-
describe('timeline flyout button', () => {
16+
describe.skip('timeline flyout button', () => {
1717
before(() => {
1818
loginAndWaitForPage(HOSTS_URL);
1919
waitForAllHostsToBeLoaded();

x-pack/plugins/security_solution/cypress/tasks/common.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ export const drag = (subject: JQuery<HTMLElement>) => {
2323
clientY: subjectLocation.top,
2424
force: true,
2525
})
26-
.wait(1000)
26+
.wait(3000)
2727
.trigger('mousemove', {
2828
button: primaryButton,
2929
clientX: subjectLocation.left + dndSloppyClickDetectionThreshold,
3030
clientY: subjectLocation.top,
3131
force: true,
3232
})
33-
.wait(1000);
33+
.wait(3000);
3434
};
3535

3636
/** Drags the subject being dragged on the specified drop target, but does not drop it */
@@ -44,9 +44,9 @@ export const dragWithoutDrop = (dropTarget: JQuery<HTMLElement>) => {
4444
export const drop = (dropTarget: JQuery<HTMLElement>) => {
4545
cy.wrap(dropTarget)
4646
.trigger('mousemove', { button: primaryButton, force: true })
47-
.wait(1000)
47+
.wait(3000)
4848
.trigger('mouseup', { force: true })
49-
.wait(1000);
49+
.wait(3000);
5050
};
5151

5252
export const reload = (afterReload: () => void) => {

x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export const dragAndDropColumn = ({
6868
.eq(column)
6969
.then((header) => drag(header));
7070

71-
cy.wait(3000); // wait for DOM updates before moving
71+
cy.wait(5000); // wait for DOM updates before moving
7272

7373
cy.get(DRAGGABLE_HEADER)
7474
.eq(newPosition)

x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ describe('Header', () => {
177177
expect(
178178
wrapper.find('[data-test-subj="timelineImmutableCallOut"]').first().prop('title')
179179
).toEqual(
180-
'This timeline is immutable, therefore not allowed to save it within the security application, though you may continue to use the timeline to search and filter security events'
180+
'This prebuilt timeline template cannot be modified. To make changes, please duplicate this template and make modifications to the duplicate template.'
181181
);
182182
});
183183
});

0 commit comments

Comments
 (0)