package org.jenkinsci.plugins.environmentdashboard; import hudson.EnvVars; import hudson.Extension; import hudson.FilePath; import hudson.Launcher; import hudson.model.AbstractProject; import hudson.model.Run; import hudson.model.TaskListener; import hudson.tasks.BuildWrapperDescriptor; import hudson.util.FormValidation; import java.io.IOException; import java.io.Serializable; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.Collections; import java.util.List; import java.util.ArrayList; import java.util.logging.Logger; import javax.servlet.ServletException; import jenkins.tasks.SimpleBuildWrapper; import net.sf.json.JSONObject; import org.jenkinsci.Symbol; import org.jenkinsci.plugins.environmentdashboard.utils.DBConnection; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; /** * Class to create Dashboard view. * * author vipin * date 15/10/2014 */ public class DashboardBuilder extends SimpleBuildWrapper implements Serializable { private final String nameOfEnv; private final String componentName; private final String buildNumber; private final String buildJob; private final String packageName; private ArrayList<ListItem> data = new ArrayList<>(); public boolean addColumns = false; static final long serialVersionUID = 42L; @DataBoundConstructor public DashboardBuilder(String nameOfEnv, String componentName, String buildNumber, String buildJob, String packageName, boolean addColumns, ArrayList<ListItem> data) { this.nameOfEnv = nameOfEnv; this.componentName = componentName; this.buildNumber = buildNumber; this.buildJob = buildJob; this.packageName = packageName; if (addColumns) { this.addColumns = addColumns; } else { this.addColumns = false; } // this.data = Collections.emptyList(); if (this.addColumns) { for (ListItem i : data) { if (!i.getColumnName().isEmpty()) { this.data.add(i); } } } } public String getNameOfEnv() { return nameOfEnv; } public String getComponentName() { return componentName; } public String getBuildNumber() { return buildNumber; } public String getBuildJob() { return buildJob; } public String getPackageName() { return packageName; } public List<ListItem> getData() { return data; } @SuppressWarnings("rawtypes") @Override public void setUp(SimpleBuildWrapper.Context context, Run<?, ?> build, FilePath workspace, Launcher launcher, TaskListener listener, EnvVars initialEnvironment) throws IOException, InterruptedException { // PreBuild final Integer numberOfDays = ((getDescriptor().getNumberOfDays() == null) ? Integer.valueOf(30) : getDescriptor().getNumberOfDays()); String passedBuildNumber = build.getEnvironment(listener).expand(buildNumber); String passedEnvName = build.getEnvironment(listener).expand(nameOfEnv); String passedCompName = build.getEnvironment(listener).expand(componentName); String passedBuildJob = build.getEnvironment(listener).expand(buildJob); String passedPackageName = build.getEnvironment(listener).expand(packageName); List<ListItem> passedColumnData = new ArrayList<ListItem>(); if (addColumns) { for (ListItem item : data) { passedColumnData.add(new ListItem(build.getEnvironment(listener).expand(item.columnName), build.getEnvironment(listener).expand(item.contents))); } } String returnComment = null; if (passedPackageName == null) { passedPackageName = ""; } if (!(passedBuildNumber.matches("^\\s*$") || passedEnvName.matches("^\\s*$") || passedCompName.matches("^\\s*$"))) { returnComment = writeToDB(build, listener, passedEnvName, passedCompName, passedBuildNumber, "PRE", passedBuildJob, numberOfDays, passedPackageName, passedColumnData); listener.getLogger().println("Pre-Build Update: " + returnComment); } else { listener.getLogger().println("Environment dashboard not updated: one or more required values were blank"); } // TearDown - This runs post all build steps class TearDownImpl extends SimpleBuildWrapper.Disposer implements Serializable { @Override public void tearDown(Run<?, ?> build, FilePath workspace, Launcher launcher, TaskListener listener) throws IOException, InterruptedException { String passedBuildNumber = build.getEnvironment(listener).expand(buildNumber); String passedEnvName = build.getEnvironment(listener).expand(nameOfEnv); String passedCompName = build.getEnvironment(listener).expand(componentName); String passedBuildJob = build.getEnvironment(listener).expand(buildJob); String passedPackageName = build.getEnvironment(listener).expand(packageName); String doDeploy = build.getEnvironment(listener).expand("$UPDATE_ENV_DASH"); // Pass column data to post-build runQuery List<ListItem> passedColumnData = new ArrayList<ListItem>(); if (addColumns) { for (ListItem item : data) { passedColumnData.add(new ListItem(build.getEnvironment(listener).expand(item.columnName), build.getEnvironment(listener).expand(item.contents))); } } String returnComment = null; if (passedPackageName == null) { passedPackageName = ""; } if (doDeploy == null || (!doDeploy.equals("true") && !doDeploy.equals("false"))) { doDeploy = "true"; } if (doDeploy.equals("true")) { if (!(passedBuildNumber.matches("^\\s*$") || passedEnvName.matches("^\\s*$") || passedCompName.matches("^\\s*$"))) { returnComment = writeToDB(build, listener, passedEnvName, passedCompName, passedBuildNumber, "POST", passedBuildJob, numberOfDays, passedPackageName, passedColumnData); listener.getLogger().println("Post-Build Update: " + returnComment); } } else { if (!(passedBuildNumber.matches("^\\s*$") || passedEnvName.matches("^\\s*$") || passedCompName.matches("^\\s*$"))) { returnComment = writeToDB(build, listener, passedEnvName, passedCompName, passedBuildNumber, "NODEPLOY", passedBuildJob, numberOfDays, passedPackageName, passedColumnData); listener.getLogger().println("Post-Build Update: " + returnComment); } } } } context.setDisposer(new TearDownImpl()); } @SuppressWarnings("rawtypes") private String writeToDB(Run<?, ?> build, TaskListener listener, String envName, String compName, String currentBuildNum, String runTime, String buildJob, Integer numberOfDays, String packageName, List<ListItem> passedColumnData) { String returnComment = null; if (envName.matches("^\\s*$") || compName.matches("^\\s*$")) { returnComment = "WARN: Either Environment name or Component name is empty."; return returnComment; } // Get DB connection Connection conn = DBConnection.getConnection(); Statement stat = null; try { stat = conn.createStatement(); } catch (SQLException e) { returnComment = "WARN: Could not execute statement."; return returnComment; } try { stat.execute( "CREATE TABLE IF NOT EXISTS env_dashboard (envComp VARCHAR(255), jobUrl VARCHAR(255), buildNum VARCHAR(255), buildStatus VARCHAR(255), envName VARCHAR(255), compName VARCHAR(255), created_at TIMESTAMP, buildJobUrl VARCHAR(255), packageName VARCHAR(255));"); } catch (SQLException e) { returnComment = "WARN: Could not create table env_dashboard."; return returnComment; } try { stat.execute("ALTER TABLE env_dashboard ADD IF NOT EXISTS packageName VARCHAR(255);"); } catch (SQLException e) { returnComment = "WARN: Could not alter table env_dashboard."; return returnComment; } String columns = ""; String contents = ""; for (ListItem item : passedColumnData) { columns = columns + ", " + item.columnName; contents = contents + "', '" + item.contents; try { stat.execute("ALTER TABLE env_dashboard ADD COLUMN IF NOT EXISTS " + item.columnName + " VARCHAR;"); } catch (SQLException e) { returnComment = "WARN: Could not alter table env_dashboard to add column " + item.columnName + "."; return returnComment; } } String indexValueofTable = envName + '=' + compName; String currentBuildResult = "UNKNOWN"; if (build.getResult() == null && runTime.equals("PRE")) { currentBuildResult = "RUNNING"; } else if (build.getResult() == null && runTime.equals("POST")) { currentBuildResult = "SUCCESS"; } else if (runTime.equals("NODEPLOY")) { currentBuildResult = "NODEPLOY"; } else { currentBuildResult = build.getResult().toString(); } String currentBuildUrl = build.getUrl(); String buildJobUrl; // Build job is an optional configuration setting if (buildJob.isEmpty()) { buildJobUrl = ""; } else { buildJobUrl = "job/" + buildJob + "/" + currentBuildNum; } String runQuery = null; if (runTime.equals("PRE")) { runQuery = "INSERT INTO env_dashboard (envComp, jobUrl, buildNum, buildStatus, envName, compName, created_at, buildJobUrl, packageName" + columns + ") VALUES( '" + indexValueofTable + "', '" + currentBuildUrl + "', '" + currentBuildNum + "', '" + currentBuildResult + "', '" + envName + "', '" + compName + "' , + current_timestamp, '" + buildJobUrl + "' , '" + packageName + contents + "');"; } else { if (runTime.equals("POST")) { // Updates build no. and additional columns also in post-build update String updateColumns = ""; for (ListItem item : passedColumnData) { updateColumns += "," + item.columnName + "=" + "'" + item.contents + "'"; } runQuery = "UPDATE env_dashboard SET buildNum = '" + currentBuildNum + "', buildStatus = '" + currentBuildResult + "', created_at = current_timestamp " + updateColumns + " WHERE envComp = '" + indexValueofTable + "' AND joburl = '" + currentBuildUrl + "';"; listener.getLogger().println("DEBUG: Final Post-Build runQuery:" + runQuery); } else { if (runTime.equals("NODEPLOY")) { runQuery = "DELETE FROM env_dashboard where envComp = '" + indexValueofTable + "' AND joburl = '" + currentBuildUrl + "';"; } } } try { stat.execute(runQuery); } catch (SQLException e) { returnComment = "Error running query " + runQuery + "."; return returnComment; } if (numberOfDays > 0) { runQuery = "DELETE FROM env_dashboard where created_at <= current_timestamp - " + numberOfDays; try { stat.execute(runQuery); } catch (SQLException e) { returnComment = "Error running delete query " + runQuery + "."; return returnComment; } } try { stat.close(); conn.close(); } catch (SQLException e) { returnComment = "Error closing connection."; return returnComment; } return "Updated Dashboard DB"; } @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl) super.getDescriptor(); } @Symbol("environmentDashboard") @Extension public static final class DescriptorImpl extends BuildWrapperDescriptor { private String numberOfDays = "30"; private Integer parseNumberOfDays; public DescriptorImpl() { load(); } @Override public String getDisplayName() { return "Details for Environment dashboard"; } public FormValidation doCheckNameOfEnv(@QueryParameter String value) throws IOException, ServletException { if (value.length() == 0) return FormValidation.error("Please set an Environment name."); return FormValidation.ok(); } public FormValidation doCheckComponentName(@QueryParameter String value) throws IOException, ServletException { if (value.length() == 0) return FormValidation.error("Please set a Component name."); return FormValidation.ok(); } public FormValidation doCheckBuildNumber(@QueryParameter String value) throws IOException, ServletException { if (value.length() == 0) return FormValidation.error("Please set the Build variable e.g: ${BUILD_NUMBER}."); return FormValidation.ok(); } public FormValidation doCheckNumberOfDays(@QueryParameter String value) throws IOException, ServletException { if (value.length() == 0) { return FormValidation.error("Please set the number of days to retain the DB data."); } else { try { parseNumberOfDays = Integer.parseInt(value); } catch (Exception parseEx) { return FormValidation.error("Please provide an integer value."); } } return FormValidation.ok(); } public boolean isApplicable(AbstractProject<?, ?> item) { return true; } @Override public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { numberOfDays = formData.getString("numberOfDays"); if (numberOfDays == null || numberOfDays.equals("")) { numberOfDays = "30"; } save(); return super.configure(req, formData); } public Integer getNumberOfDays() { return parseNumberOfDays; } } }