Commit 79d755ff authored by Daniel Smith's avatar Daniel Smith

Merge pull request #2676 from ghodss/move-to-ghodss-yaml

Move from go-yaml/yaml v1 to ghodss/yaml
parents 8a726b80 63998542
...@@ -45,6 +45,10 @@ ...@@ -45,6 +45,10 @@
"Rev": "23142f6773a676cc2cae8dd0cb90b2ea761c853f" "Rev": "23142f6773a676cc2cae8dd0cb90b2ea761c853f"
}, },
{ {
"ImportPath": "github.com/davecgh/go-spew/spew",
"Rev": "83f84dc933714d51504ceed59f43ead21d096fe7"
},
{
"ImportPath": "github.com/elazarl/go-bindata-assetfs", "ImportPath": "github.com/elazarl/go-bindata-assetfs",
"Rev": "ae4665cf2d188c65764c73fe4af5378acc549510" "Rev": "ae4665cf2d188c65764c73fe4af5378acc549510"
}, },
...@@ -59,6 +63,10 @@ ...@@ -59,6 +63,10 @@
"Rev": "15d2c6e3eb670c545d0af0604d7f9aff3871af04" "Rev": "15d2c6e3eb670c545d0af0604d7f9aff3871af04"
}, },
{ {
"ImportPath": "github.com/ghodss/yaml",
"Rev": "a4ad25344bbdf7fec4f4675f866b0091fb00e00e"
},
{
"ImportPath": "github.com/golang/glog", "ImportPath": "github.com/golang/glog",
"Rev": "44145f04b68cf362d9c4df2182967c2275eaefed" "Rev": "44145f04b68cf362d9c4df2182967c2275eaefed"
}, },
...@@ -151,8 +159,8 @@ ...@@ -151,8 +159,8 @@
"Rev": "c043f0dc72e4cdd23ae039470ea9d63e6680a1b2" "Rev": "c043f0dc72e4cdd23ae039470ea9d63e6680a1b2"
}, },
{ {
"ImportPath": "gopkg.in/v1/yaml", "ImportPath": "gopkg.in/v2/yaml",
"Rev": "1b9791953ba4027efaeb728c7355e542a203be5e" "Rev": "8fac37523b5c7a1128bf0217f6bd07be1ad266b2"
} }
] ]
} }
/*
* Copyright (c) 2013 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew_test
import (
"fmt"
"github.com/davecgh/go-spew/spew"
"reflect"
"testing"
)
// custom type to test Stinger interface on non-pointer receiver.
type stringer string
// String implements the Stringer interface for testing invocation of custom
// stringers on types with non-pointer receivers.
func (s stringer) String() string {
return "stringer " + string(s)
}
// custom type to test Stinger interface on pointer receiver.
type pstringer string
// String implements the Stringer interface for testing invocation of custom
// stringers on types with only pointer receivers.
func (s *pstringer) String() string {
return "stringer " + string(*s)
}
// xref1 and xref2 are cross referencing structs for testing circular reference
// detection.
type xref1 struct {
ps2 *xref2
}
type xref2 struct {
ps1 *xref1
}
// indirCir1, indirCir2, and indirCir3 are used to generate an indirect circular
// reference for testing detection.
type indirCir1 struct {
ps2 *indirCir2
}
type indirCir2 struct {
ps3 *indirCir3
}
type indirCir3 struct {
ps1 *indirCir1
}
// embed is used to test embedded structures.
type embed struct {
a string
}
// embedwrap is used to test embedded structures.
type embedwrap struct {
*embed
e *embed
}
// panicer is used to intentionally cause a panic for testing spew properly
// handles them
type panicer int
func (p panicer) String() string {
panic("test panic")
}
// customError is used to test custom error interface invocation.
type customError int
func (e customError) Error() string {
return fmt.Sprintf("error: %d", int(e))
}
// stringizeWants converts a slice of wanted test output into a format suitable
// for a test error message.
func stringizeWants(wants []string) string {
s := ""
for i, want := range wants {
if i > 0 {
s += fmt.Sprintf("want%d: %s", i+1, want)
} else {
s += "want: " + want
}
}
return s
}
// testFailed returns whether or not a test failed by checking if the result
// of the test is in the slice of wanted strings.
func testFailed(result string, wants []string) bool {
for _, want := range wants {
if result == want {
return false
}
}
return true
}
// TestSortValues ensures the sort functionality for relect.Value based sorting
// works as intended.
func TestSortValues(t *testing.T) {
getInterfaces := func(values []reflect.Value) []interface{} {
interfaces := []interface{}{}
for _, v := range values {
interfaces = append(interfaces, v.Interface())
}
return interfaces
}
v := reflect.ValueOf
a := v("a")
b := v("b")
c := v("c")
embedA := v(embed{"a"})
embedB := v(embed{"b"})
embedC := v(embed{"c"})
tests := []struct {
input []reflect.Value
expected []reflect.Value
}{
// No values.
{
[]reflect.Value{},
[]reflect.Value{},
},
// Bools.
{
[]reflect.Value{v(false), v(true), v(false)},
[]reflect.Value{v(false), v(false), v(true)},
},
// Ints.
{
[]reflect.Value{v(2), v(1), v(3)},
[]reflect.Value{v(1), v(2), v(3)},
},
// Uints.
{
[]reflect.Value{v(uint8(2)), v(uint8(1)), v(uint8(3))},
[]reflect.Value{v(uint8(1)), v(uint8(2)), v(uint8(3))},
},
// Floats.
{
[]reflect.Value{v(2.0), v(1.0), v(3.0)},
[]reflect.Value{v(1.0), v(2.0), v(3.0)},
},
// Strings.
{
[]reflect.Value{b, a, c},
[]reflect.Value{a, b, c},
},
// Uintptrs.
{
[]reflect.Value{v(uintptr(2)), v(uintptr(1)), v(uintptr(3))},
[]reflect.Value{v(uintptr(1)), v(uintptr(2)), v(uintptr(3))},
},
// Invalid.
{
[]reflect.Value{embedB, embedA, embedC},
[]reflect.Value{embedB, embedA, embedC},
},
}
for _, test := range tests {
spew.SortValues(test.input)
// reflect.DeepEqual cannot really make sense of reflect.Value,
// probably because of all the pointer tricks. For instance,
// v(2.0) != v(2.0) on a 32-bits system. Turn them into interface{}
// instead.
input := getInterfaces(test.input)
expected := getInterfaces(test.expected)
if !reflect.DeepEqual(input, expected) {
t.Errorf("Sort mismatch:\n %v != %v", input, expected)
}
}
}
/*
* Copyright (c) 2013 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
Package spew implements a deep pretty printer for Go data structures to aid in
debugging.
A quick overview of the additional features spew provides over the built-in
printing facilities for Go data types are as follows:
* Pointers are dereferenced and followed
* Circular data structures are detected and handled properly
* Custom Stringer/error interfaces are optionally invoked, including
on unexported types
* Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
* Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output (only when using
Dump style)
There are two different approaches spew allows for dumping Go data structures:
* Dump style which prints with newlines, customizable indentation,
and additional debug information such as types and all pointer addresses
used to indirect to the final value
* A custom Formatter interface that integrates cleanly with the standard fmt
package and replaces %v, %+v, %#v, and %#+v to provide inline printing
similar to the default %v while providing the additional functionality
outlined above and passing unsupported format verbs such as %x and %q
along to fmt
Quick Start
This section demonstrates how to quickly get started with spew. See the
sections below for further details on formatting and configuration options.
To dump a variable with full newlines, indentation, type, and pointer
information use Dump, Fdump, or Sdump:
spew.Dump(myVar1, myVar2, ...)
spew.Fdump(someWriter, myVar1, myVar2, ...)
str := spew.Sdump(myVar1, myVar2, ...)
Alternatively, if you would prefer to use format strings with a compacted inline
printing style, use the convenience wrappers Printf, Fprintf, etc with
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
%#+v (adds types and pointer addresses):
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
Configuration Options
Configuration of spew is handled by fields in the ConfigState type. For
convenience, all of the top-level functions use a global state available
via the spew.Config global.
It is also possible to create a ConfigState instance that provides methods
equivalent to the top-level functions. This allows concurrent configuration
options. See the ConfigState documentation for more details.
The following configuration options are available:
* Indent
String to use for each indentation level for Dump functions.
It is a single space by default. A popular alternative is "\t".
* MaxDepth
Maximum number of levels to descend into nested data structures.
There is no limit by default.
* DisableMethods
Disables invocation of error and Stringer interface methods.
Method invocation is enabled by default.
* DisablePointerMethods
Disables invocation of error and Stringer interface methods on types
which only accept pointer receivers from non-pointer variables.
Pointer method invocation is enabled by default.
* ContinueOnMethod
Enables recursion into types after invoking error and Stringer interface
methods. Recursion after method invocation is disabled by default.
* SortKeys
Specifies map keys should be sorted before being printed. Use
this to have a more deterministic, diffable output. Note that
only native types (bool, int, uint, floats, uintptr and string)
are supported with other types sorted according to the
reflect.Value.String() output which guarantees display stability.
Natural map order is used by default.
Dump Usage
Simply call spew.Dump with a list of variables you want to dump:
spew.Dump(myVar1, myVar2, ...)
You may also call spew.Fdump if you would prefer to output to an arbitrary
io.Writer. For example, to dump to standard error:
spew.Fdump(os.Stderr, myVar1, myVar2, ...)
A third option is to call spew.Sdump to get the formatted output as a string:
str := spew.Sdump(myVar1, myVar2, ...)
Sample Dump Output
See the Dump example for details on the setup of the types and variables being
shown here.
(main.Foo) {
unexportedField: (*main.Bar)(0xf84002e210)({
flag: (main.Flag) flagTwo,
data: (uintptr) <nil>
}),
ExportedField: (map[interface {}]interface {}) (len=1) {
(string) (len=3) "one": (bool) true
}
}
Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
command as shown.
([]uint8) (len=32 cap=32) {
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
00000020 31 32 |12|
}
Custom Formatter
Spew provides a custom formatter that implements the fmt.Formatter interface
so that it integrates cleanly with standard fmt package printing functions. The
formatter is useful for inline printing of smaller data types similar to the
standard %v format specifier.
The custom formatter only responds to the %v (most compact), %+v (adds pointer
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
combinations. Any other verbs such as %x and %q will be sent to the the
standard fmt package for formatting. In addition, the custom formatter ignores
the width and precision arguments (however they will still work on the format
specifiers not handled by the custom formatter).
Custom Formatter Usage
The simplest way to make use of the spew custom formatter is to call one of the
convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
functions have syntax you are most likely already familiar with:
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
spew.Println(myVar, myVar2)
spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
See the Index for the full list convenience functions.
Sample Formatter Output
Double pointer to a uint8:
%v: <**>5
%+v: <**>(0xf8400420d0->0xf8400420c8)5
%#v: (**uint8)5
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
Pointer to circular struct with a uint8 field and a pointer to itself:
%v: <*>{1 <*><shown>}
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
See the Printf example for details on the setup of variables being shown
here.
Errors
Since it is possible for custom Stringer/error interfaces to panic, spew
detects them and handles them internally by printing the panic information
inline with the output. Since spew is intended to provide deep pretty printing
capabilities on structures, it intentionally does not return any errors.
*/
package spew
// Copyright (c) 2013 Dave Collins <dave@davec.name>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// NOTE: Due to the following build constraints, this file will only be compiled
// when both cgo is supported and "-tags testcgo" is added to the go test
// command line. This means the cgo tests are only added (and hence run) when
// specifially requested. This configuration is used because spew itself
// does not require cgo to run even though it does handle certain cgo types
// specially. Rather than forcing all clients to require cgo and an external
// C compiler just to run the tests, this scheme makes them optional.
// +build cgo,testcgo
package spew_test
import (
"fmt"
"github.com/davecgh/go-spew/spew/testdata"
)
func addCgoDumpTests() {
// C char pointer.
v := testdata.GetCgoCharPointer()
nv := testdata.GetCgoNullCharPointer()
pv := &v
vcAddr := fmt.Sprintf("%p", v)
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "*testdata._Ctype_char"
vs := "116"
addDumpTest(v, "("+vt+")("+vcAddr+")("+vs+")\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+"->"+vcAddr+")("+vs+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+"->"+vcAddr+")("+vs+")\n")
addDumpTest(nv, "("+vt+")(<nil>)\n")
// C char array.
v2, v2l, v2c := testdata.GetCgoCharArray()
v2Len := fmt.Sprintf("%d", v2l)
v2Cap := fmt.Sprintf("%d", v2c)
v2t := "[6]testdata._Ctype_char"
v2s := "(len=" + v2Len + " cap=" + v2Cap + ") " +
"{\n 00000000 74 65 73 74 32 00 " +
" |test2.|\n}"
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
// C unsigned char array.
v3, v3l, v3c := testdata.GetCgoUnsignedCharArray()
v3Len := fmt.Sprintf("%d", v3l)
v3Cap := fmt.Sprintf("%d", v3c)
v3t := "[6]testdata._Ctype_unsignedchar"
v3s := "(len=" + v3Len + " cap=" + v3Cap + ") " +
"{\n 00000000 74 65 73 74 33 00 " +
" |test3.|\n}"
addDumpTest(v3, "("+v3t+") "+v3s+"\n")
// C signed char array.
v4, v4l, v4c := testdata.GetCgoSignedCharArray()
v4Len := fmt.Sprintf("%d", v4l)
v4Cap := fmt.Sprintf("%d", v4c)
v4t := "[6]testdata._Ctype_schar"
v4t2 := "testdata._Ctype_schar"
v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " +
"{\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 101,\n (" + v4t2 +
") 115,\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 52,\n (" + v4t2 +
") 0\n}"
addDumpTest(v4, "("+v4t+") "+v4s+"\n")
// C uint8_t array.
v5, v5l, v5c := testdata.GetCgoUint8tArray()
v5Len := fmt.Sprintf("%d", v5l)
v5Cap := fmt.Sprintf("%d", v5c)
v5t := "[6]testdata._Ctype_uint8_t"
v5s := "(len=" + v5Len + " cap=" + v5Cap + ") " +
"{\n 00000000 74 65 73 74 35 00 " +
" |test5.|\n}"
addDumpTest(v5, "("+v5t+") "+v5s+"\n")
// C typedefed unsigned char array.
v6, v6l, v6c := testdata.GetCgoTypdefedUnsignedCharArray()
v6Len := fmt.Sprintf("%d", v6l)
v6Cap := fmt.Sprintf("%d", v6c)
v6t := "[6]testdata._Ctype_custom_uchar_t"
v6s := "(len=" + v6Len + " cap=" + v6Cap + ") " +
"{\n 00000000 74 65 73 74 36 00 " +
" |test6.|\n}"
addDumpTest(v6, "("+v6t+") "+v6s+"\n")
}
// Copyright (c) 2013 Dave Collins <dave@davec.name>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// NOTE: Due to the following build constraints, this file will only be compiled
// when either cgo is not supported or "-tags testcgo" is not added to the go
// test command line. This file intentionally does not setup any cgo tests in
// this scenario.
// +build !cgo !testcgo
package spew_test
func addCgoDumpTests() {
// Don't add any tests for cgo since this file is only compiled when
// there should not be any cgo tests.
}
/*
* Copyright (c) 2013 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew_test
import (
"fmt"
"github.com/davecgh/go-spew/spew"
)
type Flag int
const (
flagOne Flag = iota
flagTwo
)
var flagStrings = map[Flag]string{
flagOne: "flagOne",
flagTwo: "flagTwo",
}
func (f Flag) String() string {
if s, ok := flagStrings[f]; ok {
return s
}
return fmt.Sprintf("Unknown flag (%d)", int(f))
}
type Bar struct {
flag Flag
data uintptr
}
type Foo struct {
unexportedField Bar
ExportedField map[interface{}]interface{}
}
// This example demonstrates how to use Dump to dump variables to stdout.
func ExampleDump() {
// The following package level declarations are assumed for this example:
/*
type Flag int
const (
flagOne Flag = iota
flagTwo
)
var flagStrings = map[Flag]string{
flagOne: "flagOne",
flagTwo: "flagTwo",
}
func (f Flag) String() string {
if s, ok := flagStrings[f]; ok {
return s
}
return fmt.Sprintf("Unknown flag (%d)", int(f))
}
type Bar struct {
flag Flag
data uintptr
}
type Foo struct {
unexportedField Bar
ExportedField map[interface{}]interface{}
}
*/
// Setup some sample data structures for the example.
bar := Bar{Flag(flagTwo), uintptr(0)}
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
f := Flag(5)
b := []byte{
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
0x31, 0x32,
}
// Dump!
spew.Dump(s1, f, b)
// Output:
// (spew_test.Foo) {
// unexportedField: (spew_test.Bar) {
// flag: (spew_test.Flag) flagTwo,
// data: (uintptr) <nil>
// },
// ExportedField: (map[interface {}]interface {}) (len=1) {
// (string) (len=3) "one": (bool) true
// }
// }
// (spew_test.Flag) Unknown flag (5)
// ([]uint8) (len=34 cap=34) {
// 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
// 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
// 00000020 31 32 |12|
// }
//
}
// This example demonstrates how to use Printf to display a variable with a
// format string and inline formatting.
func ExamplePrintf() {
// Create a double pointer to a uint 8.
ui8 := uint8(5)
pui8 := &ui8
ppui8 := &pui8
// Create a circular data type.
type circular struct {
ui8 uint8
c *circular
}
c := circular{ui8: 1}
c.c = &c
// Print!
spew.Printf("ppui8: %v\n", ppui8)
spew.Printf("circular: %v\n", c)
// Output:
// ppui8: <**>5
// circular: {1 <*>{1 <*><shown>}}
}
// This example demonstrates how to use a ConfigState.
func ExampleConfigState() {
// Modify the indent level of the ConfigState only. The global
// configuration is not modified.
scs := spew.ConfigState{Indent: "\t"}
// Output using the ConfigState instance.
v := map[string]int{"one": 1}
scs.Printf("v: %v\n", v)
scs.Dump(v)
// Output:
// v: map[one:1]
// (map[string]int) (len=1) {
// (string) (len=3) "one": (int) 1
// }
}
// This example demonstrates how to use ConfigState.Dump to dump variables to
// stdout
func ExampleConfigState_Dump() {
// See the top-level Dump example for details on the types used in this
// example.
// Create two ConfigState instances with different indentation.
scs := spew.ConfigState{Indent: "\t"}
scs2 := spew.ConfigState{Indent: " "}
// Setup some sample data structures for the example.
bar := Bar{Flag(flagTwo), uintptr(0)}
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
// Dump using the ConfigState instances.
scs.Dump(s1)
scs2.Dump(s1)
// Output:
// (spew_test.Foo) {
// unexportedField: (spew_test.Bar) {
// flag: (spew_test.Flag) flagTwo,
// data: (uintptr) <nil>
// },
// ExportedField: (map[interface {}]interface {}) (len=1) {
// (string) (len=3) "one": (bool) true
// }
// }
// (spew_test.Foo) {
// unexportedField: (spew_test.Bar) {
// flag: (spew_test.Flag) flagTwo,
// data: (uintptr) <nil>
// },
// ExportedField: (map[interface {}]interface {}) (len=1) {
// (string) (len=3) "one": (bool) true
// }
// }
//
}
// This example demonstrates how to use ConfigState.Printf to display a variable
// with a format string and inline formatting.
func ExampleConfigState_Printf() {
// See the top-level Dump example for details on the types used in this
// example.
// Create two ConfigState instances and modify the method handling of the
// first ConfigState only.
scs := spew.NewDefaultConfig()
scs2 := spew.NewDefaultConfig()
scs.DisableMethods = true
// Alternatively
// scs := spew.ConfigState{Indent: " ", DisableMethods: true}
// scs2 := spew.ConfigState{Indent: " "}
// This is of type Flag which implements a Stringer and has raw value 1.
f := flagTwo
// Dump using the ConfigState instances.
scs.Printf("f: %v\n", f)
scs2.Printf("f: %v\n", f)
// Output:
// f: 1
// f: flagTwo
}
/*
* Copyright (c) 2013 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
This test file is part of the spew package rather than than the spew_test
package because it needs access to internals to properly test certain cases
which are not possible via the public interface since they should never happen.
*/
package spew
import (
"bytes"
"reflect"
"testing"
"unsafe"
)
// dummyFmtState implements a fake fmt.State to use for testing invalid
// reflect.Value handling. This is necessary because the fmt package catches
// invalid values before invoking the formatter on them.
type dummyFmtState struct {
bytes.Buffer
}
func (dfs *dummyFmtState) Flag(f int) bool {
if f == int('+') {
return true
}
return false
}
func (dfs *dummyFmtState) Precision() (int, bool) {
return 0, false
}
func (dfs *dummyFmtState) Width() (int, bool) {
return 0, false
}
// TestInvalidReflectValue ensures the dump and formatter code handles an
// invalid reflect value properly. This needs access to internal state since it
// should never happen in real code and therefore can't be tested via the public
// API.
func TestInvalidReflectValue(t *testing.T) {
i := 1
// Dump invalid reflect value.
v := new(reflect.Value)
buf := new(bytes.Buffer)
d := dumpState{w: buf, cs: &Config}
d.dump(*v)
s := buf.String()
want := "<invalid>"
if s != want {
t.Errorf("InvalidReflectValue #%d\n got: %s want: %s", i, s, want)
}
i++
// Formatter invalid reflect value.
buf2 := new(dummyFmtState)
f := formatState{value: *v, cs: &Config, fs: buf2}
f.format(*v)
s = buf2.String()
want = "<invalid>"
if s != want {
t.Errorf("InvalidReflectValue #%d got: %s want: %s", i, s, want)
}
}
// changeKind uses unsafe to intentionally change the kind of a reflect.Value to
// the maximum kind value which does not exist. This is needed to test the
// fallback code which punts to the standard fmt library for new types that
// might get added to the language.
func changeKind(v *reflect.Value, readOnly bool) {
rvf := (*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + offsetFlag))
*rvf = *rvf | ((1<<flagKindWidth - 1) << flagKindShift)
if readOnly {
*rvf |= flagRO
} else {
*rvf &= ^uintptr(flagRO)
}
}
// TestAddedReflectValue tests functionaly of the dump and formatter code which
// falls back to the standard fmt library for new types that might get added to
// the language.
func TestAddedReflectValue(t *testing.T) {
i := 1
// Dump using a reflect.Value that is exported.
v := reflect.ValueOf(int8(5))
changeKind(&v, false)
buf := new(bytes.Buffer)
d := dumpState{w: buf, cs: &Config}
d.dump(v)
s := buf.String()
want := "(int8) 5"
if s != want {
t.Errorf("TestAddedReflectValue #%d\n got: %s want: %s", i, s, want)
}
i++
// Dump using a reflect.Value that is not exported.
changeKind(&v, true)
buf.Reset()
d.dump(v)
s = buf.String()
want = "(int8) <int8 Value>"
if s != want {
t.Errorf("TestAddedReflectValue #%d\n got: %s want: %s", i, s, want)
}
i++
// Formatter using a reflect.Value that is exported.
changeKind(&v, false)
buf2 := new(dummyFmtState)
f := formatState{value: v, cs: &Config, fs: buf2}
f.format(v)
s = buf2.String()
want = "5"
if s != want {
t.Errorf("TestAddedReflectValue #%d got: %s want: %s", i, s, want)
}
i++
// Formatter using a reflect.Value that is not exported.
changeKind(&v, true)
buf2.Reset()
f = formatState{value: v, cs: &Config, fs: buf2}
f.format(v)
s = buf2.String()
want = "<int8 Value>"
if s != want {
t.Errorf("TestAddedReflectValue #%d got: %s want: %s", i, s, want)
}
}
// SortValues makes the internal sortValues function available to the test
// package.
func SortValues(values []reflect.Value) {
sortValues(values)
}
/*
* Copyright (c) 2013 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew
import (
"fmt"
"io"
)
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the formatted string as a value that satisfies error. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
func Errorf(format string, a ...interface{}) (err error) {
return fmt.Errorf(format, convertArgs(a)...)
}
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprint(w, convertArgs(a)...)
}
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
return fmt.Fprintf(w, format, convertArgs(a)...)
}
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
// passed with a default Formatter interface returned by NewFormatter. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprintln(w, convertArgs(a)...)
}
// Print is a wrapper for fmt.Print that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
func Print(a ...interface{}) (n int, err error) {
return fmt.Print(convertArgs(a)...)
}
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
func Printf(format string, a ...interface{}) (n int, err error) {
return fmt.Printf(format, convertArgs(a)...)
}
// Println is a wrapper for fmt.Println that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
func Println(a ...interface{}) (n int, err error) {
return fmt.Println(convertArgs(a)...)
}
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
func Sprint(a ...interface{}) string {
return fmt.Sprint(convertArgs(a)...)
}
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
func Sprintf(format string, a ...interface{}) string {
return fmt.Sprintf(format, convertArgs(a)...)
}
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
// were passed with a default Formatter interface returned by NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
func Sprintln(a ...interface{}) string {
return fmt.Sprintln(convertArgs(a)...)
}
// convertArgs accepts a slice of arguments and returns a slice of the same
// length with each argument converted to a default spew Formatter interface.
func convertArgs(args []interface{}) (formatters []interface{}) {
formatters = make([]interface{}, len(args))
for index, arg := range args {
formatters[index] = NewFormatter(arg)
}
return formatters
}
/*
* Copyright (c) 2013 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew_test
import (
"bytes"
"fmt"
"github.com/davecgh/go-spew/spew"
"io/ioutil"
"os"
"testing"
)
// spewFunc is used to identify which public function of the spew package or
// ConfigState a test applies to.
type spewFunc int
const (
fCSFdump spewFunc = iota
fCSFprint
fCSFprintf
fCSFprintln
fCSPrint
fCSPrintln
fCSSdump
fCSSprint
fCSSprintf
fCSSprintln
fCSErrorf
fCSNewFormatter
fErrorf
fFprint
fFprintln
fPrint
fPrintln
fSdump
fSprint
fSprintf
fSprintln
)
// Map of spewFunc values to names for pretty printing.
var spewFuncStrings = map[spewFunc]string{
fCSFdump: "ConfigState.Fdump",
fCSFprint: "ConfigState.Fprint",
fCSFprintf: "ConfigState.Fprintf",
fCSFprintln: "ConfigState.Fprintln",
fCSSdump: "ConfigState.Sdump",
fCSPrint: "ConfigState.Print",
fCSPrintln: "ConfigState.Println",
fCSSprint: "ConfigState.Sprint",
fCSSprintf: "ConfigState.Sprintf",
fCSSprintln: "ConfigState.Sprintln",
fCSErrorf: "ConfigState.Errorf",
fCSNewFormatter: "ConfigState.NewFormatter",
fErrorf: "spew.Errorf",
fFprint: "spew.Fprint",
fFprintln: "spew.Fprintln",
fPrint: "spew.Print",
fPrintln: "spew.Println",
fSdump: "spew.Sdump",
fSprint: "spew.Sprint",
fSprintf: "spew.Sprintf",
fSprintln: "spew.Sprintln",
}
func (f spewFunc) String() string {
if s, ok := spewFuncStrings[f]; ok {
return s
}
return fmt.Sprintf("Unknown spewFunc (%d)", int(f))
}
// spewTest is used to describe a test to be performed against the public
// functions of the spew package or ConfigState.
type spewTest struct {
cs *spew.ConfigState
f spewFunc
format string
in interface{}
want string
}
// spewTests houses the tests to be performed against the public functions of
// the spew package and ConfigState.
//
// These tests are only intended to ensure the public functions are exercised
// and are intentionally not exhaustive of types. The exhaustive type
// tests are handled in the dump and format tests.
var spewTests []spewTest
// redirStdout is a helper function to return the standard output from f as a
// byte slice.
func redirStdout(f func()) ([]byte, error) {
tempFile, err := ioutil.TempFile("", "ss-test")
if err != nil {
return nil, err
}
fileName := tempFile.Name()
defer os.Remove(fileName) // Ignore error
origStdout := os.Stdout
os.Stdout = tempFile
f()
os.Stdout = origStdout
tempFile.Close()
return ioutil.ReadFile(fileName)
}
func initSpewTests() {
// Config states with various settings.
scsDefault := spew.NewDefaultConfig()
scsNoMethods := &spew.ConfigState{Indent: " ", DisableMethods: true}
scsNoPmethods := &spew.ConfigState{Indent: " ", DisablePointerMethods: true}
scsMaxDepth := &spew.ConfigState{Indent: " ", MaxDepth: 1}
scsContinue := &spew.ConfigState{Indent: " ", ContinueOnMethod: true}
// Variables for tests on types which implement Stringer interface with and
// without a pointer receiver.
ts := stringer("test")
tps := pstringer("test")
// depthTester is used to test max depth handling for structs, array, slices
// and maps.
type depthTester struct {
ic indirCir1
arr [1]string
slice []string
m map[string]int
}
dt := depthTester{indirCir1{nil}, [1]string{"arr"}, []string{"slice"},
map[string]int{"one": 1}}
// Variable for tests on types which implement error interface.
te := customError(10)
spewTests = []spewTest{
{scsDefault, fCSFdump, "", int8(127), "(int8) 127\n"},
{scsDefault, fCSFprint, "", int16(32767), "32767"},
{scsDefault, fCSFprintf, "%v", int32(2147483647), "2147483647"},
{scsDefault, fCSFprintln, "", int(2147483647), "2147483647\n"},
{scsDefault, fCSPrint, "", int64(9223372036854775807), "9223372036854775807"},
{scsDefault, fCSPrintln, "", uint8(255), "255\n"},
{scsDefault, fCSSdump, "", uint8(64), "(uint8) 64\n"},
{scsDefault, fCSSprint, "", complex(1, 2), "(1+2i)"},
{scsDefault, fCSSprintf, "%v", complex(float32(3), 4), "(3+4i)"},
{scsDefault, fCSSprintln, "", complex(float64(5), 6), "(5+6i)\n"},
{scsDefault, fCSErrorf, "%#v", uint16(65535), "(uint16)65535"},
{scsDefault, fCSNewFormatter, "%v", uint32(4294967295), "4294967295"},
{scsDefault, fErrorf, "%v", uint64(18446744073709551615), "18446744073709551615"},
{scsDefault, fFprint, "", float32(3.14), "3.14"},
{scsDefault, fFprintln, "", float64(6.28), "6.28\n"},
{scsDefault, fPrint, "", true, "true"},
{scsDefault, fPrintln, "", false, "false\n"},
{scsDefault, fSdump, "", complex(-10, -20), "(complex128) (-10-20i)\n"},
{scsDefault, fSprint, "", complex(-1, -2), "(-1-2i)"},
{scsDefault, fSprintf, "%v", complex(float32(-3), -4), "(-3-4i)"},
{scsDefault, fSprintln, "", complex(float64(-5), -6), "(-5-6i)\n"},
{scsNoMethods, fCSFprint, "", ts, "test"},
{scsNoMethods, fCSFprint, "", &ts, "<*>test"},
{scsNoMethods, fCSFprint, "", tps, "test"},
{scsNoMethods, fCSFprint, "", &tps, "<*>test"},
{scsNoPmethods, fCSFprint, "", ts, "stringer test"},
{scsNoPmethods, fCSFprint, "", &ts, "<*>stringer test"},
{scsNoPmethods, fCSFprint, "", tps, "test"},
{scsNoPmethods, fCSFprint, "", &tps, "<*>stringer test"},
{scsMaxDepth, fCSFprint, "", dt, "{{<max>} [<max>] [<max>] map[<max>]}"},
{scsMaxDepth, fCSFdump, "", dt, "(spew_test.depthTester) {\n" +
" ic: (spew_test.indirCir1) {\n <max depth reached>\n },\n" +
" arr: ([1]string) (len=1 cap=1) {\n <max depth reached>\n },\n" +
" slice: ([]string) (len=1 cap=1) {\n <max depth reached>\n },\n" +
" m: (map[string]int) (len=1) {\n <max depth reached>\n }\n}\n"},
{scsContinue, fCSFprint, "", ts, "(stringer test) test"},
{scsContinue, fCSFdump, "", ts, "(spew_test.stringer) " +
"(len=4) (stringer test) \"test\"\n"},
{scsContinue, fCSFprint, "", te, "(error: 10) 10"},
{scsContinue, fCSFdump, "", te, "(spew_test.customError) " +
"(error: 10) 10\n"},
}
}
// TestSpew executes all of the tests described by spewTests.
func TestSpew(t *testing.T) {
initSpewTests()
t.Logf("Running %d tests", len(spewTests))
for i, test := range spewTests {
buf := new(bytes.Buffer)
switch test.f {
case fCSFdump:
test.cs.Fdump(buf, test.in)
case fCSFprint:
test.cs.Fprint(buf, test.in)
case fCSFprintf:
test.cs.Fprintf(buf, test.format, test.in)
case fCSFprintln:
test.cs.Fprintln(buf, test.in)
case fCSPrint:
b, err := redirStdout(func() { test.cs.Print(test.in) })
if err != nil {
t.Errorf("%v #%d %v", test.f, i, err)
continue
}
buf.Write(b)
case fCSPrintln:
b, err := redirStdout(func() { test.cs.Println(test.in) })
if err != nil {
t.Errorf("%v #%d %v", test.f, i, err)
continue
}
buf.Write(b)
case fCSSdump:
str := test.cs.Sdump(test.in)
buf.WriteString(str)
case fCSSprint:
str := test.cs.Sprint(test.in)
buf.WriteString(str)
case fCSSprintf:
str := test.cs.Sprintf(test.format, test.in)
buf.WriteString(str)
case fCSSprintln:
str := test.cs.Sprintln(test.in)
buf.WriteString(str)
case fCSErrorf:
err := test.cs.Errorf(test.format, test.in)
buf.WriteString(err.Error())
case fCSNewFormatter:
fmt.Fprintf(buf, test.format, test.cs.NewFormatter(test.in))
case fErrorf:
err := spew.Errorf(test.format, test.in)
buf.WriteString(err.Error())
case fFprint:
spew.Fprint(buf, test.in)
case fFprintln:
spew.Fprintln(buf, test.in)
case fPrint:
b, err := redirStdout(func() { spew.Print(test.in) })
if err != nil {
t.Errorf("%v #%d %v", test.f, i, err)
continue
}
buf.Write(b)
case fPrintln:
b, err := redirStdout(func() { spew.Println(test.in) })
if err != nil {
t.Errorf("%v #%d %v", test.f, i, err)
continue
}
buf.Write(b)
case fSdump:
str := spew.Sdump(test.in)
buf.WriteString(str)
case fSprint:
str := spew.Sprint(test.in)
buf.WriteString(str)
case fSprintf:
str := spew.Sprintf(test.format, test.in)
buf.WriteString(str)
case fSprintln:
str := spew.Sprintln(test.in)
buf.WriteString(str)
default:
t.Errorf("%v #%d unrecognized function", test.f, i)
continue
}
s := buf.String()
if test.want != s {
t.Errorf("ConfigState #%d\n got: %s want: %s", i, s, test.want)
continue
}
}
}
// Copyright (c) 2013 Dave Collins <dave@davec.name>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// NOTE: Due to the following build constraints, this file will only be compiled
// when both cgo is supported and "-tags testcgo" is added to the go test
// command line. This code should really only be in the dumpcgo_test.go file,
// but unfortunately Go will not allow cgo in test files, so this is a
// workaround to allow cgo types to be tested. This configuration is used
// because spew itself does not require cgo to run even though it does handle
// certain cgo types specially. Rather than forcing all clients to require cgo
// and an external C compiler just to run the tests, this scheme makes them
// optional.
// +build cgo,testcgo
package testdata
/*
#include <stdint.h>
typedef unsigned char custom_uchar_t;
char *ncp = 0;
char *cp = "test";
char ca[6] = {'t', 'e', 's', 't', '2', '\0'};
unsigned char uca[6] = {'t', 'e', 's', 't', '3', '\0'};
signed char sca[6] = {'t', 'e', 's', 't', '4', '\0'};
uint8_t ui8ta[6] = {'t', 'e', 's', 't', '5', '\0'};
custom_uchar_t tuca[6] = {'t', 'e', 's', 't', '6', '\0'};
*/
import "C"
// GetCgoNullCharPointer returns a null char pointer via cgo. This is only
// used for tests.
func GetCgoNullCharPointer() interface{} {
return C.ncp
}
// GetCgoCharPointer returns a char pointer via cgo. This is only used for
// tests.
func GetCgoCharPointer() interface{} {
return C.cp
}
// GetCgoCharArray returns a char array via cgo and the array's len and cap.
// This is only used for tests.
func GetCgoCharArray() (interface{}, int, int) {
return C.ca, len(C.ca), cap(C.ca)
}
// GetCgoUnsignedCharArray returns an unsigned char array via cgo and the
// array's len and cap. This is only used for tests.
func GetCgoUnsignedCharArray() (interface{}, int, int) {
return C.uca, len(C.uca), cap(C.uca)
}
// GetCgoSignedCharArray returns a signed char array via cgo and the array's len
// and cap. This is only used for tests.
func GetCgoSignedCharArray() (interface{}, int, int) {
return C.sca, len(C.sca), cap(C.sca)
}
// GetCgoUint8tArray returns a uint8_t array via cgo and the array's len and
// cap. This is only used for tests.
func GetCgoUint8tArray() (interface{}, int, int) {
return C.ui8ta, len(C.ui8ta), cap(C.ui8ta)
}
// GetCgoTypdefedUnsignedCharArray returns a typedefed unsigned char array via
// cgo and the array's len and cap. This is only used for tests.
func GetCgoTypdefedUnsignedCharArray() (interface{}, int, int) {
return C.tuca, len(C.tuca), cap(C.tuca)
}
# OSX leaves these everywhere on SMB shares
._*
# Eclipse files
.classpath
.project
.settings/**
# Emacs save files
*~
# Vim-related files
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
*.un~
Session.vim
.netrwhist
# Go test binaries
*.test
The MIT License (MIT)
Copyright (c) 2014 Sam Ghods
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# YAML marshaling and unmarshaling support for Go
## Introduction
A wrapper around [go-yaml](https://github.com/go-yaml/yaml) designed to enable a better way of handling YAML when marshaling to and from structs.
In short, this library first converts YAML to JSON using go-yaml and then uses `json.Marshal` and `json.Unmarshal` to convert to or from the struct. This means that it effectively reuses the JSON struct tags as well as the custom JSON methods `MarshalJSON` and `UnmarshalJSON` unlike go-yaml. For a detailed overview of the rationale behind this method, [see this blog post](http://ghodss.com/2014/the-right-way-to-handle-yaml-in-golang/).
## Compatibility
This package uses [go-yaml v2](https://github.com/go-yaml/yaml) and therefore supports [everything go-yaml supports](https://github.com/go-yaml/yaml#compatibility).
## Caveats
**Caveat #1:** When using `yaml.Marshal` and `yaml.Unmarshal`, binary data should NOT be preceded with the `!!binary` YAML tag. If you do, go-yaml will convert the binary data from base64 to native binary data, which is not compatible with JSON. You can still use binary in your YAML files though - just store them without the `!!binary` tag and decode the base64 in your code (e.g. in the custom JSON methods `MarshalJSON` and `UnmarshalJSON`). This also has the benefit that your YAML and your JSON binary data will be decoded exactly the same way. As an example:
```
BAD:
exampleKey: !!binary gIGC
GOOD:
exampleKey: gIGC
... and decode the base64 data in your code.
```
**Caveat #2:** When using `YAMLToJSON` directly, maps with keys that are maps will result in an error since this is not supported by JSON. This error will occur in `Unmarshal` as well since you can't unmarshal map keys anyways since struct fields can't be keys.
## Installation and usage
To install, run:
```
$ go get github.com/ghodss/yaml
```
And import using:
```
import "github.com/ghodss/yaml"
```
Usage is very similar to the JSON library:
```go
import (
"fmt"
"github.com/ghodss/yaml"
)
type Person struct {
Name string `json:"name"` // Affects YAML field names too.
Age int `json:"name"`
}
func main() {
// Marshal a Person struct to YAML.
p := Person{"John", 30}
y, err := yaml.Marshal(p)
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
fmt.Println(string(y))
/* Output:
name: John
age: 30
*/
// Unmarshal the YAML back into a Person struct.
var p2 Person
err := yaml.Unmarshal(y, &p2)
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
fmt.Println(p2)
/* Output:
{John 30}
*/
}
```
`yaml.YAMLToJSON` and `yaml.JSONToYAML` methods are also available:
```go
import (
"fmt"
"github.com/ghodss/yaml"
)
func main() {
j := []byte(`{"name": "John", "age": 30}`)
y, err := yaml.JSONToYAML(j)
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
fmt.Println(string(y))
/* Output:
name: John
age: 30
*/
j2, err := yaml.YAMLToJSON(y)
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
fmt.Println(string(j2))
/* Output:
{"age":30,"name":"John"}
*/
}
```
package yaml
import (
"encoding/json"
"fmt"
"reflect"
"strconv"
"gopkg.in/v2/yaml"
)
// Marshals the object into JSON then converts JSON to YAML and returns the
// YAML.
func Marshal(o interface{}) ([]byte, error) {
j, err := json.Marshal(o)
if err != nil {
return nil, fmt.Errorf("error marshaling into JSON: ", err)
}
y, err := JSONToYAML(j)
if err != nil {
return nil, fmt.Errorf("error converting JSON to YAML: ", err)
}
return y, nil
}
// Converts YAML to JSON then uses JSON to unmarshal into an object.
func Unmarshal(y []byte, o interface{}) error {
j, err := YAMLToJSON(y)
if err != nil {
return fmt.Errorf("error converting YAML to JSON: %v", err)
}
err = json.Unmarshal(j, o)
if err != nil {
return fmt.Errorf("error unmarshaling JSON: %v", err)
}
return nil
}
// Convert JSON to YAML.
func JSONToYAML(j []byte) ([]byte, error) {
// Convert the JSON to an object.
var jsonObj interface{}
err := json.Unmarshal(j, &jsonObj)
if err != nil {
return nil, err
}
// Marshal this object into YAML.
return yaml.Marshal(jsonObj)
}
// Convert YAML to JSON. Since JSON is a subset of YAML, passing JSON through
// this method should be a no-op.
//
// Things YAML can do that are not supported by JSON:
// * In YAML you can have binary and null keys in your maps. These are invalid
// in JSON. (int and float keys are converted to strings.)
// * Binary data in YAML with the !!binary tag is not supported. If you want to
// use binary data with this library, encode the data as base64 as usual but do
// not use the !!binary tag in your YAML. This will ensure the original base64
// encoded data makes it all the way through to the JSON.
func YAMLToJSON(y []byte) ([]byte, error) {
// Convert the YAML to an object.
var yamlObj interface{}
err := yaml.Unmarshal(y, &yamlObj)
if err != nil {
return nil, err
}
// YAML objects are not completely compatible with JSON objects (e.g. you
// can have non-string keys in YAML). So, convert the YAML-compatible object
// to a JSON-compatible object, failing with an error if irrecoverable
// incompatibilties happen along the way.
jsonObj, err := convertToJSONableObject(yamlObj)
if err != nil {
return nil, err
}
// Convert this object to JSON and return the data.
return json.Marshal(jsonObj)
}
func convertToJSONableObject(yamlObj interface{}) (interface{}, error) {
var err error
switch typedYAMLObj := yamlObj.(type) {
case map[interface{}]interface{}:
// JSON does not support arbitrary keys in a map, so we must convert
// these keys to strings.
//
// From my reading of go-yaml v2 (specifically the resolve function),
// keys can only have the types string, int, int64, float64, binary
// (unsupported), or null (unsupported).
strMap := make(map[string]interface{})
for k, v := range typedYAMLObj {
// Resolve the key to a string first.
var keyString string
switch typedKey := k.(type) {
case string:
keyString = typedKey
case int:
keyString = strconv.Itoa(typedKey)
case int64:
// go-yaml will only return an int64 as a key if the system
// architecture is 32-bit and the key's value is between 32-bit
// and 64-bit. Otherwise the key type will simply be int.
keyString = strconv.FormatInt(typedKey, 10)
case float64:
// Stolen from go-yaml to use the same conversion to string as
// the go-yaml library uses to convert float to string when
// Marshaling.
s := strconv.FormatFloat(typedKey, 'g', -1, 32)
switch s {
case "+Inf":
s = ".inf"
case "-Inf":
s = "-.inf"
case "NaN":
s = ".nan"
}
keyString = s
default:
return nil, fmt.Errorf("Unsupported map key of type: %s, key: %+#v, value: %+#v",
reflect.TypeOf(k), k, v)
}
strMap[keyString], err = convertToJSONableObject(v)
if err != nil {
return nil, err
}
}
return strMap, nil
case []interface{}:
// We need to recurse into arrays in case there are any
// map[interface{}]interface{}'s inside.
arr := make([]interface{}, len(typedYAMLObj))
for i, v := range typedYAMLObj {
arr[i], err = convertToJSONableObject(v)
if err != nil {
return nil, err
}
}
return arr, nil
default:
return yamlObj, nil
}
return nil, nil
}
package yaml
import (
"fmt"
"reflect"
"testing"
)
type MarshalTest struct {
A int
}
func TestMarshalYAML(t *testing.T) {
s := MarshalTest{1}
e := []byte("A: 1\n")
y, err := Marshal(s)
if err != nil {
t.Errorf("error marshaling YAML: %v", err)
}
if !reflect.DeepEqual(y, e) {
t.Errorf("marshal YAML was unsuccessful, expected: %#v, got: %#v",
string(y), string(e))
}
}
func TestUnmarshal(t *testing.T) {
y := []byte(`a: 1`)
s := MarshalTest{}
e := MarshalTest{1}
err := Unmarshal(y, &s)
if err != nil {
t.Errorf("error unmarshaling YAML: %v", err)
}
if !reflect.DeepEqual(s, e) {
t.Errorf("unmarshal YAML was unsuccessful, expected: %+#v, got: %+#v",
e, s)
}
}
type Case struct {
input string
output string
// By default we test that reversing the output == input. But if there is a
// difference in the reversed output, you can optionally specify it here.
reverse *string
}
type RunType int
const (
RunTypeJSONToYAML RunType = iota
RunTypeYAMLToJSON
)
func TestJSONToYAML(t *testing.T) {
cases := []Case{
{
`{"t":"a"}`,
"t: a\n",
nil,
}, {
`{"t":null}`,
"t: null\n",
nil,
},
}
runCases(t, RunTypeJSONToYAML, cases)
}
func TestYAMLToJSON(t *testing.T) {
cases := []Case{
{
"t: a\n",
`{"t":"a"}`,
nil,
}, {
"t: \n",
`{"t":null}`,
strPtr("t: null\n"),
}, {
"t: null\n",
`{"t":null}`,
nil,
}, {
"1: a\n",
`{"1":"a"}`,
strPtr("\"1\": a\n"),
}, {
"1000000000000000000000000000000000000: a\n",
`{"1e+36":"a"}`,
strPtr("\"1e+36\": a\n"),
}, {
"1e+36: a\n",
`{"1e+36":"a"}`,
strPtr("\"1e+36\": a\n"),
}, {
"\"1e+36\": a\n",
`{"1e+36":"a"}`,
nil,
}, {
"\"1.2\": a\n",
`{"1.2":"a"}`,
nil,
}, {
"- t: a\n",
`[{"t":"a"}]`,
nil,
}, {
"- t: a\n" +
"- t:\n" +
" b: 1\n" +
" c: 2\n",
`[{"t":"a"},{"t":{"b":1,"c":2}}]`,
nil,
}, {
`[{t: a}, {t: {b: 1, c: 2}}]`,
`[{"t":"a"},{"t":{"b":1,"c":2}}]`,
strPtr("- t: a\n" +
"- t:\n" +
" b: 1\n" +
" c: 2\n"),
}, {
"- t: \n",
`[{"t":null}]`,
strPtr("- t: null\n"),
}, {
"- t: null\n",
`[{"t":null}]`,
nil,
},
}
// Cases that should produce errors.
_ = []Case{
{
"~: a",
`{"null":"a"}`,
nil,
}, {
"a: !!binary gIGC\n",
"{\"a\":\"\x80\x81\x82\"}",
nil,
},
}
runCases(t, RunTypeYAMLToJSON, cases)
}
func runCases(t *testing.T, runType RunType, cases []Case) {
var f func([]byte) ([]byte, error)
var invF func([]byte) ([]byte, error)
var msg string
var invMsg string
if runType == RunTypeJSONToYAML {
f = JSONToYAML
invF = YAMLToJSON
msg = "JSON to YAML"
invMsg = "YAML back to JSON"
} else {
f = YAMLToJSON
invF = JSONToYAML
msg = "YAML to JSON"
invMsg = "JSON back to YAML"
}
for _, c := range cases {
// Convert the string.
fmt.Printf("converting %s\n", c.input)
output, err := f([]byte(c.input))
if err != nil {
t.Errorf("Failed to convert %s, input: `%s`, err: %v", msg, c.input, err)
}
// Check it against the expected output.
if string(output) != c.output {
t.Errorf("Failed to convert %s, input: `%s`, expected `%s`, got `%s`",
msg, c.input, c.output, string(output))
}
// Set the string that we will compare the reversed output to.
reverse := c.input
// If a special reverse string was specified, use that instead.
if c.reverse != nil {
reverse = *c.reverse
}
// Reverse the output.
input, err := invF(output)
if err != nil {
t.Errorf("Failed to convert %s, input: `%s`, err: %v", invMsg, string(output), err)
}
// Check the reverse is equal to the input (or to *c.reverse).
if string(input) != reverse {
t.Errorf("Failed to convert %s, input: `%s`, expected `%s`, got `%s`",
invMsg, string(output), reverse, string(input))
}
}
}
// To be able to easily fill in the *Case.reverse string above.
func strPtr(s string) *string {
return &s
}
Copyright (c) 2011-2014 - Canonical Inc.
This software is licensed under the LGPLv3, included below. This software is licensed under the LGPLv3, included below.
As a special exception to the GNU Lesser General Public License version 3 As a special exception to the GNU Lesser General Public License version 3
......
...@@ -12,31 +12,31 @@ C library to parse and generate YAML data quickly and reliably. ...@@ -12,31 +12,31 @@ C library to parse and generate YAML data quickly and reliably.
Compatibility Compatibility
------------- -------------
The yaml package is almost compatible with YAML 1.1, including support for The yaml package supports most of YAML 1.1 and 1.2, including support for
anchors, tags, etc. There are still a few missing bits, such as document anchors, tags, map merging, etc. Multi-document unmarshalling is not yet
merging, base-60 floats (huh?), and multi-document unmarshalling. These implemented, and base-60 floats from YAML 1.1 are purposefully not
features are not hard to add, and will be introduced as necessary. supported since they're a poor design and are gone in YAML 1.2.
Installation and usage Installation and usage
---------------------- ----------------------
The import path for the package is *gopkg.in/yaml.v1*. The import path for the package is *gopkg.in/yaml.v2*.
To install it, run: To install it, run:
go get gopkg.in/yaml.v1 go get gopkg.in/yaml.v2
API documentation API documentation
----------------- -----------------
If opened in a browser, the import path itself leads to the API documentation: If opened in a browser, the import path itself leads to the API documentation:
* [https://gopkg.in/yaml.v1](https://gopkg.in/yaml.v1) * [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2)
API stability API stability
------------- -------------
The package API for yaml v1 will remain stable as described in [gopkg.in](https://gopkg.in). The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in).
License License
...@@ -55,7 +55,7 @@ import ( ...@@ -55,7 +55,7 @@ import (
"fmt" "fmt"
"log" "log"
"gopkg.in/yaml.v1" "gopkg.in/yaml.v2"
) )
var data = ` var data = `
......
...@@ -973,8 +973,8 @@ func yaml_emitter_analyze_tag(emitter *yaml_emitter_t, tag []byte) bool { ...@@ -973,8 +973,8 @@ func yaml_emitter_analyze_tag(emitter *yaml_emitter_t, tag []byte) bool {
if bytes.HasPrefix(tag, tag_directive.prefix) { if bytes.HasPrefix(tag, tag_directive.prefix) {
emitter.tag_data.handle = tag_directive.handle emitter.tag_data.handle = tag_directive.handle
emitter.tag_data.suffix = tag[len(tag_directive.prefix):] emitter.tag_data.suffix = tag[len(tag_directive.prefix):]
return true
} }
return true
} }
emitter.tag_data.suffix = tag emitter.tag_data.suffix = tag
return true return true
...@@ -1279,6 +1279,9 @@ func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_ ...@@ -1279,6 +1279,9 @@ func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_
for k := 0; k < w; k++ { for k := 0; k < w; k++ {
octet := value[i] octet := value[i]
i++ i++
if !put(emitter, '%') {
return false
}
c := octet >> 4 c := octet >> 4
if c < 10 { if c < 10 {
......
package yaml package yaml
import ( import (
"encoding"
"reflect" "reflect"
"regexp"
"sort" "sort"
"strconv" "strconv"
"strings"
"time" "time"
) )
...@@ -18,6 +21,7 @@ func newEncoder() (e *encoder) { ...@@ -18,6 +21,7 @@ func newEncoder() (e *encoder) {
e = &encoder{} e = &encoder{}
e.must(yaml_emitter_initialize(&e.emitter)) e.must(yaml_emitter_initialize(&e.emitter))
yaml_emitter_set_output_string(&e.emitter, &e.out) yaml_emitter_set_output_string(&e.emitter, &e.out)
yaml_emitter_set_unicode(&e.emitter, true)
e.must(yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)) e.must(yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING))
e.emit() e.emit()
e.must(yaml_document_start_event_initialize(&e.event, nil, nil, true)) e.must(yaml_document_start_event_initialize(&e.event, nil, nil, true))
...@@ -48,24 +52,45 @@ func (e *encoder) must(ok bool) { ...@@ -48,24 +52,45 @@ func (e *encoder) must(ok bool) {
if !ok { if !ok {
msg := e.emitter.problem msg := e.emitter.problem
if msg == "" { if msg == "" {
msg = "Unknown problem generating YAML content" msg = "unknown problem generating YAML content"
} }
panic(msg) failf("%s", msg)
} }
} }
func (e *encoder) marshal(tag string, in reflect.Value) { func (e *encoder) marshal(tag string, in reflect.Value) {
var value interface{} if !in.IsValid() {
if getter, ok := in.Interface().(Getter); ok { e.nilv()
tag, value = getter.GetYAML() return
if value == nil { }
//fmt.Printf("marshal 1 in: %s\n", in)
iface := in.Interface()
//fmt.Printf("marshal 1 iface: %s\n", iface)
if m, ok := iface.(Marshaler); ok {
//fmt.Printf("marshal 1 calling MarshalYAML\n")
v, err := m.MarshalYAML()
if err != nil {
fail(err)
}
if v == nil {
e.nilv() e.nilv()
return return
} }
in = reflect.ValueOf(value) in = reflect.ValueOf(v)
} else if m, ok := iface.(encoding.TextMarshaler); ok {
text, err := m.MarshalText()
if err != nil {
fail(err)
}
in = reflect.ValueOf(string(text))
} }
//fmt.Printf("marshal 2 in: %s\n", in)
//fmt.Printf("marshal 2 in string: %s\n", in.String())
//fmt.Printf("marshal 2 in Kind: %s\n", in.Kind())
//fmt.Printf("marshal 2.5 in Kind: %s\n", in.Kind())
switch in.Kind() { switch in.Kind() {
case reflect.Interface: case reflect.Interface:
//fmt.Printf("marshal 3\n")
if in.IsNil() { if in.IsNil() {
e.nilv() e.nilv()
} else { } else {
...@@ -74,20 +99,27 @@ func (e *encoder) marshal(tag string, in reflect.Value) { ...@@ -74,20 +99,27 @@ func (e *encoder) marshal(tag string, in reflect.Value) {
case reflect.Map: case reflect.Map:
e.mapv(tag, in) e.mapv(tag, in)
case reflect.Ptr: case reflect.Ptr:
//fmt.Printf("marshal 4\n")
if in.IsNil() { if in.IsNil() {
e.nilv() e.nilv()
} else { } else {
e.marshal(tag, in.Elem()) e.marshal(tag, in.Elem())
} }
case reflect.Struct: case reflect.Struct:
//fmt.Printf("marshal 5\n")
e.structv(tag, in) e.structv(tag, in)
case reflect.Slice: case reflect.Slice:
e.slicev(tag, in) if in.Type().Elem() == mapItemType {
e.itemsv(tag, in)
} else {
e.slicev(tag, in)
}
case reflect.String: case reflect.String:
//fmt.Printf("marshal 6 in.String: \n", in.String())
e.stringv(tag, in) e.stringv(tag, in)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if in.Type() == durationType { if in.Type() == durationType {
e.stringv(tag, reflect.ValueOf(in.Interface().(time.Duration).String())) e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String()))
} else { } else {
e.intv(tag, in) e.intv(tag, in)
} }
...@@ -98,7 +130,7 @@ func (e *encoder) marshal(tag string, in reflect.Value) { ...@@ -98,7 +130,7 @@ func (e *encoder) marshal(tag string, in reflect.Value) {
case reflect.Bool: case reflect.Bool:
e.boolv(tag, in) e.boolv(tag, in)
default: default:
panic("Can't marshal type yet: " + in.Type().String()) panic("cannot marshal type: " + in.Type().String())
} }
} }
...@@ -113,6 +145,16 @@ func (e *encoder) mapv(tag string, in reflect.Value) { ...@@ -113,6 +145,16 @@ func (e *encoder) mapv(tag string, in reflect.Value) {
}) })
} }
func (e *encoder) itemsv(tag string, in reflect.Value) {
e.mappingv(tag, func() {
slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem)
for _, item := range slice {
e.marshal("", reflect.ValueOf(item.Key))
e.marshal("", reflect.ValueOf(item.Value))
}
})
}
func (e *encoder) structv(tag string, in reflect.Value) { func (e *encoder) structv(tag string, in reflect.Value) {
sinfo, err := getStructInfo(in.Type()) sinfo, err := getStructInfo(in.Type())
if err != nil { if err != nil {
...@@ -131,6 +173,7 @@ func (e *encoder) structv(tag string, in reflect.Value) { ...@@ -131,6 +173,7 @@ func (e *encoder) structv(tag string, in reflect.Value) {
} }
e.marshal("", reflect.ValueOf(info.Key)) e.marshal("", reflect.ValueOf(info.Key))
e.flow = info.Flow e.flow = info.Flow
//fmt.Printf("structv mappingv value: %s\n", value)
e.marshal("", value) e.marshal("", value)
} }
}) })
...@@ -167,14 +210,54 @@ func (e *encoder) slicev(tag string, in reflect.Value) { ...@@ -167,14 +210,54 @@ func (e *encoder) slicev(tag string, in reflect.Value) {
e.emit() e.emit()
} }
// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1.
//
// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported
// in YAML 1.2 and by this package, but these should be marshalled quoted for
// the time being for compatibility with other parsers.
func isBase60Float(s string) (result bool) {
// Fast path.
if s == "" {
return false
}
c := s[0]
if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 {
return false
}
// Do the full match.
return base60float.MatchString(s)
}
// From http://yaml.org/type/float.html, except the regular expression there
// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix.
var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`)
func (e *encoder) stringv(tag string, in reflect.Value) { func (e *encoder) stringv(tag string, in reflect.Value) {
//fmt.Printf("stringv 1\n")
var style yaml_scalar_style_t var style yaml_scalar_style_t
s := in.String() s := in.String()
if rtag, _ := resolve("", s); rtag != "!!str" { //fmt.Printf("stringv 1 s: %s\n", s)
rtag, rs := resolve("", s)
if rtag == yaml_BINARY_TAG {
//fmt.Printf("stringv 2\n")
if tag == "" || tag == yaml_STR_TAG {
tag = rtag
s = rs.(string)
//fmt.Printf("stringv 3 s: %s\n", s)
} else if tag == yaml_BINARY_TAG {
failf("explicitly tagged !!binary data must be base64-encoded")
} else {
failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
}
}
if tag == "" && (rtag != yaml_STR_TAG || isBase60Float(s)) {
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
} else if strings.Contains(s, "\n") {
style = yaml_LITERAL_SCALAR_STYLE
} else { } else {
style = yaml_PLAIN_SCALAR_STYLE style = yaml_PLAIN_SCALAR_STYLE
} }
//fmt.Printf("stringv 4 style: %s\n", style)
e.emitScalar(s, "", tag, style) e.emitScalar(s, "", tag, style)
} }
...@@ -218,9 +301,6 @@ func (e *encoder) nilv() { ...@@ -218,9 +301,6 @@ func (e *encoder) nilv() {
func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) { func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) {
implicit := tag == "" implicit := tag == ""
if !implicit {
style = yaml_PLAIN_SCALAR_STYLE
}
e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
e.emit() e.emit()
} }
...@@ -2,12 +2,14 @@ package yaml_test ...@@ -2,12 +2,14 @@ package yaml_test
import ( import (
"fmt" "fmt"
"gopkg.in/yaml.v1"
. "gopkg.in/check.v1"
"math" "math"
"strconv" "strconv"
"strings" "strings"
"time" "time"
. "gopkg.in/check.v1"
"gopkg.in/yaml.v2"
"net"
) )
var marshalIntTest = 123 var marshalIntTest = 123
...@@ -17,6 +19,9 @@ var marshalTests = []struct { ...@@ -17,6 +19,9 @@ var marshalTests = []struct {
data string data string
}{ }{
{ {
nil,
"null\n",
}, {
&struct{}{}, &struct{}{},
"{}\n", "{}\n",
}, { }, {
...@@ -87,7 +92,7 @@ var marshalTests = []struct { ...@@ -87,7 +92,7 @@ var marshalTests = []struct {
"v:\n- A\n- B\n", "v:\n- A\n- B\n",
}, { }, {
map[string][]string{"v": []string{"A", "B\nC"}}, map[string][]string{"v": []string{"A", "B\nC"}},
"v:\n- A\n- 'B\n\n C'\n", "v:\n- A\n- |-\n B\n C\n",
}, { }, {
map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}}, map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}},
"v:\n- A\n- 1\n- B:\n - 2\n - 3\n", "v:\n- A\n- 1\n- B:\n - 2\n - 3\n",
...@@ -219,6 +224,49 @@ var marshalTests = []struct { ...@@ -219,6 +224,49 @@ var marshalTests = []struct {
map[string]time.Duration{"a": 3 * time.Second}, map[string]time.Duration{"a": 3 * time.Second},
"a: 3s\n", "a: 3s\n",
}, },
// Issue #24: bug in map merging logic.
{
map[string]string{"a": "<foo>"},
"a: <foo>\n",
},
// Issue #34: marshal unsupported base 60 floats quoted for compatibility
// with old YAML 1.1 parsers.
{
map[string]string{"a": "1:1"},
"a: \"1:1\"\n",
},
// Binary data.
{
map[string]string{"a": "\x00"},
"a: \"\\0\"\n",
}, {
map[string]string{"a": "\x80\x81\x82"},
"a: !!binary gIGC\n",
}, {
map[string]string{"a": strings.Repeat("\x90", 54)},
"a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n",
},
// Ordered maps.
{
&yaml.MapSlice{{"b", 2}, {"a", 1}, {"d", 4}, {"c", 3}, {"sub", yaml.MapSlice{{"e", 5}}}},
"b: 2\na: 1\nd: 4\nc: 3\nsub:\n e: 5\n",
},
// Encode unicode as utf-8 rather than in escaped form.
{
map[string]string{"a": "你好"},
"a: 你好\n",
},
// Support encoding.TextMarshaler.
{
map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)},
"a: 1.2.3.4\n",
},
} }
func (s *S) TestMarshal(c *C) { func (s *S) TestMarshal(c *C) {
...@@ -232,45 +280,26 @@ func (s *S) TestMarshal(c *C) { ...@@ -232,45 +280,26 @@ func (s *S) TestMarshal(c *C) {
var marshalErrorTests = []struct { var marshalErrorTests = []struct {
value interface{} value interface{}
error string error string
}{ panic string
{ }{{
&struct { value: &struct {
B int B int
inlineB ",inline" inlineB ",inline"
}{1, inlineB{2, inlineC{3}}}, }{1, inlineB{2, inlineC{3}}},
`Duplicated key 'b' in struct struct \{ B int; .*`, panic: `Duplicated key 'b' in struct struct \{ B int; .*`,
}, }}
}
func (s *S) TestMarshalErrors(c *C) { func (s *S) TestMarshalErrors(c *C) {
for _, item := range marshalErrorTests { for _, item := range marshalErrorTests {
_, err := yaml.Marshal(item.value) if item.panic != "" {
c.Assert(err, ErrorMatches, item.error) c.Assert(func() { yaml.Marshal(item.value) }, PanicMatches, item.panic)
} else {
_, err := yaml.Marshal(item.value)
c.Assert(err, ErrorMatches, item.error)
}
} }
} }
var marshalTaggedIfaceTest interface{} = &struct{ A string }{"B"}
var getterTests = []struct {
data, tag string
value interface{}
}{
{"_:\n hi: there\n", "", map[interface{}]interface{}{"hi": "there"}},
{"_:\n- 1\n- A\n", "", []interface{}{1, "A"}},
{"_: 10\n", "", 10},
{"_: null\n", "", nil},
{"_: !foo BAR!\n", "!foo", "BAR!"},
{"_: !foo 1\n", "!foo", "1"},
{"_: !foo '\"1\"'\n", "!foo", "\"1\""},
{"_: !foo 1.1\n", "!foo", 1.1},
{"_: !foo 1\n", "!foo", 1},
{"_: !foo 1\n", "!foo", uint(1)},
{"_: !foo true\n", "!foo", true},
{"_: !foo\n- A\n- B\n", "!foo", []string{"A", "B"}},
{"_: !foo\n A: B\n", "!foo", map[string]string{"A": "B"}},
{"_: !foo\n a: B\n", "!foo", &marshalTaggedIfaceTest},
}
func (s *S) TestMarshalTypeCache(c *C) { func (s *S) TestMarshalTypeCache(c *C) {
var data []byte var data []byte
var err error var err error
...@@ -287,23 +316,32 @@ func (s *S) TestMarshalTypeCache(c *C) { ...@@ -287,23 +316,32 @@ func (s *S) TestMarshalTypeCache(c *C) {
c.Assert(string(data), Equals, "b: 0\n") c.Assert(string(data), Equals, "b: 0\n")
} }
type typeWithGetter struct { var marshalerTests = []struct {
tag string data string
value interface{}
}{
{"_:\n hi: there\n", map[interface{}]interface{}{"hi": "there"}},
{"_:\n- 1\n- A\n", []interface{}{1, "A"}},
{"_: 10\n", 10},
{"_: null\n", nil},
{"_: BAR!\n", "BAR!"},
}
type marshalerType struct {
value interface{} value interface{}
} }
func (o typeWithGetter) GetYAML() (tag string, value interface{}) { func (o marshalerType) MarshalYAML() (interface{}, error) {
return o.tag, o.value return o.value, nil
} }
type typeWithGetterField struct { type marshalerValue struct {
Field typeWithGetter "_" Field marshalerType "_"
} }
func (s *S) TestMashalWithGetter(c *C) { func (s *S) TestMarshaler(c *C) {
for _, item := range getterTests { for _, item := range marshalerTests {
obj := &typeWithGetterField{} obj := &marshalerValue{}
obj.Field.tag = item.tag
obj.Field.value = item.value obj.Field.value = item.value
data, err := yaml.Marshal(obj) data, err := yaml.Marshal(obj)
c.Assert(err, IsNil) c.Assert(err, IsNil)
...@@ -311,15 +349,25 @@ func (s *S) TestMashalWithGetter(c *C) { ...@@ -311,15 +349,25 @@ func (s *S) TestMashalWithGetter(c *C) {
} }
} }
func (s *S) TestUnmarshalWholeDocumentWithGetter(c *C) { func (s *S) TestMarshalerWholeDocument(c *C) {
obj := &typeWithGetter{} obj := &marshalerType{}
obj.tag = ""
obj.value = map[string]string{"hello": "world!"} obj.value = map[string]string{"hello": "world!"}
data, err := yaml.Marshal(obj) data, err := yaml.Marshal(obj)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(string(data), Equals, "hello: world!\n") c.Assert(string(data), Equals, "hello: world!\n")
} }
type failingMarshaler struct{}
func (ft *failingMarshaler) MarshalYAML() (interface{}, error) {
return nil, failingErr
}
func (s *S) TestMarshalerError(c *C) {
_, err := yaml.Marshal(&failingMarshaler{})
c.Assert(err, Equals, failingErr)
}
func (s *S) TestSortedOutput(c *C) { func (s *S) TestSortedOutput(c *C) {
order := []interface{}{ order := []interface{}{
false, false,
......
package yaml package yaml
import ( import (
"encoding/base64"
"math" "math"
"strconv" "strconv"
"strings" "strings"
"unicode/utf8"
) )
// TODO: merge, timestamps, base 60 floats, omap.
type resolveMapItem struct { type resolveMapItem struct {
value interface{} value interface{}
tag string tag string
...@@ -27,25 +27,24 @@ func init() { ...@@ -27,25 +27,24 @@ func init() {
t[int(c)] = 'M' // In map t[int(c)] = 'M' // In map
} }
t[int('.')] = '.' // Float (potentially in map) t[int('.')] = '.' // Float (potentially in map)
t[int('<')] = '<' // Merge
var resolveMapList = []struct { var resolveMapList = []struct {
v interface{} v interface{}
tag string tag string
l []string l []string
}{ }{
{true, "!!bool", []string{"y", "Y", "yes", "Yes", "YES"}}, {true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}},
{true, "!!bool", []string{"true", "True", "TRUE"}}, {true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}},
{true, "!!bool", []string{"on", "On", "ON"}}, {true, yaml_BOOL_TAG, []string{"on", "On", "ON"}},
{false, "!!bool", []string{"n", "N", "no", "No", "NO"}}, {false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}},
{false, "!!bool", []string{"false", "False", "FALSE"}}, {false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}},
{false, "!!bool", []string{"off", "Off", "OFF"}}, {false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}},
{nil, "!!null", []string{"~", "null", "Null", "NULL"}}, {nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}},
{math.NaN(), "!!float", []string{".nan", ".NaN", ".NAN"}}, {math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}},
{math.Inf(+1), "!!float", []string{".inf", ".Inf", ".INF"}}, {math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}},
{math.Inf(+1), "!!float", []string{"+.inf", "+.Inf", "+.INF"}}, {math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}},
{math.Inf(-1), "!!float", []string{"-.inf", "-.Inf", "-.INF"}}, {math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}},
{"<<", "!!merge", []string{"<<"}}, {"<<", yaml_MERGE_TAG, []string{"<<"}},
} }
m := resolveMap m := resolveMap
...@@ -59,90 +58,131 @@ func init() { ...@@ -59,90 +58,131 @@ func init() {
const longTagPrefix = "tag:yaml.org,2002:" const longTagPrefix = "tag:yaml.org,2002:"
func shortTag(tag string) string { func shortTag(tag string) string {
// TODO This can easily be made faster and produce less garbage.
if strings.HasPrefix(tag, longTagPrefix) { if strings.HasPrefix(tag, longTagPrefix) {
return "!!" + tag[len(longTagPrefix):] return "!!" + tag[len(longTagPrefix):]
} }
return tag return tag
} }
func longTag(tag string) string {
if strings.HasPrefix(tag, "!!") {
return longTagPrefix + tag[2:]
}
return tag
}
func resolvableTag(tag string) bool { func resolvableTag(tag string) bool {
switch tag { switch tag {
case "", "!!str", "!!bool", "!!int", "!!float", "!!null": case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG:
return true return true
} }
return false return false
} }
func resolve(tag string, in string) (rtag string, out interface{}) { func resolve(tag string, in string) (rtag string, out interface{}) {
tag = shortTag(tag) //fmt.Printf("yv2 resolve tag: %s, in: %s\n", tag, in)
if !resolvableTag(tag) { if !resolvableTag(tag) {
return tag, in return tag, in
} }
defer func() { defer func() {
if tag != "" && tag != rtag { switch tag {
panic("Can't decode " + rtag + " '" + in + "' as a " + tag) case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG:
return
} }
failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag))
}() }()
if in == "" { // Any data is accepted as a !!str or !!binary.
return "!!null", nil // Otherwise, the prefix is enough of a hint about what it might be.
} hint := byte('N')
if in != "" {
c := resolveTable[in[0]] hint = resolveTable[in[0]]
if c == 0 {
// It's a string for sure. Nothing to do.
return "!!str", in
} }
if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG {
// Handle things we can lookup in a map.
if item, ok := resolveMap[in]; ok {
return item.tag, item.value
}
// Handle things we can lookup in a map. // Base 60 floats are a bad idea, were dropped in YAML 1.2, and
if item, ok := resolveMap[in]; ok { // are purposefully unsupported here. They're still quoted on
return item.tag, item.value // the way out for compatibility with other parser, though.
}
switch c { switch hint {
case 'M': case 'M':
// We've already checked the map above. // We've already checked the map above.
case '.': case '.':
// Not in the map, so maybe a normal float. // Not in the map, so maybe a normal float.
floatv, err := strconv.ParseFloat(in, 64) floatv, err := strconv.ParseFloat(in, 64)
if err == nil { if err == nil {
return "!!float", floatv return yaml_FLOAT_TAG, floatv
}
// XXX Handle base 60 floats here (WTF!)
case 'D', 'S':
// Int, float, or timestamp.
plain := strings.Replace(in, "_", "", -1)
intv, err := strconv.ParseInt(plain, 0, 64)
if err == nil {
if intv == int64(int(intv)) {
return "!!int", int(intv)
} else {
return "!!int", intv
} }
}
floatv, err := strconv.ParseFloat(plain, 64) case 'D', 'S':
if err == nil { // Int, float, or timestamp.
return "!!float", floatv plain := strings.Replace(in, "_", "", -1)
} intv, err := strconv.ParseInt(plain, 0, 64)
if strings.HasPrefix(plain, "0b") {
intv, err := strconv.ParseInt(plain[2:], 2, 64)
if err == nil { if err == nil {
return "!!int", int(intv) if intv == int64(int(intv)) {
return yaml_INT_TAG, int(intv)
} else {
return yaml_INT_TAG, intv
}
} }
} else if strings.HasPrefix(plain, "-0b") { floatv, err := strconv.ParseFloat(plain, 64)
intv, err := strconv.ParseInt(plain[3:], 2, 64)
if err == nil { if err == nil {
return "!!int", -int(intv) return yaml_FLOAT_TAG, floatv
} }
if strings.HasPrefix(plain, "0b") {
intv, err := strconv.ParseInt(plain[2:], 2, 64)
if err == nil {
return yaml_INT_TAG, int(intv)
}
} else if strings.HasPrefix(plain, "-0b") {
intv, err := strconv.ParseInt(plain[3:], 2, 64)
if err == nil {
return yaml_INT_TAG, -int(intv)
}
}
// XXX Handle timestamps here.
default:
panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")")
} }
// XXX Handle timestamps here. }
if tag == yaml_BINARY_TAG {
return yaml_BINARY_TAG, in
}
if utf8.ValidString(in) {
return yaml_STR_TAG, in
}
return yaml_BINARY_TAG, encodeBase64(in)
}
default: // encodeBase64 encodes s as base64 that is broken up into multiple lines
panic("resolveTable item not yet handled: " + // as appropriate for the resulting length.
string([]byte{c}) + " (with " + in + ")") func encodeBase64(s string) string {
const lineLen = 70
encLen := base64.StdEncoding.EncodedLen(len(s))
lines := encLen/lineLen + 1
buf := make([]byte, encLen*2+lines)
in := buf[0:encLen]
out := buf[encLen:]
base64.StdEncoding.Encode(in, []byte(s))
k := 0
for i := 0; i < len(in); i += lineLen {
j := i + lineLen
if j > len(in) {
j = len(in)
}
k += copy(out[k:], in[i:j])
if lines > 1 {
out[k] = '\n'
k++
}
} }
return "!!str", in return string(out[:k])
} }
...@@ -10,59 +10,52 @@ import ( ...@@ -10,59 +10,52 @@ import (
"errors" "errors"
"fmt" "fmt"
"reflect" "reflect"
"runtime"
"strings" "strings"
"sync" "sync"
) )
func handleErr(err *error) { // MapSlice encodes and decodes as a YAML map.
if r := recover(); r != nil { // The order of keys is preserved when encoding and decoding.
if _, ok := r.(runtime.Error); ok { type MapSlice []MapItem
panic(r)
} else if _, ok := r.(*reflect.ValueError); ok { // MapItem is an item in a MapSlice.
panic(r) type MapItem struct {
} else if _, ok := r.(externalPanic); ok { Key, Value interface{}
panic(r)
} else if s, ok := r.(string); ok {
*err = errors.New("YAML error: " + s)
} else if e, ok := r.(error); ok {
*err = e
} else {
panic(r)
}
}
} }
// The Setter interface may be implemented by types to do their own custom // The Unmarshaler interface may be implemented by types to customize their
// unmarshalling of YAML values, rather than being implicitly assigned by // behavior when being unmarshaled from a YAML document. The UnmarshalYAML
// the yaml package machinery. If setting the value works, the method should // method receives a function that may be called to unmarshal the original
// return true. If it returns false, the value is considered unsupported // YAML value into a field or variable. It is safe to call the unmarshal
// and is omitted from maps and slices. // function parameter more than once if necessary.
type Setter interface { type Unmarshaler interface {
SetYAML(tag string, value interface{}) bool UnmarshalYAML(unmarshal func(interface{}) error) error
} }
// The Getter interface is implemented by types to do their own custom
// marshalling into a YAML tag and value. // The Marshaler interface may be implemented by types to customize their
type Getter interface { // behavior when being marshaled into a YAML document. The returned value
GetYAML() (tag string, value interface{}) // is marshaled in place of the original value implementing Marshaler.
//
// If an error is returned by MarshalYAML, the marshaling procedure stops
// and returns with the provided error.
type Marshaler interface {
MarshalYAML() (interface{}, error)
} }
// Unmarshal decodes the first document found within the in byte slice // Unmarshal decodes the first document found within the in byte slice
// and assigns decoded values into the out value. // and assigns decoded values into the out value.
// //
// Maps and pointers (to a struct, string, int, etc) are accepted as out // Maps and pointers (to a struct, string, int, etc) are accepted as out
// values. If an internal pointer within a struct is not initialized, // values. If an internal pointer within a struct is not initialized,
// the yaml package will initialize it if necessary for unmarshalling // the yaml package will initialize it if necessary for unmarshalling
// the provided data. The out parameter must not be nil. // the provided data. The out parameter must not be nil.
// //
// The type of the decoded values and the type of out will be considered, // The type of the decoded values should be compatible with the respective
// and Unmarshal will do the best possible job to unmarshal values // values in out. If one or more values cannot be decoded due to a type
// appropriately. It is NOT considered an error, though, to skip values // mismatches, decoding continues partially until the end of the YAML
// because they are not available in the decoded YAML, or if they are not // content, and a *yaml.TypeError is returned with details for all
// compatible with the out value. To ensure something was properly // missed values.
// unmarshaled use a map or compare against the previous value for the
// field (usually the zero value).
// //
// Struct fields are only unmarshalled if they are exported (have an // Struct fields are only unmarshalled if they are exported (have an
// upper case first letter), and are unmarshalled using the field name // upper case first letter), and are unmarshalled using the field name
...@@ -78,7 +71,7 @@ type Getter interface { ...@@ -78,7 +71,7 @@ type Getter interface {
// F int `yaml:"a,omitempty"` // F int `yaml:"a,omitempty"`
// B int // B int
// } // }
// var T t // var t T
// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) // yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
// //
// See the documentation of Marshal for the format of tags and a list of // See the documentation of Marshal for the format of tags and a list of
...@@ -91,7 +84,14 @@ func Unmarshal(in []byte, out interface{}) (err error) { ...@@ -91,7 +84,14 @@ func Unmarshal(in []byte, out interface{}) (err error) {
defer p.destroy() defer p.destroy()
node := p.parse() node := p.parse()
if node != nil { if node != nil {
d.unmarshal(node, reflect.ValueOf(out)) v := reflect.ValueOf(out)
if v.Kind() == reflect.Ptr && !v.IsNil() {
v = v.Elem()
}
d.unmarshal(node, v)
}
if d.terrors != nil {
return &TypeError{d.terrors}
} }
return nil return nil
} }
...@@ -145,6 +145,40 @@ func Marshal(in interface{}) (out []byte, err error) { ...@@ -145,6 +145,40 @@ func Marshal(in interface{}) (out []byte, err error) {
return return
} }
func handleErr(err *error) {
if v := recover(); v != nil {
if e, ok := v.(yamlError); ok {
*err = e.err
} else {
panic(v)
}
}
}
type yamlError struct {
err error
}
func fail(err error) {
panic(yamlError{err})
}
func failf(format string, args ...interface{}) {
panic(yamlError{fmt.Errorf("yaml: " + format, args...)})
}
// A TypeError is returned by Unmarshal when one or more fields in
// the YAML document cannot be properly decoded into the requested
// types. When this error is returned, the value is still
// unmarshaled partially.
type TypeError struct {
Errors []string
}
func (e *TypeError) Error() string {
return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n "))
}
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// Maintain a mapping of keys to structure field indexes // Maintain a mapping of keys to structure field indexes
...@@ -174,12 +208,6 @@ type fieldInfo struct { ...@@ -174,12 +208,6 @@ type fieldInfo struct {
var structMap = make(map[reflect.Type]*structInfo) var structMap = make(map[reflect.Type]*structInfo)
var fieldMapMutex sync.RWMutex var fieldMapMutex sync.RWMutex
type externalPanic string
func (e externalPanic) String() string {
return string(e)
}
func getStructInfo(st reflect.Type) (*structInfo, error) { func getStructInfo(st reflect.Type) (*structInfo, error) {
fieldMapMutex.RLock() fieldMapMutex.RLock()
sinfo, found := structMap[st] sinfo, found := structMap[st]
...@@ -220,8 +248,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) { ...@@ -220,8 +248,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
case "inline": case "inline":
inline = true inline = true
default: default:
msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st) return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st))
panic(externalPanic(msg))
} }
} }
tag = fields[0] tag = fields[0]
...@@ -229,6 +256,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) { ...@@ -229,6 +256,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
if inline { if inline {
switch field.Type.Kind() { switch field.Type.Kind() {
// TODO: Implement support for inline maps.
//case reflect.Map: //case reflect.Map:
// if inlineMap >= 0 { // if inlineMap >= 0 {
// return nil, errors.New("Multiple ,inline maps in struct " + st.String()) // return nil, errors.New("Multiple ,inline maps in struct " + st.String())
...@@ -256,8 +284,8 @@ func getStructInfo(st reflect.Type) (*structInfo, error) { ...@@ -256,8 +284,8 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
fieldsList = append(fieldsList, finfo) fieldsList = append(fieldsList, finfo)
} }
default: default:
//panic("Option ,inline needs a struct value or map field") //return nil, errors.New("Option ,inline needs a struct value or map field")
panic("Option ,inline needs a struct value field") return nil, errors.New("Option ,inline needs a struct value field")
} }
continue continue
} }
......
...@@ -294,6 +294,10 @@ const ( ...@@ -294,6 +294,10 @@ const (
yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences. yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences.
yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping. yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping.
// Not in original libyaml.
yaml_BINARY_TAG = "tag:yaml.org,2002:binary"
yaml_MERGE_TAG = "tag:yaml.org,2002:merge"
yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str. yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str.
yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq. yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq.
yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map. yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map.
......
...@@ -16,8 +16,8 @@ $ enscope specFilename configFilename ...@@ -16,8 +16,8 @@ $ enscope specFilename configFilename
## Scope schema ## Scope schema
``` ```
type EnscopeSpec struct { type EnscopeSpec struct {
NameSuffix string `json:"nameSuffix,omitempty" yaml:"nameSuffix,omitempty"` NameSuffix string `json:"nameSuffix,omitempty"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` Labels map[string]string `json:"labels,omitempty"`
} }
``` ```
...@@ -211,4 +211,4 @@ Output: ...@@ -211,4 +211,4 @@ Output:
name: bar-solo-coolapp-prod name: bar-solo-coolapp-prod
status: status:
replicas: 0 replicas: 0
``` ```
\ No newline at end of file
...@@ -23,8 +23,8 @@ import ( ...@@ -23,8 +23,8 @@ import (
"os" "os"
"strings" "strings"
"github.com/ghodss/yaml"
"github.com/golang/glog" "github.com/golang/glog"
"gopkg.in/v1/yaml"
) )
const usage = "usage: enscope specFilename configFilename" const usage = "usage: enscope specFilename configFilename"
...@@ -38,8 +38,8 @@ func checkErr(err error) { ...@@ -38,8 +38,8 @@ func checkErr(err error) {
// TODO: If name suffix is not specified, deterministically generate it by hashing the labels. // TODO: If name suffix is not specified, deterministically generate it by hashing the labels.
type EnscopeSpec struct { type EnscopeSpec struct {
NameSuffix string `json:"nameSuffix,omitempty" yaml:"nameSuffix,omitempty"` NameSuffix string `json:"nameSuffix,omitempty"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` Labels map[string]string `json:"labels,omitempty"`
} }
func main() { func main() {
......
...@@ -37,7 +37,7 @@ import ( ...@@ -37,7 +37,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
dockerclient "github.com/fsouza/go-dockerclient" dockerclient "github.com/fsouza/go-dockerclient"
"gopkg.in/v1/yaml" "github.com/ghodss/yaml"
) )
const usage = "usage: podex [-json|-yaml] [-id PODNAME] IMAGES" const usage = "usage: podex [-json|-yaml] [-id PODNAME] IMAGES"
......
...@@ -17,13 +17,13 @@ $ simplegen http://some.blog.site.com/k8s-example.yaml ...@@ -17,13 +17,13 @@ $ simplegen http://some.blog.site.com/k8s-example.yaml
### Schema ### Schema
``` ```
// Optional: Defaults to image base name if not specified // Optional: Defaults to image base name if not specified
Name string `yaml:"name,omitempty" json:"name,omitempty"` Name string `json:"name,omitempty"`
// Required. // Required.
Image string `yaml:"image" json:"image"` Image string `json:"image"`
// Optional: Defaults to one // Optional: Defaults to one
Replicas int `yaml:"replicas,omitempty" json:"replicas,omitempty"` Replicas int `json:"replicas,omitempty"`
// Optional: Creates a service if specified: servicePort:containerPort // Optional: Creates a service if specified: servicePort:containerPort
PortSpec string `yaml:"portSpec,omitempty" json:"portSpec,omitempty"` PortSpec string `json:"portSpec,omitempty"`
``` ```
### Example ### Example
......
...@@ -35,8 +35,8 @@ import ( ...@@ -35,8 +35,8 @@ import (
// TODO: handle multiple versions correctly // TODO: handle multiple versions correctly
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/ghodss/yaml"
"github.com/golang/glog" "github.com/golang/glog"
"gopkg.in/v1/yaml"
) )
// TODO: Also handle lists of simple services, and multiple input files // TODO: Also handle lists of simple services, and multiple input files
...@@ -45,13 +45,13 @@ const usage = "usage: simplegen filename" ...@@ -45,13 +45,13 @@ const usage = "usage: simplegen filename"
type SimpleService struct { type SimpleService struct {
// Optional: Defaults to image base name if not specified // Optional: Defaults to image base name if not specified
Name string `yaml:"name,omitempty" json:"name,omitempty"` Name string `json:"name,omitempty"`
// Required. // Required.
Image string `yaml:"image" json:"image"` Image string `json:"image"`
// Optional: Defaults to one // Optional: Defaults to one
Replicas int `yaml:"replicas,omitempty" json:"replicas,omitempty"` Replicas int `json:"replicas,omitempty"`
// Optional: Creates a service if specified: servicePort:containerPort // Optional: Creates a service if specified: servicePort:containerPort
PortSpec string `yaml:"portSpec,omitempty" json:"portSpec,omitempty"` PortSpec string `json:"portSpec,omitempty"`
} }
func checkErr(err error) { func checkErr(err error) {
......
...@@ -15,9 +15,9 @@ $ srvexpand myservice.yaml ...@@ -15,9 +15,9 @@ $ srvexpand myservice.yaml
``` ```
type HierarchicalController struct { type HierarchicalController struct {
// Optional: Defaults to one // Optional: Defaults to one
Replicas int `yaml:"replicas,omitempty" json:"replicas,omitempty"` Replicas int `json:"replicas,omitempty"`
// Spec defines the behavior of a pod. // Spec defines the behavior of a pod.
Spec v1beta3.PodSpec `json:"spec,omitempty" yaml:"spec,omitempty"` Spec v1beta3.PodSpec `json:"spec,omitempty"`
} }
type ControllerMap map[string]HierarchicalController type ControllerMap map[string]HierarchicalController
...@@ -25,9 +25,9 @@ type ControllerMap map[string]HierarchicalController ...@@ -25,9 +25,9 @@ type ControllerMap map[string]HierarchicalController
type HierarchicalService struct { type HierarchicalService struct {
// Optional: Creates a service if specified: servicePort:containerPort // Optional: Creates a service if specified: servicePort:containerPort
// TODO: Support multiple protocols // TODO: Support multiple protocols
PortSpec string `yaml:"portSpec,omitempty" json:"portSpec,omitempty"` PortSpec string `json:"portSpec,omitempty"`
// Map of replication controllers to create // Map of replication controllers to create
ControllerMap ControllerMap `json:"controllers,omitempty" yaml:"controllers,omitempty"` ControllerMap ControllerMap `json:"controllers,omitempty"`
} }
type ServiceMap map[string]HierarchicalService type ServiceMap map[string]HierarchicalService
...@@ -213,4 +213,4 @@ Output: ...@@ -213,4 +213,4 @@ Output:
status: status:
replicas: 0 replicas: 0
``` ```
\ No newline at end of file
...@@ -45,8 +45,8 @@ import ( ...@@ -45,8 +45,8 @@ import (
// it should be possible to specify the version for the whole map. // it should be possible to specify the version for the whole map.
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta3" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta3"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/ghodss/yaml"
"github.com/golang/glog" "github.com/golang/glog"
"gopkg.in/v1/yaml"
) )
const usage = "usage: srvexpand filename" const usage = "usage: srvexpand filename"
...@@ -62,9 +62,9 @@ const usage = "usage: srvexpand filename" ...@@ -62,9 +62,9 @@ const usage = "usage: srvexpand filename"
type HierarchicalController struct { type HierarchicalController struct {
// Optional: Defaults to one // Optional: Defaults to one
Replicas int `yaml:"replicas,omitempty" json:"replicas,omitempty"` Replicas int `json:"replicas,omitempty"`
// Spec defines the behavior of a pod. // Spec defines the behavior of a pod.
Spec v1beta3.PodSpec `json:"spec,omitempty" yaml:"spec,omitempty"` Spec v1beta3.PodSpec `json:"spec,omitempty"`
} }
type ControllerMap map[string]HierarchicalController type ControllerMap map[string]HierarchicalController
...@@ -72,9 +72,9 @@ type ControllerMap map[string]HierarchicalController ...@@ -72,9 +72,9 @@ type ControllerMap map[string]HierarchicalController
type HierarchicalService struct { type HierarchicalService struct {
// Optional: Creates a service if specified: servicePort:containerPort // Optional: Creates a service if specified: servicePort:containerPort
// TODO: Support multiple protocols // TODO: Support multiple protocols
PortSpec string `yaml:"portSpec,omitempty" json:"portSpec,omitempty"` PortSpec string `json:"portSpec,omitempty"`
// Map of replication controllers to create // Map of replication controllers to create
ControllerMap ControllerMap `json:"controllers,omitempty" yaml:"controllers,omitempty"` ControllerMap ControllerMap `json:"controllers,omitempty"`
} }
type ServiceMap map[string]HierarchicalService type ServiceMap map[string]HierarchicalService
......
...@@ -48,14 +48,14 @@ A *Namespace* is a DNS compatible subdomain. ...@@ -48,14 +48,14 @@ A *Namespace* is a DNS compatible subdomain.
``` ```
// TypeMeta is shared by all objects sent to, or returned from the client // TypeMeta is shared by all objects sent to, or returned from the client
type TypeMeta struct { type TypeMeta struct {
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` Kind string `json:"kind,omitempty"`
Uid string `json:"uid,omitempty" yaml:"uid,omitempty"` Uid string `json:"uid,omitempty"`
CreationTimestamp util.Time `json:"creationTimestamp,omitempty" yaml:"creationTimestamp,omitempty"` CreationTimestamp util.Time `json:"creationTimestamp,omitempty"`
SelfLink string `json:"selfLink,omitempty" yaml:"selfLink,omitempty"` SelfLink string `json:"selfLink,omitempty"`
ResourceVersion uint64 `json:"resourceVersion,omitempty" yaml:"resourceVersion,omitempty"` ResourceVersion uint64 `json:"resourceVersion,omitempty"`
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` APIVersion string `json:"apiVersion,omitempty"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` Namespace string `json:"namespace,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"` Name string `json:"name,omitempty"`
} }
``` ```
......
...@@ -26,19 +26,19 @@ import ( ...@@ -26,19 +26,19 @@ import (
func TestGenericTypeMeta(t *testing.T) { func TestGenericTypeMeta(t *testing.T) {
type TypeMeta struct { type TypeMeta struct {
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` Kind string `json:"kind,omitempty"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` Namespace string `json:"namespace,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"` Name string `json:"name,omitempty"`
UID string `json:"uid,omitempty" yaml:"uid,omitempty"` UID string `json:"uid,omitempty"`
CreationTimestamp util.Time `json:"creationTimestamp,omitempty" yaml:"creationTimestamp,omitempty"` CreationTimestamp util.Time `json:"creationTimestamp,omitempty"`
SelfLink string `json:"selfLink,omitempty" yaml:"selfLink,omitempty"` SelfLink string `json:"selfLink,omitempty"`
ResourceVersion string `json:"resourceVersion,omitempty" yaml:"resourceVersion,omitempty"` ResourceVersion string `json:"resourceVersion,omitempty"`
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` APIVersion string `json:"apiVersion,omitempty"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` Labels map[string]string `json:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"` Annotations map[string]string `json:"annotations,omitempty"`
} }
type Object struct { type Object struct {
TypeMeta `json:",inline" yaml:",inline"` TypeMeta `json:",inline"`
} }
j := Object{ j := Object{
TypeMeta{ TypeMeta{
...@@ -112,19 +112,19 @@ func TestGenericTypeMeta(t *testing.T) { ...@@ -112,19 +112,19 @@ func TestGenericTypeMeta(t *testing.T) {
} }
type InternalTypeMeta struct { type InternalTypeMeta struct {
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` Kind string `json:"kind,omitempty"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` Namespace string `json:"namespace,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"` Name string `json:"name,omitempty"`
UID string `json:"uid,omitempty" yaml:"uid,omitempty"` UID string `json:"uid,omitempty"`
CreationTimestamp util.Time `json:"creationTimestamp,omitempty" yaml:"creationTimestamp,omitempty"` CreationTimestamp util.Time `json:"creationTimestamp,omitempty"`
SelfLink string `json:"selfLink,omitempty" yaml:"selfLink,omitempty"` SelfLink string `json:"selfLink,omitempty"`
ResourceVersion string `json:"resourceVersion,omitempty" yaml:"resourceVersion,omitempty"` ResourceVersion string `json:"resourceVersion,omitempty"`
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` APIVersion string `json:"apiVersion,omitempty"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` Labels map[string]string `json:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"` Annotations map[string]string `json:"annotations,omitempty"`
} }
type InternalObject struct { type InternalObject struct {
TypeMeta InternalTypeMeta `json:",inline" yaml:",inline"` TypeMeta InternalTypeMeta `json:",inline"`
} }
func (*InternalObject) IsAnAPIObject() {} func (*InternalObject) IsAnAPIObject() {}
...@@ -269,22 +269,22 @@ func TestGenericTypeMetaAccessor(t *testing.T) { ...@@ -269,22 +269,22 @@ func TestGenericTypeMetaAccessor(t *testing.T) {
func TestGenericObjectMeta(t *testing.T) { func TestGenericObjectMeta(t *testing.T) {
type TypeMeta struct { type TypeMeta struct {
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` Kind string `json:"kind,omitempty"`
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` APIVersion string `json:"apiVersion,omitempty"`
} }
type ObjectMeta struct { type ObjectMeta struct {
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` Namespace string `json:"namespace,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"` Name string `json:"name,omitempty"`
UID string `json:"uid,omitempty" yaml:"uid,omitempty"` UID string `json:"uid,omitempty"`
CreationTimestamp util.Time `json:"creationTimestamp,omitempty" yaml:"creationTimestamp,omitempty"` CreationTimestamp util.Time `json:"creationTimestamp,omitempty"`
SelfLink string `json:"selfLink,omitempty" yaml:"selfLink,omitempty"` SelfLink string `json:"selfLink,omitempty"`
ResourceVersion string `json:"resourceVersion,omitempty" yaml:"resourceVersion,omitempty"` ResourceVersion string `json:"resourceVersion,omitempty"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` Labels map[string]string `json:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"` Annotations map[string]string `json:"annotations,omitempty"`
} }
type Object struct { type Object struct {
TypeMeta `json:",inline" yaml:",inline"` TypeMeta `json:",inline"`
ObjectMeta `json:"metadata" yaml:"metadata"` ObjectMeta `json:"metadata"`
} }
j := Object{ j := Object{
TypeMeta{ TypeMeta{
...@@ -375,16 +375,16 @@ func TestGenericObjectMeta(t *testing.T) { ...@@ -375,16 +375,16 @@ func TestGenericObjectMeta(t *testing.T) {
func TestGenericListMeta(t *testing.T) { func TestGenericListMeta(t *testing.T) {
type TypeMeta struct { type TypeMeta struct {
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` Kind string `json:"kind,omitempty"`
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` APIVersion string `json:"apiVersion,omitempty"`
} }
type ListMeta struct { type ListMeta struct {
SelfLink string `json:"selfLink,omitempty" yaml:"selfLink,omitempty"` SelfLink string `json:"selfLink,omitempty"`
ResourceVersion string `json:"resourceVersion,omitempty" yaml:"resourceVersion,omitempty"` ResourceVersion string `json:"resourceVersion,omitempty"`
} }
type Object struct { type Object struct {
TypeMeta `json:",inline" yaml:",inline"` TypeMeta `json:",inline"`
ListMeta `json:"metadata" yaml:"metadata"` ListMeta `json:"metadata"`
} }
j := Object{ j := Object{
TypeMeta{ TypeMeta{
...@@ -442,7 +442,7 @@ func TestGenericListMeta(t *testing.T) { ...@@ -442,7 +442,7 @@ func TestGenericListMeta(t *testing.T) {
} }
type MyAPIObject struct { type MyAPIObject struct {
TypeMeta InternalTypeMeta `json:",inline" yaml:",inline"` TypeMeta InternalTypeMeta `json:",inline"`
} }
func (*MyAPIObject) IsAnAPIObject() {} func (*MyAPIObject) IsAnAPIObject() {}
......
...@@ -23,6 +23,7 @@ import ( ...@@ -23,6 +23,7 @@ import (
"reflect" "reflect"
"strconv" "strconv"
"testing" "testing"
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
...@@ -138,6 +139,14 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs( ...@@ -138,6 +139,14 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
c.RandString(): c.RandString(), c.RandString(): c.RandString(),
} }
}, },
func(t *time.Time, c fuzz.Continue) {
// This is necessary because the standard fuzzed time.Time object is
// completely nil, but when JSON unmarshals dates it fills in the
// unexported loc field with the time.UTC object, resulting in
// reflect.DeepEqual returning false in the round trip tests. We solve it
// by using a date that will be identical to the one JSON unmarshals.
*t = time.Date(2000, 1, 1, 1, 1, 1, 0, time.UTC)
},
) )
func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) { func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) {
...@@ -162,7 +171,7 @@ func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) { ...@@ -162,7 +171,7 @@ func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) {
return return
} }
if !reflect.DeepEqual(source, obj2) { if !reflect.DeepEqual(source, obj2) {
t.Errorf("1: %v: diff: %v\nCodec: %v\nData: %s\nSource: %#v", name, util.ObjectDiff(source, obj2), codec, string(data), source) t.Errorf("1: %v: diff: %v\nCodec: %v\nData: %s\nSource: %#v", name, util.ObjectGoPrintDiff(source, obj2), codec, string(data), source)
return return
} }
......
...@@ -22,5 +22,5 @@ package api ...@@ -22,5 +22,5 @@ package api
// version negotiation. APIVersions isn't just an unnamed array of // version negotiation. APIVersions isn't just an unnamed array of
// strings in order to allow for future evolution, though unversioned // strings in order to allow for future evolution, though unversioned
type APIVersions struct { type APIVersions struct {
Versions []string `json:"versions" yaml:"versions"` Versions []string `json:"versions"`
} }
...@@ -95,17 +95,17 @@ func init() { ...@@ -95,17 +95,17 @@ func init() {
} }
type Simple struct { type Simple struct {
api.TypeMeta `yaml:",inline" json:",inline"` api.TypeMeta `json:",inline"`
api.ObjectMeta `yaml:"metadata" json:"metadata"` api.ObjectMeta `json:"metadata"`
Other string `yaml:"other,omitempty" json:"other,omitempty"` Other string `json:"other,omitempty"`
} }
func (*Simple) IsAnAPIObject() {} func (*Simple) IsAnAPIObject() {}
type SimpleList struct { type SimpleList struct {
api.TypeMeta `yaml:",inline" json:",inline"` api.TypeMeta `json:",inline"`
api.ListMeta `yaml:"metadata,inline" json:"metadata,inline"` api.ListMeta `json:"metadata,inline"`
Items []Simple `yaml:"items,omitempty" json:"items,omitempty"` Items []Simple `json:"items,omitempty"`
} }
func (*SimpleList) IsAnAPIObject() {} func (*SimpleList) IsAnAPIObject() {}
......
...@@ -32,8 +32,8 @@ import ( ...@@ -32,8 +32,8 @@ import (
// watchJSON defines the expected JSON wire equivalent of watch.Event // watchJSON defines the expected JSON wire equivalent of watch.Event
type watchJSON struct { type watchJSON struct {
Type watch.EventType `json:"type,omitempty" yaml:"type,omitempty"` Type watch.EventType `json:"type,omitempty"`
Object json.RawMessage `json:"object,omitempty" yaml:"object,omitempty"` Object json.RawMessage `json:"object,omitempty"`
} }
var watchTestTable = []struct { var watchTestTable = []struct {
......
...@@ -37,7 +37,7 @@ import ( ...@@ -37,7 +37,7 @@ import (
// body.Namespace, if we want to add that feature, without affecting the // body.Namespace, if we want to add that feature, without affecting the
// meta.Namespace. // meta.Namespace.
type policy struct { type policy struct {
User string `json:"user,omitempty" yaml:"user,omitempty"` User string `json:"user,omitempty"`
// TODO: add support for groups as well as users. // TODO: add support for groups as well as users.
// TODO: add support for robot accounts as well as human user accounts. // TODO: add support for robot accounts as well as human user accounts.
// TODO: decide how to namespace user names when multiple authentication // TODO: decide how to namespace user names when multiple authentication
...@@ -51,9 +51,9 @@ type policy struct { ...@@ -51,9 +51,9 @@ type policy struct {
// the API, we don't have to add lots of policy? // the API, we don't have to add lots of policy?
// TODO: make this a proper REST object with its own registry. // TODO: make this a proper REST object with its own registry.
Readonly bool `json:"readonly,omitempty" yaml:"readonly,omitempty"` Readonly bool `json:"readonly,omitempty"`
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` Kind string `json:"kind,omitempty"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` Namespace string `json:"namespace,omitempty"`
// TODO: "expires" string in RFC3339 format. // TODO: "expires" string in RFC3339 format.
......
...@@ -20,7 +20,7 @@ import ( ...@@ -20,7 +20,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"gopkg.in/v1/yaml" "github.com/ghodss/yaml"
) )
// Decode converts a YAML or JSON string back into a pointer to an api object. // Decode converts a YAML or JSON string back into a pointer to an api object.
......
...@@ -20,7 +20,7 @@ import ( ...@@ -20,7 +20,7 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"gopkg.in/v1/yaml" "github.com/ghodss/yaml"
) )
// MetaFactory is used to store and retrieve the version and kind // MetaFactory is used to store and retrieve the version and kind
...@@ -55,8 +55,8 @@ type SimpleMetaFactory struct { ...@@ -55,8 +55,8 @@ type SimpleMetaFactory struct {
// encoding of an object, or an error. // encoding of an object, or an error.
func (SimpleMetaFactory) Interpret(data []byte) (version, kind string, err error) { func (SimpleMetaFactory) Interpret(data []byte) (version, kind string, err error) {
findKind := struct { findKind := struct {
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` APIVersion string `json:"apiVersion,omitempty"`
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` Kind string `json:"kind,omitempty"`
}{} }{}
// yaml is a superset of json, so we use it to decode here. That way, // yaml is a superset of json, so we use it to decode here. That way,
// we understand both. // we understand both.
......
...@@ -94,14 +94,14 @@ func TestSimpleMetaFactoryUpdateStruct(t *testing.T) { ...@@ -94,14 +94,14 @@ func TestSimpleMetaFactoryUpdateStruct(t *testing.T) {
func TestMetaValues(t *testing.T) { func TestMetaValues(t *testing.T) {
type InternalSimple struct { type InternalSimple struct {
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` APIVersion string `json:"apiVersion,omitempty"`
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` Kind string `json:"kind,omitempty"`
TestString string `json:"testString" yaml:"testString"` TestString string `json:"testString"`
} }
type ExternalSimple struct { type ExternalSimple struct {
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` APIVersion string `json:"apiVersion,omitempty"`
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` Kind string `json:"kind,omitempty"`
TestString string `json:"testString" yaml:"testString"` TestString string `json:"testString"`
} }
s := NewScheme() s := NewScheme()
s.AddKnownTypeWithName("", "Simple", &InternalSimple{}) s.AddKnownTypeWithName("", "Simple", &InternalSimple{})
...@@ -194,14 +194,14 @@ func TestMetaValues(t *testing.T) { ...@@ -194,14 +194,14 @@ func TestMetaValues(t *testing.T) {
func TestMetaValuesUnregisteredConvert(t *testing.T) { func TestMetaValuesUnregisteredConvert(t *testing.T) {
type InternalSimple struct { type InternalSimple struct {
Version string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` Version string `json:"apiVersion,omitempty"`
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` Kind string `json:"kind,omitempty"`
TestString string `json:"testString" yaml:"testString"` TestString string `json:"testString"`
} }
type ExternalSimple struct { type ExternalSimple struct {
Version string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` Version string `json:"apiVersion,omitempty"`
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` Kind string `json:"kind,omitempty"`
TestString string `json:"testString" yaml:"testString"` TestString string `json:"testString"`
} }
s := NewScheme() s := NewScheme()
s.InternalVersion = "" s.InternalVersion = ""
......
...@@ -26,73 +26,73 @@ import ( ...@@ -26,73 +26,73 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/ghodss/yaml"
"github.com/google/gofuzz" "github.com/google/gofuzz"
"gopkg.in/v1/yaml"
) )
var fuzzIters = flag.Int("fuzz_iters", 50, "How many fuzzing iterations to do.") var fuzzIters = flag.Int("fuzz_iters", 50, "How many fuzzing iterations to do.")
// Test a weird version/kind embedding format. // Test a weird version/kind embedding format.
type MyWeirdCustomEmbeddedVersionKindField struct { type MyWeirdCustomEmbeddedVersionKindField struct {
ID string `yaml:"ID,omitempty" json:"ID,omitempty"` ID string `json:"ID,omitempty"`
APIVersion string `json:"myVersionKey,omitempty" yaml:"myVersionKey,omitempty"` APIVersion string `json:"myVersionKey,omitempty"`
ObjectKind string `json:"myKindKey,omitempty" yaml:"myKindKey,omitempty"` ObjectKind string `json:"myKindKey,omitempty"`
Z string `yaml:"Z,omitempty" json:"Z,omitempty"` Z string `json:"Z,omitempty"`
Y uint64 `yaml:"Y,omitempty" json:"Y,omitempty"` Y uint64 `json:"Y,omitempty"`
} }
type TestType1 struct { type TestType1 struct {
MyWeirdCustomEmbeddedVersionKindField `json:",inline" yaml:",inline"` MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
A string `yaml:"A,omitempty" json:"A,omitempty"` A string `json:"A,omitempty"`
B int `yaml:"B,omitempty" json:"B,omitempty"` B int `json:"B,omitempty"`
C int8 `yaml:"C,omitempty" json:"C,omitempty"` C int8 `json:"C,omitempty"`
D int16 `yaml:"D,omitempty" json:"D,omitempty"` D int16 `json:"D,omitempty"`
E int32 `yaml:"E,omitempty" json:"E,omitempty"` E int32 `json:"E,omitempty"`
F int64 `yaml:"F,omitempty" json:"F,omitempty"` F int64 `json:"F,omitempty"`
G uint `yaml:"G,omitempty" json:"G,omitempty"` G uint `json:"G,omitempty"`
H uint8 `yaml:"H,omitempty" json:"H,omitempty"` H uint8 `json:"H,omitempty"`
I uint16 `yaml:"I,omitempty" json:"I,omitempty"` I uint16 `json:"I,omitempty"`
J uint32 `yaml:"J,omitempty" json:"J,omitempty"` J uint32 `json:"J,omitempty"`
K uint64 `yaml:"K,omitempty" json:"K,omitempty"` K uint64 `json:"K,omitempty"`
L bool `yaml:"L,omitempty" json:"L,omitempty"` L bool `json:"L,omitempty"`
M map[string]int `yaml:"M,omitempty" json:"M,omitempty"` M map[string]int `json:"M,omitempty"`
N map[string]TestType2 `yaml:"N,omitempty" json:"N,omitempty"` N map[string]TestType2 `json:"N,omitempty"`
O *TestType2 `yaml:"O,omitempty" json:"O,omitempty"` O *TestType2 `json:"O,omitempty"`
P []TestType2 `yaml:"Q,omitempty" json:"Q,omitempty"` P []TestType2 `json:"Q,omitempty"`
} }
type TestType2 struct { type TestType2 struct {
A string `yaml:"A,omitempty" json:"A,omitempty"` A string `json:"A,omitempty"`
B int `yaml:"B,omitempty" json:"B,omitempty"` B int `json:"B,omitempty"`
} }
type ExternalTestType2 struct { type ExternalTestType2 struct {
A string `yaml:"A,omitempty" json:"A,omitempty"` A string `json:"A,omitempty"`
B int `yaml:"B,omitempty" json:"B,omitempty"` B int `json:"B,omitempty"`
} }
type ExternalTestType1 struct { type ExternalTestType1 struct {
MyWeirdCustomEmbeddedVersionKindField `json:",inline" yaml:",inline"` MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
A string `yaml:"A,omitempty" json:"A,omitempty"` A string `json:"A,omitempty"`
B int `yaml:"B,omitempty" json:"B,omitempty"` B int `json:"B,omitempty"`
C int8 `yaml:"C,omitempty" json:"C,omitempty"` C int8 `json:"C,omitempty"`
D int16 `yaml:"D,omitempty" json:"D,omitempty"` D int16 `json:"D,omitempty"`
E int32 `yaml:"E,omitempty" json:"E,omitempty"` E int32 `json:"E,omitempty"`
F int64 `yaml:"F,omitempty" json:"F,omitempty"` F int64 `json:"F,omitempty"`
G uint `yaml:"G,omitempty" json:"G,omitempty"` G uint `json:"G,omitempty"`
H uint8 `yaml:"H,omitempty" json:"H,omitempty"` H uint8 `json:"H,omitempty"`
I uint16 `yaml:"I,omitempty" json:"I,omitempty"` I uint16 `json:"I,omitempty"`
J uint32 `yaml:"J,omitempty" json:"J,omitempty"` J uint32 `json:"J,omitempty"`
K uint64 `yaml:"K,omitempty" json:"K,omitempty"` K uint64 `json:"K,omitempty"`
L bool `yaml:"L,omitempty" json:"L,omitempty"` L bool `json:"L,omitempty"`
M map[string]int `yaml:"M,omitempty" json:"M,omitempty"` M map[string]int `json:"M,omitempty"`
N map[string]ExternalTestType2 `yaml:"N,omitempty" json:"N,omitempty"` N map[string]ExternalTestType2 `json:"N,omitempty"`
O *ExternalTestType2 `yaml:"O,omitempty" json:"O,omitempty"` O *ExternalTestType2 `json:"O,omitempty"`
P []ExternalTestType2 `yaml:"Q,omitempty" json:"Q,omitempty"` P []ExternalTestType2 `json:"Q,omitempty"`
} }
type ExternalInternalSame struct { type ExternalInternalSame struct {
MyWeirdCustomEmbeddedVersionKindField `json:",inline" yaml:",inline"` MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
A TestType2 `yaml:"A,omitempty" json:"A,omitempty"` A TestType2 `json:"A,omitempty"`
} }
// TestObjectFuzzer can randomly populate all the above objects. // TestObjectFuzzer can randomly populate all the above objects.
...@@ -139,8 +139,8 @@ type testMetaFactory struct{} ...@@ -139,8 +139,8 @@ type testMetaFactory struct{}
func (testMetaFactory) Interpret(data []byte) (version, kind string, err error) { func (testMetaFactory) Interpret(data []byte) (version, kind string, err error) {
findKind := struct { findKind := struct {
APIVersion string `json:"myVersionKey,omitempty" yaml:"myVersionKey,omitempty"` APIVersion string `json:"myVersionKey,omitempty"`
ObjectKind string `json:"myKindKey,omitempty" yaml:"myKindKey,omitempty"` ObjectKind string `json:"myKindKey,omitempty"`
}{} }{}
// yaml is a superset of json, so we use it to decode here. That way, // yaml is a superset of json, so we use it to decode here. That way,
// we understand both. // we understand both.
......
...@@ -33,8 +33,8 @@ import ( ...@@ -33,8 +33,8 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/wait" "github.com/GoogleCloudPlatform/kubernetes/pkg/util/wait"
"github.com/GoogleCloudPlatform/kubernetes/pkg/version" "github.com/GoogleCloudPlatform/kubernetes/pkg/version"
"github.com/ghodss/yaml"
"github.com/golang/glog" "github.com/golang/glog"
"gopkg.in/v1/yaml"
) )
func GetServerVersion(client *client.Client) (*version.Info, error) { func GetServerVersion(client *client.Client) (*version.Info, error) {
......
...@@ -24,7 +24,7 @@ import ( ...@@ -24,7 +24,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"gopkg.in/v1/yaml" "github.com/ghodss/yaml"
) )
func TestParseBadStorage(t *testing.T) { func TestParseBadStorage(t *testing.T) {
...@@ -121,9 +121,9 @@ func TestParseController(t *testing.T) { ...@@ -121,9 +121,9 @@ func TestParseController(t *testing.T) {
} }
type TestParseType struct { type TestParseType struct {
api.TypeMeta `json:",inline" yaml:",inline"` api.TypeMeta `json:",inline"`
api.ObjectMeta `json:"metadata" yaml:"metadata"` api.ObjectMeta `json:"metadata"`
Data string `json:"data" yaml:"data"` Data string `json:"data"`
} }
func (*TestParseType) IsAnAPIObject() {} func (*TestParseType) IsAnAPIObject() {}
......
...@@ -29,8 +29,8 @@ import ( ...@@ -29,8 +29,8 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/ghodss/yaml"
"github.com/golang/glog" "github.com/golang/glog"
"gopkg.in/v1/yaml"
) )
// ResourcePrinter is an interface that knows how to print API resources. // ResourcePrinter is an interface that knows how to print API resources.
......
...@@ -26,15 +26,15 @@ import ( ...@@ -26,15 +26,15 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"gopkg.in/v1/yaml" "github.com/ghodss/yaml"
) )
func TestYAMLPrinterPrint(t *testing.T) { func TestYAMLPrinterPrint(t *testing.T) {
type testStruct struct { type testStruct struct {
Key string `yaml:"Key" json:"Key"` Key string `json:"Key"`
Map map[string]int `yaml:"Map" json:"Map"` Map map[string]int `json:"Map"`
StringList []string `yaml:"StringList" json:"StringList"` StringList []string `json:"StringList"`
IntList []int `yaml:"IntList" json:"IntList"` IntList []int `json:"IntList"`
} }
testData := testStruct{ testData := testStruct{
"testValue", "testValue",
......
...@@ -24,9 +24,9 @@ import ( ...@@ -24,9 +24,9 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/config" "github.com/GoogleCloudPlatform/kubernetes/pkg/config"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/ghodss/yaml"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"gopkg.in/v1/yaml"
) )
// DataToObjects converts the raw JSON data into API objects // DataToObjects converts the raw JSON data into API objects
......
...@@ -29,8 +29,8 @@ import ( ...@@ -29,8 +29,8 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/ghodss/yaml"
"github.com/golang/glog" "github.com/golang/glog"
"gopkg.in/v1/yaml"
) )
// GetPrinter takes a format type, an optional format argument, a version and a convertor // GetPrinter takes a format type, an optional format argument, a version and a convertor
......
...@@ -29,16 +29,16 @@ import ( ...@@ -29,16 +29,16 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"gopkg.in/v1/yaml" "github.com/ghodss/yaml"
) )
type testStruct struct { type testStruct struct {
api.TypeMeta `yaml:",inline" json:",inline"` api.TypeMeta `json:",inline"`
api.ObjectMeta `yaml:"metadata,omitempty" json:"metadata,omitempty"` api.ObjectMeta `json:"metadata,omitempty"`
Key string `yaml:"Key" json:"Key"` Key string `json:"Key"`
Map map[string]int `yaml:"Map" json:"Map"` Map map[string]int `json:"Map"`
StringList []string `yaml:"StringList" json:"StringList"` StringList []string `json:"StringList"`
IntList []int `yaml:"IntList" json:"IntList"` IntList []int `json:"IntList"`
} }
func (ts *testStruct) IsAnAPIObject() {} func (ts *testStruct) IsAnAPIObject() {}
......
...@@ -287,7 +287,7 @@ func (s *podStorage) Sync() { ...@@ -287,7 +287,7 @@ func (s *podStorage) Sync() {
func (s *podStorage) MergedState() interface{} { func (s *podStorage) MergedState() interface{} {
s.podLock.RLock() s.podLock.RLock()
defer s.podLock.RUnlock() defer s.podLock.RUnlock()
var pods []api.BoundPod pods := make([]api.BoundPod, 0)
for _, sourcePods := range s.pods { for _, sourcePods := range s.pods {
for _, podRef := range sourcePods { for _, podRef := range sourcePods {
pod, err := api.Scheme.Copy(podRef) pod, err := api.Scheme.Copy(podRef)
......
...@@ -59,10 +59,13 @@ func CreateValidPod(name, namespace, source string) api.BoundPod { ...@@ -59,10 +59,13 @@ func CreateValidPod(name, namespace, source string) api.BoundPod {
} }
func CreatePodUpdate(op kubelet.PodOperation, pods ...api.BoundPod) kubelet.PodUpdate { func CreatePodUpdate(op kubelet.PodOperation, pods ...api.BoundPod) kubelet.PodUpdate {
// We deliberately return an empty slice instead of a nil pointer here
// because reflect.DeepEqual differentiates between the two and we need to
// pick one for consistency.
newPods := make([]api.BoundPod, len(pods))
if len(pods) == 0 { if len(pods) == 0 {
return kubelet.PodUpdate{Op: op} return kubelet.PodUpdate{newPods, op}
} }
newPods := make([]api.BoundPod, len(pods))
for i := range pods { for i := range pods {
newPods[i] = pods[i] newPods[i] = pods[i]
} }
......
...@@ -33,8 +33,8 @@ import ( ...@@ -33,8 +33,8 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/ghodss/yaml"
"github.com/golang/glog" "github.com/golang/glog"
"gopkg.in/v1/yaml"
) )
type sourceFile struct { type sourceFile struct {
...@@ -97,12 +97,13 @@ func extractFromDir(name string) ([]api.BoundPod, error) { ...@@ -97,12 +97,13 @@ func extractFromDir(name string) ([]api.BoundPod, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("glob failed: %v", err) return nil, fmt.Errorf("glob failed: %v", err)
} }
pods := make([]api.BoundPod, 0)
if len(dirents) == 0 { if len(dirents) == 0 {
return nil, nil return pods, nil
} }
sort.Strings(dirents) sort.Strings(dirents)
pods := []api.BoundPod{}
for _, path := range dirents { for _, path := range dirents {
statInfo, err := os.Stat(path) statInfo, err := os.Stat(path)
if err != nil { if err != nil {
......
...@@ -29,7 +29,7 @@ import ( ...@@ -29,7 +29,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
"gopkg.in/v1/yaml" "github.com/ghodss/yaml"
) )
func ExampleManifestAndPod(id string) (api.ContainerManifest, api.BoundPod) { func ExampleManifestAndPod(id string) (api.ContainerManifest, api.BoundPod) {
......
...@@ -29,8 +29,8 @@ import ( ...@@ -29,8 +29,8 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/ghodss/yaml"
"github.com/golang/glog" "github.com/golang/glog"
"gopkg.in/v1/yaml"
) )
type sourceURL struct { type sourceURL struct {
......
...@@ -35,9 +35,9 @@ import ( ...@@ -35,9 +35,9 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/healthz" "github.com/GoogleCloudPlatform/kubernetes/pkg/healthz"
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog" "github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
"github.com/ghodss/yaml"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/google/cadvisor/info" "github.com/google/cadvisor/info"
"gopkg.in/v1/yaml"
) )
// Server is a http.Handler which exposes kubelet functionality over HTTP. // Server is a http.Handler which exposes kubelet functionality over HTTP.
......
...@@ -43,6 +43,10 @@ const ( ...@@ -43,6 +43,10 @@ const (
// For setting the state of the system to a given state for this source configuration, set // For setting the state of the system to a given state for this source configuration, set
// Pods as desired and Op to SET, which will reset the system state to that specified in this // Pods as desired and Op to SET, which will reset the system state to that specified in this
// operation for this source channel. To remove all pods, set Pods to empty object and Op to SET. // operation for this source channel. To remove all pods, set Pods to empty object and Op to SET.
//
// Additionally, Pods should never be nil - it should always point to an empty slice. While
// functionally similar, this helps our unit tests properly check that the correct PodUpdates
// are generated.
type PodUpdate struct { type PodUpdate struct {
Pods []api.BoundPod Pods []api.BoundPod
Op PodOperation Op PodOperation
......
/*
Copyright 2014 Google Inc. All rights reserved.
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 runtime
import (
"gopkg.in/v1/yaml"
)
// Encode()/Decode() are the canonical way of converting an API object to/from
// wire format. This file provides utility functions which permit doing so
// recursively, such that API objects of types known only at run time can be
// embedded within other API types.
// UnmarshalJSON implements the json.Unmarshaler interface.
func CodecUnmarshalJSON(codec Codec, b []byte) (Object, error) {
// Handle JSON's "null": Decode() doesn't expect it.
if len(b) == 4 && string(b) == "null" {
return nil, nil
}
obj, err := codec.Decode(b)
if err != nil {
return nil, err
}
return obj, nil
}
// MarshalJSON implements the json.Marshaler interface.
func CodecMarshalJSON(codec Codec, obj Object) ([]byte, error) {
if obj == nil {
// Encode unset/nil objects as JSON's "null".
return []byte("null"), nil
}
return codec.Encode(obj)
}
// SetYAML implements the yaml.Setter interface.
func CodecSetYAML(codec Codec, tag string, value interface{}) (Object, bool) {
if value == nil {
return nil, true
}
// Why does the yaml package send value as a map[interface{}]interface{}?
// It's especially frustrating because encoding/json does the right thing
// by giving a []byte. So here we do the embarrasing thing of re-encode and
// de-encode the right way.
// TODO: Write a version of Decode that uses reflect to turn this value
// into an API object.
b, err := yaml.Marshal(value)
if err != nil {
panic("yaml can't reverse its own object")
}
obj, err := codec.Decode(b)
if err != nil {
return nil, false
}
return obj, true
}
// GetYAML implements the yaml.Getter interface.
func CodecGetYAML(codec Codec, obj Object) (tag string, value interface{}) {
if obj == nil {
value = "null"
return
}
// Encode returns JSON, which is conveniently a subset of YAML.
v, err := codec.Encode(obj)
if err != nil {
panic("impossible to encode API object!")
}
return tag, v
}
...@@ -28,17 +28,17 @@ var scheme = runtime.NewScheme() ...@@ -28,17 +28,17 @@ var scheme = runtime.NewScheme()
var Codec = runtime.CodecFor(scheme, "v1test") var Codec = runtime.CodecFor(scheme, "v1test")
type EmbeddedTest struct { type EmbeddedTest struct {
runtime.TypeMeta `yaml:",inline" json:",inline"` runtime.TypeMeta `json:",inline"`
ID string `yaml:"id,omitempty" json:"id,omitempty"` ID string `json:"id,omitempty"`
Object runtime.EmbeddedObject `yaml:"object,omitempty" json:"object,omitempty"` Object runtime.EmbeddedObject `json:"object,omitempty"`
EmptyObject runtime.EmbeddedObject `yaml:"emptyObject,omitempty" json:"emptyObject,omitempty"` EmptyObject runtime.EmbeddedObject `json:"emptyObject,omitempty"`
} }
type EmbeddedTestExternal struct { type EmbeddedTestExternal struct {
runtime.TypeMeta `yaml:",inline" json:",inline"` runtime.TypeMeta `json:",inline"`
ID string `yaml:"id,omitempty" json:"id,omitempty"` ID string `json:"id,omitempty"`
Object runtime.RawExtension `yaml:"object,omitempty" json:"object,omitempty"` Object runtime.RawExtension `json:"object,omitempty"`
EmptyObject runtime.RawExtension `yaml:"emptyObject,omitempty" json:"emptyObject,omitempty"` EmptyObject runtime.RawExtension `json:"emptyObject,omitempty"`
} }
func (*EmbeddedTest) IsAnAPIObject() {} func (*EmbeddedTest) IsAnAPIObject() {}
......
...@@ -16,11 +16,7 @@ limitations under the License. ...@@ -16,11 +16,7 @@ limitations under the License.
package runtime package runtime
import ( import "errors"
"errors"
"gopkg.in/v1/yaml"
)
func (re *RawExtension) UnmarshalJSON(in []byte) error { func (re *RawExtension) UnmarshalJSON(in []byte) error {
if re == nil { if re == nil {
...@@ -33,28 +29,3 @@ func (re *RawExtension) UnmarshalJSON(in []byte) error { ...@@ -33,28 +29,3 @@ func (re *RawExtension) UnmarshalJSON(in []byte) error {
func (re *RawExtension) MarshalJSON() ([]byte, error) { func (re *RawExtension) MarshalJSON() ([]byte, error) {
return re.RawJSON, nil return re.RawJSON, nil
} }
// SetYAML implements the yaml.Setter interface.
func (re *RawExtension) SetYAML(tag string, value interface{}) bool {
if value == nil {
re.RawJSON = []byte("null")
return true
}
// Why does the yaml package send value as a map[interface{}]interface{}?
// It's especially frustrating because encoding/json does the right thing
// by giving a []byte. So here we do the embarrasing thing of re-encode and
// de-encode the right way.
// TODO: Write a version of Decode that uses reflect to turn this value
// into an API object.
b, err := yaml.Marshal(value)
if err != nil {
panic("yaml can't reverse its own object")
}
re.RawJSON = b
return true
}
// GetYAML implements the yaml.Getter interface.
func (re *RawExtension) GetYAML() (tag string, value interface{}) {
return tag, re.RawJSON
}
...@@ -40,7 +40,7 @@ func (self *Scheme) fromScope(s conversion.Scope) (inVersion, outVersion string, ...@@ -40,7 +40,7 @@ func (self *Scheme) fromScope(s conversion.Scope) (inVersion, outVersion string,
// emptyPlugin is used to copy the Kind field to and from plugin objects. // emptyPlugin is used to copy the Kind field to and from plugin objects.
type emptyPlugin struct { type emptyPlugin struct {
PluginBase `json:",inline" yaml:",inline"` PluginBase `json:",inline"`
} }
// embeddedObjectToRawExtension does the conversion you would expect from the name, using the information // embeddedObjectToRawExtension does the conversion you would expect from the name, using the information
......
...@@ -25,18 +25,18 @@ import ( ...@@ -25,18 +25,18 @@ import (
) )
type TypeMeta struct { type TypeMeta struct {
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` Kind string `json:"kind,omitempty"`
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` APIVersion string `json:"apiVersion,omitempty"`
} }
type InternalSimple struct { type InternalSimple struct {
TypeMeta `json:",inline" yaml:",inline"` TypeMeta `json:",inline"`
TestString string `json:"testString" yaml:"testString"` TestString string `json:"testString"`
} }
type ExternalSimple struct { type ExternalSimple struct {
TypeMeta `json:",inline" yaml:",inline"` TypeMeta `json:",inline"`
TestString string `json:"testString" yaml:"testString"` TestString string `json:"testString"`
} }
func (*InternalSimple) IsAnAPIObject() {} func (*InternalSimple) IsAnAPIObject() {}
...@@ -157,23 +157,23 @@ func TestBadJSONRejection(t *testing.T) { ...@@ -157,23 +157,23 @@ func TestBadJSONRejection(t *testing.T) {
} }
type ExtensionA struct { type ExtensionA struct {
runtime.PluginBase `json:",inline" yaml:",inline"` runtime.PluginBase `json:",inline"`
TestString string `json:"testString" yaml:"testString"` TestString string `json:"testString"`
} }
type ExtensionB struct { type ExtensionB struct {
runtime.PluginBase `json:",inline" yaml:",inline"` runtime.PluginBase `json:",inline"`
TestString string `json:"testString" yaml:"testString"` TestString string `json:"testString"`
} }
type ExternalExtensionType struct { type ExternalExtensionType struct {
TypeMeta `json:",inline" yaml:",inline"` TypeMeta `json:",inline"`
Extension runtime.RawExtension `json:"extension" yaml:"extension"` Extension runtime.RawExtension `json:"extension"`
} }
type InternalExtensionType struct { type InternalExtensionType struct {
TypeMeta `json:",inline" yaml:",inline"` TypeMeta `json:",inline"`
Extension runtime.EmbeddedObject `json:"extension" yaml:"extension"` Extension runtime.EmbeddedObject `json:"extension"`
} }
func (*ExtensionA) IsAnAPIObject() {} func (*ExtensionA) IsAnAPIObject() {}
......
...@@ -26,7 +26,7 @@ import ( ...@@ -26,7 +26,7 @@ import (
// TypeMeta is shared by all top level objects. The proper way to use it is to inline it in your type, // TypeMeta is shared by all top level objects. The proper way to use it is to inline it in your type,
// like this: // like this:
// type MyAwesomeAPIObject struct { // type MyAwesomeAPIObject struct {
// runtime.TypeMeta `yaml:",inline" json:",inline"` // runtime.TypeMeta `json:",inline"`
// ... // other fields // ... // other fields
// } // }
// func (*MyAwesomeAPIObject) IsAnAPIObject() {} // func (*MyAwesomeAPIObject) IsAnAPIObject() {}
...@@ -35,21 +35,21 @@ import ( ...@@ -35,21 +35,21 @@ import (
// your own with the same fields. // your own with the same fields.
// //
type TypeMeta struct { type TypeMeta struct {
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` APIVersion string `json:"apiVersion,omitempty"`
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` Kind string `json:"kind,omitempty"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` Namespace string `json:"namespace,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"` Name string `json:"name,omitempty"`
UID string `json:"uid,omitempty" yaml:"uid,omitempty"` UID string `json:"uid,omitempty"`
CreationTimestamp util.Time `json:"creationTimestamp,omitempty" yaml:"creationTimestamp,omitempty"` CreationTimestamp util.Time `json:"creationTimestamp,omitempty"`
SelfLink string `json:"selfLink,omitempty" yaml:"selfLink,omitempty"` SelfLink string `json:"selfLink,omitempty"`
ResourceVersion string `json:"resourceVersion,omitempty" yaml:"resourceVersion,omitempty"` ResourceVersion string `json:"resourceVersion,omitempty"`
} }
// PluginBase is like TypeMeta, but it's intended for plugin objects that won't ever be encoded // PluginBase is like TypeMeta, but it's intended for plugin objects that won't ever be encoded
// except while embedded in other objects. // except while embedded in other objects.
type PluginBase struct { type PluginBase struct {
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` Kind string `json:"kind,omitempty"`
} }
// EmbeddedObject has appropriate encoder and decoder functions, such that on the wire, it's // EmbeddedObject has appropriate encoder and decoder functions, such that on the wire, it's
...@@ -74,22 +74,22 @@ type EmbeddedObject struct { ...@@ -74,22 +74,22 @@ type EmbeddedObject struct {
// //
// // Internal package: // // Internal package:
// type MyAPIObject struct { // type MyAPIObject struct {
// runtime.TypeMeta `yaml:",inline" json:",inline"` // runtime.TypeMeta `json:",inline"`
// MyPlugin runtime.EmbeddedObject `json:"myPlugin" yaml:"myPlugin"` // MyPlugin runtime.EmbeddedObject `json:"myPlugin"`
// } // }
// type PluginA struct { // type PluginA struct {
// runtime.PluginBase `yaml:",inline" json:",inline"` // runtime.PluginBase `json:",inline"`
// AOption string `yaml:"aOption" json:"aOption"` // AOption string `json:"aOption"`
// } // }
// //
// // External package: // // External package:
// type MyAPIObject struct { // type MyAPIObject struct {
// runtime.TypeMeta `yaml:",inline" json:",inline"` // runtime.TypeMeta `json:",inline"`
// MyPlugin runtime.RawExtension `json:"myPlugin" yaml:"myPlugin"` // MyPlugin runtime.RawExtension `json:"myPlugin"`
// } // }
// type PluginA struct { // type PluginA struct {
// runtime.PluginBase `yaml:",inline" json:",inline"` // runtime.PluginBase `json:",inline"`
// AOption string `yaml:"aOption" json:"aOption"` // AOption string `json:"aOption"`
// } // }
// //
// // On the wire, the JSON will look something like this: // // On the wire, the JSON will look something like this:
...@@ -118,7 +118,7 @@ type RawExtension struct { ...@@ -118,7 +118,7 @@ type RawExtension struct {
// TypeMeta features-- kind, version, resourceVersion, etc. // TypeMeta features-- kind, version, resourceVersion, etc.
// TODO: Not implemented yet! // TODO: Not implemented yet!
type Unknown struct { type Unknown struct {
TypeMeta `yaml:",inline" json:",inline"` TypeMeta `json:",inline"`
// RawJSON will hold the complete JSON of the object which couldn't be matched // RawJSON will hold the complete JSON of the object which couldn't be matched
// with a registered type. Most likely, nothing should be done with this // with a registered type. Most likely, nothing should be done with this
// except for passing it through the system. // except for passing it through the system.
......
...@@ -36,9 +36,9 @@ type fakeClientGetSet struct { ...@@ -36,9 +36,9 @@ type fakeClientGetSet struct {
} }
type TestResource struct { type TestResource struct {
api.TypeMeta `json:",inline" yaml:",inline"` api.TypeMeta `json:",inline"`
api.ObjectMeta `json:"metadata" yaml:"metadata"` api.ObjectMeta `json:"metadata"`
Value int `json:"value" yaml:"value,omitempty"` Value int `json:"value"`
} }
func (*TestResource) IsAnAPIObject() {} func (*TestResource) IsAnAPIObject() {}
......
...@@ -19,6 +19,8 @@ package util ...@@ -19,6 +19,8 @@ package util
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/davecgh/go-spew/spew"
) )
// StringDiff diffs a and b and returns a human readable diff. // StringDiff diffs a and b and returns a human readable diff.
...@@ -56,13 +58,19 @@ func ObjectDiff(a, b interface{}) string { ...@@ -56,13 +58,19 @@ func ObjectDiff(a, b interface{}) string {
return StringDiff(string(ab), string(bb)) return StringDiff(string(ab), string(bb))
} }
// ObjectGoPrintDiff is like ObjectDiff, but uses go's %#v formatter to print the // ObjectGoPrintDiff is like ObjectDiff, but uses go-spew to print the objects,
// objects, in case json isn't showing you the difference. (reflect.DeepEqual makes // which shows absolutely everything by recursing into every single pointer
// a distinction between nil and empty slices, for example, even though nothing else // (go's %#v formatters OTOH stop at a certain point). This is needed when you
// really does.) // can't figure out why reflect.DeepEqual is returning false and nothing is
// showing you differences. This will.
func ObjectGoPrintDiff(a, b interface{}) string { func ObjectGoPrintDiff(a, b interface{}) string {
s := spew.ConfigState{
Indent: " ",
// Extra deep spew.
DisableMethods: true,
}
return StringDiff( return StringDiff(
fmt.Sprintf("%#v", a), s.Sdump(a),
fmt.Sprintf("%#v", b), s.Sdump(b),
) )
} }
...@@ -79,35 +79,3 @@ func (t Time) MarshalJSON() ([]byte, error) { ...@@ -79,35 +79,3 @@ func (t Time) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Format(time.RFC3339)) return json.Marshal(t.Format(time.RFC3339))
} }
// SetYAML implements the yaml.Setter interface.
func (t *Time) SetYAML(tag string, value interface{}) bool {
if value == nil {
t.Time = time.Time{}
return true
}
str, ok := value.(string)
if !ok {
return false
}
pt, err := time.Parse(time.RFC3339, str)
if err != nil {
return false
}
t.Time = pt
return true
}
// GetYAML implements the yaml.Getter interface.
func (t Time) GetYAML() (tag string, value interface{}) {
if t.IsZero() {
value = "null"
return
}
value = t.Format(time.RFC3339)
return tag, value
}
...@@ -22,11 +22,11 @@ import ( ...@@ -22,11 +22,11 @@ import (
"testing" "testing"
"time" "time"
"gopkg.in/v1/yaml" "github.com/ghodss/yaml"
) )
type TimeHolder struct { type TimeHolder struct {
T Time `json:"t" yaml:"t"` T Time `json:"t"`
} }
func TestTimeMarshalYAML(t *testing.T) { func TestTimeMarshalYAML(t *testing.T) {
...@@ -34,7 +34,7 @@ func TestTimeMarshalYAML(t *testing.T) { ...@@ -34,7 +34,7 @@ func TestTimeMarshalYAML(t *testing.T) {
input Time input Time
result string result string
}{ }{
{Time{}, "t: \"null\"\n"}, {Time{}, "t: null\n"},
{Date(1998, time.May, 5, 5, 5, 5, 50, time.UTC), "t: 1998-05-05T05:05:05Z\n"}, {Date(1998, time.May, 5, 5, 5, 5, 50, time.UTC), "t: 1998-05-05T05:05:05Z\n"},
{Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC), "t: 1998-05-05T05:05:05Z\n"}, {Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC), "t: 1998-05-05T05:05:05Z\n"},
} }
...@@ -56,7 +56,7 @@ func TestTimeUnmarshalYAML(t *testing.T) { ...@@ -56,7 +56,7 @@ func TestTimeUnmarshalYAML(t *testing.T) {
input string input string
result Time result Time
}{ }{
{"t: \"null\"\n", Time{}}, {"t: null\n", Time{}},
{"t: 1998-05-05T05:05:05Z\n", Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC)}, {"t: 1998-05-05T05:05:05Z\n", Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC)},
} }
......
...@@ -88,34 +88,6 @@ func NewIntOrStringFromString(val string) IntOrString { ...@@ -88,34 +88,6 @@ func NewIntOrStringFromString(val string) IntOrString {
return IntOrString{Kind: IntstrString, StrVal: val} return IntOrString{Kind: IntstrString, StrVal: val}
} }
// SetYAML implements the yaml.Setter interface.
func (intstr *IntOrString) SetYAML(tag string, value interface{}) bool {
switch v := value.(type) {
case int:
intstr.Kind = IntstrInt
intstr.IntVal = v
return true
case string:
intstr.Kind = IntstrString
intstr.StrVal = v
return true
}
return false
}
// GetYAML implements the yaml.Getter interface.
func (intstr IntOrString) GetYAML() (tag string, value interface{}) {
switch intstr.Kind {
case IntstrInt:
value = intstr.IntVal
case IntstrString:
value = intstr.StrVal
default:
panic("impossible IntOrString.Kind")
}
return
}
// UnmarshalJSON implements the json.Unmarshaller interface. // UnmarshalJSON implements the json.Unmarshaller interface.
func (intstr *IntOrString) UnmarshalJSON(value []byte) error { func (intstr *IntOrString) UnmarshalJSON(value []byte) error {
if value[0] == '"' { if value[0] == '"' {
......
...@@ -21,7 +21,7 @@ import ( ...@@ -21,7 +21,7 @@ import (
"reflect" "reflect"
"testing" "testing"
"gopkg.in/v1/yaml" "github.com/ghodss/yaml"
) )
func TestHandleCrash(t *testing.T) { func TestHandleCrash(t *testing.T) {
...@@ -54,7 +54,7 @@ func TestNewIntOrStringFromString(t *testing.T) { ...@@ -54,7 +54,7 @@ func TestNewIntOrStringFromString(t *testing.T) {
} }
type IntOrStringHolder struct { type IntOrStringHolder struct {
IOrS IntOrString `json:"val" yaml:"val"` IOrS IntOrString `json:"val"`
} }
func TestIntOrStringUnmarshalYAML(t *testing.T) { func TestIntOrStringUnmarshalYAML(t *testing.T) {
......
...@@ -20,11 +20,11 @@ package version ...@@ -20,11 +20,11 @@ package version
// TODO: Add []string of api versions supported? It's still unclear // TODO: Add []string of api versions supported? It's still unclear
// how we'll want to distribute that information. // how we'll want to distribute that information.
type Info struct { type Info struct {
Major string `json:"major" yaml:"major"` Major string `json:"major"`
Minor string `json:"minor" yaml:"minor"` Minor string `json:"minor"`
GitVersion string `json:"gitVersion" yaml:"gitVersion"` GitVersion string `json:"gitVersion"`
GitCommit string `json:"gitCommit" yaml:"gitCommit"` GitCommit string `json:"gitCommit"`
GitTreeState string `json:"gitTreeState" yaml:"gitTreeState"` GitTreeState string `json:"gitTreeState"`
} }
// Get returns the overall codebase version. It's for detecting // Get returns the overall codebase version. It's for detecting
......
...@@ -29,12 +29,12 @@ import ( ...@@ -29,12 +29,12 @@ import (
// These are not API objects and are unversioned today. // These are not API objects and are unversioned today.
type watchEvent struct { type watchEvent struct {
// The type of the watch event; added, modified, or deleted. // The type of the watch event; added, modified, or deleted.
Type watch.EventType `json:"type,omitempty" yaml:"type,omitempty"` Type watch.EventType `json:"type,omitempty"`
// For added or modified objects, this is the new object; for deleted objects, // For added or modified objects, this is the new object; for deleted objects,
// it's the state of the object immediately prior to its deletion. // it's the state of the object immediately prior to its deletion.
// For errors, it's an api.Status. // For errors, it's an api.Status.
Object runtime.RawExtension `json:"object,omitempty" yaml:"object,omitempty"` Object runtime.RawExtension `json:"object,omitempty"`
} }
// Object converts a watch.Event into an appropriately serializable JSON object // Object converts a watch.Event into an appropriately serializable JSON object
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment