/
validate_features.go
125 lines (97 loc) · 4.03 KB
/
validate_features.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
package features
import (
"fmt"
"io"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/api/validation"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/admission"
configv1 "github.com/openshift/api/config/v1"
"github.com/openshift/origin/pkg/admission/customresourcevalidation"
)
const PluginName = "config.openshift.io/ValidateFeatureGate"
// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return customresourcevalidation.NewValidator(
map[schema.GroupResource]bool{
configv1.Resource("features"): true,
},
map[schema.GroupVersionKind]customresourcevalidation.ObjectValidator{
configv1.GroupVersion.WithKind("FeatureGate"): featureGateV1{},
})
})
}
func toFeatureGateV1(uncastObj runtime.Object) (*configv1.FeatureGate, field.ErrorList) {
if uncastObj == nil {
return nil, nil
}
allErrs := field.ErrorList{}
obj, ok := uncastObj.(*configv1.FeatureGate)
if !ok {
return nil, append(allErrs,
field.NotSupported(field.NewPath("kind"), fmt.Sprintf("%T", uncastObj), []string{"FeatureGate"}),
field.NotSupported(field.NewPath("apiVersion"), fmt.Sprintf("%T", uncastObj), []string{"config.openshift.io/v1"}))
}
return obj, nil
}
type featureGateV1 struct {
}
var knownFeatureSets = sets.NewString("", string(configv1.TechPreviewNoUpgrade))
func validateFeatureGateSpecCreate(spec configv1.FeatureGateSpec) field.ErrorList {
allErrs := field.ErrorList{}
// on create, we only allow values that we are aware of
if !knownFeatureSets.Has(string(spec.FeatureSet)) {
allErrs = append(allErrs, field.NotSupported(field.NewPath("spec.featureSet"), spec.FeatureSet, knownFeatureSets.List()))
}
return allErrs
}
func validateFeatureGateSpecUpdate(spec, oldSpec configv1.FeatureGateSpec) field.ErrorList {
allErrs := field.ErrorList{}
// on update, we don't fail validation on a field we don't recognize as long as it is not changing
if !knownFeatureSets.Has(string(spec.FeatureSet)) && oldSpec.FeatureSet != spec.FeatureSet {
allErrs = append(allErrs, field.NotSupported(field.NewPath("spec.featureSet"), spec.FeatureSet, knownFeatureSets.List()))
}
// we do not allow anyone to take back TechPreview
if oldSpec.FeatureSet == configv1.TechPreviewNoUpgrade && spec.FeatureSet != configv1.TechPreviewNoUpgrade {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec.featureSet"), "once enabled, tech preview features may not be disabled"))
}
return allErrs
}
func (featureGateV1) ValidateCreate(uncastObj runtime.Object) field.ErrorList {
obj, allErrs := toFeatureGateV1(uncastObj)
if len(allErrs) > 0 {
return allErrs
}
allErrs = append(allErrs, validation.ValidateObjectMeta(&obj.ObjectMeta, false, customresourcevalidation.RequireNameCluster, field.NewPath("metadata"))...)
allErrs = append(allErrs, validateFeatureGateSpecCreate(obj.Spec)...)
return allErrs
}
func (featureGateV1) ValidateUpdate(uncastObj runtime.Object, uncastOldObj runtime.Object) field.ErrorList {
obj, allErrs := toFeatureGateV1(uncastObj)
if len(allErrs) > 0 {
return allErrs
}
oldObj, allErrs := toFeatureGateV1(uncastOldObj)
if len(allErrs) > 0 {
return allErrs
}
allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&obj.ObjectMeta, &oldObj.ObjectMeta, field.NewPath("metadata"))...)
allErrs = append(allErrs, validateFeatureGateSpecUpdate(obj.Spec, oldObj.Spec)...)
return allErrs
}
func (featureGateV1) ValidateStatusUpdate(uncastObj runtime.Object, uncastOldObj runtime.Object) field.ErrorList {
obj, errs := toFeatureGateV1(uncastObj)
if len(errs) > 0 {
return errs
}
oldObj, errs := toFeatureGateV1(uncastOldObj)
if len(errs) > 0 {
return errs
}
// TODO validate the obj. remember that status validation should *never* fail on spec validation errors.
errs = append(errs, validation.ValidateObjectMetaUpdate(&obj.ObjectMeta, &oldObj.ObjectMeta, field.NewPath("metadata"))...)
return errs
}