forked from openshift/origin
/
strategy.go
142 lines (121 loc) · 4.52 KB
/
strategy.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
137
138
139
140
141
142
package build
import (
"fmt"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/validation/field"
"github.com/openshift/origin/pkg/build/api"
"github.com/openshift/origin/pkg/build/api/validation"
)
// strategy implements behavior for Build objects
type strategy struct {
runtime.ObjectTyper
kapi.NameGenerator
}
// Decorator is used to compute duration of a build since its not stored in etcd yet
var Decorator = func(obj runtime.Object) error {
build, ok := obj.(*api.Build)
if !ok {
return errors.NewBadRequest(fmt.Sprintf("not a build: %v", build))
}
if build.Status.StartTimestamp == nil {
build.Status.Duration = 0
} else {
completionTimestamp := build.Status.CompletionTimestamp
if completionTimestamp == nil {
dummy := unversioned.Now()
completionTimestamp = &dummy
}
build.Status.Duration = completionTimestamp.Rfc3339Copy().Time.Sub(build.Status.StartTimestamp.Rfc3339Copy().Time)
}
return nil
}
// Strategy is the default logic that applies when creating and updating Build objects.
var Strategy = strategy{kapi.Scheme, kapi.SimpleNameGenerator}
func (strategy) NamespaceScoped() bool {
return true
}
// AllowCreateOnUpdate is false for Build objects.
func (strategy) AllowCreateOnUpdate() bool {
return false
}
func (strategy) AllowUnconditionalUpdate() bool {
return false
}
// PrepareForCreate clears fields that are not allowed to be set by end users on creation.
func (strategy) PrepareForCreate(ctx kapi.Context, obj runtime.Object) {
build := obj.(*api.Build)
if len(build.Status.Phase) == 0 {
build.Status.Phase = api.BuildPhaseNew
}
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (strategy) PrepareForUpdate(ctx kapi.Context, obj, old runtime.Object) {
_ = obj.(*api.Build)
}
// Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) {
}
// Validate validates a new policy.
func (strategy) Validate(ctx kapi.Context, obj runtime.Object) field.ErrorList {
return validation.ValidateBuild(obj.(*api.Build))
}
// ValidateUpdate is the default update validation for an end user.
func (strategy) ValidateUpdate(ctx kapi.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateBuildUpdate(obj.(*api.Build), old.(*api.Build))
}
// CheckGracefulDelete allows a build to be gracefully deleted.
func (strategy) CheckGracefulDelete(obj runtime.Object, options *kapi.DeleteOptions) bool {
return false
}
// Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) *generic.SelectionPredicate {
return &generic.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
build, ok := obj.(*api.Build)
if !ok {
return nil, nil, fmt.Errorf("not a build")
}
return labels.Set(build.ObjectMeta.Labels), api.BuildToSelectableFields(build), nil
},
}
}
type detailsStrategy struct {
strategy
}
// Prepares a build for update by only allowing an update to build details.
// For now, this is the Spec.Revision field
func (detailsStrategy) PrepareForUpdate(ctx kapi.Context, obj, old runtime.Object) {
newBuild := obj.(*api.Build)
oldBuild := old.(*api.Build)
revision := newBuild.Spec.Revision
*newBuild = *oldBuild
newBuild.Spec.Revision = revision
}
// Validates that an update is valid by ensuring that no Revision exists and that it's not getting updated to blank
func (detailsStrategy) ValidateUpdate(ctx kapi.Context, obj, old runtime.Object) field.ErrorList {
newBuild := obj.(*api.Build)
oldBuild := old.(*api.Build)
errors := field.ErrorList{}
if oldBuild.Spec.Revision != nil {
// If there was already a revision, then return an error
errors = append(errors, field.Duplicate(field.NewPath("status", "revision"), oldBuild.Spec.Revision))
}
if newBuild.Spec.Revision == nil {
errors = append(errors, field.Invalid(field.NewPath("status", "revision"), nil, "cannot set an empty revision in build status"))
}
return errors
}
// AllowUnconditionalUpdate returns true to allow a Build with an empty resourceVersion to update the Revision
func (detailsStrategy) AllowUnconditionalUpdate() bool {
return true
}
// DetailsStrategy is the strategy used to manage updates to a Build revision
var DetailsStrategy = detailsStrategy{Strategy}