-
Notifications
You must be signed in to change notification settings - Fork 214
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Since the nonroot distroless image doesn't have a shell, we can't use run.sh to copy the porter config files into PORTER_HOME at container start. I have implemented that in Go (sorry it's a lot vs what good ole cp did for us under the hood). One trick is that when /porter-config is mounted into the container by k8s, it uses symlinks like this: /porter-config ..data/porter.config porter.config -> ..data/porter.config So it's not a straightforward as you'd think at first glance. Signed-off-by: Carolyn Van Slyck <me@carolynvanslyck.com>
- Loading branch information
Showing
15 changed files
with
260 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
* | ||
|
||
!bin/dev/porter-linux-amd64 | ||
!bin/dev/agent-linux-amd64 | ||
!bin/mixins/exec/dev/exec-linux-amd64 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,20 @@ | ||
FROM alpine:3 as builder | ||
WORKDIR /app/.porter | ||
|
||
RUN mkdir -p /root/.porter/runtimes && \ | ||
mkdir -p /root/.porter/mixins/exec/runtimes | ||
RUN mkdir runtimes && \ | ||
mkdir -p mixins/exec/runtimes | ||
|
||
COPY bin/dev/porter-linux-amd64 /root/.porter/porter | ||
COPY bin/mixins/exec/dev/exec-linux-amd64 /root/.porter/mixins/exec/exec | ||
RUN ln -s /root/.porter/porter /root/.porter/runtimes/porter-runtime && \ | ||
ln -s /root/.porter/mixins/exec/exec /root/.porter/mixins/exec/runtimes/exec-runtime | ||
|
||
RUN porter mixin install kubernetes && \ | ||
porter mixin install helm && \ | ||
porter mixin install arm && \ | ||
porter mixin install terraform && \ | ||
porter mixin install az && \ | ||
porter mixin install aws && \ | ||
porter mixin install gcloud && \ | ||
porter plugin install azure && \ | ||
porter plugin install kubernetes | ||
# Only install porter and the exec mixin, everything else | ||
# must be mounted into the container | ||
COPY bin/dev/porter-linux-amd64 porter | ||
COPY bin/mixins/exec/dev/exec-linux-amd64 mixins/exec/exec | ||
RUN ln -s /app/.porter/porter runtimes/porter-runtime && \ | ||
ln -s /app/.porter/mixins/exec/exec mixins/exec/runtimes/exec-runtime | ||
|
||
# Copy the porter installation into a distroless container without root | ||
FROM gcr.io/distroless/static:nonroot | ||
WORKDIR /app | ||
COPY --from=builder /root/.porter /app/.porter | ||
COPY --from=builder --chown=65532:65532 /app/.porter /app/.porter | ||
ENV PATH "$PATH:/app/.porter" | ||
|
||
ENTRYPOINT ["/app/.porter/porter"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"get.porter.sh/porter/pkg/agent" | ||
) | ||
|
||
// The porter agent wraps the porter cli, | ||
// handling coping config files from a mounted | ||
// volume into PORTER_HOME | ||
func main() { | ||
porterHome := os.Getenv("PORTER_HOME") | ||
if porterHome == "" { | ||
porterHome = "/app/.porter" | ||
} | ||
porterConfig := os.Getenv("PORTER_CONFIG") | ||
if porterConfig == "" { | ||
porterConfig = "/porter-config" | ||
} | ||
err, run := agent.Execute(os.Args[1:], porterHome, porterConfig) | ||
if err != nil { | ||
if !run { | ||
fmt.Fprintln(os.Stderr, err) | ||
} | ||
|
||
os.Exit(1) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package agent | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"io/fs" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"strings" | ||
|
||
"golang.org/x/sync/errgroup" | ||
) | ||
|
||
// allow the tests to capture output | ||
var ( | ||
stdout io.Writer = os.Stdout | ||
stderr io.Writer = os.Stderr | ||
) | ||
|
||
// The porter agent wraps the porter cli, | ||
// handling coping config files from a mounted | ||
// volume into PORTER_HOME | ||
// Returns any errors and if the porter command was executed | ||
func Execute(porterCommand []string, porterHome string, porterConfig string) (error, bool) { | ||
porter := porterHome + "/porter" | ||
|
||
// Copy config files into PORTER_HOME | ||
err := filepath.Walk(porterConfig, func(path string, info fs.FileInfo, err error) error { | ||
if info.IsDir() { | ||
return nil | ||
} | ||
|
||
// Determine the relative path of the file we are copying | ||
relPath, err := filepath.Rel(porterConfig, path) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Skip hidden files, these are injected by k8s when the config volume is mounted | ||
if strings.HasPrefix(relPath, ".") { | ||
return nil | ||
} | ||
|
||
// If the files are symlinks then resolve them | ||
// /porter-config | ||
// - config.toml (symlink to the file in ..data) | ||
// - ..data/config.toml | ||
resolvedPath, err := filepath.EvalSymlinks(path) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
resolvedInfo, err := os.Stat(resolvedPath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return copyConfig(relPath, resolvedPath, resolvedInfo, porterHome) | ||
}) | ||
if err != nil { | ||
return err, false | ||
} | ||
|
||
// Remind everyone the version of Porter we are using | ||
cmd := exec.Command(porter, "version") | ||
cmd.Stdout = stdout | ||
cmd.Stderr = stderr | ||
|
||
// Run the specified porter command | ||
fmt.Fprintf(stderr, "porter %s\n", strings.Join(porterCommand, " ")) | ||
cmd = exec.Command(porter, porterCommand...) | ||
cmd.Stdout = stdout | ||
cmd.Stderr = stderr | ||
cmd.Stdin = os.Stdin | ||
if err := cmd.Start(); err != nil { | ||
return err, false | ||
} | ||
return cmd.Wait(), true | ||
} | ||
|
||
func copyConfig(relPath string, configFile string, fi os.FileInfo, porterHome string) error { | ||
destFile := filepath.Join(porterHome, relPath) | ||
fmt.Fprintln(stderr, "Loading configuration", relPath, "into", destFile) | ||
src, err := os.OpenFile(configFile, os.O_RDONLY, 0) | ||
if err != nil { | ||
return err | ||
} | ||
defer src.Close() | ||
|
||
if err = os.MkdirAll(filepath.Dir(destFile), 0700); err != nil { | ||
return err | ||
} | ||
dest, err := os.OpenFile(destFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, fi.Mode()) | ||
if err != nil { | ||
return err | ||
} | ||
defer dest.Close() | ||
|
||
if !isExecutable(fi.Mode()) { | ||
// Copy the file and write out its content at the same time | ||
wg := errgroup.Group{} | ||
pr, pw := io.Pipe() | ||
tr := io.TeeReader(src, pw) | ||
|
||
// Copy the File | ||
wg.Go(func() error { | ||
defer pw.Close() | ||
|
||
_, err = io.Copy(dest, tr) | ||
return err | ||
}) | ||
|
||
// Print out the contents of the transferred file only if it's not executable | ||
wg.Go(func() error { | ||
// read from the PipeReader to stdout | ||
_, err := io.Copy(stderr, pr) | ||
|
||
// Pad with whitespace so it's easier to see the file contents | ||
fmt.Fprintf(stderr, "\n\n") | ||
return err | ||
}) | ||
|
||
return wg.Wait() | ||
} | ||
|
||
// Just copy the file if it's binary, don't print it out | ||
_, err = io.Copy(dest, src) | ||
return err | ||
} | ||
|
||
func isExecutable(mode os.FileMode) bool { | ||
return mode&0111 != 0 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// +build integration | ||
|
||
package agent | ||
|
||
import ( | ||
"bytes" | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/carolynvs/magex/shx" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestExecute(t *testing.T) { | ||
home := makeTestPorterHome(t) | ||
defer os.RemoveAll(home) | ||
cfg := "testdata" | ||
|
||
stdoutBuff := &bytes.Buffer{} | ||
stderrBuff := &bytes.Buffer{} | ||
stdout = stdoutBuff | ||
stderr = stderrBuff | ||
|
||
err, run := Execute([]string{"help"}, home, cfg) | ||
require.NoError(t, err) | ||
assert.True(t, run, "porter should have run") | ||
gotStderr := stderrBuff.String() | ||
assert.Contains(t, stdoutBuff.String(), "Usage:", "porter command output should be printed") | ||
|
||
contents, err := os.ReadFile(filepath.Join(home, "config.toml")) | ||
require.NoError(t, err) | ||
wantTomlContents := "# I am a porter config" | ||
assert.Equal(t, wantTomlContents, string(contents)) | ||
assert.Contains(t, gotStderr, wantTomlContents, "config file contents should be printed to stderr") | ||
|
||
contents, err = os.ReadFile(filepath.Join(home, "config.json")) | ||
require.NoError(t, err) | ||
wantJsonContents := "{}" | ||
assert.Equal(t, wantJsonContents, string(contents)) | ||
assert.Contains(t, gotStderr, wantJsonContents, "config file contents should be printed to stderr") | ||
|
||
contents, err = os.ReadFile(filepath.Join(home, "a-binary")) | ||
require.NoError(t, err) | ||
wantBinaryContents := "binary contents" | ||
assert.Equal(t, wantBinaryContents, string(contents)) | ||
assert.NotContains(t, gotStderr, wantBinaryContents, "binary file contents should NOT be printed") | ||
|
||
_, err = os.Stat(filepath.Join(home, ".hidden")) | ||
require.True(t, os.IsNotExist(err), "hidden files should not be copied") | ||
} | ||
|
||
func makeTestPorterHome(t *testing.T) string { | ||
home, err := ioutil.TempDir("", "porter-home") | ||
require.NoError(t, err) | ||
require.NoError(t, shx.Copy("../../bin/porter", home)) | ||
return home | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# I am a porter config |
Oops, something went wrong.