Skip to content

Commit

Permalink
translatesfx: factor out directive type (#455)
Browse files Browse the repository at this point in the history
* translatesfx: factor out `directive` type

* Address PR feedback
  • Loading branch information
Pablo Collins authored and jeffreyc-splunk committed Jun 11, 2021
1 parent 8b8dc56 commit f68b14d
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 123 deletions.
137 changes: 137 additions & 0 deletions cmd/translatesfx/translatesfx/directive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package translatesfx

import (
"fmt"
"strings"
)

type source int

const (
directiveSourceUnknown source = iota
directiveSourceFile
directiveSourceEnv
directiveSourceZK
directiveSourceEtcd2
directiveSourceConsul
directiveSourceVault
)

type directive struct {
fromPath string
defaultV string
fromType source
isDirective bool
flatten bool
optional bool
}

func parseDirective(m map[interface{}]interface{}) (out directive, err error) {
fromRaw, ok := m["#from"]
if !ok {
return
}
out.isDirective = true
from, ok := fromRaw.(string)
if !ok {
return out, fmt.Errorf("error parsing from %v", m)
}

out.fromType, out.fromPath, err = parseFrom(from)
if err != nil {
return
}

out.flatten, err = parseFlatten(m)
if err != nil {
return
}

out.defaultV, err = parseDefault(m)
if err != nil {
return
}

out.optional, err = parseOptional(m)
if err != nil {
return
}

return
}

func parseFrom(from string) (source, string, error) {
idx := strings.Index(from, ":")
if idx == -1 {
return directiveSourceFile, from, nil
}
s := strToSource(from[:idx])
if s == directiveSourceUnknown {
return s, "", fmt.Errorf("#from fromType type unknown: %s", from)
}
return s, from[idx+1:], nil
}

func strToSource(s string) source {
switch s {
case "", "file":
return directiveSourceFile
case "env":
return directiveSourceEnv
case "zookeeper", "zk":
return directiveSourceZK
case "etcd2":
return directiveSourceEtcd2
case "consul":
return directiveSourceConsul
case "vault":
return directiveSourceVault
}
return directiveSourceUnknown
}

func parseFlatten(m map[interface{}]interface{}) (bool, error) {
return parseField(m, "flatten")
}

func parseOptional(m map[interface{}]interface{}) (bool, error) {
return parseField(m, "optional")
}

func parseField(m map[interface{}]interface{}, field string) (bool, error) {
v, ok := m[field]
if !ok {
return false, nil
}
out, ok := v.(bool)
if !ok {
return false, fmt.Errorf(`unable to parse field %q: %v`, field, m)
}
return out, nil
}

func parseDefault(m map[interface{}]interface{}) (string, error) {
rawDefault, ok := m["default"]
if !ok {
return "", nil
}
out, ok := rawDefault.(string)
if !ok {
return "", fmt.Errorf("unable to parse default value: %v", m)
}
return out, nil
}
62 changes: 62 additions & 0 deletions cmd/translatesfx/translatesfx/directive_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package translatesfx

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestParseDirective_Empty(t *testing.T) {
d, err := parseDirective(map[interface{}]interface{}{})
require.NoError(t, err)
assert.False(t, d.isDirective)
}

func TestParseDirective(t *testing.T) {
d, err := parseDirective(map[interface{}]interface{}{
"#from": "/some/path",
"flatten": true,
"default": "abc123",
"optional": true,
})
require.NoError(t, err)
require.True(t, d.isDirective)
assert.Equal(t, "/some/path", d.fromPath)
assert.True(t, d.flatten)
assert.Equal(t, "abc123", d.defaultV)
assert.True(t, d.optional)
}

func TestParseFrom_Simple(t *testing.T) {
source, target, err := parseFrom("/some/path")
require.NoError(t, err)
assert.Equal(t, directiveSourceFile, source)
assert.Equal(t, "/some/path", target)
}

func TestParseFrom_Env(t *testing.T) {
source, target, err := parseFrom("env:FOO")
require.NoError(t, err)
assert.Equal(t, directiveSourceEnv, source)
assert.Equal(t, "FOO", target)
}

func TestParseFrom_Unknown(t *testing.T) {
_, _, err := parseFrom("xyz:FOO")
require.Error(t, err)
}
106 changes: 34 additions & 72 deletions cmd/translatesfx/translatesfx/sa.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package translatesfx

import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -52,12 +53,16 @@ func expandSlice(l []interface{}, wd string) (interface{}, bool) {
}

func expandMap(m map[interface{}]interface{}, wd string) (interface{}, bool) {
if _, ok := m["#from"]; ok {
replacement, flatten, err := processDirective(m, wd)
d, err := parseDirective(m)
if err != nil {
log.Fatalf("parseDirective failed: %v: error %v", m, err)
}
if d.isDirective {
replacement, err := processDirective(d, wd)
if err == nil {
return replacement, flatten
return replacement, d.flatten
}
fmt.Printf("unable to process directive: %v: error: %v\n", m, err)
log.Fatalf("processDirective failed: %v: error: %v", m, err)
}
out := map[interface{}]interface{}{}
for k, v := range m {
Expand All @@ -75,33 +80,29 @@ func expandMap(m map[interface{}]interface{}, wd string) (interface{}, bool) {
return out, false
}

func processDirective(directive map[interface{}]interface{}, wd string) (interface{}, bool, error) {
expanded, err := expandFromSource(directive, wd)
func processDirective(d directive, wd string) (interface{}, error) {
expanded, err := expandFromSource(d, wd)
if err != nil {
return nil, false, err
return nil, err
}

return expanded, flatten(directive), err
return expanded, err
}

func expandFromSource(directive map[interface{}]interface{}, wd string) (interface{}, error) {
source := directiveSource(directive["#from"].(string))
switch source {
case "", "file":
return expandFiles(directive, wd)
case "env":
return expandEnv(directive)
case "watch", "remoteWatch", "zookeeper", "etcd2", "consul", "vault":
return nil, fmt.Errorf("#from source type not supported by translatesfx at this time: %s", source)
}
return nil, fmt.Errorf("#from source type unknown: %s", source)
func expandFromSource(d directive, wd string) (interface{}, error) {
switch d.fromType {
case directiveSourceFile:
return expandFiles(d, wd)
case directiveSourceEnv:
return expandEnv(d)
case directiveSourceUnknown:
return nil, fmt.Errorf("#from fromType type unknown: %v", d.fromType)
default:
return nil, fmt.Errorf("#from fromType type not supported by translatesfx at this time: %v", d.fromType)
}
}

func expandEnv(directive map[interface{}]interface{}) (interface{}, error) {
from := directive["#from"].(string)
idx := strings.Index(from, ":")
envVar := from[idx+1:]
return fmt.Sprintf("${%s}", envVar), nil
func expandEnv(d directive) (interface{}, error) {
return fmt.Sprintf("${%s}", d.fromPath), nil
}

func directiveSource(from string) string {
Expand All @@ -112,42 +113,23 @@ func directiveSource(from string) string {
return from[:idx]
}

func flatten(directive map[interface{}]interface{}) bool {
flatten := false
if f, ok := directive["flatten"]; ok {
if ok = f.(bool); ok {
flatten = f.(bool)
}
}
return flatten
}

func expandFiles(directive map[interface{}]interface{}, wd string) (interface{}, error) {
from := directive["#from"].(string)
fromFullpath := from
if from[:1] != string(os.PathSeparator) {
fromFullpath = filepath.Join(wd, from)
func expandFiles(d directive, wd string) (interface{}, error) {
fromFullpath := d.fromPath
if d.fromPath[:1] != string(os.PathSeparator) {
fromFullpath = filepath.Join(wd, d.fromPath)
}
paths, err := filepath.Glob(fromFullpath)
if err != nil {
return nil, err
}

if len(paths) == 0 {
defaultVal, err := getDefault(directive)
if err != nil {
return nil, err
}
if defaultVal != "" {
return defaultVal, nil
if d.defaultV != "" {
return d.defaultV, nil
}

optional, err := isOptional(directive)
if err != nil {
return nil, err
}
if !optional {
return nil, fmt.Errorf("#from files not found and directive not marked optional: %v", directive)
if !d.optional {
return nil, fmt.Errorf("#from files not found and directive not marked optional: %v", d)
}
}

Expand All @@ -162,26 +144,6 @@ func expandFiles(directive map[interface{}]interface{}, wd string) (interface{},
return merge(items)
}

func getDefault(directive map[interface{}]interface{}) (string, error) {
if v, ok := directive["default"]; ok {
if out, ok := v.(string); ok {
return out, nil
}
return "", fmt.Errorf(`unable to parse "default" field in #from directive: %v`, directive)
}
return "", nil
}

func isOptional(directive map[interface{}]interface{}) (bool, error) {
if v, ok := directive["optional"]; ok {
if optional, ok := v.(bool); ok {
return optional, nil
}
return false, fmt.Errorf(`unable to parse "optional" field in #from directive: %v`, directive)
}
return false, nil
}

func unmarshal(path string) (interface{}, error) {
bytes, err := os.ReadFile(path)
if err != nil {
Expand Down
Loading

0 comments on commit f68b14d

Please sign in to comment.