-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Expand file tree
/
Copy pathstrategy.go
More file actions
153 lines (130 loc) · 5.53 KB
/
strategy.go
File metadata and controls
153 lines (130 loc) · 5.53 KB
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
143
144
145
146
147
148
149
150
151
152
153
package image
import (
"fmt"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/runtime"
utilruntime "k8s.io/kubernetes/pkg/util/runtime"
"k8s.io/kubernetes/pkg/util/validation/field"
"github.com/openshift/origin/pkg/image/api"
"github.com/openshift/origin/pkg/image/api/validation"
)
// imageStrategy implements behavior for Images.
type imageStrategy struct {
runtime.ObjectTyper
kapi.NameGenerator
}
// Strategy is the default logic that applies when creating and updating
// Image objects via the REST API.
var Strategy = imageStrategy{kapi.Scheme, kapi.SimpleNameGenerator}
// NamespaceScoped is false for images.
func (imageStrategy) NamespaceScoped() bool {
return false
}
// PrepareForCreate clears fields that are not allowed to be set by end users on creation.
// It extracts the latest information from the manifest (if available) and sets that onto the object.
func (s imageStrategy) PrepareForCreate(ctx kapi.Context, obj runtime.Object) {
newImage := obj.(*api.Image)
// ignore errors, change in place
if err := api.ImageWithMetadata(newImage); err != nil {
utilruntime.HandleError(fmt.Errorf("Unable to update image metadata for %q: %v", newImage.Name, err))
}
// clear signature fields that will be later set by server once it's able to parse the content
s.clearSignatureDetails(newImage)
}
// Validate validates a new image.
func (imageStrategy) Validate(ctx kapi.Context, obj runtime.Object) field.ErrorList {
image := obj.(*api.Image)
return validation.ValidateImage(image)
}
// AllowCreateOnUpdate is false for images.
func (imageStrategy) AllowCreateOnUpdate() bool {
return false
}
func (imageStrategy) AllowUnconditionalUpdate() bool {
return false
}
// Canonicalize normalizes the object after validation.
func (imageStrategy) Canonicalize(obj runtime.Object) {
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
// It extracts the latest info from the manifest and sets that on the object. It allows a user
// to update the manifest so that it matches the digest (in case an older server stored a manifest
// that was malformed, it can always be corrected).
func (s imageStrategy) PrepareForUpdate(ctx kapi.Context, obj, old runtime.Object) {
newImage := obj.(*api.Image)
oldImage := old.(*api.Image)
// image metadata cannot be altered
newImage.DockerImageMetadata = oldImage.DockerImageMetadata
newImage.DockerImageMetadataVersion = oldImage.DockerImageMetadataVersion
newImage.DockerImageLayers = oldImage.DockerImageLayers
if oldImage.DockerImageSignatures != nil {
newImage.DockerImageSignatures = nil
for _, v := range oldImage.DockerImageSignatures {
newImage.DockerImageSignatures = append(newImage.DockerImageSignatures, v)
}
}
// allow an image update that results in the manifest matching the digest (the name)
newManifest := newImage.DockerImageManifest
newImage.DockerImageManifest = oldImage.DockerImageManifest
if newManifest != oldImage.DockerImageManifest && len(newManifest) > 0 {
ok, err := api.ManifestMatchesImage(oldImage, []byte(newManifest))
if err != nil {
utilruntime.HandleError(fmt.Errorf("attempted to validate that a manifest change to %q matched the signature, but failed: %v", oldImage.Name, err))
} else if ok {
newImage.DockerImageManifest = newManifest
}
}
newImageConfig := newImage.DockerImageConfig
newImage.DockerImageConfig = oldImage.DockerImageConfig
if newImageConfig != oldImage.DockerImageConfig && len(newImageConfig) > 0 {
ok, err := api.ImageConfigMatchesImage(newImage, []byte(newImageConfig))
if err != nil {
utilruntime.HandleError(fmt.Errorf("attempted to validate that a new config for %q mentioned in the manifest, but failed: %v", oldImage.Name, err))
} else if ok {
newImage.DockerImageConfig = newImageConfig
}
}
if err := api.ImageWithMetadata(newImage); err != nil {
utilruntime.HandleError(fmt.Errorf("Unable to update image metadata for %q: %v", newImage.Name, err))
}
// clear signature fields that will be later set by server once it's able to parse the content
s.clearSignatureDetails(newImage)
}
// ValidateUpdate is the default update validation for an end user.
func (imageStrategy) ValidateUpdate(ctx kapi.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateImageUpdate(old.(*api.Image), obj.(*api.Image))
}
// clearSignatureDetails removes signature details from all the signatures of given image. It also clear all
// the validation data. These data will be set by the server once the signature parsing support is added.
func (imageStrategy) clearSignatureDetails(image *api.Image) {
for i := range image.Signatures {
signature := &image.Signatures[i]
signature.Conditions = nil
signature.ImageIdentity = ""
signature.SignedClaims = nil
signature.Created = nil
signature.IssuedBy = nil
signature.IssuedTo = nil
}
}
// 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(o runtime.Object) (labels.Set, fields.Set, error) {
obj, ok := o.(*api.Image)
if !ok {
return nil, nil, fmt.Errorf("not an image")
}
return labels.Set(obj.Labels), SelectableFields(obj), nil
},
}
}
// SelectableFields returns a field set that can be used for filter selection
func SelectableFields(obj *api.Image) fields.Set {
return generic.ObjectMetaFieldsSet(&obj.ObjectMeta, false)
}