/*
This file is part of Delivery Pipeline Plugin.
Delivery Pipeline Plugin is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Delivery Pipeline Plugin is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Delivery Pipeline Plugin.
If not, see <http://www.gnu.org/licenses/>.
*/
package se.diabol.jenkins.pipeline.domain.status;
import static java.lang.Math.round;
import static java.lang.System.currentTimeMillis;
import static se.diabol.jenkins.pipeline.domain.status.StatusType.CANCELLED;
import static se.diabol.jenkins.pipeline.domain.status.StatusType.DISABLED;
import static se.diabol.jenkins.pipeline.domain.status.StatusType.FAILED;
import static se.diabol.jenkins.pipeline.domain.status.StatusType.IDLE;
import static se.diabol.jenkins.pipeline.domain.status.StatusType.NOT_BUILT;
import static se.diabol.jenkins.pipeline.domain.status.StatusType.QUEUED;
import static se.diabol.jenkins.pipeline.domain.status.StatusType.RUNNING;
import static se.diabol.jenkins.pipeline.domain.status.StatusType.SUCCESS;
import static se.diabol.jenkins.pipeline.domain.status.StatusType.UNSTABLE;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Result;
import org.apache.commons.collections.CollectionUtils;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import se.diabol.jenkins.pipeline.domain.AbstractItem;
import se.diabol.jenkins.pipeline.domain.status.promotion.AbstractPromotionStatusProvider;
import se.diabol.jenkins.pipeline.domain.status.promotion.PromotionStatus;
import se.diabol.jenkins.pipeline.util.PipelineUtils;
import se.diabol.jenkins.pipeline.util.ProjectUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ExportedBean(defaultVisibility = AbstractItem.VISIBILITY)
public class SimpleStatus implements Status {
private final StatusType type;
private final long lastActivity;
private final long duration;
private final boolean promoted;
private final List<PromotionStatus> promotions;
private static PromotionStatusProviderWrapper promotionStatusProviderWrapper = new PromotionStatusProviderWrapper();
public SimpleStatus(StatusType type, long lastActivity, long duration) {
this(type, lastActivity, duration, false, Collections.<PromotionStatus>emptyList());
}
public SimpleStatus(StatusType type, long lastActivity, long duration, boolean promoted,
List<PromotionStatus> promotions) {
this.type = type;
this.lastActivity = lastActivity;
this.duration = duration;
this.promoted = promoted;
this.promotions = promotions;
}
@Exported
public List<PromotionStatus> getPromotions() {
return promotions;
}
@Exported
@Override
public boolean isPromoted() {
return promoted;
}
@Exported
public StatusType getType() {
return type;
}
@Override
public long getLastActivity() {
return lastActivity;
}
@Exported
public String getTimestamp() {
if (lastActivity != -1) {
return PipelineUtils.formatTimestamp(lastActivity);
} else {
return null;
}
}
@Exported
@Override
public long getDuration() {
return duration;
}
@Override
public boolean isIdle() {
return IDLE.equals(type);
}
@Override
public boolean isQueued() {
return QUEUED.equals(type);
}
@Override
public boolean isRunning() {
return RUNNING.equals(type);
}
@Override
@Exported
public boolean isSuccess() {
return SUCCESS.equals(type);
}
@Override
@Exported
public boolean isFailed() {
return FAILED.equals(type);
}
@Override
@Exported
public boolean isUnstable() {
return UNSTABLE.equals(type);
}
@Override
@Exported
public boolean isCancelled() {
return CANCELLED.equals(type);
}
@Override
public boolean isNotBuilt() {
return NOT_BUILT.equals(type);
}
@Override
public boolean isDisabled() {
return DISABLED.equals(type);
}
public static Status resolveStatus(AbstractProject project, AbstractBuild build, AbstractBuild firstBuild) {
if (build == null) {
if (ProjectUtil.isQueued(project, firstBuild)) {
return StatusFactory.queued(project.getQueueItem().getInQueueSince());
} else if (project.isDisabled()) {
return StatusFactory.disabled();
} else {
return StatusFactory.idle();
}
}
if (build.isBuilding()) {
int progress = calculateBuildProgress(build);
return statusWithProgress(build, progress);
}
return getStatusFromResult(build);
}
private static Status statusWithProgress(AbstractBuild build, int progress) {
return StatusFactory.running(
progress, build.getTimeInMillis(), currentTimeMillis() - build.getTimestamp().getTimeInMillis());
}
private static int calculateBuildProgress(AbstractBuild build) {
return calculateBuildProgress(
currentTimeMillis(), build.getTimestamp().getTimeInMillis(), build.getEstimatedDuration());
}
static int calculateBuildProgress(long currentTimeMillis, long timeBuildStarted, long estimatedBuildDuration) {
int progress = (int) round(100.0d * (currentTimeMillis - timeBuildStarted) / estimatedBuildDuration);
if (progress > 100 || estimatedBuildDuration < 0) {
progress = 99;
} else if (progress < 0) {
return 0;
}
return progress;
}
private static Status getStatusFromResult(AbstractBuild build) {
Result result = build.getResult();
if (Result.ABORTED.equals(result)) {
return StatusFactory.cancelled(build.getTimeInMillis(), build.getDuration());
}
if (Result.SUCCESS.equals(result)) {
return StatusFactory.success(build.getTimeInMillis(), build.getDuration(), isBuildPromoted(build),
getPromotionStatusList(build));
}
if (Result.FAILURE.equals(result)) {
return StatusFactory.failed(build.getTimeInMillis(), build.getDuration(), isBuildPromoted(build),
getPromotionStatusList(build));
}
if (Result.UNSTABLE.equals(result)) {
return StatusFactory.unstable(build.getTimeInMillis(), build.getDuration());
}
if (Result.NOT_BUILT.equals(result)) {
return StatusFactory.notBuilt(build.getTimeInMillis(), build.getDuration());
}
throw new IllegalStateException("Result " + result + " not recognized.");
}
private static boolean isBuildPromoted(AbstractBuild build) {
final List<AbstractPromotionStatusProvider> promotionStatusProviders =
SimpleStatus.promotionStatusProviderWrapper.getAllPromotionStatusProviders();
if (CollectionUtils.isNotEmpty(promotionStatusProviders)) {
final AbstractPromotionStatusProvider promotionStatusProvider = promotionStatusProviders.get(0);
if (promotionStatusProvider != null) {
return promotionStatusProvider.isBuildPromoted(build);
}
}
return false;
}
private static List<PromotionStatus> getPromotionStatusList(AbstractBuild build) {
final List<PromotionStatus> promotionStatusList = new ArrayList<PromotionStatus>();
final List<AbstractPromotionStatusProvider> promotionStatusProviders =
SimpleStatus.promotionStatusProviderWrapper.getAllPromotionStatusProviders();
if (CollectionUtils.isNotEmpty(promotionStatusProviders)) {
final AbstractPromotionStatusProvider promotionStatusProvider = promotionStatusProviders.get(0);
if (promotionStatusProvider != null) {
promotionStatusList.addAll(promotionStatusProvider.getPromotionStatusList(build));
}
}
return promotionStatusList;
}
@Override
public String toString() {
return String.valueOf(type);
}
// Decorators to make code unit-testable
static class PromotionStatusProviderWrapper {
public List<AbstractPromotionStatusProvider> getAllPromotionStatusProviders() {
return AbstractPromotionStatusProvider.all();
}
}
// package scope setters for unit testing
static void setPromotionStatusProviderWrapper(PromotionStatusProviderWrapper promotionStatusProviderWrapper) {
SimpleStatus.promotionStatusProviderWrapper = promotionStatusProviderWrapper;
}
}