Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
5869ce6d
Commit
5869ce6d
authored
Jan 16, 2019
by
Antoine Pelisse
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add structured-merge-diff vendor
This is where most of the logic actually lives.
parent
732cb100
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
2734 additions
and
0 deletions
+2734
-0
LICENSE
vendor/sigs.k8s.io/structured-merge-diff/LICENSE
+0
-0
BUILD
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/BUILD
+31
-0
doc.go
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/doc.go
+21
-0
element.go
...or/sigs.k8s.io/structured-merge-diff/fieldpath/element.go
+184
-0
fromvalue.go
.../sigs.k8s.io/structured-merge-diff/fieldpath/fromvalue.go
+123
-0
managers.go
...r/sigs.k8s.io/structured-merge-diff/fieldpath/managers.go
+73
-0
path.go
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/path.go
+75
-0
set.go
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/set.go
+305
-0
BUILD
vendor/sigs.k8s.io/structured-merge-diff/merge/BUILD
+30
-0
conflict.go
vendor/sigs.k8s.io/structured-merge-diff/merge/conflict.go
+91
-0
update.go
vendor/sigs.k8s.io/structured-merge-diff/merge/update.go
+142
-0
BUILD
vendor/sigs.k8s.io/structured-merge-diff/schema/BUILD
+27
-0
doc.go
vendor/sigs.k8s.io/structured-merge-diff/schema/doc.go
+28
-0
elements.go
vendor/sigs.k8s.io/structured-merge-diff/schema/elements.go
+219
-0
schemaschema.go
.../sigs.k8s.io/structured-merge-diff/schema/schemaschema.go
+128
-0
BUILD
vendor/sigs.k8s.io/structured-merge-diff/typed/BUILD
+36
-0
doc.go
vendor/sigs.k8s.io/structured-merge-diff/typed/doc.go
+18
-0
helpers.go
vendor/sigs.k8s.io/structured-merge-diff/typed/helpers.go
+246
-0
merge.go
vendor/sigs.k8s.io/structured-merge-diff/typed/merge.go
+0
-0
parser.go
vendor/sigs.k8s.io/structured-merge-diff/typed/parser.go
+114
-0
typed.go
vendor/sigs.k8s.io/structured-merge-diff/typed/typed.go
+213
-0
validate.go
vendor/sigs.k8s.io/structured-merge-diff/typed/validate.go
+208
-0
BUILD
vendor/sigs.k8s.io/structured-merge-diff/value/BUILD
+28
-0
doc.go
vendor/sigs.k8s.io/structured-merge-diff/value/doc.go
+21
-0
unstructured.go
...r/sigs.k8s.io/structured-merge-diff/value/unstructured.go
+234
-0
value.go
vendor/sigs.k8s.io/structured-merge-diff/value/value.go
+139
-0
No files found.
vendor/sigs.k8s.io/structured-merge-diff/LICENSE
0 → 100644
View file @
5869ce6d
This diff is collapsed.
Click to expand it.
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/BUILD
0 → 100644
View file @
5869ce6d
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"element.go",
"fromvalue.go",
"managers.go",
"path.go",
"set.go",
],
importmap = "k8s.io/kubernetes/vendor/sigs.k8s.io/structured-merge-diff/fieldpath",
importpath = "sigs.k8s.io/structured-merge-diff/fieldpath",
visibility = ["//visibility:public"],
deps = ["//vendor/sigs.k8s.io/structured-merge-diff/value:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/doc.go
0 → 100644
View file @
5869ce6d
/*
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
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/element.go
0 → 100644
View file @
5869ce6d
/*
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
)
}
}
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/fromvalue.go
0 → 100644
View file @
5869ce6d
/*
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
}
}
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/managers.go
0 → 100644
View file @
5869ce6d
/*
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
}
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/path.go
0 → 100644
View file @
5869ce6d
/*
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
,
""
)
}
// 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
}
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/set.go
0 → 100644
View file @
5869ce6d
/*
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
)
}
// 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
)
}
}
vendor/sigs.k8s.io/structured-merge-diff/merge/BUILD
0 → 100644
View file @
5869ce6d
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"conflict.go",
"update.go",
],
importmap = "k8s.io/kubernetes/vendor/sigs.k8s.io/structured-merge-diff/merge",
importpath = "sigs.k8s.io/structured-merge-diff/merge",
visibility = ["//visibility:public"],
deps = [
"//vendor/sigs.k8s.io/structured-merge-diff/fieldpath:go_default_library",
"//vendor/sigs.k8s.io/structured-merge-diff/typed:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
vendor/sigs.k8s.io/structured-merge-diff/merge/conflict.go
0 → 100644
View file @
5869ce6d
/*
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
}
vendor/sigs.k8s.io/structured-merge-diff/merge/update.go
0 → 100644
View file @
5869ce6d
/*
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
)
}
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
)
}
if
_
,
ok
:=
managers
[
manager
];
!
ok
{
managers
[
manager
]
=
&
fieldpath
.
VersionedSet
{
Set
:
fieldpath
.
NewSet
(),
}
}
managers
[
manager
]
.
Set
=
managers
[
manager
]
.
Set
.
Union
(
compare
.
Modified
)
.
Union
(
compare
.
Added
)
.
Difference
(
compare
.
Removed
)
managers
[
manager
]
.
APIVersion
=
version
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
typed
.
TypedValue
{},
fieldpath
.
ManagedFields
{},
fmt
.
Errorf
(
"failed to merge config: %v"
,
err
)
}
managers
,
err
=
s
.
update
(
liveObject
,
newObject
,
version
,
managers
,
manager
,
force
)
if
err
!=
nil
{
return
typed
.
TypedValue
{},
fieldpath
.
ManagedFields
{},
err
}
// TODO: Remove unconflicting removed fields
set
,
err
:=
configObject
.
ToFieldSet
()
if
err
!=
nil
{
return
typed
.
TypedValue
{},
fieldpath
.
ManagedFields
{},
fmt
.
Errorf
(
"failed to get field set: %v"
,
err
)
}
managers
[
manager
]
=
&
fieldpath
.
VersionedSet
{
Set
:
set
,
APIVersion
:
version
,
}
return
newObject
,
managers
,
nil
}
vendor/sigs.k8s.io/structured-merge-diff/schema/BUILD
0 → 100644
View file @
5869ce6d
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"elements.go",
"schemaschema.go",
],
importmap = "k8s.io/kubernetes/vendor/sigs.k8s.io/structured-merge-diff/schema",
importpath = "sigs.k8s.io/structured-merge-diff/schema",
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
vendor/sigs.k8s.io/structured-merge-diff/schema/doc.go
0 → 100644
View file @
5869ce6d
/*
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
vendor/sigs.k8s.io/structured-merge-diff/schema/elements.go
0 → 100644
View file @
5869ce6d
/*
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
}
vendor/sigs.k8s.io/structured-merge-diff/schema/schemaschema.go
0 → 100644
View file @
5869ce6d
/*
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
`
vendor/sigs.k8s.io/structured-merge-diff/typed/BUILD
0 → 100644
View file @
5869ce6d
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"helpers.go",
"merge.go",
"parser.go",
"typed.go",
"validate.go",
],
importmap = "k8s.io/kubernetes/vendor/sigs.k8s.io/structured-merge-diff/typed",
importpath = "sigs.k8s.io/structured-merge-diff/typed",
visibility = ["//visibility:public"],
deps = [
"//vendor/gopkg.in/yaml.v2:go_default_library",
"//vendor/sigs.k8s.io/structured-merge-diff/fieldpath:go_default_library",
"//vendor/sigs.k8s.io/structured-merge-diff/schema:go_default_library",
"//vendor/sigs.k8s.io/structured-merge-diff/value:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
vendor/sigs.k8s.io/structured-merge-diff/typed/doc.go
0 → 100644
View file @
5869ce6d
/*
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
vendor/sigs.k8s.io/structured-merge-diff/typed/helpers.go
0 → 100644
View file @
5869ce6d
/*
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
}
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
}
vendor/sigs.k8s.io/structured-merge-diff/typed/merge.go
0 → 100644
View file @
5869ce6d
This diff is collapsed.
Click to expand it.
vendor/sigs.k8s.io/structured-merge-diff/typed/parser.go
0 → 100644
View file @
5869ce6d
/*
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
struct
{
parser
*
Parser
typename
string
}
// 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
}
// New returns a new empty object with the current schema and the
// type "typename".
func
(
p
*
ParseableType
)
New
()
(
TypedValue
,
error
)
{
return
p
.
FromYAML
(
YAMLObject
(
"{}"
))
}
// 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
TypedValue
{},
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
TypedValue
{},
err
}
return
AsTyped
(
v
,
&
p
.
parser
.
Schema
,
p
.
typename
)
}
vendor/sigs.k8s.io/structured-merge-diff/typed/typed.go
0 → 100644
View file @
5869ce6d
/*
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 of some specific type.
type
TypedValue
struct
{
value
value
.
Value
typeRef
schema
.
TypeRef
schema
*
schema
.
Schema
}
// 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
TypedValue
{},
err
}
return
tv
,
nil
}
// AsValue removes the type from the TypedValue and only keeps the value.
func
(
tv
TypedValue
)
AsValue
()
*
value
.
Value
{
return
&
tv
.
value
}
// Validate returns an error with a list of every spec violation.
func
(
tv
TypedValue
)
Validate
()
error
{
if
errs
:=
tv
.
walker
()
.
validate
();
len
(
errs
)
!=
0
{
return
errs
}
return
nil
}
// ToFieldSet creates a set containing every leaf field mentioned in tv, or
// validation errors, if any were encountered.
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
}
// 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.
func
(
tv
TypedValue
)
Merge
(
pso
TypedValue
)
(
TypedValue
,
error
)
{
return
merge
(
tv
,
pso
,
ruleKeepRHS
,
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
}
// 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.
func
(
tv
TypedValue
)
Compare
(
rhs
TypedValue
)
(
c
*
Comparison
,
err
error
)
{
c
=
&
Comparison
{
Removed
:
fieldpath
.
NewSet
(),
Modified
:
fieldpath
.
NewSet
(),
Added
:
fieldpath
.
NewSet
(),
}
c
.
Merged
,
err
=
merge
(
tv
,
rhs
,
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
}
func
merge
(
lhs
,
rhs
TypedValue
,
rule
,
postRule
mergeRule
)
(
TypedValue
,
error
)
{
if
lhs
.
schema
!=
rhs
.
schema
{
return
TypedValue
{},
errorFormatter
{}
.
errorf
(
"expected objects with types from the same schema"
)
}
if
!
reflect
.
DeepEqual
(
lhs
.
typeRef
,
rhs
.
typeRef
)
{
return
TypedValue
{},
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
TypedValue
{},
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
}
// AsTypeUnvalidated is just like WithType, 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
}
vendor/sigs.k8s.io/structured-merge-diff/typed/validate.go
0 → 100644
View file @
5869ce6d
/*
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
}
vendor/sigs.k8s.io/structured-merge-diff/value/BUILD
0 → 100644
View file @
5869ce6d
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"unstructured.go",
"value.go",
],
importmap = "k8s.io/kubernetes/vendor/sigs.k8s.io/structured-merge-diff/value",
importpath = "sigs.k8s.io/structured-merge-diff/value",
visibility = ["//visibility:public"],
deps = ["//vendor/gopkg.in/yaml.v2:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
vendor/sigs.k8s.io/structured-merge-diff/value/doc.go
0 → 100644
View file @
5869ce6d
/*
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
vendor/sigs.k8s.io/structured-merge-diff/value/unstructured.go
0 → 100644
View file @
5869ce6d
/*
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
)
==
0
||
(
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
}
}
vendor/sigs.k8s.io/structured-merge-diff/value/value.go
0 → 100644
View file @
5869ce6d
/*
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"
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment