forked from openshift/origin
/
strategy.go
133 lines (112 loc) · 4.65 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
package image
import (
"fmt"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
apirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage/names"
kapi "k8s.io/kubernetes/pkg/api"
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"
)
// 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{kapi.Scheme, names.SimpleNameGenerator}
func (imageStrategy) DefaultGarbageCollectionPolicy() 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 apirequest.Context, obj runtime.Object) {
newImage := obj.(*imageapi.Image)
// ignore errors, change in place
if err := util.ImageWithMetadata(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 = ""
}
}
// Validate validates a new image.
func (imageStrategy) Validate(ctx apirequest.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 apirequest.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.ImageWithMetadata(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 = ""
}
}
// ValidateUpdate is the default update validation for an end user.
func (imageStrategy) ValidateUpdate(ctx apirequest.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateImageUpdate(old.(*imageapi.Image), obj.(*imageapi.Image))
}