diff --git a/cmd/client/command/common.go b/cmd/client/command/common.go index 4fbb9c7da5..f1c83610bd 100644 --- a/cmd/client/command/common.go +++ b/cmd/client/command/common.go @@ -163,7 +163,7 @@ func printBody(body []byte) { fmt.Printf("%s", output) } -func readFromFileOrStdin(specFile string, cmd *cobra.Command) ([]byte, string) { +func buildVisitorFromFileOrStdin(specFile string, cmd *cobra.Command) Visitor { var buff []byte var err error if specFile != "" { @@ -177,15 +177,5 @@ func readFromFileOrStdin(specFile string, cmd *cobra.Command) ([]byte, string) { ExitWithErrorf("%s failed: %v", cmd.Short, err) } } - - var spec struct { - Kind string `yaml:"kind"` - Name string `yaml:"name"` - } - err = yaml.Unmarshal(buff, &spec) - if err != nil { - ExitWithErrorf("%s failed, invalid spec: %v", cmd.Short, err) - } - - return buff, spec.Name + return NewStreamVisitor(string(buff)) } diff --git a/cmd/client/command/object.go b/cmd/client/command/object.go index e96308aac1..82deac4bf8 100644 --- a/cmd/client/command/object.go +++ b/cmd/client/command/object.go @@ -61,8 +61,10 @@ func createObjectCmd() *cobra.Command { Use: "create", Short: "Create an object from a yaml file or stdin", Run: func(cmd *cobra.Command, args []string) { - buff, _ := readFromFileOrStdin(specFile, cmd) - handleRequest(http.MethodPost, makeURL(objectsURL), buff, cmd) + visitor := buildVisitorFromFileOrStdin(specFile, cmd) + visitor.Visit(func(s *spec) { + handleRequest(http.MethodPost, makeURL(objectsURL), []byte(s.doc), cmd) + }) }, } @@ -77,8 +79,10 @@ func updateObjectCmd() *cobra.Command { Use: "update", Short: "Update an object from a yaml file or stdin", Run: func(cmd *cobra.Command, args []string) { - buff, name := readFromFileOrStdin(specFile, cmd) - handleRequest(http.MethodPut, makeURL(objectURL, name), buff, cmd) + visitor := buildVisitorFromFileOrStdin(specFile, cmd) + visitor.Visit(func(s *spec) { + handleRequest(http.MethodPost, makeURL(objectsURL), []byte(s.doc), cmd) + }) }, } diff --git a/cmd/client/command/visitor.go b/cmd/client/command/visitor.go new file mode 100644 index 0000000000..983324ed17 --- /dev/null +++ b/cmd/client/command/visitor.go @@ -0,0 +1,80 @@ +package command + +import ( + "bufio" + "io" + "strings" + + "k8s.io/apimachinery/pkg/util/yaml" +) + +// VisitorFunc executes visition logic +type VisitorFunc func(*spec) + +// Visitor walk through the document via VisitorFunc +type Visitor interface { + Visit(VisitorFunc) +} + +type spec struct { + Kind string + Name string + doc string +} + +type streamVisitor struct { + io.Reader +} + +// NewStreamVisitor returns a streamVisitor. +func NewStreamVisitor(src string) *streamVisitor { + return &streamVisitor{ + Reader: strings.NewReader(src), + } +} + +type yamlDecoder struct { + reader *yaml.YAMLReader + doc string +} + +func newYAMLDecoder(r io.Reader) *yamlDecoder { + return &yamlDecoder{ + reader: yaml.NewYAMLReader(bufio.NewReader(r)), + } +} + +// Decode reads a YAML document into bytes and tries to yaml.Unmarshal it. +func (d *yamlDecoder) Decode(into interface{}) error { + bytes, err := d.reader.Read() + if err != nil && err != io.EOF { + return err + } + d.doc = string(bytes) + if len(bytes) != 0 { + err = yaml.Unmarshal(bytes, into) + } + return err +} + +// Visit implements Visitor over a stream. +func (v *streamVisitor) Visit(fn VisitorFunc) { + d := newYAMLDecoder(v.Reader) + var validSpecs []spec + for { + var s spec + if err := d.Decode(&s); err != nil { + if err == io.EOF { + break + } else { + ExitWithErrorf("error parsing %s: %v", d.doc, err) + } + } + s.doc = d.doc + //TODO can validate spec's Kind here + validSpecs = append(validSpecs, s) + } + for _, s := range validSpecs { + fn(&s) + } +} diff --git a/cmd/client/command/wasm.go b/cmd/client/command/wasm.go index 3c79dbc255..4e907798df 100644 --- a/cmd/client/command/wasm.go +++ b/cmd/client/command/wasm.go @@ -85,8 +85,10 @@ func wasmApplyDataCmd() *cobra.Command { }, Run: func(cmd *cobra.Command, args []string) { - buf, _ := readFromFileOrStdin(specFile, cmd) - handleRequest(http.MethodPut, makeURL(wasmDataURL, args[0], args[1]), buf, cmd) + visitor := buildVisitorFromFileOrStdin(specFile, cmd) + visitor.Visit(func(s *spec) { + handleRequest(http.MethodPost, makeURL(objectsURL), []byte(s.doc), cmd) + }) }, } cmd.Flags().StringVarP(&specFile, "file", "f", "", "A yaml file specifying the object.")