forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 1
/
strategy.go
151 lines (125 loc) · 5.37 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
143
144
145
146
147
148
149
150
151
package image
import (
"context"
"fmt"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
imageapi "github.com/openshift/origin/pkg/image/apis/image"
"github.com/openshift/origin/pkg/image/apis/image/validation"
"github.com/openshift/origin/pkg/image/util"
)
// managedSignatureAnnotation used to be set by image signature import controller as a signature annotation.
const managedSignatureAnnotation = "image.openshift.io/managed-signature"
// imageStrategy implements behavior for Images.
type imageStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating
// Image objects via the REST API.
var Strategy = imageStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
var _ rest.GarbageCollectionDeleteStrategy = imageStrategy{}
func (imageStrategy) DefaultGarbageCollectionPolicy(ctx context.Context) rest.GarbageCollectionPolicy {
return rest.Unsupported
}
// 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 context.Context, obj runtime.Object) {
newImage := obj.(*imageapi.Image)
// ignore errors, change in place
if err := util.InternalImageWithMetadata(newImage); err != nil {
utilruntime.HandleError(fmt.Errorf("Unable to update image metadata for %q: %v", newImage.Name, err))
}
if newImage.Annotations[imageapi.ImageManifestBlobStoredAnnotation] == "true" {
newImage.DockerImageManifest = ""
newImage.DockerImageConfig = ""
}
removeManagedSignatureAnnotation(newImage)
}
// Validate validates a new image.
func (imageStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
image := obj.(*imageapi.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 context.Context, obj, old runtime.Object) {
newImage := obj.(*imageapi.Image)
oldImage := old.(*imageapi.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)
}
}
var err error
// allow an image update that results in the manifest matching the digest (the name)
if newImage.DockerImageManifest != oldImage.DockerImageManifest {
ok := true
if len(newImage.DockerImageManifest) > 0 {
ok, err = util.ManifestMatchesImage(oldImage, []byte(newImage.DockerImageManifest))
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))
}
}
if !ok {
newImage.DockerImageManifest = oldImage.DockerImageManifest
}
}
if newImage.DockerImageConfig != oldImage.DockerImageConfig {
ok := true
if len(newImage.DockerImageConfig) > 0 {
ok, err = util.ImageConfigMatchesImage(newImage, []byte(newImage.DockerImageConfig))
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))
}
}
if !ok {
newImage.DockerImageConfig = oldImage.DockerImageConfig
}
}
if err = util.InternalImageWithMetadata(newImage); err != nil {
utilruntime.HandleError(fmt.Errorf("Unable to update image metadata for %q: %v", newImage.Name, err))
}
if newImage.Annotations[imageapi.ImageManifestBlobStoredAnnotation] == "true" {
newImage.DockerImageManifest = ""
newImage.DockerImageConfig = ""
}
removeManagedSignatureAnnotation(newImage)
}
// ValidateUpdate is the default update validation for an end user.
func (imageStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateImageUpdate(obj.(*imageapi.Image), old.(*imageapi.Image))
}
// removeManagedSignatureAnnotation removes deprecated annotation from image signatures. A bug in image update
// logic allowed to set arbitrary annotations that would otherwise be rejected by validation.
// Resolves rhbz#1557607
func removeManagedSignatureAnnotation(img *imageapi.Image) {
for i := range img.Signatures {
delete(img.Signatures[i].Annotations, managedSignatureAnnotation)
}
}