Trying to use the following piece of code to trigger email notifications for a multi-branch pipeline job:
1 def emailNotification() {
2 def to = emailextrecipients([[$class: 'CulpritsRecipientProvider'],
3 [$class: 'DevelopersRecipientProvider'],
4 [$class: 'RequesterRecipientProvider']])
5
6 //def to = "firstname.lastname#domain.com"
7 //String currentResult = currentBuild.result
8 String currentResult = manager.build.getResult()
9 echo "CurrentResult1=${currentResult}"
10 echo "CurrentResult2=${manager.build.getResult()}"
11 echo "CurrentResult3=${manager.build.result}"
12 String previousResult = currentBuild.getPreviousBuild().result
13
14 def causes = currentBuild.rawBuild.getCauses()
15 // E.g. 'started by user', 'triggered by scm change'
16 def cause = null
17 if (!causes.isEmpty()) {
18 cause = causes[0].getShortDescription()
19 }
20
21 // Ensure we don't keep a list of causes, or we get
22 // "java.io.NotSerializableException: hudson.model.Cause$UserIdCause"
23 // see http://stackoverflow.com/a/37897833/509706
25 causes = null
26
27 String subject = "${env.JOB_NAME} ${env.BUILD_NUMBER}: ${currentResult}"
28
29 String body = """
30 <p>Triggered by: <b>${cause}</b></p>
31
32 <p>Last build result: <b>${previousResult}</b></p>
33
34
35 <p>Build <b>${env.BUILD_NUMBER}</b> ran on <b>${env.NODE_NAME}</b> and terminated with <b>${currentResult}</b>.
36 </p>
37
38 <p>See: ${env.BUILD_URL}</p>
39
40 """
41
42 String log = currentBuild.rawBuild.getLog(40).join('\n')
43 if (currentBuild != 'SUCCESS') {
44 body = body + """
45 <h2>Last lines of output</h2>
46 <pre>${log}</pre>
47 """
48 }
49
50 if (to != null && !to.isEmpty()) {
51 // Email on any failures, and on first success.
52 if (currentResult != 'SUCCESS' || currentResult != previousResult) {
53 mail to: to, subject: subject, body: body, mimeType: "text/html"
54 }
55 echo 'Sent email notification'
56 }
57 }
Now, the problems that I'm facing:
def to = emailextrecipients... is not working. I found this and this Jenkins Jira issues that this may be the causes, but no workaround. Although it seems weird that if the build is started manually, say by me a user authenticated through Github Oauth, the mail can be sent. If the Github is starting the build through the webhook, I'm getting this in the Jenkins logs:
Not sending mail to user firstname.lastname#domain.com with no
permission to view
The second issue that I'm seeing is with the PostBuild email trigger.
The Pipeline looks like this:
def emailNotification() {
//the one from above
}
try {
stage('Stage1') {
/*
creating multiple nodes based on an array provided
each node will execute:
checkout scm
buildSolution() //custom method defined
*/
parallel <stuff_above>
}
stage('Stage2') {
//do other stuff
parallel <other_stuff_above>
}
} finally {
emailNotification()
}
The echoes from above (rows 9-11) are all showing null
CurrentResult1=null
CurrentResult2=null
CurrentResult3=null
Using currentBuild.currentResult will show me only SUCCESS or FAILED, but not UNSTABLE, in case some of the tests failed.
Any ideas where the problem is?
Build status is null until something sets it or until the job finishes. Are you using any unit test steps that would cause the build to be unstable?
You don't need to use emailextrecipients instead use.
emailext body: body, mimeType: 'text/html', recipientProviders: [
[$class: 'CulpritsRecipientProvider'],
[$class: 'DevelopersRecipientProvider'],
[$class: 'RequesterRecipientProvider']], subject: subject
Not sending mail to user firstname.lastname#domain.com with no
permission to view
Means that either no jenkins user has this email address associated or the user it is associated with does not have permission to the job
Also for causes put that logic inside a different function and add #NonCPS annotation which will stop jenkins trying to serialise state while that function is running, as you currently have it there is a small chance it will still break with that exception, see https://stackoverflow.com/a/38439681/963402
Related
I have a heading <h4>Big offer!</h4> on the page, when I first ran my tests I got:
Expected: "Big offer!"
Received: <h4>Big offer!</h4>
35 | const switchToggle = screen.getByRole('checkbox');
36 | expect(switchToggle.checked).toEqual(true);
> 37 | expect(titleEl).toEqual(title);
Ok, it get's correct file, so I changed my code to actually check heading level and text, but it failed:
Expected: "Big offer!"
Received: undefined
35 | const switchToggle = screen.getByRole('checkbox');
36 | expect(switchToggle.checked).toEqual(true);
> 37 | expect(titleEl.name).toEqual(title);
| ^
38 | expect(titleEl.level).toEqual(4);
I always thought that .name is equal to getByText(). I commented out a line and tried checking for level, and it failed again:
Expected: 4
Received: undefined
36 | expect(switchToggle.checked).toEqual(true);
37 | //expect(titleEl.name).toEqual(title);
> 38 | expect(titleEl.level).toEqual(4);
I don't understand why my test cases failed. Code for test was:
const title = 'Big offer!';
render(<Component title={title}/>);
const titleEl = screen.getByRole('heading');
expect(titleEl).toEqual(title);
expect(titleEl.level).toEqual(4);
It wont work because screen.getByRole (or others querys) returns an HTMLElement (in your case returns a HTMLHeadingElement that inherits HTMLElement). And it doesnt have name or level properties. You can check more here.
To check the text rendered you should use:
expect(titleEl).toHaveTextContent(title);
And for h4 type check you just need to filter it on query:
const titleEl = screen.getByRole('heading', { level: 4 });
The full test:
it('test search input', async () => {
const title = 'Big offer!';
render(<SearchBox title={title} />);
const titleEl = screen.getByRole('heading', { level: 4 });
expect(titleEl).toBeInTheDocument();
expect(titleEl).toHaveTextContent(title);
});
I am trying to call a 3rd Party API when a Client goes to a specific route using Swift and Vapor 3; however, I am met with errors despite being able to make the call.
I have taken steps to ensure the error is caused by the get request to the API. Currently, I do nothing with the request other than print the response. Removing the request stops the error from happening.
// Route the user to HomePage
router.get { req -> Future<View> in
let token: String = "THATS_MY_TOKEN"
let client = try req.client()
let bcmsrequest = try client.get("https://api.buttercms.com/v2/posts/?page=1&page_size=10&auth_token=" + token)
print(bcmsrequest)
print("This line prints.")
// Removing the above get request stops the error below from happening
return try req.view().render("welcome")
}
// Here is the stdout printed by the server process:
Server starting on http://localhost:8080
NIO.EventLoopFuture<Vapor.Response>
This line prints.
2019-07-27 11:49:12.249196-0400 Run[6348:121267] [] nw_endpoint_get_type called with null endpoint
2019-07-27 11:49:12.249420-0400 Run[6348:121267] [] nw_endpoint_get_type called with null endpoint, dumping backtrace:
[x86_64] libnetcore-1872
0 libnetwork.dylib 0x00007fff6d188fc8 __nw_create_backtrace_string + 120
1 libnetwork.dylib 0x00007fff6ce12af4 nw_endpoint_get_type + 180
2 libboringssl.dylib 0x00007fff6b6e3af2 nw_protocol_boringssl_get_subject_name + 178
3 libboringssl.dylib 0x00007fff6b6e6997 nw_protocol_boringssl_connected + 916
4 libnetwork.dylib 0x00007fff6ce5d145 nw_socket_handle_socket_event + 1733
5 libdispatch.dylib 0x00000001017fd82f _dispatch_client_callout + 8
6 libdispatch.dylib 0x0000000101800689 _dispatch_continuation_pop + 585
7 libdispatch.dylib 0x0000000101816608 _dispatch_source_invoke + 2135
8 libdispatch.dylib 0x0000000101807665 _dispatch_workloop_invoke + 3477
9 libdispatch.dylib 0x0000000101813025 _dispatch_workloop_worker_thread + 676
10 libsystem_pthread.dylib 0x000000010188f343 _pthread_wqthread.cold.1 + 125
11 libsystem_pthread.dylib 0x0000000101889196 _pthread_wqthread + 203
12 libsystem_pthread.dylib 0x0000000101889057 start_wqthread + 15
I can see that a Future Object is being printed out, I would expect to see response content (a JSON string) - but I believe that the response has no content and is actually failing to make a request at all.
I can see that a Future Object is being printed out, I would expect to see response content
So this is the crux of the problem. Because Vapor is asynchronous, you're printing the Future<Response> as soon as you send the request, meaning it hasn't returned yet. If you change it to:
router.get { req -> Future<View> in
let token: String = "THATS_MY_TOKEN"
let client = try req.client()
let bcmsrequest = try client.get("https://api.buttercms.com/v2/posts/?page=1&page_size=10&auth_token=" + token)
return bcmsrequest.flatMap { response in
print(response)
return try req.view().render("welcome")
}
}
You'll see the full response body and see what errors you're getting (if any)
I want to log a activity which starts in the scope of a different class and ends in the scope of another class. Using TrackingLogger.getCurrentActivity() throws an exception as there is no activity in memory.Now if I try to store the activity in static HashMap in singleton Class and retrieve it in the other class, the snapshot of activity is showing confusing values. Here is what I am trying to do :
In first Class :
TrackingLogger tracker = TrackingLogger.getInstance(this.getClass()) ;
TrackingActivity activity = tracker.newActivity(); //
activity.start();
CacheUtil.addTrackingActivity("logger",activity); // Store it in static Hashmap in singleton class
In Second Class :
TrackingLogger tracker = TrackingLogger.getInstance(this.getClass());
TrackingActivity activity = CacheUtil.getTrackingActivity("logger");
activity.stop();
tracker.tnt(activity);
this is the snapshot generated :
18:37:39,780 INFO [Nastel TNT4J] {status: 'END' | time: '2014-07-30 13:07:39.778000 UTC' | sev: 'INFO' | type: 'ACTIVITY' | name: 'NOOP' | usec: '72827000' | wait.usec: '21000' | start.time: '2014-07-30 18:36:26.196000 IST' | end.time: '2014-07-30 18:37:39.023000 IST' | pid: '8092' | tid: '90' | id-count: '0' | snap-count: '6' | source: 'APPL=Nastel TNT4J#JVM=8092#FACH13140035#SERVER=FACH13140035#NETADDR=172.25.19.28#DATACENTER=default#GEOADDR=unknown' | track-id: '09d708c4-a6a9-4200-a70f-25c1da838c11'
Snapshot(CPU#Java) {
Count: 4
TotalCpuUsec: 46800.3
TotalCpuUserUsec: 46800.3
}
Snapshot(Thread#Java) {
Count: 69
DaemonCount: 46
StartedCount: 85
PeakCount: 69
BlockedCount: 7
WaitedCount: 0
BlockedUsec: 21000
WaitUsec: 0
}
Snapshot(Memory#Java) {
MaxBytes: 532742144
TotalBytes: 512499712
FreeBytes: 216341712
UsedBytes: 296158000
Usage: 57
}
Snapshot(Copy#GarbageCollector) {
Count: 477
Time: 1940
isValid: true
}
Snapshot(MarkSweepCompact#GarbageCollector) {
Count: 13
Time: 4652
isValid: true
}
Snapshot(Activity#Java) {
TotalCpuUsec: -1778411.4
SlackUsec: 74584411
WallUsec: -1757411.4
BlockedCount: -24
WaitedCount: -1
BlockedUsec: 21000
WaitUsec: 0
OverheadUsec: 24290.215
}}
Am I missing something?? Pls help ..
TrackingLogger.getCurrentActivity() will only return a valid activity if executed within the same thread. The activity handle is stored in thread local. so you most likely calling TrackingLogger.getCurrentActivity() from a different thread and therefore get an exception.
I am not exactly clear where the confusing values are. Can you elaborate?
I am trying to understand the reactive I/O concepts of Play 2.0 framework. In order to get a better understanding from the start I decided to skip the framework's helpers to construct iteratees of different kinds and to write a custom Iteratee from scratch to be used by a BodyParser to parse a request body.
Starting with the information available in Iteratees and ScalaBodyParser docs and two presentations about play reactive I/O this is what I came up with:
import play.api.mvc._
import play.api.mvc.Results._
import play.api.libs.iteratee.{Iteratee, Input}
import play.api.libs.concurrent.Promise
import play.api.libs.iteratee.Input.{El, EOF, Empty}
01 object Upload extends Controller {
02 def send = Action(BodyParser(rh => new SomeIteratee)) { request =>
03 Ok("Done")
04 }
05 }
06
07 case class SomeIteratee(state: Symbol = 'Cont, input: Input[Array[Byte]] = Empty, received: Int = 0) extends Iteratee[Array[Byte], Either[Result, Int]] {
08 println(state + " " + input + " " + received)
09
10 def fold[B](
11 done: (Either[Result, Int], Input[Array[Byte]]) => Promise[B],
12 cont: (Input[Array[Byte]] => Iteratee[Array[Byte], Either[Result, Int]]) => Promise[B],
13 error: (String, Input[Array[Byte]]) => Promise[B]
14 ): Promise[B] = state match {
15 case 'Done => { println("Done"); done(Right(received), Input.Empty) }
16 case 'Cont => cont(in => in match {
17 case in: El[Array[Byte]] => copy(input = in, received = received + in.e.length)
18 case Empty => copy(input = in)
19 case EOF => copy(state = 'Done, input = in)
20 case _ => copy(state = 'Error, input = in)
21 })
22 case _ => { println("Error"); error("Some error.", input) }
23 }
24 }
(Remark: All these things are new to me, so please forgive if something about this is total crap.)
The Iteratee is pretty dumb, it just reads all chunks, sums up the number of received bytes and prints out some messages. Everything works as expected when I call the controller action with some data - I can observe all chunks are received by the Iteratee and when all data is read it switches to state done and the request ends.
Now I started to play around with the code because I wanted to see the behaviour for these two cases:
Switching into state error before all input is read.
Switching into state done before all input is read and returning a Result instead of the Int.
My understanding of the documentation mentioned above is that both should be possible but actually I am not able to understand the observed behaviour. To test the first case I changed line 17 of the above code to:
17 case in: El[Array[Byte]] => copy(state = if(received + in.e.length > 10000) 'Error else 'Cont, input = in, received = received + in.e.length)
So I just added a condition to switch into the error state if more than 10000 bytes were received. The output I get is this:
'Cont Empty 0
'Cont El([B#38ecece6) 8192
'Error El([B#4ab50d3c) 16384
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error
Then the request hangs forever and never ends. My expectation from the above mentioned docs was that when I call the error function inside fold of an Iteratee the processing should be stopped. What is happening here is that the Iteratee's fold method is called several times after error has been called - well and then the request hangs.
When I switch into done state before reading all input the behaviour is quite similar. Changing line 15 to:
15 case 'Done => { println("Done with " + input); done(if (input == EOF) Right(received) else Left(BadRequest), Input.Empty) }
and line 17 to:
17 case in: El[Array[Byte]] => copy(state = if(received + in.e.length > 10000) 'Done else 'Cont, input = in, received = received + in.e.length)
produces the following output:
'Cont Empty 0
'Cont El([B#16ce00a8) 8192
'Done El([B#2e8d214a) 16384
Done with El([B#2e8d214a)
Done with El([B#2e8d214a)
Done with El([B#2e8d214a)
Done with El([B#2e8d214a)
and again the request hangs forever.
My main question is why the request is hanging in the above mentioned cases. If anybody could shed light on this I would greatly appreciate it!
Your understanding is perfectly right and I have just push a fix to master:
https://github.com/playframework/Play20/commit/ef70e641d9114ff8225332bf18b4dd995bd39bcc
Fixed both cases plus exceptions in the Iteratees.
Nice use of copy in case class for doing an Iteratee BTW.
Things must have changed with Play 2.1 - Promise is no longer parametric, and this example no longer compiles.
This page uses Drupals contact form to send emails: http://www.westlake.school.nz/contact
Problem is, the school staff use outlook. When they recieve email from parents etc, the email comes in as
"From: admin#example.com On Behalf Of
Westlake Boys High School"
In Gmail it comes in correctly such as
from Westlake Boys High School
parentsEmailAddress#yahoo.com
Unfortunately, I cannot tell the entire school staff to stop using Outlook and exchange.
Can Drupals drupal_mail function be altered in order to fix this?
From contact.pages.inc:
drupal_mail('contact', 'page_mail', $contact['recipients'], language_default(), $values, $from);
tmsimont explains on this URL (http://api.drupal.org/api/function/drupal_mail#comment-3243) that
the $from parameter will only alter the
From header, not the Sender, Errors-to
or Return-Path.
of the drupal_mail function that is used by contact_mail_page_submit.
with more details (code from the drupal_mail() function)
line 3 - $default_from = variable_get('site_mail', ini_get('sendmail_from'));
line 9 - 'from' => isset($from) ? $from : $default_from,
line 23 - if ($default_from) {
line 24 - // To prevent e-mail from looking like spam, the addresses in the Sender and
line 25 - // Return-Path headers should have a domain authorized to use the originating
line 26 - // SMTP server. Errors-To is redundant, but shouldn't hurt.
line 27 - $headers['From'] = $headers['Sender'] = $headers['Return-Path'] = $headers['Errors-To'] = $default_from;
line 28 - }
line 29 - if ($from) {
line 30 - $headers['From'] = $from;
line 31 - }
line 32 - $message['headers'] = $headers;
So to solve your problem you could implement hook_mail function (http://drupal.org/node/358855#comment-2079266)
More resources can be found here:
1 - http://drupal.org/node/656472
2 - http://drupal.org/node/861562
3 - http://www.nmglc.co.uk/content/overriding-drupals-mail-function