github.com/docker/libcompose@v0.4.1-0.20210616120443-2a046c0bdbf2/config/interpolation_test.go (about)

     1  package config
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"gopkg.in/yaml.v2"
    11  )
    12  
    13  func testInterpolatedLine(t *testing.T, expectedLine, interpolatedLine string, envVariables map[string]string) {
    14  	interpolatedLine, _ = parseLine(interpolatedLine, func(s string) string {
    15  		return envVariables[s]
    16  	})
    17  
    18  	assert.Equal(t, expectedLine, interpolatedLine)
    19  }
    20  
    21  func testInvalidInterpolatedLine(t *testing.T, line string) {
    22  	_, success := parseLine(line, func(string) string {
    23  		return ""
    24  	})
    25  
    26  	assert.Equal(t, false, success)
    27  }
    28  
    29  func testInterpolatedDefault(t *testing.T, line string, delim string, expectedVar string, expectedVal string) {
    30  	envVar, _ := parseLine(line, func(env string) string { return env })
    31  	pos := strings.Index(line, delim)
    32  	envDefault, _, _ := parseDefaultValue(line, pos)
    33  	assert.Equal(t, expectedVal, envDefault)
    34  	assert.Equal(t, expectedVar, envVar)
    35  }
    36  
    37  func TestParseLine(t *testing.T) {
    38  	variables := map[string]string{
    39  		"A":           "ABC",
    40  		"X":           "XYZ",
    41  		"E":           "",
    42  		"lower":       "WORKED",
    43  		"MiXeD":       "WORKED",
    44  		"split_VaLue": "WORKED",
    45  		"9aNumber":    "WORKED",
    46  		"a9Number":    "WORKED",
    47  		"defTest":     "WORKED",
    48  	}
    49  
    50  	testInterpolatedDefault(t, "${defVar:-defVal}", ":-", "defVar", "defVal")
    51  	testInterpolatedDefault(t, "${defVar2-defVal2}", "-", "defVar2", "defVal2")
    52  	testInterpolatedDefault(t, "${defVar:-def:Val}", ":-", "defVar", "def:Val")
    53  	testInterpolatedDefault(t, "${defVar:-def-Val}", ":-", "defVar", "def-Val")
    54  
    55  	testInterpolatedLine(t, "WORKED", "$lower", variables)
    56  	testInterpolatedLine(t, "WORKED", "${MiXeD}", variables)
    57  	testInterpolatedLine(t, "WORKED", "${split_VaLue}", variables)
    58  	// make sure variable name is parsed correctly with default value
    59  	testInterpolatedLine(t, "WORKED", "${defTest:-sometest}", variables)
    60  	testInterpolatedLine(t, "WORKED", "${defTest-sometest}", variables)
    61  	// Starting with a number isn't valid
    62  	testInterpolatedLine(t, "", "$9aNumber", variables)
    63  	testInterpolatedLine(t, "WORKED", "$a9Number", variables)
    64  
    65  	testInterpolatedLine(t, "ABC", "$A", variables)
    66  	testInterpolatedLine(t, "ABC", "${A}", variables)
    67  
    68  	testInterpolatedLine(t, "ABC DE", "$A DE", variables)
    69  	testInterpolatedLine(t, "ABCDE", "${A}DE", variables)
    70  
    71  	testInterpolatedLine(t, "$A", "$$A", variables)
    72  	testInterpolatedLine(t, "${A}", "$${A}", variables)
    73  
    74  	testInterpolatedLine(t, "$ABC", "$$${A}", variables)
    75  	testInterpolatedLine(t, "$ABC", "$$$A", variables)
    76  
    77  	testInterpolatedLine(t, "ABC XYZ", "$A $X", variables)
    78  	testInterpolatedLine(t, "ABCXYZ", "$A$X", variables)
    79  	testInterpolatedLine(t, "ABCXYZ", "${A}${X}", variables)
    80  
    81  	testInterpolatedLine(t, "", "$B", variables)
    82  	testInterpolatedLine(t, "", "${B}", variables)
    83  	testInterpolatedLine(t, "", "$ADE", variables)
    84  
    85  	testInterpolatedLine(t, "", "$E", variables)
    86  	testInterpolatedLine(t, "", "${E}", variables)
    87  
    88  	testInvalidInterpolatedLine(t, "${df:val}")
    89  	testInvalidInterpolatedLine(t, "${")
    90  	testInvalidInterpolatedLine(t, "$}")
    91  	testInvalidInterpolatedLine(t, "${}")
    92  	testInvalidInterpolatedLine(t, "${ }")
    93  	testInvalidInterpolatedLine(t, "${A }")
    94  	testInvalidInterpolatedLine(t, "${ A}")
    95  	testInvalidInterpolatedLine(t, "${A!}")
    96  	testInvalidInterpolatedLine(t, "$!")
    97  }
    98  
    99  type MockEnvironmentLookup struct {
   100  	Variables map[string]string
   101  }
   102  
   103  func (m MockEnvironmentLookup) Lookup(key string, config *ServiceConfig) []string {
   104  	return []string{fmt.Sprintf("%s=%s", key, m.Variables[key])}
   105  }
   106  
   107  func testInterpolatedConfig(t *testing.T, expectedConfig, interpolatedConfig string, envVariables map[string]string) {
   108  	for k, v := range envVariables {
   109  		os.Setenv(k, v)
   110  	}
   111  
   112  	expectedConfigBytes := []byte(expectedConfig)
   113  	interpolatedConfigBytes := []byte(interpolatedConfig)
   114  
   115  	expectedData := make(RawServiceMap)
   116  	interpolatedData := make(RawServiceMap)
   117  
   118  	yaml.Unmarshal(expectedConfigBytes, &expectedData)
   119  	yaml.Unmarshal(interpolatedConfigBytes, &interpolatedData)
   120  
   121  	_ = InterpolateRawServiceMap(&interpolatedData, MockEnvironmentLookup{envVariables})
   122  
   123  	for k := range envVariables {
   124  		os.Unsetenv(k)
   125  	}
   126  
   127  	assert.Equal(t, expectedData, interpolatedData)
   128  }
   129  
   130  func testInvalidInterpolatedConfig(t *testing.T, interpolatedConfig string) {
   131  	interpolatedConfigBytes := []byte(interpolatedConfig)
   132  	interpolatedData := make(RawServiceMap)
   133  	yaml.Unmarshal(interpolatedConfigBytes, &interpolatedData)
   134  
   135  	err := InterpolateRawServiceMap(&interpolatedData, new(MockEnvironmentLookup))
   136  	assert.NotNil(t, err)
   137  }
   138  
   139  func TestInterpolate(t *testing.T) {
   140  	testInterpolatedConfig(t,
   141  		`web:
   142    # unbracketed name
   143    image: busybox
   144  
   145    # array element
   146    ports:
   147      - "80:8000"
   148  
   149    # dictionary item value
   150    labels:
   151      mylabel: "myvalue"
   152  
   153    # unset value
   154    hostname: "host-"
   155  
   156    # escaped interpolation
   157    command: "${ESCAPED}"`,
   158  		`web:
   159    # unbracketed name
   160    image: $IMAGE
   161  
   162    # array element
   163    ports:
   164      - "${HOST_PORT}:8000"
   165  
   166    # dictionary item value
   167    labels:
   168      mylabel: "${LABEL_VALUE}"
   169  
   170    # unset value
   171    hostname: "host-${UNSET_VALUE}"
   172  
   173    # escaped interpolation
   174    command: "$${ESCAPED}"`, map[string]string{
   175  			"IMAGE":       "busybox",
   176  			"HOST_PORT":   "80",
   177  			"LABEL_VALUE": "myvalue",
   178  		})
   179  
   180  	// Same as above, but testing with equal signs in variables
   181  	testInterpolatedConfig(t,
   182  		`web:
   183    # unbracketed name
   184    image: =busybox
   185  
   186    # array element
   187    ports:
   188      - "=:8000"
   189  
   190    # dictionary item value
   191    labels:
   192      mylabel: "myvalue=="
   193  
   194    # unset value
   195    hostname: "host-"
   196  
   197    # escaped interpolation
   198    command: "${ESCAPED}"`,
   199  		`web:
   200    # unbracketed name
   201    image: $IMAGE
   202  
   203    # array element
   204    ports:
   205      - "${HOST_PORT}:8000"
   206  
   207    # dictionary item value
   208    labels:
   209      mylabel: "${LABEL_VALUE}"
   210  
   211    # unset value
   212    hostname: "host-${UNSET_VALUE}"
   213  
   214    # escaped interpolation
   215    command: "$${ESCAPED}"`, map[string]string{
   216  			"IMAGE":       "=busybox",
   217  			"HOST_PORT":   "=",
   218  			"LABEL_VALUE": "myvalue==",
   219  		})
   220  	// same as above but with default values
   221  	testInterpolatedConfig(t,
   222  		`web:
   223    # unbracketed name
   224    image: busybox
   225  
   226    # array element
   227    ports:
   228      - "80:8000"
   229  
   230    # dictionary item value
   231    labels:
   232      mylabel: "my-val:ue"
   233  
   234    # unset value
   235    hostname: "host-"
   236  
   237    # escaped interpolation
   238    command: "${ESCAPED}"`,
   239  
   240  		`web:
   241    # unbracketed name
   242    image: ${IMAGE:-busybox}
   243  
   244    # array element
   245    ports:
   246      - "${HOST_PORT:-80}:8000"
   247  
   248    # dictionary item value
   249    labels:
   250      mylabel: "${LABEL_VALUE-my-val:ue}"
   251  
   252    # unset value
   253    hostname: "host-${UNSET_VALUE}"
   254  
   255    # escaped interpolation
   256    command: "$${ESCAPED}"`, map[string]string{})
   257  
   258  	testInvalidInterpolatedConfig(t,
   259  		`web:
   260    image: "${"`)
   261  
   262  	testInvalidInterpolatedConfig(t,
   263  		`web:
   264    image: busybox
   265  
   266    # array element
   267    ports:
   268      - "${}:8000"`)
   269  
   270  	testInvalidInterpolatedConfig(t,
   271  		`web:
   272    image: busybox
   273  
   274    # array element
   275    ports:
   276      - "80:8000"
   277  
   278    # dictionary item value
   279    labels:
   280      mylabel: "${ LABEL_VALUE}"`)
   281  }