Skip to content

Commit

Permalink
[FAB-9450] Simplify path substitution
Browse files Browse the repository at this point in the history
This change removes the old style of path variable $GOPATH.
By doing so, the substitution code can be simplified.

Additionally environment variables are now allowed to be substituted
and variables can be substituted anywhere in the string.

Change-Id: Ie3a466e244a35108df708b49b744e8c25fd3c8ca
Signed-off-by: Troy Ronda <troy@troyronda.com>
  • Loading branch information
troyronda committed Apr 10, 2018
1 parent 0bab3c9 commit 65c0afe
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 98 deletions.
80 changes: 33 additions & 47 deletions pkg/util/pathvar/subst.go
Expand Up @@ -9,6 +9,7 @@ package pathvar
import (
"bytes"
"go/build"
"os"
"path/filepath"
"strings"

Expand All @@ -25,69 +26,54 @@ func goPath() string {
}

// Subst replaces instances of '${VARNAME}' (eg ${GOPATH}) with the variable.
// As a special case, $GOPATH is also replaced.
// NOTE: this function currently only performs substitution when the path string starts with $
// as the path variables are intended to assist with testing.
// Variables names that are not set by the SDK are replaced with the environment variable.
func Subst(path string) string {
if !strings.HasPrefix(path, "$") {
return path
}

splits := strings.Split(path, "$")
const (
sepPrefix = "${"
sepSuffix = "}"
)

// Due to the first check above, the following code is currently not possible:
//if len(splits) == 1 && path == splits[0] {
// // No variables are in the path
// return path
//}
splits := strings.Split(path, sepPrefix)

var buffer bytes.Buffer
buffer.WriteString(splits[0]) // first split precedes the first $ so should always be written
for _, s := range splits[1:] {
// special case for GOPATH
if strings.HasPrefix(s, "GOPATH") {
buffer.WriteString(goPath())
buffer.WriteString(s[6:]) // Skip "GOPATH"
continue
}
buffer.WriteString(splits[0]) // first split precedes the first sepPrefix so should always be written

if !strings.HasPrefix(s, "{") {
// not a variable
buffer.WriteString("$")
buffer.WriteString(s)
continue
}
for _, s := range splits[1:] {
subst, rest := substVar(s, sepPrefix, sepSuffix)
buffer.WriteString(subst)
buffer.WriteString(rest)
}

endPos := strings.Index(s, "}") // not worrying about embedded '{'
if endPos == -1 {
// not a variable
buffer.WriteString("$")
buffer.WriteString(s)
continue
}
return buffer.String()
}

subs, ok := substVar(s[1:endPos]) // fix if not ASCII variable names
if !ok {
// not a variable
buffer.WriteString("$")
buffer.WriteString(s)
continue
}
// substVar searches for an instance of a variables name and replaces them with their value.
// The first return value is substituted portion of the string or noMatch if no replacement occurred.
// The second return value is the unconsumed portion of s.
func substVar(s string, noMatch string, sep string) (string, string) {
endPos := strings.Index(s, sep)
if endPos == -1 {
return noMatch, s
}

buffer.WriteString(subs)
buffer.WriteString(s[endPos+1:]) // fix if not ASCII variable names
v, ok := lookupVar(s[:endPos])
if !ok {
return noMatch, s
}
return buffer.String()

return v, s[endPos+1:]
}

// substVar returns the substituted variable
func substVar(v string) (s string, ok bool) {
// lookupVar returns the value of the variable.
// The local variable table is consulted first, followed by environment variables.
// Returns false if the variable doesn't exist.
func lookupVar(v string) (string, bool) {
// TODO: optimize if the number of variable names grows
switch v {
case "GOPATH":
return goPath(), true
case "CRYPTOCONFIG_FIXTURES_PATH":
return metadata.CryptoConfigPath, true
}
return "", false
return os.LookupEnv(v)
}
118 changes: 67 additions & 51 deletions pkg/util/pathvar/subst_test.go
Expand Up @@ -7,136 +7,152 @@ SPDX-License-Identifier: Apache-2.0
package pathvar

import (
"os"
"testing"

"github.com/hyperledger/fabric-sdk-go/test/metadata"
"github.com/stretchr/testify/assert"
)

func TestSubstOff(t *testing.T) {
o := "foo${CRYPTOCONFIG_FIXTURES_PATH}${GOPATH}foo"
s := Subst(o)
e := "foo${CRYPTOCONFIG_FIXTURES_PATH}${GOPATH}foo"

if s != e {
t.Fatalf("Unexpected path substitution (%s, %s)", s, e)
}
}

func TestSubstCryptoConfigMiddle(t *testing.T) {
o := "$foo${CRYPTOCONFIG_FIXTURES_PATH}foo"
s := Subst(o)
e := "$foo" + metadata.CryptoConfigPath + "foo"

if s != e {
t.Fatalf("Unexpected path substitution (%s, %s)", s, e)
}
assert.Equal(t, e, s, "Unexpected path substitution")
}

func TestSubstCryptoConfigPrefix(t *testing.T) {
o := "$foo${CRYPTOCONFIG_FIXTURES_PATH}"
s := Subst(o)
e := "$foo" + metadata.CryptoConfigPath

if s != e {
t.Fatalf("Unexpected path substitution (%s, %s)", s, e)
}
assert.Equal(t, e, s, "Unexpected path substitution")
}
func TestSubstCryptoConfigWithPostfix(t *testing.T) {
o := "${CRYPTOCONFIG_FIXTURES_PATH}foo"
s := Subst(o)
e := metadata.CryptoConfigPath + "foo"

if s != e {
t.Fatalf("Unexpected path substitution (%s, %s)", s, e)
}
assert.Equal(t, e, s, "Unexpected path substitution")
}

func TestSubstCryptoConfigOnly(t *testing.T) {
o := "${CRYPTOCONFIG_FIXTURES_PATH}"
s := Subst(o)
e := metadata.CryptoConfigPath

if s != e {
t.Fatalf("Unexpected path substitution (%s, %s)", s, e)
}
assert.Equal(t, e, s, "Unexpected path substitution")
}

func TestSubstAlmostVar1(t *testing.T) {
o := "${FOO}"
func TestSubstNotAKey(t *testing.T) {
const envKey = "FABGOSDK_TESTVAR"

o := "${" + envKey + "}"
s := Subst(o)
e := o

if s != e {
t.Fatalf("Unexpected path substitution (%s, %s)", s, e)
}
assert.Equal(t, e, s, "Unexpected path substitution")
}

func TestSubstAlmostVar2(t *testing.T) {
o := "${FOO${}${}$"
func TestSubstAlmostVar(t *testing.T) {
const envKey = "FABGOSDK_TESTVAR"

o := "${" + envKey + "{}${}$"
s := Subst(o)
e := o

if s != e {
t.Fatalf("Unexpected path substitution (%s, %s)", s, e)
}
assert.Equal(t, e, s, "Unexpected path substitution")
}

func TestSubstNoVar(t *testing.T) {
o := "foo"
s := Subst(o)
e := o

if s != e {
t.Fatalf("Unexpected path substitution (%s, %s)", s, e)
}
assert.Equal(t, e, s, "Unexpected path substitution")
}

func TestSubstEmptyVar(t *testing.T) {
o := ""
s := Subst(o)
e := o

if s != e {
t.Fatalf("Unexpected path substitution (%s, %s)", s, e)
}
assert.Equal(t, e, s, "Unexpected path substitution")
}

func TestSubstGoPath1(t *testing.T) {
o := "$foo${GOPATH}foo"
s := Subst(o)
e := "$foo" + goPath() + "foo"

if s != e {
t.Fatalf("Unexpected path substitution (%s, %s)", s, e)
}
assert.Equal(t, e, s, "Unexpected path substitution")
}

func TestSubstGoPath2(t *testing.T) {
o := "$foo${GOPATH}foo"
s := Subst(o)
e := "$foo" + goPath() + "foo"

if s != e {
t.Fatalf("Unexpected path substitution (%s, %s)", s, e)
}
assert.Equal(t, e, s, "Unexpected path substitution")
}

func TestSubstGoPathAndVar(t *testing.T) {
o := "$foo${GOPATH}foo${CRYPTOCONFIG_FIXTURES_PATH}"
s := Subst(o)
e := "$foo" + goPath() + "foo" + metadata.CryptoConfigPath

if s != e {
t.Fatalf("Unexpected path substitution (%s, %s)", s, e)
}
assert.Equal(t, e, s, "Unexpected path substitution")
}

func TestSubstGoPathOldStyle(t *testing.T) {
o := "$foo$GOPATHfoo"
s := Subst(o)
e := "$foo" + goPath() + "foo"
e := "$foo$GOPATHfoo"

assert.Equal(t, e, s, "Unexpected path substitution")
}

func TestEnvSubst(t *testing.T) {

const envKey = "FABGOSDK_TESTVAR"
const envVar = "I AM SET"

err := os.Setenv(envKey, envVar)
assert.Nil(t, err, "Setenv should have succeeded")

o := "$foo${" + envKey + "}foo"
s := Subst(o)
e := "$foo" + envVar + "foo"

assert.Equal(t, e, s, "Unexpected path substitution")

err = os.Unsetenv("FABGOSDK_TESTVAR")
assert.Nil(t, err, "Unsetenv should have succeeded")
}

func TestEnvPriority(t *testing.T) {

const envKey = "CRYPTOCONFIG_FIXTURES_PATH"
const envVar = "I AM SET"

err := os.Setenv(envKey, envVar)
assert.Nil(t, err, "Setenv should have succeeded")

o := "$foo${GOPATH}foo${CRYPTOCONFIG_FIXTURES_PATH}"
s := Subst(o)
e := "$foo" + goPath() + "foo" + metadata.CryptoConfigPath

assert.Equal(t, e, s, "Unexpected path substitution (SDK variable should take priority)")

err = os.Unsetenv("FABGOSDK_TESTVAR")
assert.Nil(t, err, "Unsetenv should have succeeded")
}

func TestSubstMiddleNoVarPrefix(t *testing.T) {
o := "foo${CRYPTOCONFIG_FIXTURES_PATH}${GOPATH}foo"
s := Subst(o)
e := "foo" + metadata.CryptoConfigPath + goPath() + "foo"

if s != e {
t.Fatalf("Unexpected path substitution (%s, %s)", s, e)
}
assert.Equal(t, e, s, "Unexpected path substitution")
}

0 comments on commit 65c0afe

Please sign in to comment.