Useful events and config.

JSON build info is now emitted in the published events and the plugin is
configurable. Config option include whether or not events should be
emitted (global option and per project option) and the TCP port to
listen on.
This commit is contained in:
Clark Boylan 2013-01-23 16:10:54 -08:00
parent 28a4fa86e1
commit fd1480b87d
10 changed files with 433 additions and 44 deletions

View File

@ -70,13 +70,17 @@
<version>2.1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.zeromq</groupId>
<artifactId>jzmq</artifactId>
<version>2.1.0-SNAPSHOT</version>
<classifier>native-${os.arch}-${native.os}</classifier>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
<profiles>

View File

@ -0,0 +1,92 @@
/*
* Copyright Authors of the Jenkins Notification Plugin
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jenkinsci.plugins.ZMQEventPublisher;
import hudson.Extension;
import hudson.model.Job;
import hudson.model.JobProperty;
import hudson.model.JobPropertyDescriptor;
import hudson.model.AbstractProject;
import hudson.util.FormValidation;
import java.util.ArrayList;
import java.util.List;
import net.sf.json.JSON;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
public class HudsonNotificationProperty extends
JobProperty<AbstractProject<?, ?>> {
final public boolean enabled;
@DataBoundConstructor
public HudsonNotificationProperty(boolean enabled) {
this.enabled = enabled;
}
public boolean isEnabled() {
return enabled;
}
@Override
public HudsonNotificationPropertyDescriptor getDescriptor() {
return (HudsonNotificationPropertyDescriptor) super.getDescriptor();
}
@Extension
public static final class HudsonNotificationPropertyDescriptor extends JobPropertyDescriptor {
private boolean globallyEnabled;
private int port;
public HudsonNotificationPropertyDescriptor() {
globallyEnabled = false;
port = 8888;
load();
}
@Override
public boolean isApplicable(@SuppressWarnings("rawtypes") Class<? extends Job> jobType) {
return true;
}
public String getDisplayName() {
return "Jenkins ZMQ Event Publisher";
}
@Override
public boolean configure(StaplerRequest staplerRequest, JSONObject json) throws FormException {
globallyEnabled = json.getBoolean("globallyEnabled");
port = json.getInt("port");
save();
return true;
}
public boolean isGloballyEnabled() {
return globallyEnabled;
}
public int getPort() {
return port;
}
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright Authors of the Jenkins Notification Plugin
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jenkinsci.plugins.ZMQEventPublisher;
import hudson.EnvVars;
import hudson.model.AbstractBuild;
import hudson.model.Hudson;
import hudson.model.Job;
import hudson.model.ParameterValue;
import hudson.model.ParametersAction;
import hudson.model.Run;
import hudson.model.TaskListener;
import java.io.IOException;
import java.util.List;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.jenkinsci.plugins.ZMQEventPublisher.model.BuildState;;
import org.jenkinsci.plugins.ZMQEventPublisher.model.JobState;;
public enum Phase {
STARTED, COMPLETED, FINISHED;
@SuppressWarnings({ "unchecked", "rawtypes" })
public String handlePhase(Run run, String status, TaskListener listener) {
HudsonNotificationProperty property = (HudsonNotificationProperty) run.getParent().getProperty(HudsonNotificationProperty.class);
if (property != null) {
HudsonNotificationProperty.HudsonNotificationPropertyDescriptor globalProperty = property.getDescriptor();
if (globalProperty.isGloballyEnabled() || property.isEnabled()) {
return buildMessage(run.getParent(), run, status);
}
}
return null;
}
private Gson gson = new GsonBuilder().setFieldNamingPolicy(
FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
private String buildMessage(Job job, Run run, String status) {
JobState jobState = new JobState();
jobState.setName(job.getName());
jobState.setUrl(job.getUrl());
BuildState buildState = new BuildState();
buildState.setNumber(run.number);
buildState.setUrl(run.getUrl());
buildState.setPhase(this);
buildState.setStatus(status);
String rootUrl = Hudson.getInstance().getRootUrl();
if (rootUrl != null) {
buildState.setFullUrl(rootUrl + run.getUrl());
}
jobState.setBuild(buildState);
ParametersAction paramsAction = run.getAction(ParametersAction.class);
if (paramsAction != null && run instanceof AbstractBuild) {
AbstractBuild build = (AbstractBuild) run;
EnvVars env = new EnvVars();
for (ParameterValue value : paramsAction.getParameters())
if (!value.isSensitive())
value.buildEnvVars(build, env);
buildState.setParameters(env);
}
return gson.toJson(jobState);
}
}

View File

@ -1,71 +1,121 @@
/*
* Copyright 2013 Hewlett-Packard Development Company, L.P.
* Copyright Authors of the Jenkins Notification Plugin
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.jenkinsci.plugins.ZMQEventPublisher;
import hudson.Extension;
import hudson.EnvVars;
import hudson.model.AbstractBuild;
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.model.listeners.RunListener;
import org.zeromq.ZMQ;
import java.io.IOException;
import java.lang.InterruptedException;
/*
* Listener to publish Jenkins build events through ZMQ
*
* @author Clark Boylan
*/
@Extension
public class RunListenerImpl extends RunListener<AbstractBuild> {
private final int port;
public class RunListenerImpl extends RunListener<Run> {
private int port;
private String bind_addr;
private ZMQ.Context context;
private ZMQ.Socket publisher;
private final String bind_addr;
public RunListenerImpl() {
super(AbstractBuild.class);
this.port = 8888;
this.context = ZMQ.context(1);
this.publisher = context.socket(ZMQ.PUB);
bind_addr = String.format("tcp://*:%d", this.port);
this.publisher.bind(bind_addr);
super(Run.class);
context = ZMQ.context(1);
}
@Override
public void onCompleted(AbstractBuild build, TaskListener listener) {
String update = String.format("onCompleted");
try {
EnvVars env = build.getEnvironment(listener);
update = update + " " + env.toString();
} catch (IOException e) {
} catch (InterruptedException e) {
private int getPort(Run build) {
HudsonNotificationProperty property = (HudsonNotificationProperty) build.getParent().getProperty(HudsonNotificationProperty.class);
if (property != null) {
HudsonNotificationProperty.HudsonNotificationPropertyDescriptor globalProperty = property.getDescriptor();
return globalProperty.getPort();
}
return 8888;
}
private void bindSocket(Run build) {
int tmpPort = getPort(build);
if (publisher == null) {
port = tmpPort;
publisher = context.socket(ZMQ.PUB);
bind_addr = String.format("tcp://*:%d", port);
publisher.bind(bind_addr);
}
else if (tmpPort != port) {
publisher.unbind(bind_addr);
publisher.close();
publisher = context.socket(ZMQ.PUB);
port = tmpPort;
bind_addr = String.format("tcp://*:%d", port);
publisher.bind(bind_addr);
}
this.publisher.send(update.getBytes(), 0);
}
@Override
public void onDeleted(AbstractBuild build) {
public void onCompleted(Run build, TaskListener listener) {
String event = "onCompleted";
String json = Phase.COMPLETED.handlePhase(build, getStatus(build), listener);
if (json != null) {
bindSocket(build);
event = event + " " + json;
publisher.send(event.getBytes(), 0);
}
}
/*
@Override
public void onDeleted(Run build) {
String update = String.format("onDeleted");
this.publisher.send(update.getBytes(), 0);
bindSocket(build);
publisher.send(update.getBytes(), 0);
}
*/
@Override
public void onFinalized(AbstractBuild build) {
String update = String.format("onFinalized");
this.publisher.send(update.getBytes(), 0);
}
@Override
public void onStarted(AbstractBuild build, TaskListener listener) {
String update = String.format("onStarted");
try {
EnvVars env = build.getEnvironment(listener);
update = update + " " + env.toString();
} catch (IOException e) {
} catch (InterruptedException e) {
public void onFinalized(Run build) {
String event = "onFinalized";
String json = Phase.FINISHED.handlePhase(build, getStatus(build), TaskListener.NULL);
if (json != null) {
bindSocket(build);
event = event + " " + json;
publisher.send(event.getBytes(), 0);
}
this.publisher.send(update.getBytes(), 0);
}
@Override
public void onStarted(Run build, TaskListener listener) {
String event = "onStarted";
String json = Phase.STARTED.handlePhase(build, getStatus(build), listener);
if (json != null) {
bindSocket(build);
event = event + " " + json;
publisher.send(event.getBytes(), 0);
}
}
private String getStatus(Run r) {
Result result = r.getResult();
String status = null;
if (result != null) {
status = result.toString();
}
return status;
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright Authors of the Jenkins Notification Plugin
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jenkinsci.plugins.ZMQEventPublisher.model;
import org.jenkinsci.plugins.ZMQEventPublisher.Phase;
import java.util.Map;
public class BuildState {
private String fullUrl;
private int number;
private Phase phase;
private String status;
private String url;
private Map<String, String> parameters;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public Phase getPhase() {
return phase;
}
public void setPhase(Phase phase) {
this.phase = phase;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getFullUrl() {
return fullUrl;
}
public void setFullUrl(String fullUrl) {
this.fullUrl = fullUrl;
}
public Map<String, String> getParameters() {
return parameters;
}
public void setParameters(Map<String, String> params) {
this.parameters = params;
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright Authors of the Jenkins Notification Plugin
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jenkinsci.plugins.ZMQEventPublisher.model;
public class JobState {
private String name;
private String url;
private BuildState build;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public BuildState getBuild() {
return build;
}
public void setBuild(BuildState build) {
this.build = build;
}
}

View File

@ -0,0 +1,9 @@
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define"
xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<f:section title="ZMQ Event Publisher">
<f:entry title="ZMQ Events" field="enabled">
<f:checkbox title="Check if ZMQ events should be published for this project." name="zmqpublisher.enabled" checked="${HudsonNotificationProperty.isEnabled()}" />
</f:entry>
</f:section>
</j:jelly>

View File

@ -0,0 +1,12 @@
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<f:section title="ZMQ Event Publisher">
<f:entry title="Enable on all Jobs"
description="Check if we should publish events for all jobs.">
<f:checkbox name="zmqpublisher.globallyEnabled" checked="${descriptor.isGloballyEnabled()}" />
</f:entry>
<f:entry title="TCP port to publish on"
description="Bind to this TCP port and publish events on it">
<f:textbox name="zmqpublisher.port" value="${descriptor.getPort()}" />
</f:entry>
</f:section>
</j:jelly>

View File

@ -1,2 +0,0 @@
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
</j:jelly>

View File

@ -0,0 +1,6 @@
<div>
<p>
Plugin that will publish Jenkins Job run events on the specified port
via ZMQ PUB SUB.
</p>
</div>