Skip to content

Commit 1c54a29

Browse files
committed
container_create: Expand env variables for commands or entrypoint with format $(ENV)
Fixes: containers#15983 Signed-off-by: Chee Hau Lim <[email protected]>
1 parent 79eed08 commit 1c54a29

File tree

6 files changed

+526
-0
lines changed

6 files changed

+526
-0
lines changed

pkg/specgen/generate/container_create.go

+9
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/json"
66
"errors"
77
"fmt"
8+
"github.com/containers/podman/v4/pkg/util/expansion"
89
"os"
910
"path/filepath"
1011
"strings"
@@ -26,6 +27,14 @@ import (
2627
// Returns the created, container and any warnings resulting from creating the
2728
// container, or an error.
2829
func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator, clone bool, c *libpod.Container) (*specs.Spec, *specgen.SpecGenerator, []libpod.CtrCreateOption, error) {
30+
mapping := expansion.MappingFuncFor(s.Env)
31+
for i, subCmd := range s.Entrypoint {
32+
s.Entrypoint[i] = expansion.Expand(subCmd, mapping)
33+
}
34+
for i, subCmd := range s.Command {
35+
s.Command[i] = expansion.Expand(subCmd, mapping)
36+
}
37+
2938
rtc, err := rt.GetConfigNoCopy()
3039
if err != nil {
3140
return nil, nil, nil, err

pkg/util/expansion/LICENSE

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Copyright (c) 2009 The Go Authors. All rights reserved.
2+
3+
Redistribution and use in source and binary forms, with or without
4+
modification, are permitted provided that the following conditions are
5+
met:
6+
7+
* Redistributions of source code must retain the above copyright
8+
notice, this list of conditions and the following disclaimer.
9+
* Redistributions in binary form must reproduce the above
10+
copyright notice, this list of conditions and the following disclaimer
11+
in the documentation and/or other materials provided with the
12+
distribution.
13+
* Neither the name of Google Inc. nor the names of its
14+
contributors may be used to endorse or promote products derived from
15+
this software without specific prior written permission.
16+
17+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

pkg/util/expansion/PATENTS

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Additional IP Rights Grant (Patents)
2+
3+
"This implementation" means the copyrightable works distributed by
4+
Google as part of the Go project.
5+
6+
Google hereby grants to You a perpetual, worldwide, non-exclusive,
7+
no-charge, royalty-free, irrevocable (except as stated in this section)
8+
patent license to make, have made, use, offer to sell, sell, import,
9+
transfer and otherwise run, modify and propagate the contents of this
10+
implementation of Go, where such license applies only to those patent
11+
claims, both currently owned or controlled by Google and acquired in
12+
the future, licensable by Google that are necessarily infringed by this
13+
implementation of Go. This grant does not include claims that would be
14+
infringed only as a consequence of further modification of this
15+
implementation. If you or your agent or exclusive licensee institute or
16+
order or agree to the institution of patent litigation against any
17+
entity (including a cross-claim or counterclaim in a lawsuit) alleging
18+
that this implementation of Go or any code incorporated within this
19+
implementation of Go constitutes direct or contributory patent
20+
infringement, or inducement of patent infringement, then any patent
21+
rights granted to you under this License for this implementation of Go
22+
shall terminate as of the date such litigation is filed.

pkg/util/expansion/expand.go

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package expansion
2+
3+
import (
4+
"bytes"
5+
)
6+
7+
const (
8+
operator = '$'
9+
referenceOpener = '('
10+
referenceCloser = ')'
11+
)
12+
13+
// syntaxWrap returns the input string wrapped by the expansion syntax.
14+
func syntaxWrap(input string) string {
15+
return string(operator) + string(referenceOpener) + input + string(referenceCloser)
16+
}
17+
18+
// MappingFuncFor returns a mapping function for use with Expand that
19+
// implements the expansion semantics defined in the expansion spec; it
20+
// returns the input string wrapped in the expansion syntax if no mapping
21+
// for the input is found.
22+
func MappingFuncFor(context ...map[string]string) func(string) string {
23+
return func(input string) string {
24+
for _, vars := range context {
25+
val, ok := vars[input]
26+
if ok {
27+
return val
28+
}
29+
}
30+
31+
return syntaxWrap(input)
32+
}
33+
}
34+
35+
// Expand replaces variable references in the input string according to
36+
// the expansion spec using the given mapping function to resolve the
37+
// values of variables.
38+
func Expand(input string, mapping func(string) string) string {
39+
var buf bytes.Buffer
40+
checkpoint := 0
41+
for cursor := 0; cursor < len(input); cursor++ {
42+
if input[cursor] == operator && cursor+1 < len(input) {
43+
// Copy the portion of the input string since the last
44+
// checkpoint into the buffer
45+
buf.WriteString(input[checkpoint:cursor])
46+
47+
// Attempt to read the variable name as defined by the
48+
// syntax from the input string
49+
read, isVar, advance := tryReadVariableName(input[cursor+1:])
50+
51+
if isVar {
52+
// We were able to read a variable name correctly;
53+
// apply the mapping to the variable name and copy the
54+
// bytes into the buffer
55+
buf.WriteString(mapping(read))
56+
} else {
57+
// Not a variable name; copy the read bytes into the buffer
58+
buf.WriteString(read)
59+
}
60+
61+
// Advance the cursor in the input string to account for
62+
// bytes consumed to read the variable name expression
63+
cursor += advance
64+
65+
// Advance the checkpoint in the input string
66+
checkpoint = cursor + 1
67+
}
68+
}
69+
70+
// Return the buffer and any remaining unwritten bytes in the
71+
// input string.
72+
return buf.String() + input[checkpoint:]
73+
}
74+
75+
// tryReadVariableName attempts to read a variable name from the input
76+
// string and returns the content read from the input, whether that content
77+
// represents a variable name to perform mapping on, and the number of bytes
78+
// consumed in the input string.
79+
//
80+
// The input string is assumed not to contain the initial operator.
81+
func tryReadVariableName(input string) (string, bool, int) {
82+
switch input[0] {
83+
case operator:
84+
// Escaped operator; return it.
85+
return input[0:1], false, 1
86+
case referenceOpener:
87+
// Scan to expression closer
88+
for i := 1; i < len(input); i++ {
89+
if input[i] == referenceCloser {
90+
return input[1:i], true, i + 1
91+
}
92+
}
93+
94+
// Incomplete reference; return it.
95+
return string(operator) + string(referenceOpener), false, 1
96+
default:
97+
// Not the beginning of an expression, ie, an operator
98+
// that doesn't begin an expression. Return the operator
99+
// and the first rune in the string.
100+
return (string(operator) + string(input[0])), false, 1
101+
}
102+
}

0 commit comments

Comments
 (0)