/
policy.go
136 lines (124 loc) · 4.71 KB
/
policy.go
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package policy
import (
"fmt"
"strconv"
"k8s.io/klog"
buildv1 "github.com/openshift/api/build/v1"
buildlister "github.com/openshift/client-go/build/listers/build/v1"
buildclient "github.com/openshift/origin/pkg/build/client"
buildutil "github.com/openshift/origin/pkg/build/util"
)
// RunPolicy is an interface that define handler for the build runPolicy field.
// The run policy controls how and when the new builds are 'run'.
type RunPolicy interface {
// IsRunnable returns true of the given build should be executed.
IsRunnable(*buildv1.Build) (bool, error)
// Handles returns true if the run policy handles a specific policy
Handles(buildv1.BuildRunPolicy) bool
}
// GetAllRunPolicies returns a set of all run policies.
func GetAllRunPolicies(lister buildlister.BuildLister, updater buildclient.BuildUpdater) []RunPolicy {
return []RunPolicy{
&ParallelPolicy{BuildLister: lister, BuildUpdater: updater},
&SerialPolicy{BuildLister: lister, BuildUpdater: updater},
&SerialLatestOnlyPolicy{BuildLister: lister, BuildUpdater: updater},
}
}
// ForBuild picks the appropriate run policy for the given build.
func ForBuild(build *buildv1.Build, policies []RunPolicy) RunPolicy {
buildPolicy := buildRunPolicy(build)
for _, s := range policies {
if s.Handles(buildPolicy) {
klog.V(5).Infof("Using %T run policy for build %s/%s", s, build.Namespace, build.Name)
return s
}
}
return nil
}
// hasRunningSerialBuild indicates that there is a running or pending serial
// build. This function is used to prevent running parallel builds because
// serial builds should always run alone.
func hasRunningSerialBuild(lister buildlister.BuildLister, namespace, buildConfigName string) bool {
var hasRunningBuilds bool
buildutil.BuildConfigBuilds(lister, namespace, buildConfigName, func(b *buildv1.Build) bool {
switch b.Status.Phase {
case buildv1.BuildPhasePending, buildv1.BuildPhaseRunning:
switch buildRunPolicy(b) {
case buildv1.BuildRunPolicySerial, buildv1.BuildRunPolicySerialLatestOnly:
hasRunningBuilds = true
}
}
return false
})
return hasRunningBuilds
}
// GetNextConfigBuild returns the build that will be executed next for the given
// build configuration. It also returns the indication whether there are
// currently running builds, to make sure there is no race-condition between
// re-listing the builds.
func GetNextConfigBuild(lister buildlister.BuildLister, namespace, buildConfigName string) ([]*buildv1.Build, bool, error) {
var (
nextBuild *buildv1.Build
hasRunningBuilds bool
previousBuildNumber int64
)
builds, err := buildutil.BuildConfigBuilds(lister, namespace, buildConfigName, func(b *buildv1.Build) bool {
switch b.Status.Phase {
case buildv1.BuildPhasePending, buildv1.BuildPhaseRunning:
hasRunningBuilds = true
case buildv1.BuildPhaseNew:
return true
}
return false
})
if err != nil {
return nil, hasRunningBuilds, err
}
nextParallelBuilds := []*buildv1.Build{}
for i, b := range builds {
buildNumber, err := buildNumber(b)
if err != nil {
return nil, hasRunningBuilds, err
}
if buildRunPolicy(b) == buildv1.BuildRunPolicyParallel {
nextParallelBuilds = append(nextParallelBuilds, b)
}
if previousBuildNumber == 0 || buildNumber < previousBuildNumber {
nextBuild = builds[i]
previousBuildNumber = buildNumber
}
}
nextBuilds := []*buildv1.Build{}
// if the next build is a parallel build, then start all the queued parallel builds,
// otherwise just start the next build if there is one.
if nextBuild != nil && buildRunPolicy(nextBuild) == buildv1.BuildRunPolicyParallel {
nextBuilds = nextParallelBuilds
} else if nextBuild != nil {
nextBuilds = append(nextBuilds, nextBuild)
}
return nextBuilds, hasRunningBuilds, nil
}
// buildNumber returns the given build number.
func buildNumber(build *buildv1.Build) (int64, error) {
annotations := build.GetAnnotations()
if stringNumber, ok := annotations[buildutil.BuildNumberAnnotation]; ok {
return strconv.ParseInt(stringNumber, 10, 64)
}
return 0, fmt.Errorf("build %s/%s does not have %s annotation", build.Namespace, build.Name, buildutil.BuildNumberAnnotation)
}
// buildRunPolicy returns the scheduling policy for the build based on the "queued" label.
func buildRunPolicy(build *buildv1.Build) buildv1.BuildRunPolicy {
labels := build.GetLabels()
if value, found := labels[buildutil.BuildRunPolicyLabel]; found {
switch value {
case "Parallel":
return buildv1.BuildRunPolicyParallel
case "Serial":
return buildv1.BuildRunPolicySerial
case "SerialLatestOnly":
return buildv1.BuildRunPolicySerialLatestOnly
}
}
klog.V(5).Infof("Build %s/%s does not have start policy label set, using default (Serial)", build.Namespace, build.Name)
return buildv1.BuildRunPolicySerial
}