aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobStatus.java
blob: 3770c9cd6949d2b2a8823221a6233d47c3268cd9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.deployment;

import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;

import java.util.NavigableMap;
import java.util.Objects;
import java.util.Optional;

/**
 * Aggregates information about all known runs of a given job to provide the high level status.
 *
 * @author jonmv
 */
public class JobStatus {

    private final JobId id;
    private final NavigableMap<RunId, Run> runs;
    private final Optional<Run> lastTriggered;
    private final Optional<Run> lastCompleted;
    private final Optional<Run> lastSuccess;
    private final Optional<Run> firstFailing;

    public JobStatus(JobId id, NavigableMap<RunId, Run> runs) {
        this.id = Objects.requireNonNull(id);
        this.runs = Objects.requireNonNull(runs);
        this.lastTriggered = runs.descendingMap().values().stream().findFirst();
        this.lastCompleted = lastCompleted(runs);
        this.lastSuccess = lastSuccess(runs);
        this.firstFailing = firstFailing(runs);
    }

    public JobId id() {
        return id;
    }

    public NavigableMap<RunId, Run> runs() {
        return runs;
    }

    public Optional<Run> lastTriggered() {
        return lastTriggered;
    }

    public Optional<Run> lastCompleted() {
        return lastCompleted;
    }

    public Optional<Run> lastSuccess() {
        return lastSuccess;
    }

    public Optional<Run> firstFailing() {
        return firstFailing;
    }

    public Optional<RunStatus> lastStatus() {
        return lastCompleted().map(Run::status);
    }

    public boolean isSuccess() {
        return lastCompleted.map(last -> ! last.hasFailed()).orElse(false);
    }

    public boolean isRunning() {
        return lastTriggered.isPresent() && ! lastTriggered.get().hasEnded();
    }

    public boolean isNodeAllocationFailure() {
        return lastStatus().isPresent() && lastStatus().get() == RunStatus.nodeAllocationFailure;
    }

    @Override
    public String toString() {
        return "JobStatus{" +
               "id=" + id +
               ", lastTriggered=" + lastTriggered +
               ", lastCompleted=" + lastCompleted +
               ", lastSuccess=" + lastSuccess +
               ", firstFailing=" + firstFailing +
               '}';
    }

    static Optional<Run> lastCompleted(NavigableMap<RunId, Run> runs) {
        return runs.descendingMap().values().stream()
                   .filter(run -> run.hasEnded())
                   .findFirst();
    }

    static Optional<Run> lastSuccess(NavigableMap<RunId, Run> runs) {
        return runs.descendingMap().values().stream()
                   .filter(Run::hasSucceeded)
                   .findFirst();
    }

    static Optional<Run> firstFailing(NavigableMap<RunId, Run> runs) {
        Run failed = null;
        for (Run run : runs.descendingMap().values()) {
            if ( ! run.hasEnded()) continue;
            if ( ! run.hasFailed()) break;
            failed = run;
        }
        return Optional.ofNullable(failed);
    }

}