mirror of
https://github.com/openfaas/faasd.git
synced 2025-06-08 16:06:47 +00:00
**What** - Use the compose-go library to read the service definitions from an external compose file instead of building them in Go - Add default compose file and copy during `faasd install` - Add test for load and parse of compose file - Make testing easier by sorting the env keys - Allow append to instantiate the slices so that we can more easily test for proper parsing (e.g. nil is still nil etc) - Add the arch suffix to the compose file and set this as part of the env when we parse the compose file. This allows faasd to dynamically set the arch suffix used for the basic auth and the gateway images. Signed-off-by: Lucas Roesler <roesler.lucas@gmail.com>
391 lines
9.6 KiB
Go
Generated
391 lines
9.6 KiB
Go
Generated
package yaml
|
|
|
|
import (
|
|
"encoding"
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
"regexp"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// jsonNumber is the interface of the encoding/json.Number datatype.
|
|
// Repeating the interface here avoids a dependency on encoding/json, and also
|
|
// supports other libraries like jsoniter, which use a similar datatype with
|
|
// the same interface. Detecting this interface is useful when dealing with
|
|
// structures containing json.Number, which is a string under the hood. The
|
|
// encoder should prefer the use of Int64(), Float64() and string(), in that
|
|
// order, when encoding this type.
|
|
type jsonNumber interface {
|
|
Float64() (float64, error)
|
|
Int64() (int64, error)
|
|
String() string
|
|
}
|
|
|
|
type encoder struct {
|
|
emitter yaml_emitter_t
|
|
event yaml_event_t
|
|
out []byte
|
|
flow bool
|
|
// doneInit holds whether the initial stream_start_event has been
|
|
// emitted.
|
|
doneInit bool
|
|
}
|
|
|
|
func newEncoder() *encoder {
|
|
e := &encoder{}
|
|
yaml_emitter_initialize(&e.emitter)
|
|
yaml_emitter_set_output_string(&e.emitter, &e.out)
|
|
yaml_emitter_set_unicode(&e.emitter, true)
|
|
return e
|
|
}
|
|
|
|
func newEncoderWithWriter(w io.Writer) *encoder {
|
|
e := &encoder{}
|
|
yaml_emitter_initialize(&e.emitter)
|
|
yaml_emitter_set_output_writer(&e.emitter, w)
|
|
yaml_emitter_set_unicode(&e.emitter, true)
|
|
return e
|
|
}
|
|
|
|
func (e *encoder) init() {
|
|
if e.doneInit {
|
|
return
|
|
}
|
|
yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)
|
|
e.emit()
|
|
e.doneInit = true
|
|
}
|
|
|
|
func (e *encoder) finish() {
|
|
e.emitter.open_ended = false
|
|
yaml_stream_end_event_initialize(&e.event)
|
|
e.emit()
|
|
}
|
|
|
|
func (e *encoder) destroy() {
|
|
yaml_emitter_delete(&e.emitter)
|
|
}
|
|
|
|
func (e *encoder) emit() {
|
|
// This will internally delete the e.event value.
|
|
e.must(yaml_emitter_emit(&e.emitter, &e.event))
|
|
}
|
|
|
|
func (e *encoder) must(ok bool) {
|
|
if !ok {
|
|
msg := e.emitter.problem
|
|
if msg == "" {
|
|
msg = "unknown problem generating YAML content"
|
|
}
|
|
failf("%s", msg)
|
|
}
|
|
}
|
|
|
|
func (e *encoder) marshalDoc(tag string, in reflect.Value) {
|
|
e.init()
|
|
yaml_document_start_event_initialize(&e.event, nil, nil, true)
|
|
e.emit()
|
|
e.marshal(tag, in)
|
|
yaml_document_end_event_initialize(&e.event, true)
|
|
e.emit()
|
|
}
|
|
|
|
func (e *encoder) marshal(tag string, in reflect.Value) {
|
|
if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() {
|
|
e.nilv()
|
|
return
|
|
}
|
|
iface := in.Interface()
|
|
switch m := iface.(type) {
|
|
case jsonNumber:
|
|
integer, err := m.Int64()
|
|
if err == nil {
|
|
// In this case the json.Number is a valid int64
|
|
in = reflect.ValueOf(integer)
|
|
break
|
|
}
|
|
float, err := m.Float64()
|
|
if err == nil {
|
|
// In this case the json.Number is a valid float64
|
|
in = reflect.ValueOf(float)
|
|
break
|
|
}
|
|
// fallback case - no number could be obtained
|
|
in = reflect.ValueOf(m.String())
|
|
case time.Time, *time.Time:
|
|
// Although time.Time implements TextMarshaler,
|
|
// we don't want to treat it as a string for YAML
|
|
// purposes because YAML has special support for
|
|
// timestamps.
|
|
case Marshaler:
|
|
v, err := m.MarshalYAML()
|
|
if err != nil {
|
|
fail(err)
|
|
}
|
|
if v == nil {
|
|
e.nilv()
|
|
return
|
|
}
|
|
in = reflect.ValueOf(v)
|
|
case encoding.TextMarshaler:
|
|
text, err := m.MarshalText()
|
|
if err != nil {
|
|
fail(err)
|
|
}
|
|
in = reflect.ValueOf(string(text))
|
|
case nil:
|
|
e.nilv()
|
|
return
|
|
}
|
|
switch in.Kind() {
|
|
case reflect.Interface:
|
|
e.marshal(tag, in.Elem())
|
|
case reflect.Map:
|
|
e.mapv(tag, in)
|
|
case reflect.Ptr:
|
|
if in.Type() == ptrTimeType {
|
|
e.timev(tag, in.Elem())
|
|
} else {
|
|
e.marshal(tag, in.Elem())
|
|
}
|
|
case reflect.Struct:
|
|
if in.Type() == timeType {
|
|
e.timev(tag, in)
|
|
} else {
|
|
e.structv(tag, in)
|
|
}
|
|
case reflect.Slice, reflect.Array:
|
|
if in.Type().Elem() == mapItemType {
|
|
e.itemsv(tag, in)
|
|
} else {
|
|
e.slicev(tag, in)
|
|
}
|
|
case reflect.String:
|
|
e.stringv(tag, in)
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
if in.Type() == durationType {
|
|
e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String()))
|
|
} else {
|
|
e.intv(tag, in)
|
|
}
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
e.uintv(tag, in)
|
|
case reflect.Float32, reflect.Float64:
|
|
e.floatv(tag, in)
|
|
case reflect.Bool:
|
|
e.boolv(tag, in)
|
|
default:
|
|
panic("cannot marshal type: " + in.Type().String())
|
|
}
|
|
}
|
|
|
|
func (e *encoder) mapv(tag string, in reflect.Value) {
|
|
e.mappingv(tag, func() {
|
|
keys := keyList(in.MapKeys())
|
|
sort.Sort(keys)
|
|
for _, k := range keys {
|
|
e.marshal("", k)
|
|
e.marshal("", in.MapIndex(k))
|
|
}
|
|
})
|
|
}
|
|
|
|
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) {
|
|
sinfo, err := getStructInfo(in.Type())
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
e.mappingv(tag, func() {
|
|
for _, info := range sinfo.FieldsList {
|
|
var value reflect.Value
|
|
if info.Inline == nil {
|
|
value = in.Field(info.Num)
|
|
} else {
|
|
value = in.FieldByIndex(info.Inline)
|
|
}
|
|
if info.OmitEmpty && isZero(value) {
|
|
continue
|
|
}
|
|
e.marshal("", reflect.ValueOf(info.Key))
|
|
e.flow = info.Flow
|
|
e.marshal("", value)
|
|
}
|
|
if sinfo.InlineMap >= 0 {
|
|
m := in.Field(sinfo.InlineMap)
|
|
if m.Len() > 0 {
|
|
e.flow = false
|
|
keys := keyList(m.MapKeys())
|
|
sort.Sort(keys)
|
|
for _, k := range keys {
|
|
if _, found := sinfo.FieldsMap[k.String()]; found {
|
|
panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String()))
|
|
}
|
|
e.marshal("", k)
|
|
e.flow = false
|
|
e.marshal("", m.MapIndex(k))
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func (e *encoder) mappingv(tag string, f func()) {
|
|
implicit := tag == ""
|
|
style := yaml_BLOCK_MAPPING_STYLE
|
|
if e.flow {
|
|
e.flow = false
|
|
style = yaml_FLOW_MAPPING_STYLE
|
|
}
|
|
yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)
|
|
e.emit()
|
|
f()
|
|
yaml_mapping_end_event_initialize(&e.event)
|
|
e.emit()
|
|
}
|
|
|
|
func (e *encoder) slicev(tag string, in reflect.Value) {
|
|
implicit := tag == ""
|
|
style := yaml_BLOCK_SEQUENCE_STYLE
|
|
if e.flow {
|
|
e.flow = false
|
|
style = yaml_FLOW_SEQUENCE_STYLE
|
|
}
|
|
e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
|
|
e.emit()
|
|
n := in.Len()
|
|
for i := 0; i < n; i++ {
|
|
e.marshal("", in.Index(i))
|
|
}
|
|
e.must(yaml_sequence_end_event_initialize(&e.event))
|
|
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) {
|
|
var style yaml_scalar_style_t
|
|
s := in.String()
|
|
canUsePlain := true
|
|
switch {
|
|
case !utf8.ValidString(s):
|
|
if tag == yaml_BINARY_TAG {
|
|
failf("explicitly tagged !!binary data must be base64-encoded")
|
|
}
|
|
if tag != "" {
|
|
failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
|
|
}
|
|
// It can't be encoded directly as YAML so use a binary tag
|
|
// and encode it as base64.
|
|
tag = yaml_BINARY_TAG
|
|
s = encodeBase64(s)
|
|
case tag == "":
|
|
// Check to see if it would resolve to a specific
|
|
// tag when encoded unquoted. If it doesn't,
|
|
// there's no need to quote it.
|
|
rtag, _ := resolve("", s)
|
|
canUsePlain = rtag == yaml_STR_TAG && !isBase60Float(s)
|
|
}
|
|
// Note: it's possible for user code to emit invalid YAML
|
|
// if they explicitly specify a tag and a string containing
|
|
// text that's incompatible with that tag.
|
|
switch {
|
|
case strings.Contains(s, "\n"):
|
|
style = yaml_LITERAL_SCALAR_STYLE
|
|
case canUsePlain:
|
|
style = yaml_PLAIN_SCALAR_STYLE
|
|
default:
|
|
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
|
|
}
|
|
e.emitScalar(s, "", tag, style)
|
|
}
|
|
|
|
func (e *encoder) boolv(tag string, in reflect.Value) {
|
|
var s string
|
|
if in.Bool() {
|
|
s = "true"
|
|
} else {
|
|
s = "false"
|
|
}
|
|
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
|
}
|
|
|
|
func (e *encoder) intv(tag string, in reflect.Value) {
|
|
s := strconv.FormatInt(in.Int(), 10)
|
|
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
|
}
|
|
|
|
func (e *encoder) uintv(tag string, in reflect.Value) {
|
|
s := strconv.FormatUint(in.Uint(), 10)
|
|
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
|
}
|
|
|
|
func (e *encoder) timev(tag string, in reflect.Value) {
|
|
t := in.Interface().(time.Time)
|
|
s := t.Format(time.RFC3339Nano)
|
|
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
|
}
|
|
|
|
func (e *encoder) floatv(tag string, in reflect.Value) {
|
|
// Issue #352: When formatting, use the precision of the underlying value
|
|
precision := 64
|
|
if in.Kind() == reflect.Float32 {
|
|
precision = 32
|
|
}
|
|
|
|
s := strconv.FormatFloat(in.Float(), 'g', -1, precision)
|
|
switch s {
|
|
case "+Inf":
|
|
s = ".inf"
|
|
case "-Inf":
|
|
s = "-.inf"
|
|
case "NaN":
|
|
s = ".nan"
|
|
}
|
|
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
|
}
|
|
|
|
func (e *encoder) nilv() {
|
|
e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE)
|
|
}
|
|
|
|
func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) {
|
|
implicit := tag == ""
|
|
e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
|
|
e.emit()
|
|
}
|