mirror of
https://github.com/kubernetes/sample-controller.git
synced 2025-02-20 23:56:23 +08:00
Merge pull request #72947 from apelisse/wip-feature-serverside-apply-merge
Merge feature-serverside-apply back in master Kubernetes-commit: 2a5a41a08b08075aa2960170c8342d974ccc2cd3
This commit is contained in:
parent
3787f3c1f6
commit
cfd89cde21
488
Godeps/Godeps.json
generated
488
Godeps/Godeps.json
generated
File diff suppressed because it is too large
Load Diff
96
vendor/github.com/google/gofuzz/fuzz.go
generated
vendored
96
vendor/github.com/google/gofuzz/fuzz.go
generated
vendored
@ -34,21 +34,27 @@ type Fuzzer struct {
|
||||
nilChance float64
|
||||
minElements int
|
||||
maxElements int
|
||||
maxDepth int
|
||||
}
|
||||
|
||||
// New returns a new Fuzzer. Customize your Fuzzer further by calling Funcs,
|
||||
// RandSource, NilChance, or NumElements in any order.
|
||||
func New() *Fuzzer {
|
||||
return NewWithSeed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
func NewWithSeed(seed int64) *Fuzzer {
|
||||
f := &Fuzzer{
|
||||
defaultFuzzFuncs: fuzzFuncMap{
|
||||
reflect.TypeOf(&time.Time{}): reflect.ValueOf(fuzzTime),
|
||||
},
|
||||
|
||||
fuzzFuncs: fuzzFuncMap{},
|
||||
r: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
r: rand.New(rand.NewSource(seed)),
|
||||
nilChance: .2,
|
||||
minElements: 1,
|
||||
maxElements: 10,
|
||||
maxDepth: 100,
|
||||
}
|
||||
return f
|
||||
}
|
||||
@ -136,6 +142,14 @@ func (f *Fuzzer) genShouldFill() bool {
|
||||
return f.r.Float64() > f.nilChance
|
||||
}
|
||||
|
||||
// MaxDepth sets the maximum number of recursive fuzz calls that will be made
|
||||
// before stopping. This includes struct members, pointers, and map and slice
|
||||
// elements.
|
||||
func (f *Fuzzer) MaxDepth(d int) *Fuzzer {
|
||||
f.maxDepth = d
|
||||
return f
|
||||
}
|
||||
|
||||
// Fuzz recursively fills all of obj's fields with something random. First
|
||||
// this tries to find a custom fuzz function (see Funcs). If there is no
|
||||
// custom function this tests whether the object implements fuzz.Interface and,
|
||||
@ -144,17 +158,19 @@ func (f *Fuzzer) genShouldFill() bool {
|
||||
// fails, this will generate random values for all primitive fields and then
|
||||
// recurse for all non-primitives.
|
||||
//
|
||||
// Not safe for cyclic or tree-like structs!
|
||||
// This is safe for cyclic or tree-like structs, up to a limit. Use the
|
||||
// MaxDepth method to adjust how deep you need it to recurse.
|
||||
//
|
||||
// obj must be a pointer. Only exported (public) fields can be set (thanks, golang :/ )
|
||||
// Intended for tests, so will panic on bad input or unimplemented fields.
|
||||
// obj must be a pointer. Only exported (public) fields can be set (thanks,
|
||||
// golang :/ ) Intended for tests, so will panic on bad input or unimplemented
|
||||
// fields.
|
||||
func (f *Fuzzer) Fuzz(obj interface{}) {
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
panic("needed ptr!")
|
||||
}
|
||||
v = v.Elem()
|
||||
f.doFuzz(v, 0)
|
||||
f.fuzzWithContext(v, 0)
|
||||
}
|
||||
|
||||
// FuzzNoCustom is just like Fuzz, except that any custom fuzz function for
|
||||
@ -170,7 +186,7 @@ func (f *Fuzzer) FuzzNoCustom(obj interface{}) {
|
||||
panic("needed ptr!")
|
||||
}
|
||||
v = v.Elem()
|
||||
f.doFuzz(v, flagNoCustomFuzz)
|
||||
f.fuzzWithContext(v, flagNoCustomFuzz)
|
||||
}
|
||||
|
||||
const (
|
||||
@ -178,69 +194,87 @@ const (
|
||||
flagNoCustomFuzz uint64 = 1 << iota
|
||||
)
|
||||
|
||||
func (f *Fuzzer) doFuzz(v reflect.Value, flags uint64) {
|
||||
func (f *Fuzzer) fuzzWithContext(v reflect.Value, flags uint64) {
|
||||
fc := &fuzzerContext{fuzzer: f}
|
||||
fc.doFuzz(v, flags)
|
||||
}
|
||||
|
||||
// fuzzerContext carries context about a single fuzzing run, which lets Fuzzer
|
||||
// be thread-safe.
|
||||
type fuzzerContext struct {
|
||||
fuzzer *Fuzzer
|
||||
curDepth int
|
||||
}
|
||||
|
||||
func (fc *fuzzerContext) doFuzz(v reflect.Value, flags uint64) {
|
||||
if fc.curDepth >= fc.fuzzer.maxDepth {
|
||||
return
|
||||
}
|
||||
fc.curDepth++
|
||||
defer func() { fc.curDepth-- }()
|
||||
|
||||
if !v.CanSet() {
|
||||
return
|
||||
}
|
||||
|
||||
if flags&flagNoCustomFuzz == 0 {
|
||||
// Check for both pointer and non-pointer custom functions.
|
||||
if v.CanAddr() && f.tryCustom(v.Addr()) {
|
||||
if v.CanAddr() && fc.tryCustom(v.Addr()) {
|
||||
return
|
||||
}
|
||||
if f.tryCustom(v) {
|
||||
if fc.tryCustom(v) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if fn, ok := fillFuncMap[v.Kind()]; ok {
|
||||
fn(v, f.r)
|
||||
fn(v, fc.fuzzer.r)
|
||||
return
|
||||
}
|
||||
switch v.Kind() {
|
||||
case reflect.Map:
|
||||
if f.genShouldFill() {
|
||||
if fc.fuzzer.genShouldFill() {
|
||||
v.Set(reflect.MakeMap(v.Type()))
|
||||
n := f.genElementCount()
|
||||
n := fc.fuzzer.genElementCount()
|
||||
for i := 0; i < n; i++ {
|
||||
key := reflect.New(v.Type().Key()).Elem()
|
||||
f.doFuzz(key, 0)
|
||||
fc.doFuzz(key, 0)
|
||||
val := reflect.New(v.Type().Elem()).Elem()
|
||||
f.doFuzz(val, 0)
|
||||
fc.doFuzz(val, 0)
|
||||
v.SetMapIndex(key, val)
|
||||
}
|
||||
return
|
||||
}
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
case reflect.Ptr:
|
||||
if f.genShouldFill() {
|
||||
if fc.fuzzer.genShouldFill() {
|
||||
v.Set(reflect.New(v.Type().Elem()))
|
||||
f.doFuzz(v.Elem(), 0)
|
||||
fc.doFuzz(v.Elem(), 0)
|
||||
return
|
||||
}
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
case reflect.Slice:
|
||||
if f.genShouldFill() {
|
||||
n := f.genElementCount()
|
||||
if fc.fuzzer.genShouldFill() {
|
||||
n := fc.fuzzer.genElementCount()
|
||||
v.Set(reflect.MakeSlice(v.Type(), n, n))
|
||||
for i := 0; i < n; i++ {
|
||||
f.doFuzz(v.Index(i), 0)
|
||||
fc.doFuzz(v.Index(i), 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
case reflect.Array:
|
||||
if f.genShouldFill() {
|
||||
if fc.fuzzer.genShouldFill() {
|
||||
n := v.Len()
|
||||
for i := 0; i < n; i++ {
|
||||
f.doFuzz(v.Index(i), 0)
|
||||
fc.doFuzz(v.Index(i), 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
case reflect.Struct:
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
f.doFuzz(v.Field(i), 0)
|
||||
fc.doFuzz(v.Field(i), 0)
|
||||
}
|
||||
case reflect.Chan:
|
||||
fallthrough
|
||||
@ -255,20 +289,20 @@ func (f *Fuzzer) doFuzz(v reflect.Value, flags uint64) {
|
||||
|
||||
// tryCustom searches for custom handlers, and returns true iff it finds a match
|
||||
// and successfully randomizes v.
|
||||
func (f *Fuzzer) tryCustom(v reflect.Value) bool {
|
||||
func (fc *fuzzerContext) tryCustom(v reflect.Value) bool {
|
||||
// First: see if we have a fuzz function for it.
|
||||
doCustom, ok := f.fuzzFuncs[v.Type()]
|
||||
doCustom, ok := fc.fuzzer.fuzzFuncs[v.Type()]
|
||||
if !ok {
|
||||
// Second: see if it can fuzz itself.
|
||||
if v.CanInterface() {
|
||||
intf := v.Interface()
|
||||
if fuzzable, ok := intf.(Interface); ok {
|
||||
fuzzable.Fuzz(Continue{f: f, Rand: f.r})
|
||||
fuzzable.Fuzz(Continue{fc: fc, Rand: fc.fuzzer.r})
|
||||
return true
|
||||
}
|
||||
}
|
||||
// Finally: see if there is a default fuzz function.
|
||||
doCustom, ok = f.defaultFuzzFuncs[v.Type()]
|
||||
doCustom, ok = fc.fuzzer.defaultFuzzFuncs[v.Type()]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
@ -294,8 +328,8 @@ func (f *Fuzzer) tryCustom(v reflect.Value) bool {
|
||||
}
|
||||
|
||||
doCustom.Call([]reflect.Value{v, reflect.ValueOf(Continue{
|
||||
f: f,
|
||||
Rand: f.r,
|
||||
fc: fc,
|
||||
Rand: fc.fuzzer.r,
|
||||
})})
|
||||
return true
|
||||
}
|
||||
@ -310,7 +344,7 @@ type Interface interface {
|
||||
// Continue can be passed to custom fuzzing functions to allow them to use
|
||||
// the correct source of randomness and to continue fuzzing their members.
|
||||
type Continue struct {
|
||||
f *Fuzzer
|
||||
fc *fuzzerContext
|
||||
|
||||
// For convenience, Continue implements rand.Rand via embedding.
|
||||
// Use this for generating any randomness if you want your fuzzing
|
||||
@ -325,7 +359,7 @@ func (c Continue) Fuzz(obj interface{}) {
|
||||
panic("needed ptr!")
|
||||
}
|
||||
v = v.Elem()
|
||||
c.f.doFuzz(v, 0)
|
||||
c.fc.doFuzz(v, 0)
|
||||
}
|
||||
|
||||
// FuzzNoCustom continues fuzzing obj, except that any custom fuzz function for
|
||||
@ -338,7 +372,7 @@ func (c Continue) FuzzNoCustom(obj interface{}) {
|
||||
panic("needed ptr!")
|
||||
}
|
||||
v = v.Elem()
|
||||
c.f.doFuzz(v, flagNoCustomFuzz)
|
||||
c.fc.doFuzz(v, flagNoCustomFuzz)
|
||||
}
|
||||
|
||||
// RandString makes a random string up to 20 characters long. The returned string
|
||||
|
4
vendor/k8s.io/api/core/v1/generated.proto
generated
vendored
4
vendor/k8s.io/api/core/v1/generated.proto
generated
vendored
@ -3156,6 +3156,7 @@ message PodSpec {
|
||||
|
||||
// EnableServiceLinks indicates whether information about services should be injected into pod's
|
||||
// environment variables, matching the syntax of Docker links.
|
||||
// Optional: Defaults to true.
|
||||
// +optional
|
||||
optional bool enableServiceLinks = 30;
|
||||
}
|
||||
@ -4247,6 +4248,9 @@ message ServiceSpec {
|
||||
// More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies
|
||||
// +patchMergeKey=port
|
||||
// +patchStrategy=merge
|
||||
// +listType=map
|
||||
// +listMapKey=port
|
||||
// +listMapKey=protocol
|
||||
repeated ServicePort ports = 1;
|
||||
|
||||
// Route service traffic to pods with label keys and values matching this
|
||||
|
4
vendor/k8s.io/api/core/v1/types.go
generated
vendored
4
vendor/k8s.io/api/core/v1/types.go
generated
vendored
@ -2920,6 +2920,7 @@ type PodSpec struct {
|
||||
RuntimeClassName *string `json:"runtimeClassName,omitempty" protobuf:"bytes,29,opt,name=runtimeClassName"`
|
||||
// EnableServiceLinks indicates whether information about services should be injected into pod's
|
||||
// environment variables, matching the syntax of Docker links.
|
||||
// Optional: Defaults to true.
|
||||
// +optional
|
||||
EnableServiceLinks *bool `json:"enableServiceLinks,omitempty" protobuf:"varint,30,opt,name=enableServiceLinks"`
|
||||
}
|
||||
@ -3450,6 +3451,9 @@ type ServiceSpec struct {
|
||||
// More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies
|
||||
// +patchMergeKey=port
|
||||
// +patchStrategy=merge
|
||||
// +listType=map
|
||||
// +listMapKey=port
|
||||
// +listMapKey=protocol
|
||||
Ports []ServicePort `json:"ports,omitempty" patchStrategy:"merge" patchMergeKey:"port" protobuf:"bytes,1,rep,name=ports"`
|
||||
|
||||
// Route service traffic to pods with label keys and values matching this
|
||||
|
2
vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go
generated
vendored
2
vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go
generated
vendored
@ -1540,7 +1540,7 @@ var map_PodSpec = map[string]string{
|
||||
"dnsConfig": "Specifies the DNS parameters of a pod. Parameters specified here will be merged to the generated DNS configuration based on DNSPolicy.",
|
||||
"readinessGates": "If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://github.com/kubernetes/community/blob/master/keps/sig-network/0007-pod-ready%2B%2B.md",
|
||||
"runtimeClassName": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://github.com/kubernetes/community/blob/master/keps/sig-node/0014-runtime-class.md This is an alpha feature and may change in the future.",
|
||||
"enableServiceLinks": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links.",
|
||||
"enableServiceLinks": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.",
|
||||
}
|
||||
|
||||
func (PodSpec) SwaggerDoc() map[string]string {
|
||||
|
2
vendor/k8s.io/api/rbac/v1/generated.proto
generated
vendored
2
vendor/k8s.io/api/rbac/v1/generated.proto
generated
vendored
@ -43,6 +43,7 @@ message ClusterRole {
|
||||
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
|
||||
|
||||
// Rules holds all the PolicyRules for this ClusterRole
|
||||
// +optional
|
||||
repeated PolicyRule rules = 2;
|
||||
|
||||
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
|
||||
@ -121,6 +122,7 @@ message Role {
|
||||
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
|
||||
|
||||
// Rules holds all the PolicyRules for this Role
|
||||
// +optional
|
||||
repeated PolicyRule rules = 2;
|
||||
}
|
||||
|
||||
|
2
vendor/k8s.io/api/rbac/v1/types.go
generated
vendored
2
vendor/k8s.io/api/rbac/v1/types.go
generated
vendored
@ -108,6 +108,7 @@ type Role struct {
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
|
||||
// Rules holds all the PolicyRules for this Role
|
||||
// +optional
|
||||
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
|
||||
}
|
||||
|
||||
@ -170,6 +171,7 @@ type ClusterRole struct {
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
|
||||
// Rules holds all the PolicyRules for this ClusterRole
|
||||
// +optional
|
||||
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
|
||||
|
||||
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
|
||||
|
2
vendor/k8s.io/api/rbac/v1alpha1/generated.proto
generated
vendored
2
vendor/k8s.io/api/rbac/v1alpha1/generated.proto
generated
vendored
@ -43,6 +43,7 @@ message ClusterRole {
|
||||
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
|
||||
|
||||
// Rules holds all the PolicyRules for this ClusterRole
|
||||
// +optional
|
||||
repeated PolicyRule rules = 2;
|
||||
|
||||
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
|
||||
@ -122,6 +123,7 @@ message Role {
|
||||
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
|
||||
|
||||
// Rules holds all the PolicyRules for this Role
|
||||
// +optional
|
||||
repeated PolicyRule rules = 2;
|
||||
}
|
||||
|
||||
|
2
vendor/k8s.io/api/rbac/v1alpha1/types.go
generated
vendored
2
vendor/k8s.io/api/rbac/v1alpha1/types.go
generated
vendored
@ -110,6 +110,7 @@ type Role struct {
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
|
||||
// Rules holds all the PolicyRules for this Role
|
||||
// +optional
|
||||
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
|
||||
}
|
||||
|
||||
@ -172,6 +173,7 @@ type ClusterRole struct {
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
|
||||
// Rules holds all the PolicyRules for this ClusterRole
|
||||
// +optional
|
||||
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
|
||||
|
||||
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
|
||||
|
2
vendor/k8s.io/api/rbac/v1beta1/generated.proto
generated
vendored
2
vendor/k8s.io/api/rbac/v1beta1/generated.proto
generated
vendored
@ -43,6 +43,7 @@ message ClusterRole {
|
||||
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
|
||||
|
||||
// Rules holds all the PolicyRules for this ClusterRole
|
||||
// +optional
|
||||
repeated PolicyRule rules = 2;
|
||||
|
||||
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
|
||||
@ -122,6 +123,7 @@ message Role {
|
||||
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
|
||||
|
||||
// Rules holds all the PolicyRules for this Role
|
||||
// +optional
|
||||
repeated PolicyRule rules = 2;
|
||||
}
|
||||
|
||||
|
2
vendor/k8s.io/api/rbac/v1beta1/types.go
generated
vendored
2
vendor/k8s.io/api/rbac/v1beta1/types.go
generated
vendored
@ -109,6 +109,7 @@ type Role struct {
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
|
||||
// Rules holds all the PolicyRules for this Role
|
||||
// +optional
|
||||
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
|
||||
}
|
||||
|
||||
@ -171,6 +172,7 @@ type ClusterRole struct {
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
|
||||
// Rules holds all the PolicyRules for this ClusterRole
|
||||
// +optional
|
||||
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
|
||||
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
|
||||
// If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be
|
||||
|
24
vendor/k8s.io/apimachinery/pkg/api/errors/errors.go
generated
vendored
24
vendor/k8s.io/apimachinery/pkg/api/errors/errors.go
generated
vendored
@ -27,6 +27,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"sigs.k8s.io/structured-merge-diff/merge"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -184,6 +185,29 @@ func NewConflict(qualifiedResource schema.GroupResource, name string, err error)
|
||||
}}
|
||||
}
|
||||
|
||||
// NewApplyConflict returns an error including details on the requests apply conflicts
|
||||
func NewApplyConflict(conflicts merge.Conflicts) *StatusError {
|
||||
causes := make([]metav1.StatusCause, 0, len(conflicts))
|
||||
for _, conflict := range conflicts {
|
||||
causes = append(causes, metav1.StatusCause{
|
||||
Type: metav1.CauseType("conflict"),
|
||||
Message: conflict.Error(),
|
||||
Field: conflict.Path.String(),
|
||||
})
|
||||
}
|
||||
|
||||
return &StatusError{ErrStatus: metav1.Status{
|
||||
Status: metav1.StatusFailure,
|
||||
Code: http.StatusConflict,
|
||||
Reason: metav1.StatusReasonConflict,
|
||||
Details: &metav1.StatusDetails{
|
||||
// TODO: Get obj details here?
|
||||
Causes: causes,
|
||||
},
|
||||
Message: fmt.Sprintf("Apply failed with %d conflicts: %s", len(conflicts), conflicts.Error()),
|
||||
}}
|
||||
}
|
||||
|
||||
// NewGone returns an error indicating the item no longer available at the server and no forwarding address is known.
|
||||
func NewGone(message string) *StatusError {
|
||||
return &StatusError{metav1.Status{
|
||||
|
4
vendor/k8s.io/apimachinery/pkg/api/meta/meta.go
generated
vendored
4
vendor/k8s.io/apimachinery/pkg/api/meta/meta.go
generated
vendored
@ -20,14 +20,13 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/klog"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/conversion"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// errNotList is returned when an object implements the Object style interfaces but not the List style
|
||||
@ -138,6 +137,7 @@ func AsPartialObjectMetadata(m metav1.Object) *metav1beta1.PartialObjectMetadata
|
||||
Finalizers: m.GetFinalizers(),
|
||||
ClusterName: m.GetClusterName(),
|
||||
Initializers: m.GetInitializers(),
|
||||
ManagedFields: m.GetManagedFields(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
2
vendor/k8s.io/apimachinery/pkg/api/resource/quantity.go
generated
vendored
2
vendor/k8s.io/apimachinery/pkg/api/resource/quantity.go
generated
vendored
@ -680,7 +680,7 @@ func NewScaledQuantity(value int64, scale Scale) *Quantity {
|
||||
}
|
||||
}
|
||||
|
||||
// Value returns the value of q; any fractional part will be lost.
|
||||
// Value returns the unscaled value of q rounded up to the nearest integer away from 0.
|
||||
func (q *Quantity) Value() int64 {
|
||||
return q.ScaledValue(0)
|
||||
}
|
||||
|
1197
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/generated.pb.go
generated
vendored
1197
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/generated.pb.go
generated
vendored
File diff suppressed because it is too large
Load Diff
74
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto
generated
vendored
74
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto
generated
vendored
@ -202,6 +202,23 @@ message ExportOptions {
|
||||
optional bool exact = 2;
|
||||
}
|
||||
|
||||
// Fields stores a set of fields in a data structure like a Trie.
|
||||
// To understand how this is used, see: https://github.com/kubernetes-sigs/structured-merge-diff
|
||||
message Fields {
|
||||
// Map stores a set of fields in a data structure like a Trie.
|
||||
//
|
||||
// Each key is either a '.' representing the field itself, and will always map to an empty set,
|
||||
// or a string representing a sub-field or item. The string will follow one of these four formats:
|
||||
// 'f:<name>', where <name> is the name of a field in a struct, or key in a map
|
||||
// 'v:<value>', where <value> is the exact json formatted value of a list item
|
||||
// 'i:<index>', where <index> is position of a item in a list
|
||||
// 'k:<keys>', where <keys> is a map of a list item's key fields to their unique values
|
||||
// If a key maps to an empty Fields value, the field that key represents is part of the set.
|
||||
//
|
||||
// The exact format is defined in k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal
|
||||
map<string, Fields> map = 1;
|
||||
}
|
||||
|
||||
// GetOptions is the standard query options to the standard REST get call.
|
||||
message GetOptions {
|
||||
// When specified:
|
||||
@ -436,6 +453,31 @@ message ListOptions {
|
||||
optional string continue = 8;
|
||||
}
|
||||
|
||||
// ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource
|
||||
// that the fieldset applies to.
|
||||
message ManagedFieldsEntry {
|
||||
// Manager is an identifier of the workflow managing these fields.
|
||||
optional string manager = 1;
|
||||
|
||||
// Operation is the type of operation which lead to this ManagedFieldsEntry being created.
|
||||
// The only valid values for this field are 'Apply' and 'Update'.
|
||||
optional string operation = 2;
|
||||
|
||||
// APIVersion defines the version of this resource that this field set
|
||||
// applies to. The format is "group/version" just like the top-level
|
||||
// APIVersion field. It is necessary to track the version of a field
|
||||
// set because it cannot be automatically converted.
|
||||
optional string apiVersion = 3;
|
||||
|
||||
// Time is timestamp of when these fields were set. It should always be empty if Operation is 'Apply'
|
||||
// +optional
|
||||
optional Time time = 4;
|
||||
|
||||
// Fields identifies a set of fields.
|
||||
// +optional
|
||||
optional Fields fields = 5;
|
||||
}
|
||||
|
||||
// MicroTime is version of Time with microsecond level precision.
|
||||
//
|
||||
// +protobuf.options.marshal=false
|
||||
@ -617,6 +659,19 @@ message ObjectMeta {
|
||||
// This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.
|
||||
// +optional
|
||||
optional string clusterName = 15;
|
||||
|
||||
// ManagedFields maps workflow-id and version to the set of fields
|
||||
// that are managed by that workflow. This is mostly for internal
|
||||
// housekeeping, and users typically shouldn't need to set or
|
||||
// understand this field. A workflow can be the user's name, a
|
||||
// controller's name, or the name of a specific apply path like
|
||||
// "ci-cd". The set of fields is always in the version that the
|
||||
// workflow used when modifying the object.
|
||||
//
|
||||
// This field is alpha and can be changed or removed without notice.
|
||||
//
|
||||
// +optional
|
||||
repeated ManagedFieldsEntry managedFields = 17;
|
||||
}
|
||||
|
||||
// OwnerReference contains enough information to let you identify an owning
|
||||
@ -656,6 +711,24 @@ message OwnerReference {
|
||||
message Patch {
|
||||
}
|
||||
|
||||
// PatchOptions may be provided when patching an API object.
|
||||
// PatchOptions is meant to be a superset of UpdateOptions.
|
||||
message PatchOptions {
|
||||
// When present, indicates that modifications should not be
|
||||
// persisted. An invalid or unrecognized dryRun directive will
|
||||
// result in an error response and no further processing of the
|
||||
// request. Valid values are:
|
||||
// - All: all dry run stages will be processed
|
||||
// +optional
|
||||
repeated string dryRun = 1;
|
||||
|
||||
// Force is going to "force" Apply requests. It means user will
|
||||
// re-acquire conflicting fields owned by other people. Force
|
||||
// flag must be unset for non-apply patch requests.
|
||||
// +optional
|
||||
optional bool force = 2;
|
||||
}
|
||||
|
||||
// Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.
|
||||
message Preconditions {
|
||||
// Specifies the target UID.
|
||||
@ -841,6 +914,7 @@ message TypeMeta {
|
||||
}
|
||||
|
||||
// UpdateOptions may be provided when updating an API object.
|
||||
// All fields in UpdateOptions should also be present in PatchOptions.
|
||||
message UpdateOptions {
|
||||
// When present, indicates that modifications should not be
|
||||
// persisted. An invalid or unrecognized dryRun directive will
|
||||
|
15
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/helpers.go
generated
vendored
15
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/helpers.go
generated
vendored
@ -17,6 +17,7 @@ limitations under the License.
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
@ -243,4 +244,18 @@ func ResetObjectMetaForStatus(meta, existingMeta Object) {
|
||||
meta.SetAnnotations(existingMeta.GetAnnotations())
|
||||
meta.SetFinalizers(existingMeta.GetFinalizers())
|
||||
meta.SetOwnerReferences(existingMeta.GetOwnerReferences())
|
||||
meta.SetManagedFields(existingMeta.GetManagedFields())
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler
|
||||
func (f Fields) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&f.Map)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler
|
||||
func (f *Fields) UnmarshalJSON(b []byte) error {
|
||||
return json.Unmarshal(b, &f.Map)
|
||||
}
|
||||
|
||||
var _ json.Marshaler = Fields{}
|
||||
var _ json.Unmarshaler = &Fields{}
|
||||
|
10
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/meta.go
generated
vendored
10
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/meta.go
generated
vendored
@ -63,6 +63,8 @@ type Object interface {
|
||||
SetOwnerReferences([]OwnerReference)
|
||||
GetClusterName() string
|
||||
SetClusterName(clusterName string)
|
||||
GetManagedFields() []ManagedFieldsEntry
|
||||
SetManagedFields(managedFields []ManagedFieldsEntry)
|
||||
}
|
||||
|
||||
// ListMetaAccessor retrieves the list interface from an object
|
||||
@ -166,5 +168,9 @@ func (meta *ObjectMeta) GetOwnerReferences() []OwnerReference { return m
|
||||
func (meta *ObjectMeta) SetOwnerReferences(references []OwnerReference) {
|
||||
meta.OwnerReferences = references
|
||||
}
|
||||
func (meta *ObjectMeta) GetClusterName() string { return meta.ClusterName }
|
||||
func (meta *ObjectMeta) SetClusterName(clusterName string) { meta.ClusterName = clusterName }
|
||||
func (meta *ObjectMeta) GetClusterName() string { return meta.ClusterName }
|
||||
func (meta *ObjectMeta) SetClusterName(clusterName string) { meta.ClusterName = clusterName }
|
||||
func (meta *ObjectMeta) GetManagedFields() []ManagedFieldsEntry { return meta.ManagedFields }
|
||||
func (meta *ObjectMeta) SetManagedFields(managedFields []ManagedFieldsEntry) {
|
||||
meta.ManagedFields = managedFields
|
||||
}
|
||||
|
2
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/register.go
generated
vendored
2
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/register.go
generated
vendored
@ -55,6 +55,7 @@ func AddToGroupVersion(scheme *runtime.Scheme, groupVersion schema.GroupVersion)
|
||||
&DeleteOptions{},
|
||||
&CreateOptions{},
|
||||
&UpdateOptions{},
|
||||
&PatchOptions{},
|
||||
)
|
||||
utilruntime.Must(scheme.AddConversionFuncs(
|
||||
Convert_v1_WatchEvent_To_watch_Event,
|
||||
@ -90,6 +91,7 @@ func init() {
|
||||
&DeleteOptions{},
|
||||
&CreateOptions{},
|
||||
&UpdateOptions{},
|
||||
&PatchOptions{},
|
||||
)
|
||||
|
||||
// register manually. This usually goes through the SchemeBuilder, which we cannot use here.
|
||||
|
82
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go
generated
vendored
82
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go
generated
vendored
@ -252,6 +252,19 @@ type ObjectMeta struct {
|
||||
// This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.
|
||||
// +optional
|
||||
ClusterName string `json:"clusterName,omitempty" protobuf:"bytes,15,opt,name=clusterName"`
|
||||
|
||||
// ManagedFields maps workflow-id and version to the set of fields
|
||||
// that are managed by that workflow. This is mostly for internal
|
||||
// housekeeping, and users typically shouldn't need to set or
|
||||
// understand this field. A workflow can be the user's name, a
|
||||
// controller's name, or the name of a specific apply path like
|
||||
// "ci-cd". The set of fields is always in the version that the
|
||||
// workflow used when modifying the object.
|
||||
//
|
||||
// This field is alpha and can be changed or removed without notice.
|
||||
//
|
||||
// +optional
|
||||
ManagedFields []ManagedFieldsEntry `json:"managedFields,omitempty" protobuf:"bytes,17,rep,name=managedFields"`
|
||||
}
|
||||
|
||||
// Initializers tracks the progress of initialization.
|
||||
@ -494,7 +507,30 @@ type CreateOptions struct {
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// PatchOptions may be provided when patching an API object.
|
||||
// PatchOptions is meant to be a superset of UpdateOptions.
|
||||
type PatchOptions struct {
|
||||
TypeMeta `json:",inline"`
|
||||
|
||||
// When present, indicates that modifications should not be
|
||||
// persisted. An invalid or unrecognized dryRun directive will
|
||||
// result in an error response and no further processing of the
|
||||
// request. Valid values are:
|
||||
// - All: all dry run stages will be processed
|
||||
// +optional
|
||||
DryRun []string `json:"dryRun,omitempty" protobuf:"bytes,1,rep,name=dryRun"`
|
||||
|
||||
// Force is going to "force" Apply requests. It means user will
|
||||
// re-acquire conflicting fields owned by other people. Force
|
||||
// flag must be unset for non-apply patch requests.
|
||||
// +optional
|
||||
Force *bool `json:"force,omitempty" protobuf:"varint,2,opt,name=force"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// UpdateOptions may be provided when updating an API object.
|
||||
// All fields in UpdateOptions should also be present in PatchOptions.
|
||||
type UpdateOptions struct {
|
||||
TypeMeta `json:",inline"`
|
||||
|
||||
@ -1009,3 +1045,49 @@ const (
|
||||
LabelSelectorOpExists LabelSelectorOperator = "Exists"
|
||||
LabelSelectorOpDoesNotExist LabelSelectorOperator = "DoesNotExist"
|
||||
)
|
||||
|
||||
// ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource
|
||||
// that the fieldset applies to.
|
||||
type ManagedFieldsEntry struct {
|
||||
// Manager is an identifier of the workflow managing these fields.
|
||||
Manager string `json:"manager,omitempty" protobuf:"bytes,1,opt,name=manager"`
|
||||
// Operation is the type of operation which lead to this ManagedFieldsEntry being created.
|
||||
// The only valid values for this field are 'Apply' and 'Update'.
|
||||
Operation ManagedFieldsOperationType `json:"operation,omitempty" protobuf:"bytes,2,opt,name=operation,casttype=ManagedFieldsOperationType"`
|
||||
// APIVersion defines the version of this resource that this field set
|
||||
// applies to. The format is "group/version" just like the top-level
|
||||
// APIVersion field. It is necessary to track the version of a field
|
||||
// set because it cannot be automatically converted.
|
||||
APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,3,opt,name=apiVersion"`
|
||||
// Time is timestamp of when these fields were set. It should always be empty if Operation is 'Apply'
|
||||
// +optional
|
||||
Time *Time `json:"time,omitempty" protobuf:"bytes,4,opt,name=time"`
|
||||
// Fields identifies a set of fields.
|
||||
// +optional
|
||||
Fields *Fields `json:"fields,omitempty" protobuf:"bytes,5,opt,name=fields,casttype=Fields"`
|
||||
}
|
||||
|
||||
// ManagedFieldsOperationType is the type of operation which lead to a ManagedFieldsEntry being created.
|
||||
type ManagedFieldsOperationType string
|
||||
|
||||
const (
|
||||
ManagedFieldsOperationApply ManagedFieldsOperationType = "Apply"
|
||||
ManagedFieldsOperationUpdate ManagedFieldsOperationType = "Update"
|
||||
)
|
||||
|
||||
// Fields stores a set of fields in a data structure like a Trie.
|
||||
// To understand how this is used, see: https://github.com/kubernetes-sigs/structured-merge-diff
|
||||
type Fields struct {
|
||||
// Map stores a set of fields in a data structure like a Trie.
|
||||
//
|
||||
// Each key is either a '.' representing the field itself, and will always map to an empty set,
|
||||
// or a string representing a sub-field or item. The string will follow one of these four formats:
|
||||
// 'f:<name>', where <name> is the name of a field in a struct, or key in a map
|
||||
// 'v:<value>', where <value> is the exact json formatted value of a list item
|
||||
// 'i:<index>', where <index> is position of a item in a list
|
||||
// 'k:<keys>', where <keys> is a map of a list item's key fields to their unique values
|
||||
// If a key maps to an empty Fields value, the field that key represents is part of the set.
|
||||
//
|
||||
// The exact format is defined in k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal
|
||||
Map map[string]Fields `json:",inline" protobuf:"bytes,1,rep,name=map"`
|
||||
}
|
||||
|
34
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types_swagger_doc_generated.go
generated
vendored
34
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types_swagger_doc_generated.go
generated
vendored
@ -118,6 +118,14 @@ func (ExportOptions) SwaggerDoc() map[string]string {
|
||||
return map_ExportOptions
|
||||
}
|
||||
|
||||
var map_Fields = map[string]string{
|
||||
"": "Fields stores a set of fields in a data structure like a Trie. To understand how this is used, see: https://github.com/kubernetes-sigs/structured-merge-diff",
|
||||
}
|
||||
|
||||
func (Fields) SwaggerDoc() map[string]string {
|
||||
return map_Fields
|
||||
}
|
||||
|
||||
var map_GetOptions = map[string]string{
|
||||
"": "GetOptions is the standard query options to the standard REST get call.",
|
||||
"resourceVersion": "When specified: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.",
|
||||
@ -213,6 +221,19 @@ func (ListOptions) SwaggerDoc() map[string]string {
|
||||
return map_ListOptions
|
||||
}
|
||||
|
||||
var map_ManagedFieldsEntry = map[string]string{
|
||||
"": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.",
|
||||
"manager": "Manager is an identifier of the workflow managing these fields.",
|
||||
"operation": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.",
|
||||
"apiVersion": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.",
|
||||
"time": "Time is timestamp of when these fields were set. It should always be empty if Operation is 'Apply'",
|
||||
"fields": "Fields identifies a set of fields.",
|
||||
}
|
||||
|
||||
func (ManagedFieldsEntry) SwaggerDoc() map[string]string {
|
||||
return map_ManagedFieldsEntry
|
||||
}
|
||||
|
||||
var map_ObjectMeta = map[string]string{
|
||||
"": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.",
|
||||
"name": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names",
|
||||
@ -231,6 +252,7 @@ var map_ObjectMeta = map[string]string{
|
||||
"initializers": "An initializer is a controller which enforces some system invariant at object creation time. This field is a list of initializers that have not yet acted on this object. If nil or empty, this object has been completely initialized. Otherwise, the object is considered uninitialized and is hidden (in list/watch and get calls) from clients that haven't explicitly asked to observe uninitialized objects.\n\nWhen an object is created, the system will populate this list with the current set of initializers. Only privileged users may set or modify this list. Once it is empty, it may not be modified further by any user.\n\nDEPRECATED - initializers are an alpha field and will be removed in v1.15.",
|
||||
"finalizers": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.",
|
||||
"clusterName": "The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.",
|
||||
"managedFields": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.\n\nThis field is alpha and can be changed or removed without notice.",
|
||||
}
|
||||
|
||||
func (ObjectMeta) SwaggerDoc() map[string]string {
|
||||
@ -259,6 +281,16 @@ func (Patch) SwaggerDoc() map[string]string {
|
||||
return map_Patch
|
||||
}
|
||||
|
||||
var map_PatchOptions = map[string]string{
|
||||
"": "PatchOptions may be provided when patching an API object. PatchOptions is meant to be a superset of UpdateOptions.",
|
||||
"dryRun": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
|
||||
"force": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.",
|
||||
}
|
||||
|
||||
func (PatchOptions) SwaggerDoc() map[string]string {
|
||||
return map_PatchOptions
|
||||
}
|
||||
|
||||
var map_Preconditions = map[string]string{
|
||||
"": "Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.",
|
||||
"uid": "Specifies the target UID.",
|
||||
@ -337,7 +369,7 @@ func (TypeMeta) SwaggerDoc() map[string]string {
|
||||
}
|
||||
|
||||
var map_UpdateOptions = map[string]string{
|
||||
"": "UpdateOptions may be provided when updating an API object.",
|
||||
"": "UpdateOptions may be provided when updating an API object. All fields in UpdateOptions should also be present in PatchOptions.",
|
||||
"dryRun": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
|
||||
}
|
||||
|
||||
|
50
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go
generated
vendored
50
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go
generated
vendored
@ -143,13 +143,20 @@ func (u *Unstructured) setNestedField(value interface{}, fields ...string) {
|
||||
SetNestedField(u.Object, value, fields...)
|
||||
}
|
||||
|
||||
func (u *Unstructured) setNestedSlice(value []string, fields ...string) {
|
||||
func (u *Unstructured) setNestedStringSlice(value []string, fields ...string) {
|
||||
if u.Object == nil {
|
||||
u.Object = make(map[string]interface{})
|
||||
}
|
||||
SetNestedStringSlice(u.Object, value, fields...)
|
||||
}
|
||||
|
||||
func (u *Unstructured) setNestedSlice(value []interface{}, fields ...string) {
|
||||
if u.Object == nil {
|
||||
u.Object = make(map[string]interface{})
|
||||
}
|
||||
SetNestedSlice(u.Object, value, fields...)
|
||||
}
|
||||
|
||||
func (u *Unstructured) setNestedMap(value map[string]string, fields ...string) {
|
||||
if u.Object == nil {
|
||||
u.Object = make(map[string]interface{})
|
||||
@ -436,7 +443,7 @@ func (u *Unstructured) SetFinalizers(finalizers []string) {
|
||||
RemoveNestedField(u.Object, "metadata", "finalizers")
|
||||
return
|
||||
}
|
||||
u.setNestedSlice(finalizers, "metadata", "finalizers")
|
||||
u.setNestedStringSlice(finalizers, "metadata", "finalizers")
|
||||
}
|
||||
|
||||
func (u *Unstructured) GetClusterName() string {
|
||||
@ -450,3 +457,42 @@ func (u *Unstructured) SetClusterName(clusterName string) {
|
||||
}
|
||||
u.setNestedField(clusterName, "metadata", "clusterName")
|
||||
}
|
||||
|
||||
func (u *Unstructured) GetManagedFields() []metav1.ManagedFieldsEntry {
|
||||
items, found, err := NestedSlice(u.Object, "metadata", "managedFields")
|
||||
if !found || err != nil {
|
||||
return nil
|
||||
}
|
||||
managedFields := []metav1.ManagedFieldsEntry{}
|
||||
for _, item := range items {
|
||||
m, ok := item.(map[string]interface{})
|
||||
if !ok {
|
||||
utilruntime.HandleError(fmt.Errorf("unable to retrieve managedFields for object, item %v is not a map", item))
|
||||
return nil
|
||||
}
|
||||
out := metav1.ManagedFieldsEntry{}
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(m, &out); err != nil {
|
||||
utilruntime.HandleError(fmt.Errorf("unable to retrieve managedFields for object: %v", err))
|
||||
return nil
|
||||
}
|
||||
managedFields = append(managedFields, out)
|
||||
}
|
||||
return managedFields
|
||||
}
|
||||
|
||||
func (u *Unstructured) SetManagedFields(managedFields []metav1.ManagedFieldsEntry) {
|
||||
if managedFields == nil {
|
||||
RemoveNestedField(u.Object, "metadata", "managedFields")
|
||||
return
|
||||
}
|
||||
items := []interface{}{}
|
||||
for _, managedFieldsEntry := range managedFields {
|
||||
out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&managedFieldsEntry)
|
||||
if err != nil {
|
||||
utilruntime.HandleError(fmt.Errorf("unable to set managedFields for object: %v", err))
|
||||
return
|
||||
}
|
||||
items = append(items, out)
|
||||
}
|
||||
u.setNestedSlice(items, "metadata", "managedFields")
|
||||
}
|
||||
|
90
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/zz_generated.deepcopy.go
generated
vendored
90
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/zz_generated.deepcopy.go
generated
vendored
@ -312,6 +312,29 @@ func (in *ExportOptions) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Fields) DeepCopyInto(out *Fields) {
|
||||
*out = *in
|
||||
if in.Map != nil {
|
||||
in, out := &in.Map, &out.Map
|
||||
*out = make(map[string]Fields, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = *val.DeepCopy()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Fields.
|
||||
func (in *Fields) DeepCopy() *Fields {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Fields)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GetOptions) DeepCopyInto(out *GetOptions) {
|
||||
*out = *in
|
||||
@ -624,6 +647,31 @@ func (in *ListOptions) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ManagedFieldsEntry) DeepCopyInto(out *ManagedFieldsEntry) {
|
||||
*out = *in
|
||||
if in.Time != nil {
|
||||
in, out := &in.Time, &out.Time
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
if in.Fields != nil {
|
||||
in, out := &in.Fields, &out.Fields
|
||||
*out = new(Fields)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagedFieldsEntry.
|
||||
func (in *ManagedFieldsEntry) DeepCopy() *ManagedFieldsEntry {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ManagedFieldsEntry)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MicroTime.
|
||||
func (in *MicroTime) DeepCopy() *MicroTime {
|
||||
if in == nil {
|
||||
@ -678,6 +726,13 @@ func (in *ObjectMeta) DeepCopyInto(out *ObjectMeta) {
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.ManagedFields != nil {
|
||||
in, out := &in.ManagedFields, &out.ManagedFields
|
||||
*out = make([]ManagedFieldsEntry, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -733,6 +788,41 @@ func (in *Patch) DeepCopy() *Patch {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PatchOptions) DeepCopyInto(out *PatchOptions) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if in.DryRun != nil {
|
||||
in, out := &in.DryRun, &out.DryRun
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Force != nil {
|
||||
in, out := &in.Force, &out.Force
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PatchOptions.
|
||||
func (in *PatchOptions) DeepCopy() *PatchOptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PatchOptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *PatchOptions) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Preconditions) DeepCopyInto(out *Preconditions) {
|
||||
*out = *in
|
||||
|
1
vendor/k8s.io/apimachinery/pkg/runtime/types.go
generated
vendored
1
vendor/k8s.io/apimachinery/pkg/runtime/types.go
generated
vendored
@ -42,6 +42,7 @@ type TypeMeta struct {
|
||||
|
||||
const (
|
||||
ContentTypeJSON string = "application/json"
|
||||
ContentTypeYAML string = "application/yaml"
|
||||
)
|
||||
|
||||
// RawExtension is used to hold extensions in external versions.
|
||||
|
1
vendor/k8s.io/apimachinery/pkg/types/patch.go
generated
vendored
1
vendor/k8s.io/apimachinery/pkg/types/patch.go
generated
vendored
@ -25,4 +25,5 @@ const (
|
||||
JSONPatchType PatchType = "application/json-patch+json"
|
||||
MergePatchType PatchType = "application/merge-patch+json"
|
||||
StrategicMergePatchType PatchType = "application/strategic-merge-patch+json"
|
||||
ApplyPatchType PatchType = "application/apply-patch+yaml"
|
||||
)
|
||||
|
2
vendor/k8s.io/apimachinery/pkg/util/yaml/decoder.go
generated
vendored
2
vendor/k8s.io/apimachinery/pkg/util/yaml/decoder.go
generated
vendored
@ -217,11 +217,9 @@ func (d *YAMLOrJSONDecoder) Decode(into interface{}) error {
|
||||
if d.decoder == nil {
|
||||
buffer, origData, isJSON := GuessJSONStream(d.r, d.bufferSize)
|
||||
if isJSON {
|
||||
klog.V(4).Infof("decoding stream as JSON")
|
||||
d.decoder = json.NewDecoder(buffer)
|
||||
d.rawData = origData
|
||||
} else {
|
||||
klog.V(4).Infof("decoding stream as YAML")
|
||||
d.decoder = NewYAMLToJSONDecoder(buffer)
|
||||
}
|
||||
}
|
||||
|
30
vendor/k8s.io/code-generator/generate-groups.sh
generated
vendored
30
vendor/k8s.io/code-generator/generate-groups.sh
generated
vendored
@ -23,7 +23,7 @@ set -o pipefail
|
||||
|
||||
if [ "$#" -lt 4 ] || [ "${1}" == "--help" ]; then
|
||||
cat <<EOF
|
||||
Usage: $(basename $0) <generators> <output-package> <apis-package> <groups-versions> ...
|
||||
Usage: $(basename "$0") <generators> <output-package> <apis-package> <groups-versions> ...
|
||||
|
||||
<generators> the generators comma separated to run (deepcopy,defaulter,client,lister,informer) or "all".
|
||||
<output-package> the output package name (e.g. github.com/example/project/pkg/generated).
|
||||
@ -34,8 +34,8 @@ Usage: $(basename $0) <generators> <output-package> <apis-package> <groups-versi
|
||||
|
||||
|
||||
Examples:
|
||||
$(basename $0) all github.com/example/project/pkg/client github.com/example/project/pkg/apis "foo:v1 bar:v1alpha1,v1beta1"
|
||||
$(basename $0) deepcopy,client github.com/example/project/pkg/client github.com/example/project/pkg/apis "foo:v1 bar:v1alpha1,v1beta1"
|
||||
$(basename "$0") all github.com/example/project/pkg/client github.com/example/project/pkg/apis "foo:v1 bar:v1alpha1,v1beta1"
|
||||
$(basename "$0") deepcopy,client github.com/example/project/pkg/client github.com/example/project/pkg/apis "foo:v1 bar:v1alpha1,v1beta1"
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
@ -49,8 +49,8 @@ shift 4
|
||||
(
|
||||
# To support running this script from anywhere, we have to first cd into this directory
|
||||
# so we can install the tools.
|
||||
cd $(dirname "${0}")
|
||||
go install ${GOFLAGS:-} ./cmd/{defaulter-gen,client-gen,lister-gen,informer-gen,deepcopy-gen}
|
||||
cd "$(dirname "${0}")"
|
||||
go install ./cmd/{defaulter-gen,client-gen,lister-gen,informer-gen,deepcopy-gen}
|
||||
)
|
||||
|
||||
function codegen::join() { local IFS="$1"; shift; echo "$*"; }
|
||||
@ -58,35 +58,35 @@ function codegen::join() { local IFS="$1"; shift; echo "$*"; }
|
||||
# enumerate group versions
|
||||
FQ_APIS=() # e.g. k8s.io/api/apps/v1
|
||||
for GVs in ${GROUPS_WITH_VERSIONS}; do
|
||||
IFS=: read G Vs <<<"${GVs}"
|
||||
IFS=: read -r G Vs <<<"${GVs}"
|
||||
|
||||
# enumerate versions
|
||||
for V in ${Vs//,/ }; do
|
||||
FQ_APIS+=(${APIS_PKG}/${G}/${V})
|
||||
FQ_APIS+=("${APIS_PKG}/${G}/${V}")
|
||||
done
|
||||
done
|
||||
|
||||
if [ "${GENS}" = "all" ] || grep -qw "deepcopy" <<<"${GENS}"; then
|
||||
echo "Generating deepcopy funcs"
|
||||
${GOPATH}/bin/deepcopy-gen --input-dirs $(codegen::join , "${FQ_APIS[@]}") -O zz_generated.deepcopy --bounding-dirs ${APIS_PKG} "$@"
|
||||
"${GOPATH}/bin/deepcopy-gen" --input-dirs "$(codegen::join , "${FQ_APIS[@]}")" -O zz_generated.deepcopy --bounding-dirs "${APIS_PKG}" "$@"
|
||||
fi
|
||||
|
||||
if [ "${GENS}" = "all" ] || grep -qw "client" <<<"${GENS}"; then
|
||||
echo "Generating clientset for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}"
|
||||
${GOPATH}/bin/client-gen --clientset-name ${CLIENTSET_NAME_VERSIONED:-versioned} --input-base "" --input $(codegen::join , "${FQ_APIS[@]}") --output-package ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset} "$@"
|
||||
"${GOPATH}/bin/client-gen" --clientset-name "${CLIENTSET_NAME_VERSIONED:-versioned}" --input-base "" --input "$(codegen::join , "${FQ_APIS[@]}")" --output-package "${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}" "$@"
|
||||
fi
|
||||
|
||||
if [ "${GENS}" = "all" ] || grep -qw "lister" <<<"${GENS}"; then
|
||||
echo "Generating listers for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/listers"
|
||||
${GOPATH}/bin/lister-gen --input-dirs $(codegen::join , "${FQ_APIS[@]}") --output-package ${OUTPUT_PKG}/listers "$@"
|
||||
"${GOPATH}/bin/lister-gen" --input-dirs "$(codegen::join , "${FQ_APIS[@]}")" --output-package "${OUTPUT_PKG}/listers" "$@"
|
||||
fi
|
||||
|
||||
if [ "${GENS}" = "all" ] || grep -qw "informer" <<<"${GENS}"; then
|
||||
echo "Generating informers for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/informers"
|
||||
${GOPATH}/bin/informer-gen \
|
||||
--input-dirs $(codegen::join , "${FQ_APIS[@]}") \
|
||||
--versioned-clientset-package ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}/${CLIENTSET_NAME_VERSIONED:-versioned} \
|
||||
--listers-package ${OUTPUT_PKG}/listers \
|
||||
--output-package ${OUTPUT_PKG}/informers \
|
||||
"${GOPATH}/bin/informer-gen" \
|
||||
--input-dirs "$(codegen::join , "${FQ_APIS[@]}")" \
|
||||
--versioned-clientset-package "${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}/${CLIENTSET_NAME_VERSIONED:-versioned}" \
|
||||
--listers-package "${OUTPUT_PKG}/listers" \
|
||||
--output-package "${OUTPUT_PKG}/informers" \
|
||||
"$@"
|
||||
fi
|
||||
|
35
vendor/k8s.io/code-generator/generate-internal-groups.sh
generated
vendored
35
vendor/k8s.io/code-generator/generate-internal-groups.sh
generated
vendored
@ -23,7 +23,7 @@ set -o pipefail
|
||||
|
||||
if [ "$#" -lt 5 ] || [ "${1}" == "--help" ]; then
|
||||
cat <<EOF
|
||||
Usage: $(basename $0) <generators> <output-package> <internal-apis-package> <extensiona-apis-package> <groups-versions> ...
|
||||
Usage: $(basename "$0") <generators> <output-package> <internal-apis-package> <extensiona-apis-package> <groups-versions> ...
|
||||
|
||||
<generators> the generators comma separated to run (deepcopy,defaulter,conversion,client,lister,informer) or "all".
|
||||
<output-package> the output package name (e.g. github.com/example/project/pkg/generated).
|
||||
@ -34,8 +34,8 @@ Usage: $(basename $0) <generators> <output-package> <internal-apis-package> <ext
|
||||
... arbitrary flags passed to all generator binaries.
|
||||
|
||||
Examples:
|
||||
$(basename $0) all github.com/example/project/pkg/client github.com/example/project/pkg/apis github.com/example/project/pkg/apis "foo:v1 bar:v1alpha1,v1beta1"
|
||||
$(basename $0) deepcopy,defaulter,conversion github.com/example/project/pkg/client github.com/example/project/pkg/apis github.com/example/project/apis "foo:v1 bar:v1alpha1,v1beta1"
|
||||
$(basename "$0") all github.com/example/project/pkg/client github.com/example/project/pkg/apis github.com/example/project/pkg/apis "foo:v1 bar:v1alpha1,v1beta1"
|
||||
$(basename "$0") deepcopy,defaulter,conversion github.com/example/project/pkg/client github.com/example/project/pkg/apis github.com/example/project/apis "foo:v1 bar:v1alpha1,v1beta1"
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
@ -47,7 +47,7 @@ EXT_APIS_PKG="$4"
|
||||
GROUPS_WITH_VERSIONS="$5"
|
||||
shift 5
|
||||
|
||||
go install ${GOFLAGS:-} ./$(dirname "${0}")/cmd/{defaulter-gen,conversion-gen,client-gen,lister-gen,informer-gen,deepcopy-gen}
|
||||
go install ./"$(dirname "${0}")"/cmd/{defaulter-gen,conversion-gen,client-gen,lister-gen,informer-gen,deepcopy-gen}
|
||||
function codegen::join() { local IFS="$1"; shift; echo "$*"; }
|
||||
|
||||
# enumerate group versions
|
||||
@ -55,7 +55,7 @@ ALL_FQ_APIS=() # e.g. k8s.io/kubernetes/pkg/apis/apps k8s.io/api/apps/v1
|
||||
INT_FQ_APIS=() # e.g. k8s.io/kubernetes/pkg/apis/apps
|
||||
EXT_FQ_APIS=() # e.g. k8s.io/api/apps/v1
|
||||
for GVs in ${GROUPS_WITH_VERSIONS}; do
|
||||
IFS=: read G Vs <<<"${GVs}"
|
||||
IFS=: read -r G Vs <<<"${GVs}"
|
||||
|
||||
if [ -n "${INT_APIS_PKG}" ]; then
|
||||
ALL_FQ_APIS+=("${INT_APIS_PKG}/${G}")
|
||||
@ -71,39 +71,40 @@ done
|
||||
|
||||
if [ "${GENS}" = "all" ] || grep -qw "deepcopy" <<<"${GENS}"; then
|
||||
echo "Generating deepcopy funcs"
|
||||
${GOPATH}/bin/deepcopy-gen --input-dirs $(codegen::join , "${ALL_FQ_APIS[@]}") -O zz_generated.deepcopy --bounding-dirs ${INT_APIS_PKG},${EXT_APIS_PKG} "$@"
|
||||
"${GOPATH}/bin/deepcopy-gen" --input-dirs "$(codegen::join , "${ALL_FQ_APIS[@]}")" -O zz_generated.deepcopy --bounding-dirs "${INT_APIS_PKG},${EXT_APIS_PKG}" "$@"
|
||||
fi
|
||||
|
||||
if [ "${GENS}" = "all" ] || grep -qw "defaulter" <<<"${GENS}"; then
|
||||
echo "Generating defaulters"
|
||||
${GOPATH}/bin/defaulter-gen --input-dirs $(codegen::join , "${EXT_FQ_APIS[@]}") -O zz_generated.defaults "$@"
|
||||
"${GOPATH}/bin/defaulter-gen" --input-dirs "$(codegen::join , "${EXT_FQ_APIS[@]}")" -O zz_generated.defaults "$@"
|
||||
fi
|
||||
|
||||
if [ "${GENS}" = "all" ] || grep -qw "conversion" <<<"${GENS}"; then
|
||||
echo "Generating conversions"
|
||||
${GOPATH}/bin/conversion-gen --input-dirs $(codegen::join , "${ALL_FQ_APIS[@]}") -O zz_generated.conversion "$@"
|
||||
"${GOPATH}/bin/conversion-gen" --input-dirs "$(codegen::join , "${ALL_FQ_APIS[@]}")" -O zz_generated.conversion "$@"
|
||||
fi
|
||||
|
||||
if [ "${GENS}" = "all" ] || grep -qw "client" <<<"${GENS}"; then
|
||||
echo "Generating clientset for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}"
|
||||
if [ -n "${INT_APIS_PKG}" ]; then
|
||||
${GOPATH}/bin/client-gen --clientset-name ${CLIENTSET_NAME_INTERNAL:-internalversion} --input-base "" --input $(codegen::join , $(printf '%s/ ' "${INT_FQ_APIS[@]}")) --output-package ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset} "$@"
|
||||
IFS=" " read -r -a APIS <<< "$(printf '%s/ ' "${INT_FQ_APIS[@]}")"
|
||||
"${GOPATH}/bin/client-gen" --clientset-name "${CLIENTSET_NAME_INTERNAL:-internalversion}" --input-base "" --input "$(codegen::join , "${APIS[@]}")" --output-package "${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}" "$@"
|
||||
fi
|
||||
${GOPATH}/bin/client-gen --clientset-name ${CLIENTSET_NAME_VERSIONED:-versioned} --input-base "" --input $(codegen::join , "${EXT_FQ_APIS[@]}") --output-package ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset} "$@"
|
||||
"${GOPATH}/bin/client-gen" --clientset-name "${CLIENTSET_NAME_VERSIONED:-versioned}" --input-base "" --input "$(codegen::join , "${EXT_FQ_APIS[@]}")" --output-package "${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}" "$@"
|
||||
fi
|
||||
|
||||
if [ "${GENS}" = "all" ] || grep -qw "lister" <<<"${GENS}"; then
|
||||
echo "Generating listers for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/listers"
|
||||
${GOPATH}/bin/lister-gen --input-dirs $(codegen::join , "${ALL_FQ_APIS[@]}") --output-package ${OUTPUT_PKG}/listers "$@"
|
||||
"${GOPATH}/bin/lister-gen" --input-dirs "$(codegen::join , "${ALL_FQ_APIS[@]}")" --output-package "${OUTPUT_PKG}/listers" "$@"
|
||||
fi
|
||||
|
||||
if [ "${GENS}" = "all" ] || grep -qw "informer" <<<"${GENS}"; then
|
||||
echo "Generating informers for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/informers"
|
||||
${GOPATH}/bin/informer-gen \
|
||||
--input-dirs $(codegen::join , "${ALL_FQ_APIS[@]}") \
|
||||
--versioned-clientset-package ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}/${CLIENTSET_NAME_VERSIONED:-versioned} \
|
||||
--internal-clientset-package ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}/${CLIENTSET_NAME_INTERNAL:-internalversion} \
|
||||
--listers-package ${OUTPUT_PKG}/listers \
|
||||
--output-package ${OUTPUT_PKG}/informers \
|
||||
"${GOPATH}/bin/informer-gen" \
|
||||
--input-dirs "$(codegen::join , "${ALL_FQ_APIS[@]}")" \
|
||||
--versioned-clientset-package "${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}/${CLIENTSET_NAME_VERSIONED:-versioned}" \
|
||||
--internal-clientset-package "${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}/${CLIENTSET_NAME_INTERNAL:-internalversion}" \
|
||||
--listers-package "${OUTPUT_PKG}/listers" \
|
||||
--output-package "${OUTPUT_PKG}/informers" \
|
||||
"$@"
|
||||
fi
|
||||
|
12
vendor/k8s.io/code-generator/hack/update-codegen.sh
generated
vendored
12
vendor/k8s.io/code-generator/hack/update-codegen.sh
generated
vendored
@ -22,15 +22,15 @@ set -o pipefail
|
||||
# - --output-base because this script should also be able to run inside the vendor dir of
|
||||
# k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir
|
||||
# instead of the $GOPATH directly. For normal projects this can be dropped.
|
||||
$(dirname ${BASH_SOURCE})/../generate-internal-groups.sh all \
|
||||
"$(dirname "${BASH_SOURCE[0]}")"/../generate-internal-groups.sh all \
|
||||
k8s.io/code-generator/_examples/apiserver k8s.io/code-generator/_examples/apiserver/apis k8s.io/code-generator/_examples/apiserver/apis \
|
||||
"example:v1 example2:v1" \
|
||||
--output-base "$(dirname ${BASH_SOURCE})/../../.."
|
||||
$(dirname ${BASH_SOURCE})/../generate-groups.sh all \
|
||||
--output-base "$(dirname "${BASH_SOURCE[0]}")/../../.."
|
||||
"$(dirname "${BASH_SOURCE[0]}")"/../generate-groups.sh all \
|
||||
k8s.io/code-generator/_examples/crd k8s.io/code-generator/_examples/crd/apis \
|
||||
"example:v1 example2:v1" \
|
||||
--output-base "$(dirname ${BASH_SOURCE})/../../.."
|
||||
$(dirname ${BASH_SOURCE})/../generate-groups.sh all \
|
||||
--output-base "$(dirname "${BASH_SOURCE[0]}")/../../.."
|
||||
"$(dirname "${BASH_SOURCE[0]}")"/../generate-groups.sh all \
|
||||
k8s.io/code-generator/_examples/MixedCase k8s.io/code-generator/_examples/MixedCase/apis \
|
||||
"example:v1" \
|
||||
--output-base "$(dirname ${BASH_SOURCE})/../../.."
|
||||
--output-base "$(dirname "${BASH_SOURCE[0]}")/../../.."
|
||||
|
9
vendor/k8s.io/code-generator/hack/verify-codegen.sh
generated
vendored
9
vendor/k8s.io/code-generator/hack/verify-codegen.sh
generated
vendored
@ -18,8 +18,7 @@ set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")/..
|
||||
SCRIPT_BASE=${SCRIPT_ROOT}/../..
|
||||
SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
|
||||
|
||||
DIFFROOT="${SCRIPT_ROOT}/_examples"
|
||||
TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/_examples"
|
||||
@ -50,6 +49,6 @@ fi
|
||||
|
||||
# smoke test
|
||||
echo "Smoke testing _example by compiling..."
|
||||
go build ./${SCRIPT_ROOT}/_examples/crd/...
|
||||
go build ./${SCRIPT_ROOT}/_examples/apiserver/...
|
||||
go build ./${SCRIPT_ROOT}/_examples/MixedCase/...
|
||||
go build "./${SCRIPT_ROOT}/_examples/crd/..."
|
||||
go build "./${SCRIPT_ROOT}/_examples/apiserver/..."
|
||||
go build "./${SCRIPT_ROOT}/_examples/MixedCase/..."
|
||||
|
201
vendor/sigs.k8s.io/structured-merge-diff/LICENSE
generated
vendored
Normal file
201
vendor/sigs.k8s.io/structured-merge-diff/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
21
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/doc.go
generated
vendored
Normal file
21
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/doc.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package fieldpath defines a way for referencing path elements (e.g., an
|
||||
// index in an array, or a key in a map). It provides types for arranging these
|
||||
// into paths for referencing nested fields, and for grouping those into sets,
|
||||
// for referencing multiple nested fields.
|
||||
package fieldpath
|
184
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/element.go
generated
vendored
Normal file
184
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/element.go
generated
vendored
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fieldpath
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
// PathElement describes how to select a child field given a containing object.
|
||||
type PathElement struct {
|
||||
// Exactly one of the following fields should be non-nil.
|
||||
|
||||
// FieldName selects a single field from a map (reminder: this is also
|
||||
// how structs are represented). The containing object must be a map.
|
||||
FieldName *string
|
||||
|
||||
// Key selects the list element which has fields matching those given.
|
||||
// The containing object must be an associative list with map typed
|
||||
// elements.
|
||||
Key []value.Field
|
||||
|
||||
// Value selects the list element with the given value. The containing
|
||||
// object must be an associative list with a primitive typed element
|
||||
// (i.e., a set).
|
||||
Value *value.Value
|
||||
|
||||
// Index selects a list element by its index number. The containing
|
||||
// object must be an atomic list.
|
||||
Index *int
|
||||
}
|
||||
|
||||
// String presents the path element as a human-readable string.
|
||||
func (e PathElement) String() string {
|
||||
switch {
|
||||
case e.FieldName != nil:
|
||||
return "." + *e.FieldName
|
||||
case len(e.Key) > 0:
|
||||
strs := make([]string, len(e.Key))
|
||||
for i, k := range e.Key {
|
||||
strs[i] = fmt.Sprintf("%v=%v", k.Name, k.Value)
|
||||
}
|
||||
// The order must be canonical, since we use the string value
|
||||
// in a set structure.
|
||||
sort.Strings(strs)
|
||||
return "[" + strings.Join(strs, ",") + "]"
|
||||
case e.Value != nil:
|
||||
return fmt.Sprintf("[=%v]", e.Value)
|
||||
case e.Index != nil:
|
||||
return fmt.Sprintf("[%v]", *e.Index)
|
||||
default:
|
||||
return "{{invalid path element}}"
|
||||
}
|
||||
}
|
||||
|
||||
// KeyByFields is a helper function which constructs a key for an associative
|
||||
// list type. `nameValues` must have an even number of entries, alternating
|
||||
// names (type must be string) with values (type must be value.Value). If these
|
||||
// conditions are not met, KeyByFields will panic--it's intended for static
|
||||
// construction and shouldn't have user-produced values passed to it.
|
||||
func KeyByFields(nameValues ...interface{}) []value.Field {
|
||||
if len(nameValues)%2 != 0 {
|
||||
panic("must have a value for every name")
|
||||
}
|
||||
out := []value.Field{}
|
||||
for i := 0; i < len(nameValues)-1; i += 2 {
|
||||
out = append(out, value.Field{
|
||||
Name: nameValues[i].(string),
|
||||
Value: nameValues[i+1].(value.Value),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// PathElementSet is a set of path elements.
|
||||
// TODO: serialize as a list.
|
||||
type PathElementSet struct {
|
||||
// The strange construction is because there's no way to test
|
||||
// PathElements for equality (it can't be used as a key for a map).
|
||||
members map[string]PathElement
|
||||
}
|
||||
|
||||
// Insert adds pe to the set.
|
||||
func (s *PathElementSet) Insert(pe PathElement) {
|
||||
serialized := pe.String()
|
||||
if s.members == nil {
|
||||
s.members = map[string]PathElement{
|
||||
serialized: pe,
|
||||
}
|
||||
return
|
||||
}
|
||||
if _, ok := s.members[serialized]; !ok {
|
||||
s.members[serialized] = pe
|
||||
}
|
||||
}
|
||||
|
||||
// Union returns a set containing elements that appear in either s or s2.
|
||||
func (s *PathElementSet) Union(s2 *PathElementSet) *PathElementSet {
|
||||
out := &PathElementSet{
|
||||
members: map[string]PathElement{},
|
||||
}
|
||||
for k, v := range s.members {
|
||||
out.members[k] = v
|
||||
}
|
||||
for k, v := range s2.members {
|
||||
out.members[k] = v
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Intersection returns a set containing elements which appear in both s and s2.
|
||||
func (s *PathElementSet) Intersection(s2 *PathElementSet) *PathElementSet {
|
||||
out := &PathElementSet{
|
||||
members: map[string]PathElement{},
|
||||
}
|
||||
for k, v := range s.members {
|
||||
if _, ok := s2.members[k]; ok {
|
||||
out.members[k] = v
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Difference returns a set containing elements which appear in s but not in s2.
|
||||
func (s *PathElementSet) Difference(s2 *PathElementSet) *PathElementSet {
|
||||
out := &PathElementSet{
|
||||
members: map[string]PathElement{},
|
||||
}
|
||||
for k, v := range s.members {
|
||||
if _, ok := s2.members[k]; !ok {
|
||||
out.members[k] = v
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Size retuns the number of elements in the set.
|
||||
func (s *PathElementSet) Size() int { return len(s.members) }
|
||||
|
||||
// Has returns true if pe is a member of the set.
|
||||
func (s *PathElementSet) Has(pe PathElement) bool {
|
||||
if s.members == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := s.members[pe.String()]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Equals returns true if s and s2 have exactly the same members.
|
||||
func (s *PathElementSet) Equals(s2 *PathElementSet) bool {
|
||||
if len(s.members) != len(s2.members) {
|
||||
return false
|
||||
}
|
||||
for k := range s.members {
|
||||
if _, ok := s2.members[k]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Iterate calls f for each PathElement in the set.
|
||||
func (s *PathElementSet) Iterate(f func(PathElement)) {
|
||||
for _, pe := range s.members {
|
||||
f(pe)
|
||||
}
|
||||
}
|
123
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/fromvalue.go
generated
vendored
Normal file
123
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/fromvalue.go
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fieldpath
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
// SetFromValue creates a set containing every leaf field mentioned in v.
|
||||
func SetFromValue(v value.Value) *Set {
|
||||
s := NewSet()
|
||||
|
||||
w := objectWalker{
|
||||
path: Path{},
|
||||
value: v,
|
||||
do: func(p Path) { s.Insert(p) },
|
||||
}
|
||||
|
||||
w.walk()
|
||||
return s
|
||||
}
|
||||
|
||||
type objectWalker struct {
|
||||
path Path
|
||||
value value.Value
|
||||
|
||||
do func(Path)
|
||||
}
|
||||
|
||||
func (w *objectWalker) walk() {
|
||||
switch {
|
||||
case w.value.Null:
|
||||
case w.value.FloatValue != nil:
|
||||
case w.value.IntValue != nil:
|
||||
case w.value.StringValue != nil:
|
||||
case w.value.BooleanValue != nil:
|
||||
// All leaf fields handled the same way (after the switch
|
||||
// statement).
|
||||
|
||||
// Descend
|
||||
case w.value.ListValue != nil:
|
||||
// If the list were atomic, we'd break here, but we don't have
|
||||
// a schema, so we can't tell.
|
||||
|
||||
for i, child := range w.value.ListValue.Items {
|
||||
w2 := *w
|
||||
w2.path = append(w.path, GuessBestListPathElement(i, child))
|
||||
w2.value = child
|
||||
w2.walk()
|
||||
}
|
||||
return
|
||||
case w.value.MapValue != nil:
|
||||
// If the map/struct were atomic, we'd break here, but we don't
|
||||
// have a schema, so we can't tell.
|
||||
|
||||
for i := range w.value.MapValue.Items {
|
||||
child := w.value.MapValue.Items[i]
|
||||
w2 := *w
|
||||
w2.path = append(w.path, PathElement{FieldName: &child.Name})
|
||||
w2.value = child.Value
|
||||
w2.walk()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Leaf fields get added to the set.
|
||||
if len(w.path) > 0 {
|
||||
w.do(w.path)
|
||||
}
|
||||
}
|
||||
|
||||
// AssociativeListCandidateFieldNames lists the field names which are
|
||||
// considered keys if found in a list element.
|
||||
var AssociativeListCandidateFieldNames = []string{
|
||||
"key",
|
||||
"id",
|
||||
"name",
|
||||
}
|
||||
|
||||
// GuessBestListPathElement guesses whether item is an associative list
|
||||
// element, which should be referenced by key(s), or if it is not and therefore
|
||||
// referencing by index is acceptable. Currently this is done by checking
|
||||
// whether item has any of the fields listed in
|
||||
// AssociativeListCandidateFieldNames which have scalar values.
|
||||
func GuessBestListPathElement(index int, item value.Value) PathElement {
|
||||
if item.MapValue == nil {
|
||||
// Non map items could be parts of sets or regular "atomic"
|
||||
// lists. We won't try to guess whether something should be a
|
||||
// set or not.
|
||||
return PathElement{Index: &index}
|
||||
}
|
||||
|
||||
var keys []value.Field
|
||||
for _, name := range AssociativeListCandidateFieldNames {
|
||||
f, ok := item.MapValue.Get(name)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
// only accept primitive/scalar types as keys.
|
||||
if f.Value.Null || f.Value.MapValue != nil || f.Value.ListValue != nil {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, *f)
|
||||
}
|
||||
if len(keys) > 0 {
|
||||
return PathElement{Key: keys}
|
||||
}
|
||||
return PathElement{Index: &index}
|
||||
}
|
73
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/managers.go
generated
vendored
Normal file
73
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/managers.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fieldpath
|
||||
|
||||
// APIVersion describes the version of an object or of a fieldset.
|
||||
type APIVersion string
|
||||
|
||||
// VersionedSet associates a version to a set.
|
||||
type VersionedSet struct {
|
||||
*Set
|
||||
APIVersion APIVersion
|
||||
}
|
||||
|
||||
// ManagedFields is a map from manager to VersionedSet (what they own in
|
||||
// what version).
|
||||
type ManagedFields map[string]*VersionedSet
|
||||
|
||||
// Difference returns a symmetric difference between two Managers. If a
|
||||
// given user's entry has version X in lhs and version Y in rhs, then
|
||||
// the return value for that user will be from rhs. If the difference for
|
||||
// a user is an empty set, that user will not be inserted in the map.
|
||||
func (lhs ManagedFields) Difference(rhs ManagedFields) ManagedFields {
|
||||
diff := ManagedFields{}
|
||||
|
||||
for manager, left := range lhs {
|
||||
right, ok := rhs[manager]
|
||||
if !ok {
|
||||
if !left.Empty() {
|
||||
diff[manager] = left
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// If we have sets in both but their version
|
||||
// differs, we don't even diff and keep the
|
||||
// entire thing.
|
||||
if left.APIVersion != right.APIVersion {
|
||||
diff[manager] = right
|
||||
continue
|
||||
}
|
||||
|
||||
newSet := left.Difference(right.Set).Union(right.Difference(left.Set))
|
||||
if !newSet.Empty() {
|
||||
diff[manager] = &VersionedSet{
|
||||
Set: newSet,
|
||||
APIVersion: right.APIVersion,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for manager, set := range rhs {
|
||||
if _, ok := lhs[manager]; ok {
|
||||
// Already done
|
||||
continue
|
||||
}
|
||||
if !set.Empty() {
|
||||
diff[manager] = set
|
||||
}
|
||||
}
|
||||
|
||||
return diff
|
||||
}
|
81
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/path.go
generated
vendored
Normal file
81
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/path.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fieldpath
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
// Path describes how to select a potentially deeply-nested child field given a
|
||||
// containing object.
|
||||
type Path []PathElement
|
||||
|
||||
func (fp Path) String() string {
|
||||
strs := make([]string, len(fp))
|
||||
for i := range fp {
|
||||
strs[i] = fp[i].String()
|
||||
}
|
||||
return strings.Join(strs, "")
|
||||
}
|
||||
|
||||
func (fp Path) Copy() Path {
|
||||
new := make(Path, len(fp))
|
||||
copy(new, fp)
|
||||
return new
|
||||
}
|
||||
|
||||
// MakePath constructs a Path. The parts may be PathElements, ints, strings.
|
||||
func MakePath(parts ...interface{}) (Path, error) {
|
||||
var fp Path
|
||||
for _, p := range parts {
|
||||
switch t := p.(type) {
|
||||
case PathElement:
|
||||
fp = append(fp, t)
|
||||
case int:
|
||||
// TODO: Understand schema and object and convert this to the
|
||||
// FieldSpecifier below if appropriate.
|
||||
fp = append(fp, PathElement{Index: &t})
|
||||
case string:
|
||||
fp = append(fp, PathElement{FieldName: &t})
|
||||
case []value.Field:
|
||||
if len(t) == 0 {
|
||||
return nil, fmt.Errorf("associative list key type path elements must have at least one key (got zero)")
|
||||
}
|
||||
fp = append(fp, PathElement{Key: t})
|
||||
case value.Value:
|
||||
// TODO: understand schema and verify that this is a set type
|
||||
// TODO: make a copy of t
|
||||
fp = append(fp, PathElement{Value: &t})
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to make %#v into a path element", p)
|
||||
}
|
||||
}
|
||||
return fp, nil
|
||||
}
|
||||
|
||||
// MakePathOrDie panics if parts can't be turned into a path. Good for things
|
||||
// that are known at complie time.
|
||||
func MakePathOrDie(parts ...interface{}) Path {
|
||||
fp, err := MakePath(parts...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return fp
|
||||
}
|
315
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/set.go
generated
vendored
Normal file
315
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/set.go
generated
vendored
Normal file
@ -0,0 +1,315 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fieldpath
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Set identifies a set of fields.
|
||||
type Set struct {
|
||||
// Members lists fields that are part of the set.
|
||||
// TODO: will be serialized as a list of path elements.
|
||||
Members PathElementSet
|
||||
|
||||
// Children lists child fields which themselves have children that are
|
||||
// members of the set. Appearance in this list does not imply membership.
|
||||
// Note: this is a tree, not an arbitrary graph.
|
||||
Children SetNodeMap
|
||||
}
|
||||
|
||||
// NewSet makes a set from a list of paths.
|
||||
func NewSet(paths ...Path) *Set {
|
||||
s := &Set{}
|
||||
for _, p := range paths {
|
||||
s.Insert(p)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Insert adds the field identified by `p` to the set. Important: parent fields
|
||||
// are NOT added to the set; if that is desired, they must be added separately.
|
||||
func (s *Set) Insert(p Path) {
|
||||
if len(p) == 0 {
|
||||
// Zero-length path identifies the entire object; we don't
|
||||
// track top-level ownership.
|
||||
return
|
||||
}
|
||||
for {
|
||||
if len(p) == 1 {
|
||||
s.Members.Insert(p[0])
|
||||
return
|
||||
}
|
||||
s = s.Children.Descend(p[0])
|
||||
p = p[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// Union returns a Set containing elements which appear in either s or s2.
|
||||
func (s *Set) Union(s2 *Set) *Set {
|
||||
return &Set{
|
||||
Members: *s.Members.Union(&s2.Members),
|
||||
Children: *s.Children.Union(&s2.Children),
|
||||
}
|
||||
}
|
||||
|
||||
// Intersection returns a Set containing leaf elements which appear in both s
|
||||
// and s2. Intersection can be constructed from Union and Difference operations
|
||||
// (example in the tests) but it's much faster to do it in one pass.
|
||||
func (s *Set) Intersection(s2 *Set) *Set {
|
||||
return &Set{
|
||||
Members: *s.Members.Intersection(&s2.Members),
|
||||
Children: *s.Children.Intersection(&s2.Children),
|
||||
}
|
||||
}
|
||||
|
||||
// Difference returns a Set containing elements which:
|
||||
// * appear in s
|
||||
// * do not appear in s2
|
||||
// * and are not children of elements that appear in s2.
|
||||
//
|
||||
// In other words, for leaf fields, this acts like a regular set difference
|
||||
// operation. When non leaf fields are compared with leaf fields ("parents"
|
||||
// which contain "children"), the effect is:
|
||||
// * parent - child = parent
|
||||
// * child - parent = {empty set}
|
||||
func (s *Set) Difference(s2 *Set) *Set {
|
||||
return &Set{
|
||||
Members: *s.Members.Difference(&s2.Members),
|
||||
Children: *s.Children.Difference(s2),
|
||||
}
|
||||
}
|
||||
|
||||
// Size returns the number of members of the set.
|
||||
func (s *Set) Size() int {
|
||||
return s.Members.Size() + s.Children.Size()
|
||||
}
|
||||
|
||||
// Empty returns true if there are no members of the set. It is a separate
|
||||
// function from Size since it's common to check whether size > 0, and
|
||||
// potentially much faster to return as soon as a single element is found.
|
||||
func (s *Set) Empty() bool {
|
||||
if s.Members.Size() > 0 {
|
||||
return false
|
||||
}
|
||||
return s.Children.Empty()
|
||||
}
|
||||
|
||||
// Has returns true if the field referenced by `p` is a member of the set.
|
||||
func (s *Set) Has(p Path) bool {
|
||||
if len(p) == 0 {
|
||||
// No one owns "the entire object"
|
||||
return false
|
||||
}
|
||||
for {
|
||||
if len(p) == 1 {
|
||||
return s.Members.Has(p[0])
|
||||
}
|
||||
var ok bool
|
||||
s, ok = s.Children.Get(p[0])
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
p = p[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// Equals returns true if s and s2 have exactly the same members.
|
||||
func (s *Set) Equals(s2 *Set) bool {
|
||||
return s.Members.Equals(&s2.Members) && s.Children.Equals(&s2.Children)
|
||||
}
|
||||
|
||||
// String returns the set one element per line.
|
||||
func (s *Set) String() string {
|
||||
elements := []string{}
|
||||
s.Iterate(func(p Path) {
|
||||
elements = append(elements, p.String())
|
||||
})
|
||||
return strings.Join(elements, "\n")
|
||||
}
|
||||
|
||||
// Iterate calls f once for each field that is a member of the set (preorder
|
||||
// DFS). The path passed to f will be reused so make a copy if you wish to keep
|
||||
// it.
|
||||
func (s *Set) Iterate(f func(Path)) {
|
||||
s.iteratePrefix(Path{}, f)
|
||||
}
|
||||
|
||||
func (s *Set) iteratePrefix(prefix Path, f func(Path)) {
|
||||
s.Members.Iterate(func(pe PathElement) { f(append(prefix, pe)) })
|
||||
s.Children.iteratePrefix(prefix, f)
|
||||
}
|
||||
|
||||
// WithPrefix returns the subset of paths which begin with the given prefix,
|
||||
// with the prefix not included.
|
||||
func (s *Set) WithPrefix(pe PathElement) *Set {
|
||||
subset, ok := s.Children.Get(pe)
|
||||
if !ok {
|
||||
return NewSet()
|
||||
}
|
||||
return subset
|
||||
}
|
||||
|
||||
// setNode is a pair of PathElement / Set, for the purpose of expressing
|
||||
// nested set membership.
|
||||
type setNode struct {
|
||||
pathElement PathElement
|
||||
set *Set
|
||||
}
|
||||
|
||||
// SetNodeMap is a map of PathElement to subset.
|
||||
type SetNodeMap struct {
|
||||
members map[string]setNode
|
||||
}
|
||||
|
||||
// Descend adds pe to the set if necessary, returning the associated subset.
|
||||
func (s *SetNodeMap) Descend(pe PathElement) *Set {
|
||||
serialized := pe.String()
|
||||
if s.members == nil {
|
||||
s.members = map[string]setNode{}
|
||||
}
|
||||
if n, ok := s.members[serialized]; ok {
|
||||
return n.set
|
||||
}
|
||||
ss := &Set{}
|
||||
s.members[serialized] = setNode{
|
||||
pathElement: pe,
|
||||
set: ss,
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
// Size returns the sum of the number of members of all subsets.
|
||||
func (s *SetNodeMap) Size() int {
|
||||
count := 0
|
||||
for _, v := range s.members {
|
||||
count += v.set.Size()
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// Empty returns false if there's at least one member in some child set.
|
||||
func (s *SetNodeMap) Empty() bool {
|
||||
for _, n := range s.members {
|
||||
if !n.set.Empty() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Get returns (the associated set, true) or (nil, false) if there is none.
|
||||
func (s *SetNodeMap) Get(pe PathElement) (*Set, bool) {
|
||||
if s.members == nil {
|
||||
return nil, false
|
||||
}
|
||||
serialized := pe.String()
|
||||
if n, ok := s.members[serialized]; ok {
|
||||
return n.set, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Equals returns true if s and s2 have the same structure (same nested
|
||||
// child sets).
|
||||
func (s *SetNodeMap) Equals(s2 *SetNodeMap) bool {
|
||||
if len(s.members) != len(s2.members) {
|
||||
return false
|
||||
}
|
||||
for k, v := range s.members {
|
||||
v2, ok := s2.members[k]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if !v.set.Equals(v2.set) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Union returns a SetNodeMap with members that appear in either s or s2.
|
||||
func (s *SetNodeMap) Union(s2 *SetNodeMap) *SetNodeMap {
|
||||
out := &SetNodeMap{}
|
||||
for k, sn := range s.members {
|
||||
pe := sn.pathElement
|
||||
if sn2, ok := s2.members[k]; ok {
|
||||
*out.Descend(pe) = *sn.set.Union(sn2.set)
|
||||
} else {
|
||||
*out.Descend(pe) = *sn.set
|
||||
}
|
||||
}
|
||||
for k, sn2 := range s2.members {
|
||||
pe := sn2.pathElement
|
||||
if _, ok := s.members[k]; ok {
|
||||
// already handled
|
||||
continue
|
||||
}
|
||||
*out.Descend(pe) = *sn2.set
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Intersection returns a SetNodeMap with members that appear in both s and s2.
|
||||
func (s *SetNodeMap) Intersection(s2 *SetNodeMap) *SetNodeMap {
|
||||
out := &SetNodeMap{}
|
||||
for k, sn := range s.members {
|
||||
pe := sn.pathElement
|
||||
if sn2, ok := s2.members[k]; ok {
|
||||
i := *sn.set.Intersection(sn2.set)
|
||||
if !i.Empty() {
|
||||
*out.Descend(pe) = i
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Difference returns a SetNodeMap with members that appear in s but not in s2.
|
||||
func (s *SetNodeMap) Difference(s2 *Set) *SetNodeMap {
|
||||
out := &SetNodeMap{}
|
||||
for k, sn := range s.members {
|
||||
pe := sn.pathElement
|
||||
if s2.Members.Has(pe) {
|
||||
continue
|
||||
}
|
||||
if sn2, ok := s2.Children.members[k]; ok {
|
||||
diff := *sn.set.Difference(sn2.set)
|
||||
// We aren't permitted to add nodes with no elements.
|
||||
if !diff.Empty() {
|
||||
*out.Descend(pe) = diff
|
||||
}
|
||||
} else {
|
||||
*out.Descend(pe) = *sn.set
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Iterate calls f for each PathElement in the set.
|
||||
func (s *SetNodeMap) Iterate(f func(PathElement)) {
|
||||
for _, n := range s.members {
|
||||
f(n.pathElement)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SetNodeMap) iteratePrefix(prefix Path, f func(Path)) {
|
||||
for _, n := range s.members {
|
||||
pe := n.pathElement
|
||||
n.set.iteratePrefix(append(prefix, pe), f)
|
||||
}
|
||||
}
|
91
vendor/sigs.k8s.io/structured-merge-diff/merge/conflict.go
generated
vendored
Normal file
91
vendor/sigs.k8s.io/structured-merge-diff/merge/conflict.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package merge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
)
|
||||
|
||||
// Conflict is a conflict on a specific field with the current manager of
|
||||
// that field. It does implement the error interface so that it can be
|
||||
// used as an error.
|
||||
type Conflict struct {
|
||||
Manager string
|
||||
Path fieldpath.Path
|
||||
}
|
||||
|
||||
// Conflict is an error.
|
||||
var _ error = Conflict{}
|
||||
|
||||
// Error formats the conflict as an error.
|
||||
func (c Conflict) Error() string {
|
||||
return fmt.Sprintf("conflict with %q: %v", c.Manager, c.Path)
|
||||
}
|
||||
|
||||
// Conflicts accumulates multiple conflicts and aggregates them by managers.
|
||||
type Conflicts []Conflict
|
||||
|
||||
var _ error = Conflicts{}
|
||||
|
||||
// Error prints the list of conflicts, grouped by sorted managers.
|
||||
func (conflicts Conflicts) Error() string {
|
||||
if len(conflicts) == 1 {
|
||||
return conflicts[0].Error()
|
||||
}
|
||||
|
||||
m := map[string][]fieldpath.Path{}
|
||||
for _, conflict := range conflicts {
|
||||
m[conflict.Manager] = append(m[conflict.Manager], conflict.Path)
|
||||
}
|
||||
|
||||
managers := []string{}
|
||||
for manager := range m {
|
||||
managers = append(managers, manager)
|
||||
}
|
||||
|
||||
// Print conflicts by sorted managers.
|
||||
sort.Strings(managers)
|
||||
|
||||
messages := []string{}
|
||||
for _, manager := range managers {
|
||||
messages = append(messages, fmt.Sprintf("conflicts with %q:", manager))
|
||||
for _, path := range m[manager] {
|
||||
messages = append(messages, fmt.Sprintf("- %v", path))
|
||||
}
|
||||
}
|
||||
return strings.Join(messages, "\n")
|
||||
}
|
||||
|
||||
// ConflictsFromManagers creates a list of conflicts given Managers sets.
|
||||
func ConflictsFromManagers(sets fieldpath.ManagedFields) Conflicts {
|
||||
conflicts := []Conflict{}
|
||||
|
||||
for manager, set := range sets {
|
||||
set.Iterate(func(p fieldpath.Path) {
|
||||
conflicts = append(conflicts, Conflict{
|
||||
Manager: manager,
|
||||
Path: p,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return conflicts
|
||||
}
|
168
vendor/sigs.k8s.io/structured-merge-diff/merge/update.go
generated
vendored
Normal file
168
vendor/sigs.k8s.io/structured-merge-diff/merge/update.go
generated
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package merge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/typed"
|
||||
)
|
||||
|
||||
// Converter is an interface to the conversion logic. The converter
|
||||
// needs to be able to convert objects from one version to another.
|
||||
type Converter interface {
|
||||
Convert(object typed.TypedValue, version fieldpath.APIVersion) (typed.TypedValue, error)
|
||||
}
|
||||
|
||||
// Updater is the object used to compute updated FieldSets and also
|
||||
// merge the object on Apply.
|
||||
type Updater struct {
|
||||
Converter Converter
|
||||
}
|
||||
|
||||
func (s *Updater) update(oldObject, newObject typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, workflow string, force bool) (fieldpath.ManagedFields, error) {
|
||||
if managers == nil {
|
||||
managers = fieldpath.ManagedFields{}
|
||||
}
|
||||
conflicts := fieldpath.ManagedFields{}
|
||||
type Versioned struct {
|
||||
oldObject typed.TypedValue
|
||||
newObject typed.TypedValue
|
||||
}
|
||||
versions := map[fieldpath.APIVersion]Versioned{
|
||||
version: Versioned{
|
||||
oldObject: oldObject,
|
||||
newObject: newObject,
|
||||
},
|
||||
}
|
||||
|
||||
for manager, managerSet := range managers {
|
||||
if manager == workflow {
|
||||
continue
|
||||
}
|
||||
versioned, ok := versions[managerSet.APIVersion]
|
||||
if !ok {
|
||||
var err error
|
||||
versioned.oldObject, err = s.Converter.Convert(oldObject, managerSet.APIVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert old object: %v", err)
|
||||
}
|
||||
versioned.newObject, err = s.Converter.Convert(newObject, managerSet.APIVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert new object: %v", err)
|
||||
}
|
||||
versions[managerSet.APIVersion] = versioned
|
||||
}
|
||||
compare, err := versioned.oldObject.Compare(versioned.newObject)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to compare objects: %v", err)
|
||||
}
|
||||
|
||||
conflictSet := managerSet.Intersection(compare.Modified.Union(compare.Added))
|
||||
if !conflictSet.Empty() {
|
||||
conflicts[manager] = &fieldpath.VersionedSet{
|
||||
Set: conflictSet,
|
||||
APIVersion: managerSet.APIVersion,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !force && len(conflicts) != 0 {
|
||||
return nil, ConflictsFromManagers(conflicts)
|
||||
}
|
||||
|
||||
for manager, conflictSet := range conflicts {
|
||||
managers[manager].Set = managers[manager].Set.Difference(conflictSet.Set)
|
||||
if managers[manager].Set.Empty() {
|
||||
delete(managers, manager)
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := managers[workflow]; !ok {
|
||||
managers[workflow] = &fieldpath.VersionedSet{
|
||||
Set: fieldpath.NewSet(),
|
||||
}
|
||||
}
|
||||
|
||||
return managers, nil
|
||||
}
|
||||
|
||||
// Update is the method you should call once you've merged your final
|
||||
// object on CREATE/UPDATE/PATCH verbs. newObject must be the object
|
||||
// that you intend to persist (after applying the patch if this is for a
|
||||
// PATCH call), and liveObject must be the original object (empty if
|
||||
// this is a CREATE call).
|
||||
func (s *Updater) Update(liveObject, newObject typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, manager string) (fieldpath.ManagedFields, error) {
|
||||
var err error
|
||||
managers, err = s.update(liveObject, newObject, version, managers, manager, true)
|
||||
if err != nil {
|
||||
return fieldpath.ManagedFields{}, err
|
||||
}
|
||||
compare, err := liveObject.Compare(newObject)
|
||||
if err != nil {
|
||||
return fieldpath.ManagedFields{}, fmt.Errorf("failed to compare live and new objects: %v", err)
|
||||
}
|
||||
managers[manager].Set = managers[manager].Set.Union(compare.Modified).Union(compare.Added).Difference(compare.Removed)
|
||||
managers[manager].APIVersion = version
|
||||
if managers[manager].Set.Empty() {
|
||||
delete(managers, manager)
|
||||
}
|
||||
return managers, nil
|
||||
}
|
||||
|
||||
// Apply should be called when Apply is run, given the current object as
|
||||
// well as the configuration that is applied. This will merge the object
|
||||
// and return it.
|
||||
func (s *Updater) Apply(liveObject, configObject typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, manager string, force bool) (typed.TypedValue, fieldpath.ManagedFields, error) {
|
||||
newObject, err := liveObject.Merge(configObject)
|
||||
if err != nil {
|
||||
return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to merge config: %v", err)
|
||||
}
|
||||
managers, err = s.update(liveObject, newObject, version, managers, manager, force)
|
||||
if err != nil {
|
||||
return nil, fieldpath.ManagedFields{}, err
|
||||
}
|
||||
newObject, err = s.removeDisownedItems(newObject, configObject, managers[manager])
|
||||
if err != nil {
|
||||
return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to remove fields: %v", err)
|
||||
}
|
||||
set, err := configObject.ToFieldSet()
|
||||
if err != nil {
|
||||
return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to get field set: %v", err)
|
||||
}
|
||||
managers[manager] = &fieldpath.VersionedSet{
|
||||
Set: set,
|
||||
APIVersion: version,
|
||||
}
|
||||
if managers[manager].Set.Empty() {
|
||||
delete(managers, manager)
|
||||
}
|
||||
return newObject, managers, nil
|
||||
}
|
||||
|
||||
func (s *Updater) removeDisownedItems(merged, applied typed.TypedValue, lastSet *fieldpath.VersionedSet) (typed.TypedValue, error) {
|
||||
if lastSet.Set.Empty() {
|
||||
return merged, nil
|
||||
}
|
||||
convertedApplied, err := s.Converter.Convert(applied, lastSet.APIVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert applied config to last applied version: %v", err)
|
||||
}
|
||||
appliedSet, err := convertedApplied.ToFieldSet()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create field set from applied config in last applied version: %v", err)
|
||||
}
|
||||
return merged.RemoveItems(lastSet.Set.Difference(appliedSet)), nil
|
||||
}
|
28
vendor/sigs.k8s.io/structured-merge-diff/schema/doc.go
generated
vendored
Normal file
28
vendor/sigs.k8s.io/structured-merge-diff/schema/doc.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package schema defines a targeted schema language which allows one to
|
||||
// represent all the schema information necessary to perform "structured"
|
||||
// merges and diffs.
|
||||
//
|
||||
// Due to the targeted nature of the data model, the schema language can fit in
|
||||
// just a few hundred lines of go code, making it much more understandable and
|
||||
// concise than e.g. OpenAPI.
|
||||
//
|
||||
// This schema was derived by observing the API objects used by Kubernetes, and
|
||||
// formalizing a model which allows certain operations ("apply") to be more
|
||||
// well defined. It is currently missing one feature: one-of ("unions").
|
||||
package schema
|
219
vendor/sigs.k8s.io/structured-merge-diff/schema/elements.go
generated
vendored
Normal file
219
vendor/sigs.k8s.io/structured-merge-diff/schema/elements.go
generated
vendored
Normal file
@ -0,0 +1,219 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
// Schema is a list of named types.
|
||||
type Schema struct {
|
||||
Types []TypeDef `yaml:"types,omitempty"`
|
||||
}
|
||||
|
||||
// A TypeSpecifier references a particular type in a schema.
|
||||
type TypeSpecifier struct {
|
||||
Type TypeRef `yaml:"type,omitempty"`
|
||||
Schema Schema `yaml:"schema,omitempty"`
|
||||
}
|
||||
|
||||
// TypeDef represents a named type in a schema.
|
||||
type TypeDef struct {
|
||||
// Top level types should be named. Every type must have a unique name.
|
||||
Name string `yaml:"name,omitempty"`
|
||||
|
||||
Atom `yaml:"atom,omitempty,inline"`
|
||||
}
|
||||
|
||||
// TypeRef either refers to a named type or declares an inlined type.
|
||||
type TypeRef struct {
|
||||
// Either the name or one member of Atom should be set.
|
||||
NamedType *string `yaml:"namedType,omitempty"`
|
||||
Inlined Atom `yaml:",inline,omitempty"`
|
||||
}
|
||||
|
||||
// Atom represents the smallest possible pieces of the type system.
|
||||
type Atom struct {
|
||||
// Exactly one of the below must be set.
|
||||
*Scalar `yaml:"scalar,omitempty"`
|
||||
*Struct `yaml:"struct,omitempty"`
|
||||
*List `yaml:"list,omitempty"`
|
||||
*Map `yaml:"map,omitempty"`
|
||||
*Untyped `yaml:"untyped,omitempty"`
|
||||
}
|
||||
|
||||
// Scalar (AKA "primitive") represents a type which has a single value which is
|
||||
// either numeric, string, or boolean.
|
||||
//
|
||||
// TODO: split numeric into float/int? Something even more fine-grained?
|
||||
type Scalar string
|
||||
|
||||
const (
|
||||
Numeric = Scalar("numeric")
|
||||
String = Scalar("string")
|
||||
Boolean = Scalar("boolean")
|
||||
)
|
||||
|
||||
// ElementRelationship is an enum of the different possible relationships
|
||||
// between the elements of container types (maps, lists, structs, untyped).
|
||||
type ElementRelationship string
|
||||
|
||||
const (
|
||||
// Associative only applies to lists (see the documentation there).
|
||||
Associative = ElementRelationship("associative")
|
||||
// Atomic makes container types (lists, maps, structs, untyped) behave
|
||||
// as scalars / leaf fields (which is the default for untyped data).
|
||||
Atomic = ElementRelationship("atomic")
|
||||
// Separable means the items of the container type have no particular
|
||||
// relationship (default behavior for maps and structs).
|
||||
Separable = ElementRelationship("separable")
|
||||
)
|
||||
|
||||
// Struct represents a type which is composed of a number of different fields.
|
||||
// Each field has a name and a type.
|
||||
//
|
||||
// TODO: in the future, we will add one-of groups (sometimes called unions).
|
||||
type Struct struct {
|
||||
// Each struct field appears exactly once in this list. The order in
|
||||
// this list defines the canonical field ordering.
|
||||
Fields []StructField `yaml:"fields,omitempty"`
|
||||
|
||||
// TODO: Implement unions, either this way or by inlining.
|
||||
// Unions are groupings of fields with special rules. They may refer to
|
||||
// one or more fields in the above list. A given field from the above
|
||||
// list may be referenced in exactly 0 or 1 places in the below list.
|
||||
// Unions []Union `yaml:"unions,omitempty"`
|
||||
|
||||
// ElementRelationship states the relationship between the struct's items.
|
||||
// * `separable` (or unset) implies that each element is 100% independent.
|
||||
// * `atomic` implies that all elements depend on each other, and this
|
||||
// is effectively a scalar / leaf field; it doesn't make sense for
|
||||
// separate actors to set the elements. Example: an RGB color struct;
|
||||
// it would never make sense to "own" only one component of the
|
||||
// color.
|
||||
// The default behavior for structs is `separable`; it's permitted to
|
||||
// leave this unset to get the default behavior.
|
||||
ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"`
|
||||
}
|
||||
|
||||
// StructField pairs a field name with a field type.
|
||||
type StructField struct {
|
||||
// Name is the field name.
|
||||
Name string `yaml:"name,omitempty"`
|
||||
// Type is the field type.
|
||||
Type TypeRef `yaml:"type,omitempty"`
|
||||
}
|
||||
|
||||
// List represents a type which contains a zero or more elements, all of the
|
||||
// same subtype. Lists may be either associative: each element is more or less
|
||||
// independent and could be managed by separate entities in the system; or
|
||||
// atomic, where the elements are heavily dependent on each other: it is not
|
||||
// sensible to change one element without considering the ramifications on all
|
||||
// the other elements.
|
||||
type List struct {
|
||||
// ElementType is the type of the list's elements.
|
||||
ElementType TypeRef `yaml:"elementType,omitempty"`
|
||||
|
||||
// ElementRelationship states the relationship between the list's elements
|
||||
// and must have one of these values:
|
||||
// * `atomic`: the list is treated as a single entity, like a scalar.
|
||||
// * `associative`:
|
||||
// - If the list element is a scalar, the list is treated as a set.
|
||||
// - If the list element is a struct, the list is treated as a map.
|
||||
// - The list element must not be a map or a list itself.
|
||||
// There is no default for this value for lists; all schemas must
|
||||
// explicitly state the element relationship for all lists.
|
||||
ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"`
|
||||
|
||||
// Iff ElementRelationship is `associative`, and the element type is
|
||||
// struct, then Keys must have non-zero length, and it lists the fields
|
||||
// of the element's struct type which are to be used as the keys of the
|
||||
// list.
|
||||
//
|
||||
// TODO: change this to "non-atomic struct" above and make the code reflect this.
|
||||
//
|
||||
// Each key must refer to a single field name (no nesting, not JSONPath).
|
||||
Keys []string `yaml:"keys,omitempty"`
|
||||
}
|
||||
|
||||
// Map is a key-value pair. Its default semantics are the same as an
|
||||
// associative list, but:
|
||||
// * It is serialized differently:
|
||||
// map: {"k": {"value": "v"}}
|
||||
// list: [{"key": "k", "value": "v"}]
|
||||
// * Keys must be string typed.
|
||||
// * Keys can't have multiple components.
|
||||
//
|
||||
// Although serialized the same, maps are different from structs in that each
|
||||
// map item must have the same type.
|
||||
//
|
||||
// Optionally, maps may be atomic (for example, imagine representing an RGB
|
||||
// color value--it doesn't make sense to have different actors own the R and G
|
||||
// values).
|
||||
type Map struct {
|
||||
// ElementType is the type of the list's elements.
|
||||
ElementType TypeRef `yaml:"elementType,omitempty"`
|
||||
|
||||
// ElementRelationship states the relationship between the map's items.
|
||||
// * `separable` implies that each element is 100% independent.
|
||||
// * `atomic` implies that all elements depend on each other, and this
|
||||
// is effectively a scalar / leaf field; it doesn't make sense for
|
||||
// separate actors to set the elements.
|
||||
// TODO: find a simple example.
|
||||
// The default behavior for maps is `separable`; it's permitted to
|
||||
// leave this unset to get the default behavior.
|
||||
ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"`
|
||||
}
|
||||
|
||||
// Untyped represents types that allow arbitrary content. (Think: plugin
|
||||
// objects.)
|
||||
type Untyped struct {
|
||||
// ElementRelationship states the relationship between the items, if
|
||||
// container-typed data happens to be present here.
|
||||
// * `atomic` implies that all elements depend on each other, and this
|
||||
// is effectively a scalar / leaf field; it doesn't make sense for
|
||||
// separate actors to set the elements.
|
||||
// TODO: support "guess" (guesses at associative list keys)
|
||||
// TODO: support "lookup" (calls a lookup function to figure out the
|
||||
// schema based on the data)
|
||||
// The default behavior for untyped data is `atomic`; it's permitted to
|
||||
// leave this unset to get the default behavior.
|
||||
ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"`
|
||||
}
|
||||
|
||||
// FindNamedType is a convenience function that returns the referenced TypeDef,
|
||||
// if it exists, or (nil, false) if it doesn't.
|
||||
func (s Schema) FindNamedType(name string) (TypeDef, bool) {
|
||||
for _, t := range s.Types {
|
||||
if t.Name == name {
|
||||
return t, true
|
||||
}
|
||||
}
|
||||
return TypeDef{}, false
|
||||
}
|
||||
|
||||
// Resolve is a convenience function which returns the atom referenced, whether
|
||||
// it is inline or named. Returns (Atom{}, false) if the type can't be resolved.
|
||||
//
|
||||
// This allows callers to not care about the difference between a (possibly
|
||||
// inlined) reference and a definition.
|
||||
func (s *Schema) Resolve(tr TypeRef) (Atom, bool) {
|
||||
if tr.NamedType != nil {
|
||||
t, ok := s.FindNamedType(*tr.NamedType)
|
||||
if !ok {
|
||||
return Atom{}, false
|
||||
}
|
||||
return t.Atom, true
|
||||
}
|
||||
return tr.Inlined, true
|
||||
}
|
57
vendor/sigs.k8s.io/structured-merge-diff/schema/fromvalue.go
generated
vendored
Normal file
57
vendor/sigs.k8s.io/structured-merge-diff/schema/fromvalue.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
// TypeRefFromValue creates an inlined type from a value v
|
||||
func TypeRefFromValue(v value.Value) TypeRef {
|
||||
atom := atomFor(v)
|
||||
return TypeRef{
|
||||
Inlined: atom,
|
||||
}
|
||||
}
|
||||
|
||||
func atomFor(v value.Value) Atom {
|
||||
switch {
|
||||
// Untyped cases (handled at the bottom of this function)
|
||||
case v.Null:
|
||||
case v.ListValue != nil:
|
||||
case v.FloatValue != nil:
|
||||
case v.IntValue != nil:
|
||||
case v.StringValue != nil:
|
||||
case v.BooleanValue != nil:
|
||||
// Recursive case
|
||||
case v.MapValue != nil:
|
||||
s := Struct{}
|
||||
for i := range v.MapValue.Items {
|
||||
child := v.MapValue.Items[i]
|
||||
field := StructField{
|
||||
Name: child.Name,
|
||||
Type: TypeRef{
|
||||
Inlined: atomFor(child.Value),
|
||||
},
|
||||
}
|
||||
s.Fields = append(s.Fields, field)
|
||||
}
|
||||
return Atom{Struct: &s}
|
||||
}
|
||||
|
||||
return Atom{Untyped: &Untyped{}}
|
||||
}
|
128
vendor/sigs.k8s.io/structured-merge-diff/schema/schemaschema.go
generated
vendored
Normal file
128
vendor/sigs.k8s.io/structured-merge-diff/schema/schemaschema.go
generated
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
// SchemaSchemaYAML is a schema against which you can validate other schemas.
|
||||
// It will validate itself. It can be unmarshalled into a Schema type.
|
||||
var SchemaSchemaYAML = `types:
|
||||
- name: schema
|
||||
struct:
|
||||
fields:
|
||||
- name: types
|
||||
type:
|
||||
list:
|
||||
elementRelationship: associative
|
||||
elementType:
|
||||
namedType: typeDef
|
||||
keys:
|
||||
- name
|
||||
- name: typeDef
|
||||
struct:
|
||||
fields:
|
||||
- name: name
|
||||
type:
|
||||
scalar: string
|
||||
- name: scalar
|
||||
type:
|
||||
scalar: string
|
||||
- name: struct
|
||||
type:
|
||||
namedType: struct
|
||||
- name: list
|
||||
type:
|
||||
namedType: list
|
||||
- name: map
|
||||
type:
|
||||
namedType: map
|
||||
- name: untyped
|
||||
type:
|
||||
namedType: untyped
|
||||
- name: typeRef
|
||||
struct:
|
||||
fields:
|
||||
- name: namedType
|
||||
type:
|
||||
scalar: string
|
||||
- name: scalar
|
||||
type:
|
||||
scalar: string
|
||||
- name: struct
|
||||
type:
|
||||
namedType: struct
|
||||
- name: list
|
||||
type:
|
||||
namedType: list
|
||||
- name: map
|
||||
type:
|
||||
namedType: map
|
||||
- name: untyped
|
||||
type:
|
||||
namedType: untyped
|
||||
- name: scalar
|
||||
scalar: string
|
||||
- name: struct
|
||||
struct:
|
||||
fields:
|
||||
- name: fields
|
||||
type:
|
||||
list:
|
||||
elementType:
|
||||
namedType: structField
|
||||
elementRelationship: associative
|
||||
keys: [ "name" ]
|
||||
- name: elementRelationship
|
||||
type:
|
||||
scalar: string
|
||||
- name: structField
|
||||
struct:
|
||||
fields:
|
||||
- name: name
|
||||
type:
|
||||
scalar: string
|
||||
- name: type
|
||||
type:
|
||||
namedType: typeRef
|
||||
- name: list
|
||||
struct:
|
||||
fields:
|
||||
- name: elementType
|
||||
type:
|
||||
namedType: typeRef
|
||||
- name: elementRelationship
|
||||
type:
|
||||
scalar: string
|
||||
- name: keys
|
||||
type:
|
||||
list:
|
||||
elementType:
|
||||
scalar: string
|
||||
- name: map
|
||||
struct:
|
||||
fields:
|
||||
- name: elementType
|
||||
type:
|
||||
namedType: typeRef
|
||||
- name: elementRelationship
|
||||
type:
|
||||
scalar: string
|
||||
- name: untyped
|
||||
struct:
|
||||
fields:
|
||||
- name: elementRelationship
|
||||
type:
|
||||
scalar: string
|
||||
`
|
178
vendor/sigs.k8s.io/structured-merge-diff/typed/deduced.go
generated
vendored
Normal file
178
vendor/sigs.k8s.io/structured-merge-diff/typed/deduced.go
generated
vendored
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typed
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
// deducedTypedValue holds a value and guesses what it is and what to
|
||||
// do with it.
|
||||
type deducedTypedValue struct {
|
||||
value value.Value
|
||||
}
|
||||
|
||||
// AsTypedDeduced is going to generate it's own type definition based on
|
||||
// the content of the object. This is useful for CRDs that don't have a
|
||||
// validation field.
|
||||
func AsTypedDeduced(v value.Value) TypedValue {
|
||||
return deducedTypedValue{value: v}
|
||||
}
|
||||
|
||||
func (dv deducedTypedValue) AsValue() *value.Value {
|
||||
return &dv.value
|
||||
}
|
||||
|
||||
func (deducedTypedValue) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dv deducedTypedValue) ToFieldSet() (*fieldpath.Set, error) {
|
||||
set := fieldpath.NewSet()
|
||||
fieldsetDeduced(dv.value, fieldpath.Path{}, set)
|
||||
return set, nil
|
||||
}
|
||||
|
||||
func fieldsetDeduced(v value.Value, path fieldpath.Path, set *fieldpath.Set) {
|
||||
if v.MapValue == nil {
|
||||
set.Insert(path)
|
||||
return
|
||||
}
|
||||
|
||||
// We have a map.
|
||||
// copy the existing path, append each item, and recursively call.
|
||||
for i := range v.MapValue.Items {
|
||||
child := v.MapValue.Items[i]
|
||||
np := path.Copy()
|
||||
np = append(np, fieldpath.PathElement{FieldName: &child.Name})
|
||||
fieldsetDeduced(child.Value, np, set)
|
||||
}
|
||||
}
|
||||
|
||||
func (dv deducedTypedValue) Merge(pso TypedValue) (TypedValue, error) {
|
||||
tpso, ok := pso.(deducedTypedValue)
|
||||
if !ok {
|
||||
return nil, errorFormatter{}.
|
||||
errorf("can't merge deducedTypedValue with %T", tpso)
|
||||
}
|
||||
return AsTypedDeduced(mergeDeduced(dv.value, tpso.value)), nil
|
||||
}
|
||||
|
||||
func mergeDeduced(lhs, rhs value.Value) value.Value {
|
||||
// If both sides are maps, merge them, otherwise return right
|
||||
// side.
|
||||
if rhs.MapValue == nil || lhs.MapValue == nil {
|
||||
return rhs
|
||||
}
|
||||
|
||||
v := value.Value{MapValue: &value.Map{}}
|
||||
for i := range lhs.MapValue.Items {
|
||||
child := lhs.MapValue.Items[i]
|
||||
v.MapValue.Set(child.Name, child.Value)
|
||||
}
|
||||
for i := range rhs.MapValue.Items {
|
||||
child := rhs.MapValue.Items[i]
|
||||
if sub, ok := v.MapValue.Get(child.Name); ok {
|
||||
new := mergeDeduced(sub.Value, child.Value)
|
||||
v.MapValue.Set(child.Name, new)
|
||||
} else {
|
||||
v.MapValue.Set(child.Name, child.Value)
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (dv deducedTypedValue) Compare(rhs TypedValue) (c *Comparison, err error) {
|
||||
trhs, ok := rhs.(deducedTypedValue)
|
||||
if !ok {
|
||||
return nil, errorFormatter{}.
|
||||
errorf("can't merge deducedTypedValue with %T", rhs)
|
||||
}
|
||||
|
||||
c = &Comparison{
|
||||
Removed: fieldpath.NewSet(),
|
||||
Modified: fieldpath.NewSet(),
|
||||
Added: fieldpath.NewSet(),
|
||||
}
|
||||
|
||||
added(dv.value, trhs.value, fieldpath.Path{}, c.Added)
|
||||
added(trhs.value, dv.value, fieldpath.Path{}, c.Removed)
|
||||
modified(dv.value, trhs.value, fieldpath.Path{}, c.Modified)
|
||||
|
||||
merge, err := dv.Merge(rhs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Merged = merge
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func added(lhs, rhs value.Value, path fieldpath.Path, set *fieldpath.Set) {
|
||||
if lhs.MapValue == nil && rhs.MapValue == nil {
|
||||
// Both non-maps, nothing added, do nothing.
|
||||
} else if lhs.MapValue == nil && rhs.MapValue != nil {
|
||||
// From leaf to map, add leaf fields of map.
|
||||
fieldsetDeduced(rhs, path, set)
|
||||
} else if lhs.MapValue != nil && rhs.MapValue == nil {
|
||||
// Went from map to field, add field.
|
||||
set.Insert(path)
|
||||
} else {
|
||||
// Both are maps.
|
||||
for i := range rhs.MapValue.Items {
|
||||
child := rhs.MapValue.Items[i]
|
||||
np := path.Copy()
|
||||
np = append(np, fieldpath.PathElement{FieldName: &child.Name})
|
||||
|
||||
if v, ok := lhs.MapValue.Get(child.Name); ok {
|
||||
added(v.Value, child.Value, np, set)
|
||||
} else {
|
||||
fieldsetDeduced(child.Value, np, set)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func modified(lhs, rhs value.Value, path fieldpath.Path, set *fieldpath.Set) {
|
||||
if lhs.MapValue == nil && rhs.MapValue == nil {
|
||||
if !reflect.DeepEqual(lhs, rhs) {
|
||||
set.Insert(path)
|
||||
}
|
||||
} else if lhs.MapValue != nil && rhs.MapValue != nil {
|
||||
// Both are maps.
|
||||
for i := range rhs.MapValue.Items {
|
||||
child := rhs.MapValue.Items[i]
|
||||
|
||||
v, ok := lhs.MapValue.Get(child.Name)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
np := path.Copy()
|
||||
np = append(np, fieldpath.PathElement{FieldName: &child.Name})
|
||||
modified(v.Value, child.Value, np, set)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveItems does nothing because all lists in a deducedTypedValue are considered atomic,
|
||||
// and there are no maps because it is indistinguishable from a struct.
|
||||
func (dv deducedTypedValue) RemoveItems(_ *fieldpath.Set) TypedValue {
|
||||
return dv
|
||||
}
|
18
vendor/sigs.k8s.io/structured-merge-diff/typed/doc.go
generated
vendored
Normal file
18
vendor/sigs.k8s.io/structured-merge-diff/typed/doc.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package typed contains logic for operating on values with given schemas.
|
||||
package typed
|
249
vendor/sigs.k8s.io/structured-merge-diff/typed/helpers.go
generated
vendored
Normal file
249
vendor/sigs.k8s.io/structured-merge-diff/typed/helpers.go
generated
vendored
Normal file
@ -0,0 +1,249 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typed
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
// ValidationError reports an error about a particular field
|
||||
type ValidationError struct {
|
||||
Path fieldpath.Path
|
||||
ErrorMessage string
|
||||
}
|
||||
|
||||
// Error returns a human readable error message.
|
||||
func (ve ValidationError) Error() string {
|
||||
if len(ve.Path) == 0 {
|
||||
return ve.ErrorMessage
|
||||
}
|
||||
return fmt.Sprintf("%s: %v", ve.Path, ve.ErrorMessage)
|
||||
}
|
||||
|
||||
// ValidationErrors accumulates multiple validation error messages.
|
||||
type ValidationErrors []ValidationError
|
||||
|
||||
// Error returns a human readable error message reporting each error in the
|
||||
// list.
|
||||
func (errs ValidationErrors) Error() string {
|
||||
if len(errs) == 1 {
|
||||
return errs[0].Error()
|
||||
}
|
||||
messages := []string{"errors:"}
|
||||
for _, e := range errs {
|
||||
messages = append(messages, " "+e.Error())
|
||||
}
|
||||
return strings.Join(messages, "\n")
|
||||
}
|
||||
|
||||
// errorFormatter makes it easy to keep a list of validation errors. They
|
||||
// should all be packed into a single error object before leaving the package
|
||||
// boundary, since it's weird to have functions not return a plain error type.
|
||||
type errorFormatter struct {
|
||||
path fieldpath.Path
|
||||
}
|
||||
|
||||
func (ef *errorFormatter) descend(pe fieldpath.PathElement) {
|
||||
ef.path = append(ef.path, pe)
|
||||
}
|
||||
|
||||
func (ef errorFormatter) errorf(format string, args ...interface{}) ValidationErrors {
|
||||
return ValidationErrors{{
|
||||
Path: append(fieldpath.Path{}, ef.path...),
|
||||
ErrorMessage: fmt.Sprintf(format, args...),
|
||||
}}
|
||||
}
|
||||
|
||||
func (ef errorFormatter) error(err error) ValidationErrors {
|
||||
return ValidationErrors{{
|
||||
Path: append(fieldpath.Path{}, ef.path...),
|
||||
ErrorMessage: err.Error(),
|
||||
}}
|
||||
}
|
||||
|
||||
func (ef errorFormatter) prefixError(prefix string, err error) ValidationErrors {
|
||||
return ValidationErrors{{
|
||||
Path: append(fieldpath.Path{}, ef.path...),
|
||||
ErrorMessage: prefix + err.Error(),
|
||||
}}
|
||||
}
|
||||
|
||||
type atomHandler interface {
|
||||
doScalar(schema.Scalar) ValidationErrors
|
||||
doStruct(schema.Struct) ValidationErrors
|
||||
doList(schema.List) ValidationErrors
|
||||
doMap(schema.Map) ValidationErrors
|
||||
doUntyped(schema.Untyped) ValidationErrors
|
||||
|
||||
errorf(msg string, args ...interface{}) ValidationErrors
|
||||
}
|
||||
|
||||
func resolveSchema(s *schema.Schema, tr schema.TypeRef, ah atomHandler) ValidationErrors {
|
||||
a, ok := s.Resolve(tr)
|
||||
if !ok {
|
||||
return ah.errorf("schema error: no type found matching: %v", *tr.NamedType)
|
||||
}
|
||||
|
||||
switch {
|
||||
case a.Scalar != nil:
|
||||
return ah.doScalar(*a.Scalar)
|
||||
case a.Struct != nil:
|
||||
return ah.doStruct(*a.Struct)
|
||||
case a.List != nil:
|
||||
return ah.doList(*a.List)
|
||||
case a.Map != nil:
|
||||
return ah.doMap(*a.Map)
|
||||
case a.Untyped != nil:
|
||||
return ah.doUntyped(*a.Untyped)
|
||||
}
|
||||
|
||||
name := "inlined"
|
||||
if tr.NamedType != nil {
|
||||
name = "named type: " + *tr.NamedType
|
||||
}
|
||||
|
||||
return ah.errorf("schema error: invalid atom: %v", name)
|
||||
}
|
||||
|
||||
func (ef errorFormatter) validateScalar(t schema.Scalar, v *value.Value, prefix string) (errs ValidationErrors) {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
if v.Null {
|
||||
return nil
|
||||
}
|
||||
switch t {
|
||||
case schema.Numeric:
|
||||
if v.FloatValue == nil && v.IntValue == nil {
|
||||
// TODO: should the schema separate int and float?
|
||||
return ef.errorf("%vexpected numeric (int or float), got %v", prefix, v)
|
||||
}
|
||||
case schema.String:
|
||||
if v.StringValue == nil {
|
||||
return ef.errorf("%vexpected string, got %v", prefix, v)
|
||||
}
|
||||
case schema.Boolean:
|
||||
if v.BooleanValue == nil {
|
||||
return ef.errorf("%vexpected boolean, got %v", prefix, v)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns the list, or an error. Reminder: nil is a valid list and might be returned.
|
||||
func listValue(val value.Value) (*value.List, error) {
|
||||
switch {
|
||||
case val.Null:
|
||||
// Null is a valid list.
|
||||
return nil, nil
|
||||
case val.ListValue != nil:
|
||||
return val.ListValue, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("expected list, got %v", val)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the map, or an error. Reminder: nil is a valid map and might be returned.
|
||||
func mapOrStructValue(val value.Value, typeName string) (*value.Map, error) {
|
||||
switch {
|
||||
case val.Null:
|
||||
return nil, nil
|
||||
case val.MapValue != nil:
|
||||
return val.MapValue, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("expected %v, got %v", typeName, val)
|
||||
}
|
||||
}
|
||||
|
||||
func (ef errorFormatter) rejectExtraStructFields(m *value.Map, allowedNames map[string]struct{}, prefix string) (errs ValidationErrors) {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
for _, f := range m.Items {
|
||||
if _, allowed := allowedNames[f.Name]; !allowed {
|
||||
errs = append(errs, ef.errorf("%vfield %v is not mentioned in the schema", prefix, f.Name)...)
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func keyedAssociativeListItemToPathElement(list schema.List, index int, child value.Value) (fieldpath.PathElement, error) {
|
||||
pe := fieldpath.PathElement{}
|
||||
if child.Null {
|
||||
// For now, the keys are required which means that null entries
|
||||
// are illegal.
|
||||
return pe, errors.New("associative list with keys may not have a null element")
|
||||
}
|
||||
if child.MapValue == nil {
|
||||
return pe, errors.New("associative list with keys may not have non-map elements")
|
||||
}
|
||||
for _, fieldName := range list.Keys {
|
||||
var fieldValue value.Value
|
||||
field, ok := child.MapValue.Get(fieldName)
|
||||
if ok {
|
||||
fieldValue = field.Value
|
||||
} else {
|
||||
// Treat keys as required.
|
||||
return pe, fmt.Errorf("associative list with keys has an element that omits key field %q", fieldName)
|
||||
}
|
||||
pe.Key = append(pe.Key, value.Field{
|
||||
Name: fieldName,
|
||||
Value: fieldValue,
|
||||
})
|
||||
}
|
||||
return pe, nil
|
||||
}
|
||||
|
||||
func setItemToPathElement(list schema.List, index int, child value.Value) (fieldpath.PathElement, error) {
|
||||
pe := fieldpath.PathElement{}
|
||||
switch {
|
||||
case child.MapValue != nil:
|
||||
// TODO: atomic maps should be acceptable.
|
||||
return pe, errors.New("associative list without keys has an element that's a map type")
|
||||
case child.ListValue != nil:
|
||||
// Should we support a set of lists? For the moment
|
||||
// let's say we don't.
|
||||
// TODO: atomic lists should be acceptable.
|
||||
return pe, errors.New("not supported: associative list with lists as elements")
|
||||
case child.Null:
|
||||
return pe, errors.New("associative list without keys has an element that's an explicit null")
|
||||
default:
|
||||
// We are a set type.
|
||||
pe.Value = &child
|
||||
return pe, nil
|
||||
}
|
||||
}
|
||||
|
||||
func listItemToPathElement(list schema.List, index int, child value.Value) (fieldpath.PathElement, error) {
|
||||
if list.ElementRelationship == schema.Associative {
|
||||
if len(list.Keys) > 0 {
|
||||
return keyedAssociativeListItemToPathElement(list, index, child)
|
||||
}
|
||||
|
||||
// If there's no keys, then we must be a set of primitives.
|
||||
return setItemToPathElement(list, index, child)
|
||||
}
|
||||
|
||||
// Use the index as a key for atomic lists.
|
||||
return fieldpath.PathElement{Index: &index}, nil
|
||||
}
|
410
vendor/sigs.k8s.io/structured-merge-diff/typed/merge.go
generated
vendored
Normal file
410
vendor/sigs.k8s.io/structured-merge-diff/typed/merge.go
generated
vendored
Normal file
@ -0,0 +1,410 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typed
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
type mergingWalker struct {
|
||||
errorFormatter
|
||||
lhs *value.Value
|
||||
rhs *value.Value
|
||||
schema *schema.Schema
|
||||
typeRef schema.TypeRef
|
||||
|
||||
// How to merge. Called after schema validation for all leaf fields.
|
||||
rule mergeRule
|
||||
|
||||
// If set, called after non-leaf items have been merged. (`out` is
|
||||
// probably already set.)
|
||||
postItemHook mergeRule
|
||||
|
||||
// output of the merge operation (nil if none)
|
||||
out *value.Value
|
||||
|
||||
// internal housekeeping--don't set when constructing.
|
||||
inLeaf bool // Set to true if we're in a "big leaf"--atomic map/list
|
||||
}
|
||||
|
||||
// merge rules examine w.lhs and w.rhs (up to one of which may be nil) and
|
||||
// optionally set w.out. If lhs and rhs are both set, they will be of
|
||||
// comparable type.
|
||||
type mergeRule func(w *mergingWalker)
|
||||
|
||||
var (
|
||||
ruleKeepRHS = mergeRule(func(w *mergingWalker) {
|
||||
if w.rhs != nil {
|
||||
v := *w.rhs
|
||||
w.out = &v
|
||||
} else if w.lhs != nil {
|
||||
v := *w.lhs
|
||||
w.out = &v
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
// merge sets w.out.
|
||||
func (w *mergingWalker) merge() ValidationErrors {
|
||||
if w.lhs == nil && w.rhs == nil {
|
||||
// check this condidition here instead of everywhere below.
|
||||
return w.errorf("at least one of lhs and rhs must be provided")
|
||||
}
|
||||
errs := resolveSchema(w.schema, w.typeRef, w)
|
||||
if !w.inLeaf && w.postItemHook != nil {
|
||||
w.postItemHook(w)
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// doLeaf should be called on leaves before descending into children, if there
|
||||
// will be a descent. It modifies w.inLeaf.
|
||||
func (w *mergingWalker) doLeaf() {
|
||||
if w.inLeaf {
|
||||
// We're in a "big leaf", an atomic map or list. Ignore
|
||||
// subsequent leaves.
|
||||
return
|
||||
}
|
||||
w.inLeaf = true
|
||||
|
||||
// We don't recurse into leaf fields for merging.
|
||||
w.rule(w)
|
||||
}
|
||||
|
||||
func (w *mergingWalker) doScalar(t schema.Scalar) (errs ValidationErrors) {
|
||||
errs = append(errs, w.validateScalar(t, w.lhs, "lhs: ")...)
|
||||
errs = append(errs, w.validateScalar(t, w.rhs, "rhs: ")...)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
// All scalars are leaf fields.
|
||||
w.doLeaf()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *mergingWalker) prepareDescent(pe fieldpath.PathElement, tr schema.TypeRef) *mergingWalker {
|
||||
w2 := *w
|
||||
w2.typeRef = tr
|
||||
w2.errorFormatter.descend(pe)
|
||||
w2.lhs = nil
|
||||
w2.rhs = nil
|
||||
w2.out = nil
|
||||
return &w2
|
||||
}
|
||||
|
||||
func (w *mergingWalker) visitStructFields(t schema.Struct, lhs, rhs *value.Map) (errs ValidationErrors) {
|
||||
out := &value.Map{}
|
||||
|
||||
valOrNil := func(m *value.Map, name string) *value.Value {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
val, ok := m.Get(name)
|
||||
if ok {
|
||||
return &val.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
allowedNames := map[string]struct{}{}
|
||||
for i := range t.Fields {
|
||||
// I don't want to use the loop variable since a reference
|
||||
// might outlive the loop iteration (in an error message).
|
||||
f := t.Fields[i]
|
||||
allowedNames[f.Name] = struct{}{}
|
||||
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &f.Name}, f.Type)
|
||||
w2.lhs = valOrNil(lhs, f.Name)
|
||||
w2.rhs = valOrNil(rhs, f.Name)
|
||||
if w2.lhs == nil && w2.rhs == nil {
|
||||
// All fields are optional
|
||||
continue
|
||||
}
|
||||
if newErrs := w2.merge(); len(newErrs) > 0 {
|
||||
errs = append(errs, newErrs...)
|
||||
} else if w2.out != nil {
|
||||
out.Set(f.Name, *w2.out)
|
||||
}
|
||||
}
|
||||
|
||||
// All fields may be optional, but unknown fields are not allowed.
|
||||
errs = append(errs, w.rejectExtraStructFields(lhs, allowedNames, "lhs: ")...)
|
||||
errs = append(errs, w.rejectExtraStructFields(rhs, allowedNames, "rhs: ")...)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
if len(out.Items) > 0 {
|
||||
w.out = &value.Value{MapValue: out}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *mergingWalker) derefMapOrStruct(prefix, typeName string, v *value.Value, dest **value.Map) (errs ValidationErrors) {
|
||||
// taking dest as input so that it can be called as a one-liner with
|
||||
// append.
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
m, err := mapOrStructValue(*v, typeName)
|
||||
if err != nil {
|
||||
return w.prefixError(prefix, err)
|
||||
}
|
||||
*dest = m
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *mergingWalker) doStruct(t schema.Struct) (errs ValidationErrors) {
|
||||
var lhs, rhs *value.Map
|
||||
errs = append(errs, w.derefMapOrStruct("lhs: ", "struct", w.lhs, &lhs)...)
|
||||
errs = append(errs, w.derefMapOrStruct("rhs: ", "struct", w.rhs, &rhs)...)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
// If both lhs and rhs are empty/null, treat it as a
|
||||
// leaf: this helps preserve the empty/null
|
||||
// distinction.
|
||||
emptyPromoteToLeaf := (lhs == nil || len(lhs.Items) == 0) &&
|
||||
(rhs == nil || len(rhs.Items) == 0)
|
||||
|
||||
if t.ElementRelationship == schema.Atomic || emptyPromoteToLeaf {
|
||||
w.doLeaf()
|
||||
return nil
|
||||
}
|
||||
|
||||
if lhs == nil && rhs == nil {
|
||||
// nil is a valid map!
|
||||
return nil
|
||||
}
|
||||
|
||||
errs = w.visitStructFields(t, lhs, rhs)
|
||||
|
||||
// TODO: Check unions.
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *mergingWalker) visitListItems(t schema.List, lhs, rhs *value.List) (errs ValidationErrors) {
|
||||
out := &value.List{}
|
||||
|
||||
// TODO: ordering is totally wrong.
|
||||
// TODO: might as well make the map order work the same way.
|
||||
|
||||
// This is a cheap hack to at least make the output order stable.
|
||||
rhsOrder := []fieldpath.PathElement{}
|
||||
|
||||
// First, collect all RHS children.
|
||||
observedRHS := map[string]value.Value{}
|
||||
if rhs != nil {
|
||||
for i, child := range rhs.Items {
|
||||
pe, err := listItemToPathElement(t, i, child)
|
||||
if err != nil {
|
||||
errs = append(errs, w.errorf("rhs: element %v: %v", i, err.Error())...)
|
||||
// If we can't construct the path element, we can't
|
||||
// even report errors deeper in the schema, so bail on
|
||||
// this element.
|
||||
continue
|
||||
}
|
||||
keyStr := pe.String()
|
||||
if _, found := observedRHS[keyStr]; found {
|
||||
errs = append(errs, w.errorf("rhs: duplicate entries for key %v", keyStr)...)
|
||||
}
|
||||
observedRHS[keyStr] = child
|
||||
rhsOrder = append(rhsOrder, pe)
|
||||
}
|
||||
}
|
||||
|
||||
// Then merge with LHS children.
|
||||
observedLHS := map[string]struct{}{}
|
||||
if lhs != nil {
|
||||
for i, child := range lhs.Items {
|
||||
pe, err := listItemToPathElement(t, i, child)
|
||||
if err != nil {
|
||||
errs = append(errs, w.errorf("lhs: element %v: %v", i, err.Error())...)
|
||||
// If we can't construct the path element, we can't
|
||||
// even report errors deeper in the schema, so bail on
|
||||
// this element.
|
||||
continue
|
||||
}
|
||||
keyStr := pe.String()
|
||||
if _, found := observedLHS[keyStr]; found {
|
||||
errs = append(errs, w.errorf("lhs: duplicate entries for key %v", keyStr)...)
|
||||
continue
|
||||
}
|
||||
observedLHS[keyStr] = struct{}{}
|
||||
w2 := w.prepareDescent(pe, t.ElementType)
|
||||
w2.lhs = &child
|
||||
if rchild, ok := observedRHS[keyStr]; ok {
|
||||
w2.rhs = &rchild
|
||||
}
|
||||
if newErrs := w2.merge(); len(newErrs) > 0 {
|
||||
errs = append(errs, newErrs...)
|
||||
} else if w2.out != nil {
|
||||
out.Items = append(out.Items, *w2.out)
|
||||
}
|
||||
// Keep track of children that have been handled
|
||||
delete(observedRHS, keyStr)
|
||||
}
|
||||
}
|
||||
|
||||
for _, rhsToCheck := range rhsOrder {
|
||||
if unmergedChild, ok := observedRHS[rhsToCheck.String()]; ok {
|
||||
w2 := w.prepareDescent(rhsToCheck, t.ElementType)
|
||||
w2.rhs = &unmergedChild
|
||||
if newErrs := w2.merge(); len(newErrs) > 0 {
|
||||
errs = append(errs, newErrs...)
|
||||
} else if w2.out != nil {
|
||||
out.Items = append(out.Items, *w2.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(out.Items) > 0 {
|
||||
w.out = &value.Value{ListValue: out}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *mergingWalker) derefList(prefix string, v *value.Value, dest **value.List) (errs ValidationErrors) {
|
||||
// taking dest as input so that it can be called as a one-liner with
|
||||
// append.
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
l, err := listValue(*v)
|
||||
if err != nil {
|
||||
return w.prefixError(prefix, err)
|
||||
}
|
||||
*dest = l
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *mergingWalker) doList(t schema.List) (errs ValidationErrors) {
|
||||
var lhs, rhs *value.List
|
||||
errs = append(errs, w.derefList("lhs: ", w.lhs, &lhs)...)
|
||||
errs = append(errs, w.derefList("rhs: ", w.rhs, &rhs)...)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
// If both lhs and rhs are empty/null, treat it as a
|
||||
// leaf: this helps preserve the empty/null
|
||||
// distinction.
|
||||
emptyPromoteToLeaf := (lhs == nil || len(lhs.Items) == 0) &&
|
||||
(rhs == nil || len(rhs.Items) == 0)
|
||||
|
||||
if t.ElementRelationship == schema.Atomic || emptyPromoteToLeaf {
|
||||
w.doLeaf()
|
||||
return nil
|
||||
}
|
||||
|
||||
if lhs == nil && rhs == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs = w.visitListItems(t, lhs, rhs)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *mergingWalker) visitMapItems(t schema.Map, lhs, rhs *value.Map) (errs ValidationErrors) {
|
||||
out := &value.Map{}
|
||||
|
||||
if lhs != nil {
|
||||
for _, litem := range lhs.Items {
|
||||
name := litem.Name
|
||||
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &name}, t.ElementType)
|
||||
w2.lhs = &litem.Value
|
||||
if rhs != nil {
|
||||
if ritem, ok := rhs.Get(litem.Name); ok {
|
||||
w2.rhs = &ritem.Value
|
||||
}
|
||||
}
|
||||
if newErrs := w2.merge(); len(newErrs) > 0 {
|
||||
errs = append(errs, newErrs...)
|
||||
} else if w2.out != nil {
|
||||
out.Set(name, *w2.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if rhs != nil {
|
||||
for _, ritem := range rhs.Items {
|
||||
if lhs != nil {
|
||||
if _, ok := lhs.Get(ritem.Name); ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
name := ritem.Name
|
||||
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &name}, t.ElementType)
|
||||
w2.rhs = &ritem.Value
|
||||
if newErrs := w2.merge(); len(newErrs) > 0 {
|
||||
errs = append(errs, newErrs...)
|
||||
} else if w2.out != nil {
|
||||
out.Set(name, *w2.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(out.Items) > 0 {
|
||||
w.out = &value.Value{MapValue: out}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *mergingWalker) doMap(t schema.Map) (errs ValidationErrors) {
|
||||
var lhs, rhs *value.Map
|
||||
errs = append(errs, w.derefMapOrStruct("lhs: ", "map", w.lhs, &lhs)...)
|
||||
errs = append(errs, w.derefMapOrStruct("rhs: ", "map", w.rhs, &rhs)...)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
// If both lhs and rhs are empty/null, treat it as a
|
||||
// leaf: this helps preserve the empty/null
|
||||
// distinction.
|
||||
emptyPromoteToLeaf := (lhs == nil || len(lhs.Items) == 0) &&
|
||||
(rhs == nil || len(rhs.Items) == 0)
|
||||
|
||||
if t.ElementRelationship == schema.Atomic || emptyPromoteToLeaf {
|
||||
w.doLeaf()
|
||||
return nil
|
||||
}
|
||||
|
||||
if lhs == nil && rhs == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs = w.visitMapItems(t, lhs, rhs)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *mergingWalker) doUntyped(t schema.Untyped) (errs ValidationErrors) {
|
||||
if t.ElementRelationship == "" || t.ElementRelationship == schema.Atomic {
|
||||
// Untyped sections allow anything, and are considered leaf
|
||||
// fields.
|
||||
w.doLeaf()
|
||||
}
|
||||
return nil
|
||||
}
|
147
vendor/sigs.k8s.io/structured-merge-diff/typed/parser.go
generated
vendored
Normal file
147
vendor/sigs.k8s.io/structured-merge-diff/typed/parser.go
generated
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
"sigs.k8s.io/structured-merge-diff/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
// YAMLObject is an object encoded in YAML.
|
||||
type YAMLObject string
|
||||
|
||||
// Parser implements YAMLParser and allows introspecting the schema.
|
||||
type Parser struct {
|
||||
Schema schema.Schema
|
||||
}
|
||||
|
||||
// create builds an unvalidated parser.
|
||||
func create(schema YAMLObject) (*Parser, error) {
|
||||
p := Parser{}
|
||||
err := yaml.Unmarshal([]byte(schema), &p.Schema)
|
||||
return &p, err
|
||||
}
|
||||
|
||||
func createOrDie(schema YAMLObject) *Parser {
|
||||
p, err := create(schema)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to create parser: %v", err))
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
var ssParser = createOrDie(YAMLObject(schema.SchemaSchemaYAML))
|
||||
|
||||
// NewParser will build a YAMLParser from a schema. The schema is validated.
|
||||
func NewParser(schema YAMLObject) (*Parser, error) {
|
||||
_, err := ssParser.Type("schema").FromYAML(schema)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to validate schema: %v", err)
|
||||
}
|
||||
return create(schema)
|
||||
}
|
||||
|
||||
// TypeNames returns a list of types this parser understands.
|
||||
func (p *Parser) TypeNames() (names []string) {
|
||||
for _, td := range p.Schema.Types {
|
||||
names = append(names, td.Name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// Type returns a helper which can produce objects of the given type. Any
|
||||
// errors are deferred until a further function is called.
|
||||
func (p *Parser) Type(name string) ParseableType {
|
||||
return &parseableType{
|
||||
parser: p,
|
||||
typename: name,
|
||||
}
|
||||
}
|
||||
|
||||
// ParseableType allows for easy production of typed objects.
|
||||
type ParseableType interface {
|
||||
IsValid() bool
|
||||
FromYAML(YAMLObject) (TypedValue, error)
|
||||
FromUnstructured(interface{}) (TypedValue, error)
|
||||
}
|
||||
|
||||
type parseableType struct {
|
||||
parser *Parser
|
||||
typename string
|
||||
}
|
||||
|
||||
var _ ParseableType = &parseableType{}
|
||||
|
||||
// IsValid return true if p's schema and typename are valid.
|
||||
func (p *parseableType) IsValid() bool {
|
||||
_, ok := p.parser.Schema.Resolve(schema.TypeRef{NamedType: &p.typename})
|
||||
return ok
|
||||
}
|
||||
|
||||
// FromYAML parses a yaml string into an object with the current schema
|
||||
// and the type "typename" or an error if validation fails.
|
||||
func (p *parseableType) FromYAML(object YAMLObject) (TypedValue, error) {
|
||||
v, err := value.FromYAML([]byte(object))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return AsTyped(v, &p.parser.Schema, p.typename)
|
||||
}
|
||||
|
||||
// FromUnstructured converts a go interface to a TypedValue. It will return an
|
||||
// error if the resulting object fails schema validation.
|
||||
func (p *parseableType) FromUnstructured(in interface{}) (TypedValue, error) {
|
||||
v, err := value.FromUnstructured(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return AsTyped(v, &p.parser.Schema, p.typename)
|
||||
}
|
||||
|
||||
// DeducedParseableType is a ParseableType that deduces the type from
|
||||
// the content of the object.
|
||||
type DeducedParseableType struct{}
|
||||
|
||||
var _ ParseableType = DeducedParseableType{}
|
||||
|
||||
// IsValid always returns true for a DeducedParseableType.
|
||||
func (p DeducedParseableType) IsValid() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// FromYAML parses a yaml string into an object and deduces the type for
|
||||
// that object.
|
||||
func (p DeducedParseableType) FromYAML(object YAMLObject) (TypedValue, error) {
|
||||
v, err := value.FromYAML([]byte(object))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return AsTypedDeduced(v), nil
|
||||
}
|
||||
|
||||
// FromUnstructured converts a go interface to a TypedValue. It will return an
|
||||
// error if the input object uses un-handled types.
|
||||
func (p DeducedParseableType) FromUnstructured(in interface{}) (TypedValue, error) {
|
||||
v, err := value.FromUnstructured(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return AsTypedDeduced(v), nil
|
||||
}
|
119
vendor/sigs.k8s.io/structured-merge-diff/typed/remove.go
generated
vendored
Normal file
119
vendor/sigs.k8s.io/structured-merge-diff/typed/remove.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typed
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
type removingWalker struct {
|
||||
value *value.Value
|
||||
schema *schema.Schema
|
||||
toRemove *fieldpath.Set
|
||||
}
|
||||
|
||||
func removeItemsWithSchema(value *value.Value, toRemove *fieldpath.Set, schema *schema.Schema, typeRef schema.TypeRef) {
|
||||
w := &removingWalker{
|
||||
value: value,
|
||||
schema: schema,
|
||||
toRemove: toRemove,
|
||||
}
|
||||
resolveSchema(schema, typeRef, w)
|
||||
}
|
||||
|
||||
// doLeaf should be called on leaves before descending into children, if there
|
||||
// will be a descent. It modifies w.inLeaf.
|
||||
func (w *removingWalker) doLeaf() ValidationErrors { return nil }
|
||||
|
||||
func (w *removingWalker) doScalar(t schema.Scalar) ValidationErrors { return nil }
|
||||
|
||||
func (w *removingWalker) doStruct(t schema.Struct) ValidationErrors {
|
||||
s := w.value.MapValue
|
||||
|
||||
// If struct is null, empty, or atomic just return
|
||||
if s == nil || len(s.Items) == 0 || t.ElementRelationship == schema.Atomic {
|
||||
return nil
|
||||
}
|
||||
|
||||
fieldTypes := map[string]schema.TypeRef{}
|
||||
for _, structField := range t.Fields {
|
||||
fieldTypes[structField.Name] = structField.Type
|
||||
}
|
||||
|
||||
for i, _ := range s.Items {
|
||||
item := s.Items[i]
|
||||
pe := fieldpath.PathElement{FieldName: &item.Name}
|
||||
if subset := w.toRemove.WithPrefix(pe); !subset.Empty() {
|
||||
removeItemsWithSchema(&s.Items[i].Value, subset, w.schema, fieldTypes[item.Name])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *removingWalker) doList(t schema.List) (errs ValidationErrors) {
|
||||
l := w.value.ListValue
|
||||
|
||||
// If list is null, empty, or atomic just return
|
||||
if l == nil || len(l.Items) == 0 || t.ElementRelationship == schema.Atomic {
|
||||
return nil
|
||||
}
|
||||
|
||||
newItems := []value.Value{}
|
||||
for i, _ := range l.Items {
|
||||
item := l.Items[i]
|
||||
// Ignore error because we have already validated this list
|
||||
pe, _ := listItemToPathElement(t, i, item)
|
||||
path, _ := fieldpath.MakePath(pe)
|
||||
if w.toRemove.Has(path) {
|
||||
continue
|
||||
}
|
||||
if subset := w.toRemove.WithPrefix(pe); !subset.Empty() {
|
||||
removeItemsWithSchema(&l.Items[i], subset, w.schema, t.ElementType)
|
||||
}
|
||||
newItems = append(newItems, l.Items[i])
|
||||
}
|
||||
l.Items = newItems
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *removingWalker) doMap(t schema.Map) ValidationErrors {
|
||||
m := w.value.MapValue
|
||||
|
||||
// If map is null, empty, or atomic just return
|
||||
if m == nil || len(m.Items) == 0 || t.ElementRelationship == schema.Atomic {
|
||||
return nil
|
||||
}
|
||||
|
||||
newMap := &value.Map{}
|
||||
for i, _ := range m.Items {
|
||||
item := m.Items[i]
|
||||
pe := fieldpath.PathElement{FieldName: &item.Name}
|
||||
path, _ := fieldpath.MakePath(pe)
|
||||
if w.toRemove.Has(path) {
|
||||
continue
|
||||
}
|
||||
if subset := w.toRemove.WithPrefix(pe); !subset.Empty() {
|
||||
removeItemsWithSchema(&m.Items[i].Value, subset, w.schema, t.ElementType)
|
||||
}
|
||||
newMap.Set(item.Name, m.Items[i].Value)
|
||||
}
|
||||
w.value.MapValue = newMap
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*removingWalker) doUntyped(_ schema.Untyped) ValidationErrors { return nil }
|
||||
|
||||
func (*removingWalker) errorf(_ string, _ ...interface{}) ValidationErrors { return nil }
|
242
vendor/sigs.k8s.io/structured-merge-diff/typed/typed.go
generated
vendored
Normal file
242
vendor/sigs.k8s.io/structured-merge-diff/typed/typed.go
generated
vendored
Normal file
@ -0,0 +1,242 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
// TypedValue is a value with an associated type.
|
||||
type TypedValue interface {
|
||||
// AsValue removes the type from the TypedValue and only keeps the value.
|
||||
AsValue() *value.Value
|
||||
// Validate returns an error with a list of every spec violation.
|
||||
Validate() error
|
||||
// ToFieldSet creates a set containing every leaf field mentioned, or
|
||||
// validation errors, if any were encountered.
|
||||
ToFieldSet() (*fieldpath.Set, error)
|
||||
// Merge returns the result of merging tv and pso ("partially specified
|
||||
// object") together. Of note:
|
||||
// * No fields can be removed by this operation.
|
||||
// * If both tv and pso specify a given leaf field, the result will keep pso's
|
||||
// value.
|
||||
// * Container typed elements will have their items ordered:
|
||||
// * like tv, if pso doesn't change anything in the container
|
||||
// * like pso, if pso does change something in the container.
|
||||
// tv and pso must both be of the same type (their Schema and TypeRef must
|
||||
// match), or an error will be returned. Validation errors will be returned if
|
||||
// the objects don't conform to the schema.
|
||||
Merge(pso TypedValue) (TypedValue, error)
|
||||
// Compare compares the two objects. See the comments on the `Comparison`
|
||||
// struct for details on the return value.
|
||||
//
|
||||
// tv and rhs must both be of the same type (their Schema and TypeRef must
|
||||
// match), or an error will be returned. Validation errors will be returned if
|
||||
// the objects don't conform to the schema.
|
||||
Compare(rhs TypedValue) (c *Comparison, err error)
|
||||
// RemoveItems removes each provided list or map item from the value.
|
||||
RemoveItems(items *fieldpath.Set) TypedValue
|
||||
}
|
||||
|
||||
// AsTyped accepts a value and a type and returns a TypedValue. 'v' must have
|
||||
// type 'typeName' in the schema. An error is returned if the v doesn't conform
|
||||
// to the schema.
|
||||
func AsTyped(v value.Value, s *schema.Schema, typeName string) (TypedValue, error) {
|
||||
tv := typedValue{
|
||||
value: v,
|
||||
typeRef: schema.TypeRef{NamedType: &typeName},
|
||||
schema: s,
|
||||
}
|
||||
if err := tv.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tv, nil
|
||||
}
|
||||
|
||||
// AsTypeUnvalidated is just like AsTyped, but doesn't validate that the type
|
||||
// conforms to the schema, for cases where that has already been checked or
|
||||
// where you're going to call a method that validates as a side-effect (like
|
||||
// ToFieldSet).
|
||||
func AsTypedUnvalidated(v value.Value, s *schema.Schema, typeName string) TypedValue {
|
||||
tv := typedValue{
|
||||
value: v,
|
||||
typeRef: schema.TypeRef{NamedType: &typeName},
|
||||
schema: s,
|
||||
}
|
||||
return tv
|
||||
}
|
||||
|
||||
// typedValue is a value of some specific type.
|
||||
type typedValue struct {
|
||||
value value.Value
|
||||
typeRef schema.TypeRef
|
||||
schema *schema.Schema
|
||||
}
|
||||
|
||||
var _ TypedValue = typedValue{}
|
||||
|
||||
func (tv typedValue) AsValue() *value.Value {
|
||||
return &tv.value
|
||||
}
|
||||
|
||||
func (tv typedValue) Validate() error {
|
||||
if errs := tv.walker().validate(); len(errs) != 0 {
|
||||
return errs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tv typedValue) ToFieldSet() (*fieldpath.Set, error) {
|
||||
s := fieldpath.NewSet()
|
||||
w := tv.walker()
|
||||
w.leafFieldCallback = func(p fieldpath.Path) { s.Insert(p) }
|
||||
if errs := w.validate(); len(errs) != 0 {
|
||||
return nil, errs
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (tv typedValue) Merge(pso TypedValue) (TypedValue, error) {
|
||||
tpso, ok := pso.(typedValue)
|
||||
if !ok {
|
||||
return nil, errorFormatter{}.
|
||||
errorf("can't merge typedValue with %T", pso)
|
||||
}
|
||||
return merge(tv, tpso, ruleKeepRHS, nil)
|
||||
}
|
||||
|
||||
func (tv typedValue) Compare(rhs TypedValue) (c *Comparison, err error) {
|
||||
trhs, ok := rhs.(typedValue)
|
||||
if !ok {
|
||||
return nil, errorFormatter{}.
|
||||
errorf("can't compare typedValue with %T", rhs)
|
||||
}
|
||||
c = &Comparison{
|
||||
Removed: fieldpath.NewSet(),
|
||||
Modified: fieldpath.NewSet(),
|
||||
Added: fieldpath.NewSet(),
|
||||
}
|
||||
c.Merged, err = merge(tv, trhs, func(w *mergingWalker) {
|
||||
if w.lhs == nil {
|
||||
c.Added.Insert(w.path)
|
||||
} else if w.rhs == nil {
|
||||
c.Removed.Insert(w.path)
|
||||
} else if !reflect.DeepEqual(w.rhs, w.lhs) {
|
||||
// TODO: reflect.DeepEqual is not sufficient for this.
|
||||
// Need to implement equality check on the value type.
|
||||
c.Modified.Insert(w.path)
|
||||
}
|
||||
|
||||
ruleKeepRHS(w)
|
||||
}, func(w *mergingWalker) {
|
||||
if w.lhs == nil {
|
||||
c.Added.Insert(w.path)
|
||||
} else if w.rhs == nil {
|
||||
c.Removed.Insert(w.path)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// RemoveItems removes each provided list or map item from the value.
|
||||
func (tv typedValue) RemoveItems(items *fieldpath.Set) TypedValue {
|
||||
removeItemsWithSchema(&tv.value, items, tv.schema, tv.typeRef)
|
||||
return tv
|
||||
}
|
||||
|
||||
func merge(lhs, rhs typedValue, rule, postRule mergeRule) (TypedValue, error) {
|
||||
if lhs.schema != rhs.schema {
|
||||
return nil, errorFormatter{}.
|
||||
errorf("expected objects with types from the same schema")
|
||||
}
|
||||
if !reflect.DeepEqual(lhs.typeRef, rhs.typeRef) {
|
||||
return nil, errorFormatter{}.
|
||||
errorf("expected objects of the same type, but got %v and %v", lhs.typeRef, rhs.typeRef)
|
||||
}
|
||||
|
||||
mw := mergingWalker{
|
||||
lhs: &lhs.value,
|
||||
rhs: &rhs.value,
|
||||
schema: lhs.schema,
|
||||
typeRef: lhs.typeRef,
|
||||
rule: rule,
|
||||
postItemHook: postRule,
|
||||
}
|
||||
errs := mw.merge()
|
||||
if len(errs) > 0 {
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
out := typedValue{
|
||||
schema: lhs.schema,
|
||||
typeRef: lhs.typeRef,
|
||||
}
|
||||
if mw.out == nil {
|
||||
out.value = value.Value{Null: true}
|
||||
} else {
|
||||
out.value = *mw.out
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Comparison is the return value of a TypedValue.Compare() operation.
|
||||
//
|
||||
// No field will appear in more than one of the three fieldsets. If all of the
|
||||
// fieldsets are empty, then the objects must have been equal.
|
||||
type Comparison struct {
|
||||
// Merged is the result of merging the two objects, as explained in the
|
||||
// comments on TypedValue.Merge().
|
||||
Merged TypedValue
|
||||
|
||||
// Removed contains any fields removed by rhs (the right-hand-side
|
||||
// object in the comparison).
|
||||
Removed *fieldpath.Set
|
||||
// Modified contains fields present in both objects but different.
|
||||
Modified *fieldpath.Set
|
||||
// Added contains any fields added by rhs.
|
||||
Added *fieldpath.Set
|
||||
}
|
||||
|
||||
// IsSame returns true if the comparison returned no changes (the two
|
||||
// compared objects are similar).
|
||||
func (c *Comparison) IsSame() bool {
|
||||
return c.Removed.Empty() && c.Modified.Empty() && c.Added.Empty()
|
||||
}
|
||||
|
||||
// String returns a human readable version of the comparison.
|
||||
func (c *Comparison) String() string {
|
||||
str := fmt.Sprintf("- Merged Object:\n%v\n", c.Merged.AsValue())
|
||||
if !c.Modified.Empty() {
|
||||
str += fmt.Sprintf("- Modified Fields:\n%v\n", c.Modified)
|
||||
}
|
||||
if !c.Added.Empty() {
|
||||
str += fmt.Sprintf("- Added Fields:\n%v\n", c.Added)
|
||||
}
|
||||
if !c.Removed.Empty() {
|
||||
str += fmt.Sprintf("- Removed Fields:\n%v\n", c.Removed)
|
||||
}
|
||||
return str
|
||||
}
|
208
vendor/sigs.k8s.io/structured-merge-diff/typed/validate.go
generated
vendored
Normal file
208
vendor/sigs.k8s.io/structured-merge-diff/typed/validate.go
generated
vendored
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typed
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
func (tv typedValue) walker() *validatingObjectWalker {
|
||||
return &validatingObjectWalker{
|
||||
value: tv.value,
|
||||
schema: tv.schema,
|
||||
typeRef: tv.typeRef,
|
||||
}
|
||||
}
|
||||
|
||||
type validatingObjectWalker struct {
|
||||
errorFormatter
|
||||
value value.Value
|
||||
schema *schema.Schema
|
||||
typeRef schema.TypeRef
|
||||
|
||||
// If set, this is called on "leaf fields":
|
||||
// * scalars: int/string/float/bool
|
||||
// * atomic maps and lists
|
||||
// * untyped fields
|
||||
leafFieldCallback func(fieldpath.Path)
|
||||
|
||||
// internal housekeeping--don't set when constructing.
|
||||
inLeaf bool // Set to true if we're in a "big leaf"--atomic map/list
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) validate() ValidationErrors {
|
||||
return resolveSchema(v.schema, v.typeRef, v)
|
||||
}
|
||||
|
||||
// doLeaf should be called on leaves before descending into children, if there
|
||||
// will be a descent. It modifies v.inLeaf.
|
||||
func (v *validatingObjectWalker) doLeaf() {
|
||||
if v.inLeaf {
|
||||
// We're in a "big leaf", an atomic map or list. Ignore
|
||||
// subsequent leaves.
|
||||
return
|
||||
}
|
||||
v.inLeaf = true
|
||||
|
||||
if v.leafFieldCallback != nil {
|
||||
// At the moment, this is only used to build fieldsets; we can
|
||||
// add more than the path in here if needed.
|
||||
v.leafFieldCallback(v.path)
|
||||
}
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) doScalar(t schema.Scalar) ValidationErrors {
|
||||
if errs := v.validateScalar(t, &v.value, ""); len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
// All scalars are leaf fields.
|
||||
v.doLeaf()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) visitStructFields(t schema.Struct, m *value.Map) (errs ValidationErrors) {
|
||||
allowedNames := map[string]struct{}{}
|
||||
for i := range t.Fields {
|
||||
// I don't want to use the loop variable since a reference
|
||||
// might outlive the loop iteration (in an error message).
|
||||
f := t.Fields[i]
|
||||
allowedNames[f.Name] = struct{}{}
|
||||
child, ok := m.Get(f.Name)
|
||||
if !ok {
|
||||
// All fields are optional
|
||||
continue
|
||||
}
|
||||
v2 := v
|
||||
v2.errorFormatter.descend(fieldpath.PathElement{FieldName: &f.Name})
|
||||
v2.value = child.Value
|
||||
v2.typeRef = f.Type
|
||||
errs = append(errs, v2.validate()...)
|
||||
}
|
||||
|
||||
// All fields may be optional, but unknown fields are not allowed.
|
||||
return append(errs, v.rejectExtraStructFields(m, allowedNames, "")...)
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) doStruct(t schema.Struct) (errs ValidationErrors) {
|
||||
m, err := mapOrStructValue(v.value, "struct")
|
||||
if err != nil {
|
||||
return v.error(err)
|
||||
}
|
||||
|
||||
if t.ElementRelationship == schema.Atomic {
|
||||
v.doLeaf()
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
// nil is a valid map!
|
||||
return nil
|
||||
}
|
||||
|
||||
errs = v.visitStructFields(t, m)
|
||||
|
||||
// TODO: Check unions.
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) visitListItems(t schema.List, list *value.List) (errs ValidationErrors) {
|
||||
observedKeys := map[string]struct{}{}
|
||||
for i, child := range list.Items {
|
||||
pe, err := listItemToPathElement(t, i, child)
|
||||
if err != nil {
|
||||
errs = append(errs, v.errorf("element %v: %v", i, err.Error())...)
|
||||
// If we can't construct the path element, we can't
|
||||
// even report errors deeper in the schema, so bail on
|
||||
// this element.
|
||||
continue
|
||||
}
|
||||
keyStr := pe.String()
|
||||
if _, found := observedKeys[keyStr]; found {
|
||||
errs = append(errs, v.errorf("duplicate entries for key %v", keyStr)...)
|
||||
}
|
||||
observedKeys[keyStr] = struct{}{}
|
||||
v2 := v
|
||||
v2.errorFormatter.descend(pe)
|
||||
v2.value = child
|
||||
v2.typeRef = t.ElementType
|
||||
errs = append(errs, v2.validate()...)
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) doList(t schema.List) (errs ValidationErrors) {
|
||||
list, err := listValue(v.value)
|
||||
if err != nil {
|
||||
return v.error(err)
|
||||
}
|
||||
|
||||
if t.ElementRelationship == schema.Atomic {
|
||||
v.doLeaf()
|
||||
}
|
||||
|
||||
if list == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs = v.visitListItems(t, list)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) visitMapItems(t schema.Map, m *value.Map) (errs ValidationErrors) {
|
||||
for _, item := range m.Items {
|
||||
v2 := v
|
||||
name := item.Name
|
||||
v2.errorFormatter.descend(fieldpath.PathElement{FieldName: &name})
|
||||
v2.value = item.Value
|
||||
v2.typeRef = t.ElementType
|
||||
errs = append(errs, v2.validate()...)
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) doMap(t schema.Map) (errs ValidationErrors) {
|
||||
m, err := mapOrStructValue(v.value, "map")
|
||||
if err != nil {
|
||||
return v.error(err)
|
||||
}
|
||||
|
||||
if t.ElementRelationship == schema.Atomic {
|
||||
v.doLeaf()
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs = v.visitMapItems(t, m)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) doUntyped(t schema.Untyped) (errs ValidationErrors) {
|
||||
if t.ElementRelationship == "" || t.ElementRelationship == schema.Atomic {
|
||||
// Untyped sections allow anything, and are considered leaf
|
||||
// fields.
|
||||
v.doLeaf()
|
||||
}
|
||||
return nil
|
||||
}
|
21
vendor/sigs.k8s.io/structured-merge-diff/value/doc.go
generated
vendored
Normal file
21
vendor/sigs.k8s.io/structured-merge-diff/value/doc.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package value defines types for an in-memory representation of yaml or json
|
||||
// objects, organized for convenient comparison with a schema (as defined by
|
||||
// the sibling schema package). Functions for reading and writing the objects
|
||||
// are also provided.
|
||||
package value
|
234
vendor/sigs.k8s.io/structured-merge-diff/value/unstructured.go
generated
vendored
Normal file
234
vendor/sigs.k8s.io/structured-merge-diff/value/unstructured.go
generated
vendored
Normal file
@ -0,0 +1,234 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// FromYAML is a helper function for reading a YAML document; it attempts to
|
||||
// preserve order of keys within maps/structs. This is as a convenience to
|
||||
// humans keeping YAML documents, not because there is a behavior difference.
|
||||
//
|
||||
// Known bug: objects with top-level arrays don't parse correctly.
|
||||
func FromYAML(input []byte) (Value, error) {
|
||||
var decoded interface{}
|
||||
|
||||
if len(input) == 4 && string(input) == "null" {
|
||||
// Special case since the yaml package doesn't accurately
|
||||
// preserve this.
|
||||
return Value{Null: true}, nil
|
||||
}
|
||||
|
||||
// This attempts to enable order sensitivity; note the yaml package is
|
||||
// broken for documents that have root-level arrays, hence the two-step
|
||||
// approach. TODO: This is a horrific hack. Is it worth it?
|
||||
var ms yaml.MapSlice
|
||||
if err := yaml.Unmarshal(input, &ms); err == nil {
|
||||
decoded = ms
|
||||
} else if err := yaml.Unmarshal(input, &decoded); err != nil {
|
||||
return Value{}, err
|
||||
}
|
||||
|
||||
v, err := FromUnstructured(decoded)
|
||||
if err != nil {
|
||||
return Value{}, fmt.Errorf("failed to interpret (%v):\n%s", err, input)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// FromJSON is a helper function for reading a JSON document
|
||||
func FromJSON(input []byte) (Value, error) {
|
||||
var decoded interface{}
|
||||
|
||||
if err := json.Unmarshal(input, &decoded); err != nil {
|
||||
return Value{}, err
|
||||
}
|
||||
|
||||
v, err := FromUnstructured(decoded)
|
||||
if err != nil {
|
||||
return Value{}, fmt.Errorf("failed to interpret (%v):\n%s", err, input)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// FromUnstructured will convert a go interface to a Value.
|
||||
// It's most commonly expected to be used with map[string]interface{} as the
|
||||
// input. `in` must not have any structures with cycles in them.
|
||||
// yaml.MapSlice may be used for order-preservation.
|
||||
func FromUnstructured(in interface{}) (Value, error) {
|
||||
if in == nil {
|
||||
return Value{Null: true}, nil
|
||||
}
|
||||
switch t := in.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
m := Map{}
|
||||
for rawKey, rawVal := range t {
|
||||
k, ok := rawKey.(string)
|
||||
if !ok {
|
||||
return Value{}, fmt.Errorf("key %#v: not a string", k)
|
||||
}
|
||||
v, err := FromUnstructured(rawVal)
|
||||
if err != nil {
|
||||
return Value{}, fmt.Errorf("key %v: %v", k, err)
|
||||
}
|
||||
m.Set(k, v)
|
||||
}
|
||||
return Value{MapValue: &m}, nil
|
||||
case map[string]interface{}:
|
||||
m := Map{}
|
||||
for k, rawVal := range t {
|
||||
v, err := FromUnstructured(rawVal)
|
||||
if err != nil {
|
||||
return Value{}, fmt.Errorf("key %v: %v", k, err)
|
||||
}
|
||||
m.Set(k, v)
|
||||
}
|
||||
return Value{MapValue: &m}, nil
|
||||
case yaml.MapSlice:
|
||||
m := Map{}
|
||||
for _, item := range t {
|
||||
k, ok := item.Key.(string)
|
||||
if !ok {
|
||||
return Value{}, fmt.Errorf("key %#v is not a string", item.Key)
|
||||
}
|
||||
v, err := FromUnstructured(item.Value)
|
||||
if err != nil {
|
||||
return Value{}, fmt.Errorf("key %v: %v", k, err)
|
||||
}
|
||||
m.Set(k, v)
|
||||
}
|
||||
return Value{MapValue: &m}, nil
|
||||
case []interface{}:
|
||||
l := List{}
|
||||
for i, rawVal := range t {
|
||||
v, err := FromUnstructured(rawVal)
|
||||
if err != nil {
|
||||
return Value{}, fmt.Errorf("index %v: %v", i, err)
|
||||
}
|
||||
l.Items = append(l.Items, v)
|
||||
}
|
||||
return Value{ListValue: &l}, nil
|
||||
case int:
|
||||
n := Int(t)
|
||||
return Value{IntValue: &n}, nil
|
||||
case int8:
|
||||
n := Int(t)
|
||||
return Value{IntValue: &n}, nil
|
||||
case int16:
|
||||
n := Int(t)
|
||||
return Value{IntValue: &n}, nil
|
||||
case int32:
|
||||
n := Int(t)
|
||||
return Value{IntValue: &n}, nil
|
||||
case int64:
|
||||
n := Int(t)
|
||||
return Value{IntValue: &n}, nil
|
||||
case uint:
|
||||
n := Int(t)
|
||||
return Value{IntValue: &n}, nil
|
||||
case uint8:
|
||||
n := Int(t)
|
||||
return Value{IntValue: &n}, nil
|
||||
case uint16:
|
||||
n := Int(t)
|
||||
return Value{IntValue: &n}, nil
|
||||
case uint32:
|
||||
n := Int(t)
|
||||
return Value{IntValue: &n}, nil
|
||||
case float32:
|
||||
f := Float(t)
|
||||
return Value{FloatValue: &f}, nil
|
||||
case float64:
|
||||
f := Float(t)
|
||||
return Value{FloatValue: &f}, nil
|
||||
case string:
|
||||
return StringValue(t), nil
|
||||
case bool:
|
||||
return BooleanValue(t), nil
|
||||
default:
|
||||
return Value{}, fmt.Errorf("type unimplemented: %t", in)
|
||||
}
|
||||
}
|
||||
|
||||
// ToYAML is a helper function for producing a YAML document; it attempts to
|
||||
// preserve order of keys within maps/structs. This is as a convenience to
|
||||
// humans keeping YAML documents, not because there is a behavior difference.
|
||||
func (v *Value) ToYAML() ([]byte, error) {
|
||||
return yaml.Marshal(v.ToUnstructured(true))
|
||||
}
|
||||
|
||||
// ToJSON is a helper function for producing a JSon document.
|
||||
func (v *Value) ToJSON() ([]byte, error) {
|
||||
return json.Marshal(v.ToUnstructured(false))
|
||||
}
|
||||
|
||||
// ToUnstructured will convert the Value into a go-typed object.
|
||||
// If preserveOrder is true, then maps will be converted to the yaml.MapSlice
|
||||
// type. Otherwise, map[string]interface{} must be used-- this destroys
|
||||
// ordering information and is not recommended if the result of this will be
|
||||
// serialized. Other types:
|
||||
// * list -> []interface{}
|
||||
// * others -> corresponding go type, wrapped in an interface{}
|
||||
//
|
||||
// Of note, floats and ints will always come out as float64 and int64,
|
||||
// respectively.
|
||||
func (v *Value) ToUnstructured(preserveOrder bool) interface{} {
|
||||
switch {
|
||||
case v.FloatValue != nil:
|
||||
f := float64(*v.FloatValue)
|
||||
return f
|
||||
case v.IntValue != nil:
|
||||
i := int64(*v.IntValue)
|
||||
return i
|
||||
case v.StringValue != nil:
|
||||
return string(*v.StringValue)
|
||||
case v.BooleanValue != nil:
|
||||
return bool(*v.BooleanValue)
|
||||
case v.ListValue != nil:
|
||||
out := []interface{}{}
|
||||
for _, item := range v.ListValue.Items {
|
||||
out = append(out, item.ToUnstructured(preserveOrder))
|
||||
}
|
||||
return out
|
||||
case v.MapValue != nil:
|
||||
m := v.MapValue
|
||||
if preserveOrder {
|
||||
ms := make(yaml.MapSlice, len(m.Items))
|
||||
for i := range m.Items {
|
||||
ms[i] = yaml.MapItem{
|
||||
Key: m.Items[i].Name,
|
||||
Value: m.Items[i].Value.ToUnstructured(preserveOrder),
|
||||
}
|
||||
}
|
||||
return ms
|
||||
}
|
||||
// This case is unavoidably lossy.
|
||||
out := map[string]interface{}{}
|
||||
for i := range m.Items {
|
||||
out[m.Items[i].Name] = m.Items[i].Value.ToUnstructured(preserveOrder)
|
||||
}
|
||||
return out
|
||||
default:
|
||||
fallthrough
|
||||
case v.Null == true:
|
||||
return nil
|
||||
}
|
||||
}
|
139
vendor/sigs.k8s.io/structured-merge-diff/value/value.go
generated
vendored
Normal file
139
vendor/sigs.k8s.io/structured-merge-diff/value/value.go
generated
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A Value is an object; it corresponds to an 'atom' in the schema.
|
||||
type Value struct {
|
||||
// Exactly one of the below must be set.
|
||||
FloatValue *Float
|
||||
IntValue *Int
|
||||
StringValue *String
|
||||
BooleanValue *Boolean
|
||||
ListValue *List
|
||||
MapValue *Map
|
||||
Null bool // represents an explicit `"foo" = null`
|
||||
}
|
||||
|
||||
type Int int64
|
||||
type Float float64
|
||||
type String string
|
||||
type Boolean bool
|
||||
|
||||
// Field is an individual key-value pair.
|
||||
type Field struct {
|
||||
Name string
|
||||
Value Value
|
||||
}
|
||||
|
||||
// List is a list of items.
|
||||
type List struct {
|
||||
Items []Value
|
||||
}
|
||||
|
||||
// Map is a map of key-value pairs. It represents both structs and maps. We use
|
||||
// a list and a go-language map to preserve order.
|
||||
//
|
||||
// Set and Get helpers are provided.
|
||||
type Map struct {
|
||||
Items []Field
|
||||
|
||||
// may be nil; lazily constructed.
|
||||
// TODO: Direct modifications to Items above will cause serious problems.
|
||||
index map[string]*Field
|
||||
}
|
||||
|
||||
// Get returns the (Field, true) or (nil, false) if it is not present
|
||||
func (m *Map) Get(key string) (*Field, bool) {
|
||||
if m.index == nil {
|
||||
m.index = map[string]*Field{}
|
||||
for i := range m.Items {
|
||||
f := &m.Items[i]
|
||||
m.index[f.Name] = f
|
||||
}
|
||||
}
|
||||
f, ok := m.index[key]
|
||||
return f, ok
|
||||
}
|
||||
|
||||
// Set inserts or updates the given item.
|
||||
func (m *Map) Set(key string, value Value) {
|
||||
if f, ok := m.Get(key); ok {
|
||||
f.Value = value
|
||||
return
|
||||
}
|
||||
m.Items = append(m.Items, Field{Name: key, Value: value})
|
||||
m.index = nil // Since the append might have reallocated
|
||||
}
|
||||
|
||||
// StringValue returns s as a scalar string Value.
|
||||
func StringValue(s string) Value {
|
||||
s2 := String(s)
|
||||
return Value{StringValue: &s2}
|
||||
}
|
||||
|
||||
// IntValue returns i as a scalar numeric (integer) Value.
|
||||
func IntValue(i int) Value {
|
||||
i2 := Int(i)
|
||||
return Value{IntValue: &i2}
|
||||
}
|
||||
|
||||
// FloatValue returns f as a scalar numeric (float) Value.
|
||||
func FloatValue(f float64) Value {
|
||||
f2 := Float(f)
|
||||
return Value{FloatValue: &f2}
|
||||
}
|
||||
|
||||
// BooleanValue returns b as a scalar boolean Value.
|
||||
func BooleanValue(b bool) Value {
|
||||
b2 := Boolean(b)
|
||||
return Value{BooleanValue: &b2}
|
||||
}
|
||||
|
||||
// String returns a human-readable representation of the value.
|
||||
func (v Value) String() string {
|
||||
switch {
|
||||
case v.FloatValue != nil:
|
||||
return fmt.Sprintf("%v", *v.FloatValue)
|
||||
case v.IntValue != nil:
|
||||
return fmt.Sprintf("%v", *v.IntValue)
|
||||
case v.StringValue != nil:
|
||||
return fmt.Sprintf("%q", *v.StringValue)
|
||||
case v.BooleanValue != nil:
|
||||
return fmt.Sprintf("%v", *v.BooleanValue)
|
||||
case v.ListValue != nil:
|
||||
strs := []string{}
|
||||
for _, item := range v.ListValue.Items {
|
||||
strs = append(strs, item.String())
|
||||
}
|
||||
return "[" + strings.Join(strs, ",") + "]"
|
||||
case v.MapValue != nil:
|
||||
strs := []string{}
|
||||
for _, i := range v.MapValue.Items {
|
||||
strs = append(strs, fmt.Sprintf("%v=%v", i.Name, i.Value))
|
||||
}
|
||||
return "{" + strings.Join(strs, ";") + "}"
|
||||
default:
|
||||
fallthrough
|
||||
case v.Null == true:
|
||||
return "null"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user