pkg/converter/decoder.go (137 lines of code) (raw):
// Copyright 2018 Google LLC
//
// 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
//
// https://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 converter
import (
"bytes"
"encoding/json"
"fmt"
"path/filepath"
"strings"
"github.com/ghodss/yaml"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
bundle "github.com/GoogleCloudPlatform/k8s-cluster-bundle/pkg/apis/bundle/v1alpha1"
)
// FromYAMLString creates a Decoder instance from a YAML string.
func FromYAMLString(s string) *Decoder {
return FromYAML([]byte(s))
}
// FromYAML creates a Decoder instance from a YAML byte array.
func FromYAML(b []byte) *Decoder {
return &Decoder{
data: b,
format: YAML,
}
}
// FromJSONString creates a Decoder instance from a JSON string.
func FromJSONString(s string) *Decoder {
return FromJSON([]byte(s))
}
// FromJSON creates a Decoder instance from a JSON byte array.
func FromJSON(b []byte) *Decoder {
return &Decoder{
data: b,
format: YAML,
}
}
// FromFileName creates a Decoder instance by guessing the type based on the file
// extension.
func FromFileName(fname string, contents []byte) *Decoder {
ext := filepath.Ext(fname)
var d *Decoder
switch ext {
case ".yaml", ".yml":
d = FromYAML(contents)
case ".json":
d = FromJSON(contents)
default:
// This will be an error during conversion
d = &Decoder{format: UnknownContent}
}
d.fname = fname
return d
}
// FromContentType takes an explicit content type to use for creating a
// conversion Decoder.
func FromContentType(ctype string, contents []byte) *Decoder {
ctype = strings.ToLower(ctype)
return &Decoder{format: ContentType(ctype), data: contents}
}
// Decoder converts from an object's serialized format to an actual instance of
// the object. By default, the Decoder does not allow unknown fields.
type Decoder struct {
data []byte
format ContentType
allowUnknownFields bool
// If the content came from FromFileName, store the filename and report in
// errors for debugging.
fname string
}
// AllowUnknownFields indicates whether to allow unknown fields during decoding.
func (m *Decoder) AllowUnknownFields(allow bool) *Decoder {
return &Decoder{
data: m.data,
format: m.format,
allowUnknownFields: allow,
}
}
func (m *Decoder) decode(f interface{}) error {
var err error
switch m.format {
case YAML:
var mod yaml.JSONOpt = func(d *json.Decoder) *json.Decoder {
if !m.allowUnknownFields {
d.DisallowUnknownFields()
}
return d
}
err = yaml.Unmarshal(m.data, f, mod)
case JSON:
jsonDecoder := json.NewDecoder(bytes.NewReader(m.data))
if !m.allowUnknownFields {
jsonDecoder.DisallowUnknownFields()
}
err = jsonDecoder.Decode(f)
default:
err = fmt.Errorf("unknown content type: %q", m.format)
}
if err != nil && m.fname != "" {
return fmt.Errorf("while decoding contents from file %v, %v", m.fname, err)
}
return err
}
// ToBundle converts input data to the Bundle type.
func (m *Decoder) ToBundle() (*bundle.Bundle, error) {
d := &bundle.Bundle{}
if err := m.decode(d); err != nil {
return nil, fmt.Errorf("while converting to Bundle, %v", err)
}
return d, nil
}
// ToBundleBuilder converts input data to the BundleBuilder type.
func (m *Decoder) ToBundleBuilder() (*bundle.BundleBuilder, error) {
d := &bundle.BundleBuilder{}
if err := m.decode(d); err != nil {
return nil, fmt.Errorf("while converting to BundleBuilder, %v", err)
}
return d, nil
}
// ToComponent converts input data to the Component type.
func (m *Decoder) ToComponent() (*bundle.Component, error) {
d := &bundle.Component{}
if err := m.decode(d); err != nil {
return nil, fmt.Errorf("while converting to Component, %v", err)
}
return d, nil
}
// ToComponentBuilder converts input data to the ComponentBuilder type.
func (m *Decoder) ToComponentBuilder() (*bundle.ComponentBuilder, error) {
d := &bundle.ComponentBuilder{}
if err := m.decode(d); err != nil {
return nil, fmt.Errorf("while converting to ComponentBuilder, %v", err)
}
return d, nil
}
// ToComponentSet converts input data to the ComponentSet type.
func (m *Decoder) ToComponentSet() (*bundle.ComponentSet, error) {
d := &bundle.ComponentSet{}
if err := m.decode(d); err != nil {
return nil, fmt.Errorf("while converting to ComponentSet, %v", err)
}
return d, nil
}
// ToUnstructured converts input data to the Unstructured type.
func (m *Decoder) ToUnstructured() (*unstructured.Unstructured, error) {
d := &unstructured.Unstructured{}
if err := m.decode(d); err != nil {
return nil, fmt.Errorf("while converting to Unstructured, %v", err)
}
return d, nil
}
// ToJSONMap converts from json/yaml data to a map of string-to-interface.
func (m *Decoder) ToJSONMap() (map[string]interface{}, error) {
d := make(map[string]interface{})
if err := m.decode(&d); err != nil {
return nil, fmt.Errorf("while converting to JSON Map, %v", err)
}
return d, nil
}
// ToObject converts to an arbitrary object via standard YAML / JSON
// serialization.
func (m *Decoder) ToObject(obj interface{}) error {
return m.decode(obj)
}