Copy past golang/expansion from kubernetes/kubernetes
Try to stop depending on kubernetes/kubenetes. Copy golang/expansion into the virtual-kubelet repo. The upstream code looks super stable, so there is little harm to copy it here. Signed-off-by: Miek Gieben <miek@miek.nl>
This commit is contained in:
27
internal/expansion/LICENSE
Normal file
27
internal/expansion/LICENSE
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
4
internal/expansion/README.md
Normal file
4
internal/expansion/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
Copied from
|
||||
https://github.com/kubernetes/kubernetes/tree/master/third_party/forked/golang/expansion .
|
||||
|
||||
This is to eliminate a direct dependency on kubernetes/kubernetes.
|
||||
102
internal/expansion/expand.go
Normal file
102
internal/expansion/expand.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package expansion
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
const (
|
||||
operator = '$'
|
||||
referenceOpener = '('
|
||||
referenceCloser = ')'
|
||||
)
|
||||
|
||||
// syntaxWrap returns the input string wrapped by the expansion syntax.
|
||||
func syntaxWrap(input string) string {
|
||||
return string(operator) + string(referenceOpener) + input + string(referenceCloser)
|
||||
}
|
||||
|
||||
// MappingFuncFor returns a mapping function for use with Expand that
|
||||
// implements the expansion semantics defined in the expansion spec; it
|
||||
// returns the input string wrapped in the expansion syntax if no mapping
|
||||
// for the input is found.
|
||||
func MappingFuncFor(context ...map[string]string) func(string) string {
|
||||
return func(input string) string {
|
||||
for _, vars := range context {
|
||||
val, ok := vars[input]
|
||||
if ok {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
return syntaxWrap(input)
|
||||
}
|
||||
}
|
||||
|
||||
// Expand replaces variable references in the input string according to
|
||||
// the expansion spec using the given mapping function to resolve the
|
||||
// values of variables.
|
||||
func Expand(input string, mapping func(string) string) string {
|
||||
var buf bytes.Buffer
|
||||
checkpoint := 0
|
||||
for cursor := 0; cursor < len(input); cursor++ {
|
||||
if input[cursor] == operator && cursor+1 < len(input) {
|
||||
// Copy the portion of the input string since the last
|
||||
// checkpoint into the buffer
|
||||
buf.WriteString(input[checkpoint:cursor])
|
||||
|
||||
// Attempt to read the variable name as defined by the
|
||||
// syntax from the input string
|
||||
read, isVar, advance := tryReadVariableName(input[cursor+1:])
|
||||
|
||||
if isVar {
|
||||
// We were able to read a variable name correctly;
|
||||
// apply the mapping to the variable name and copy the
|
||||
// bytes into the buffer
|
||||
buf.WriteString(mapping(read))
|
||||
} else {
|
||||
// Not a variable name; copy the read bytes into the buffer
|
||||
buf.WriteString(read)
|
||||
}
|
||||
|
||||
// Advance the cursor in the input string to account for
|
||||
// bytes consumed to read the variable name expression
|
||||
cursor += advance
|
||||
|
||||
// Advance the checkpoint in the input string
|
||||
checkpoint = cursor + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Return the buffer and any remaining unwritten bytes in the
|
||||
// input string.
|
||||
return buf.String() + input[checkpoint:]
|
||||
}
|
||||
|
||||
// tryReadVariableName attempts to read a variable name from the input
|
||||
// string and returns the content read from the input, whether that content
|
||||
// represents a variable name to perform mapping on, and the number of bytes
|
||||
// consumed in the input string.
|
||||
//
|
||||
// The input string is assumed not to contain the initial operator.
|
||||
func tryReadVariableName(input string) (string, bool, int) {
|
||||
switch input[0] {
|
||||
case operator:
|
||||
// Escaped operator; return it.
|
||||
return input[0:1], false, 1
|
||||
case referenceOpener:
|
||||
// Scan to expression closer
|
||||
for i := 1; i < len(input); i++ {
|
||||
if input[i] == referenceCloser {
|
||||
return input[1:i], true, i + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Incomplete reference; return it.
|
||||
return string(operator) + string(referenceOpener), false, 1
|
||||
default:
|
||||
// Not the beginning of an expression, ie, an operator
|
||||
// that doesn't begin an expression. Return the operator
|
||||
// and the first rune in the string.
|
||||
return (string(operator) + string(input[0])), false, 1
|
||||
}
|
||||
}
|
||||
285
internal/expansion/expand_test.go
Normal file
285
internal/expansion/expand_test.go
Normal file
@@ -0,0 +1,285 @@
|
||||
package expansion
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
)
|
||||
|
||||
func TestMapReference(t *testing.T) {
|
||||
envs := []api.EnvVar{
|
||||
{
|
||||
Name: "FOO",
|
||||
Value: "bar",
|
||||
},
|
||||
{
|
||||
Name: "ZOO",
|
||||
Value: "$(FOO)-1",
|
||||
},
|
||||
{
|
||||
Name: "BLU",
|
||||
Value: "$(ZOO)-2",
|
||||
},
|
||||
}
|
||||
|
||||
declaredEnv := map[string]string{
|
||||
"FOO": "bar",
|
||||
"ZOO": "$(FOO)-1",
|
||||
"BLU": "$(ZOO)-2",
|
||||
}
|
||||
|
||||
serviceEnv := map[string]string{}
|
||||
|
||||
mapping := MappingFuncFor(declaredEnv, serviceEnv)
|
||||
|
||||
for _, env := range envs {
|
||||
declaredEnv[env.Name] = Expand(env.Value, mapping)
|
||||
}
|
||||
|
||||
expectedEnv := map[string]string{
|
||||
"FOO": "bar",
|
||||
"ZOO": "bar-1",
|
||||
"BLU": "bar-1-2",
|
||||
}
|
||||
|
||||
for k, v := range expectedEnv {
|
||||
if e, a := v, declaredEnv[k]; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
} else {
|
||||
delete(declaredEnv, k)
|
||||
}
|
||||
}
|
||||
|
||||
if len(declaredEnv) != 0 {
|
||||
t.Errorf("Unexpected keys in declared env: %v", declaredEnv)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapping(t *testing.T) {
|
||||
context := map[string]string{
|
||||
"VAR_A": "A",
|
||||
"VAR_B": "B",
|
||||
"VAR_C": "C",
|
||||
"VAR_REF": "$(VAR_A)",
|
||||
"VAR_EMPTY": "",
|
||||
}
|
||||
mapping := MappingFuncFor(context)
|
||||
|
||||
doExpansionTest(t, mapping)
|
||||
}
|
||||
|
||||
func TestMappingDual(t *testing.T) {
|
||||
context := map[string]string{
|
||||
"VAR_A": "A",
|
||||
"VAR_EMPTY": "",
|
||||
}
|
||||
context2 := map[string]string{
|
||||
"VAR_B": "B",
|
||||
"VAR_C": "C",
|
||||
"VAR_REF": "$(VAR_A)",
|
||||
}
|
||||
mapping := MappingFuncFor(context, context2)
|
||||
|
||||
doExpansionTest(t, mapping)
|
||||
}
|
||||
|
||||
func doExpansionTest(t *testing.T, mapping func(string) string) {
|
||||
cases := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "whole string",
|
||||
input: "$(VAR_A)",
|
||||
expected: "A",
|
||||
},
|
||||
{
|
||||
name: "repeat",
|
||||
input: "$(VAR_A)-$(VAR_A)",
|
||||
expected: "A-A",
|
||||
},
|
||||
{
|
||||
name: "beginning",
|
||||
input: "$(VAR_A)-1",
|
||||
expected: "A-1",
|
||||
},
|
||||
{
|
||||
name: "middle",
|
||||
input: "___$(VAR_B)___",
|
||||
expected: "___B___",
|
||||
},
|
||||
{
|
||||
name: "end",
|
||||
input: "___$(VAR_C)",
|
||||
expected: "___C",
|
||||
},
|
||||
{
|
||||
name: "compound",
|
||||
input: "$(VAR_A)_$(VAR_B)_$(VAR_C)",
|
||||
expected: "A_B_C",
|
||||
},
|
||||
{
|
||||
name: "escape & expand",
|
||||
input: "$$(VAR_B)_$(VAR_A)",
|
||||
expected: "$(VAR_B)_A",
|
||||
},
|
||||
{
|
||||
name: "compound escape",
|
||||
input: "$$(VAR_A)_$$(VAR_B)",
|
||||
expected: "$(VAR_A)_$(VAR_B)",
|
||||
},
|
||||
{
|
||||
name: "mixed in escapes",
|
||||
input: "f000-$$VAR_A",
|
||||
expected: "f000-$VAR_A",
|
||||
},
|
||||
{
|
||||
name: "backslash escape ignored",
|
||||
input: "foo\\$(VAR_C)bar",
|
||||
expected: "foo\\Cbar",
|
||||
},
|
||||
{
|
||||
name: "backslash escape ignored",
|
||||
input: "foo\\\\$(VAR_C)bar",
|
||||
expected: "foo\\\\Cbar",
|
||||
},
|
||||
{
|
||||
name: "lots of backslashes",
|
||||
input: "foo\\\\\\\\$(VAR_A)bar",
|
||||
expected: "foo\\\\\\\\Abar",
|
||||
},
|
||||
{
|
||||
name: "nested var references",
|
||||
input: "$(VAR_A$(VAR_B))",
|
||||
expected: "$(VAR_A$(VAR_B))",
|
||||
},
|
||||
{
|
||||
name: "nested var references second type",
|
||||
input: "$(VAR_A$(VAR_B)",
|
||||
expected: "$(VAR_A$(VAR_B)",
|
||||
},
|
||||
{
|
||||
name: "value is a reference",
|
||||
input: "$(VAR_REF)",
|
||||
expected: "$(VAR_A)",
|
||||
},
|
||||
{
|
||||
name: "value is a reference x 2",
|
||||
input: "%%$(VAR_REF)--$(VAR_REF)%%",
|
||||
expected: "%%$(VAR_A)--$(VAR_A)%%",
|
||||
},
|
||||
{
|
||||
name: "empty var",
|
||||
input: "foo$(VAR_EMPTY)bar",
|
||||
expected: "foobar",
|
||||
},
|
||||
{
|
||||
name: "unterminated expression",
|
||||
input: "foo$(VAR_Awhoops!",
|
||||
expected: "foo$(VAR_Awhoops!",
|
||||
},
|
||||
{
|
||||
name: "expression without operator",
|
||||
input: "f00__(VAR_A)__",
|
||||
expected: "f00__(VAR_A)__",
|
||||
},
|
||||
{
|
||||
name: "shell special vars pass through",
|
||||
input: "$?_boo_$!",
|
||||
expected: "$?_boo_$!",
|
||||
},
|
||||
{
|
||||
name: "bare operators are ignored",
|
||||
input: "$VAR_A",
|
||||
expected: "$VAR_A",
|
||||
},
|
||||
{
|
||||
name: "undefined vars are passed through",
|
||||
input: "$(VAR_DNE)",
|
||||
expected: "$(VAR_DNE)",
|
||||
},
|
||||
{
|
||||
name: "multiple (even) operators, var undefined",
|
||||
input: "$$$$$$(BIG_MONEY)",
|
||||
expected: "$$$(BIG_MONEY)",
|
||||
},
|
||||
{
|
||||
name: "multiple (even) operators, var defined",
|
||||
input: "$$$$$$(VAR_A)",
|
||||
expected: "$$$(VAR_A)",
|
||||
},
|
||||
{
|
||||
name: "multiple (odd) operators, var undefined",
|
||||
input: "$$$$$$$(GOOD_ODDS)",
|
||||
expected: "$$$$(GOOD_ODDS)",
|
||||
},
|
||||
{
|
||||
name: "multiple (odd) operators, var defined",
|
||||
input: "$$$$$$$(VAR_A)",
|
||||
expected: "$$$A",
|
||||
},
|
||||
{
|
||||
name: "missing open expression",
|
||||
input: "$VAR_A)",
|
||||
expected: "$VAR_A)",
|
||||
},
|
||||
{
|
||||
name: "shell syntax ignored",
|
||||
input: "${VAR_A}",
|
||||
expected: "${VAR_A}",
|
||||
},
|
||||
{
|
||||
name: "trailing incomplete expression not consumed",
|
||||
input: "$(VAR_B)_______$(A",
|
||||
expected: "B_______$(A",
|
||||
},
|
||||
{
|
||||
name: "trailing incomplete expression, no content, is not consumed",
|
||||
input: "$(VAR_C)_______$(",
|
||||
expected: "C_______$(",
|
||||
},
|
||||
{
|
||||
name: "operator at end of input string is preserved",
|
||||
input: "$(VAR_A)foobarzab$",
|
||||
expected: "Afoobarzab$",
|
||||
},
|
||||
{
|
||||
name: "shell escaped incomplete expr",
|
||||
input: "foo-\\$(VAR_A",
|
||||
expected: "foo-\\$(VAR_A",
|
||||
},
|
||||
{
|
||||
name: "lots of $( in middle",
|
||||
input: "--$($($($($--",
|
||||
expected: "--$($($($($--",
|
||||
},
|
||||
{
|
||||
name: "lots of $( in beginning",
|
||||
input: "$($($($($--foo$(",
|
||||
expected: "$($($($($--foo$(",
|
||||
},
|
||||
{
|
||||
name: "lots of $( at end",
|
||||
input: "foo0--$($($($(",
|
||||
expected: "foo0--$($($($(",
|
||||
},
|
||||
{
|
||||
name: "escaped operators in variable names are not escaped",
|
||||
input: "$(foo$$var)",
|
||||
expected: "$(foo$$var)",
|
||||
},
|
||||
{
|
||||
name: "newline not expanded",
|
||||
input: "\n",
|
||||
expected: "\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
expanded := Expand(tc.input, mapping)
|
||||
if e, a := tc.expected, expanded; e != a {
|
||||
t.Errorf("%v: expected %q, got %q", tc.name, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
2
internal/podutils/env.go
Executable file → Normal file
2
internal/podutils/env.go
Executable file → Normal file
@@ -20,6 +20,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/virtual-kubelet/virtual-kubelet/internal/expansion"
|
||||
"github.com/virtual-kubelet/virtual-kubelet/internal/manager"
|
||||
"github.com/virtual-kubelet/virtual-kubelet/log"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -32,7 +33,6 @@ import (
|
||||
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||
fieldpath "k8s.io/kubernetes/pkg/fieldpath"
|
||||
"k8s.io/kubernetes/pkg/kubelet/envvars"
|
||||
"k8s.io/kubernetes/third_party/forked/golang/expansion"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user