Skip to content

Commit

Permalink
fix: ecr creds helper and credential_process (#510)
Browse files Browse the repository at this point in the history
Issue #, if available: #656

*Description of changes:*
- invoke `aws configure export-credentials --format process` before
running any commands that could need credentials (build, pull, push)
- Pass the results into the VM in the as environment variables if the
command returns output
- Not every user may have the AWS CLI on the correct version to have the
`export-credentials --format process command`, falls back to previous
behavior in this case

TODO: add tests for new function

*Testing done:*
  - local testing



- [x] I've reviewed the guidance in CONTRIBUTING.md


#### License Acceptance

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.

---------

Signed-off-by: Justin Alvarez <[email protected]>
  • Loading branch information
pendo324 authored Oct 31, 2023
1 parent 24fd649 commit 0525753
Show file tree
Hide file tree
Showing 5 changed files with 376 additions and 20 deletions.
12 changes: 9 additions & 3 deletions cmd/finch/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ var newApp = func(logger flog.Logger, fp path.Finch, fs afero.Fs, fc *config.Fin
)

// append nerdctl commands
allCommands := initializeNerdctlCommands(lcc, logger, fs)
allCommands := initializeNerdctlCommands(lcc, ecc, logger, fs, fc)
// append finch specific commands
allCommands = append(allCommands,
newVersionCommand(lcc, logger, stdOut),
Expand Down Expand Up @@ -140,8 +140,14 @@ func virtualMachineCommands(
)
}

func initializeNerdctlCommands(lcc command.LimaCmdCreator, logger flog.Logger, fs afero.Fs) []*cobra.Command {
nerdctlCommandCreator := newNerdctlCommandCreator(lcc, system.NewStdLib(), logger, fs)
func initializeNerdctlCommands(
lcc command.LimaCmdCreator,
ecc *command.ExecCmdCreator,
logger flog.Logger,
fs afero.Fs,
fc *config.Finch,
) []*cobra.Command {
nerdctlCommandCreator := newNerdctlCommandCreator(lcc, ecc, system.NewStdLib(), logger, fs, fc)
var allNerdctlCommands []*cobra.Command
for cmdName, cmdDescription := range nerdctlCmds {
allNerdctlCommands = append(allNerdctlCommands, nerdctlCommandCreator.create(cmdName, cmdDescription))
Expand Down
83 changes: 72 additions & 11 deletions cmd/finch/nerdctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,21 @@ package main

import (
"bufio"
"encoding/json"
"fmt"
"path/filepath"
"strings"

dockerops "github.com/docker/docker/opts"
"github.com/lima-vm/lima/pkg/networks"
"golang.org/x/exp/slices"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/spf13/afero"
"github.com/spf13/cobra"

"github.com/runfinch/finch/pkg/command"
"github.com/runfinch/finch/pkg/config"
"github.com/runfinch/finch/pkg/flog"
"github.com/runfinch/finch/pkg/lima"
"github.com/runfinch/finch/pkg/system"
Expand All @@ -33,19 +37,23 @@ type NerdctlCommandSystemDeps interface {
}

type nerdctlCommandCreator struct {
creator command.LimaCmdCreator
lcc command.LimaCmdCreator
ecc command.Creator
systemDeps NerdctlCommandSystemDeps
logger flog.Logger
fs afero.Fs
fc *config.Finch
}

func newNerdctlCommandCreator(
creator command.LimaCmdCreator,
lcc command.LimaCmdCreator,
ecc command.Creator,
systemDeps NerdctlCommandSystemDeps,
logger flog.Logger,
fs afero.Fs,
fc *config.Finch,
) *nerdctlCommandCreator {
return &nerdctlCommandCreator{creator: creator, systemDeps: systemDeps, logger: logger, fs: fs}
return &nerdctlCommandCreator{lcc: lcc, ecc: ecc, systemDeps: systemDeps, logger: logger, fs: fs, fc: fc}
}

func (ncc *nerdctlCommandCreator) create(cmdName string, cmdDesc string) *cobra.Command {
Expand All @@ -56,29 +64,38 @@ func (ncc *nerdctlCommandCreator) create(cmdName string, cmdDesc string) *cobra.
// the args passed to nerdctlCommand.run will be empty because
// cobra will try to parse `-d alpine` as if alpine is the value of the `-d` flag.
DisableFlagParsing: true,
RunE: newNerdctlCommand(ncc.creator, ncc.systemDeps, ncc.logger, ncc.fs).runAdapter,
RunE: newNerdctlCommand(ncc.lcc, ncc.ecc, ncc.systemDeps, ncc.logger, ncc.fs, ncc.fc).runAdapter,
}

return command
}

type nerdctlCommand struct {
creator command.LimaCmdCreator
lcc command.LimaCmdCreator
ecc command.Creator
systemDeps NerdctlCommandSystemDeps
logger flog.Logger
fs afero.Fs
fc *config.Finch
}

func newNerdctlCommand(creator command.LimaCmdCreator, systemDeps NerdctlCommandSystemDeps, logger flog.Logger, fs afero.Fs) *nerdctlCommand {
return &nerdctlCommand{creator: creator, systemDeps: systemDeps, logger: logger, fs: fs}
func newNerdctlCommand(
lcc command.LimaCmdCreator,
ecc command.Creator,
systemDeps NerdctlCommandSystemDeps,
logger flog.Logger,
fs afero.Fs,
fc *config.Finch,
) *nerdctlCommand {
return &nerdctlCommand{lcc: lcc, ecc: ecc, systemDeps: systemDeps, logger: logger, fs: fs, fc: fc}
}

func (nc *nerdctlCommand) runAdapter(cmd *cobra.Command, args []string) error {
return nc.run(cmd.Name(), args)
}

func (nc *nerdctlCommand) run(cmdName string, args []string) error {
err := nc.assertVMIsRunning(nc.creator, nc.logger)
err := nc.assertVMIsRunning(nc.lcc, nc.logger)
if err != nil {
return err
}
Expand Down Expand Up @@ -154,9 +171,15 @@ func (nc *nerdctlCommand) run(cmdName string, args []string) error {
}
}

var additionalEnv []string
switch cmdName {
case "build", "pull", "push":
ensureRemoteCredentials(nc.fc, nc.ecc, &additionalEnv, nc.logger)
}

// Add -E to sudo command in order to preserve existing environment variables, more info:
// https://stackoverflow.com/questions/8633461/how-to-keep-environment-variables-when-using-sudo/8633575#8633575
limaArgs := append([]string{"shell", limaInstanceName, "sudo", "-E"}, passedEnvArgs...)
limaArgs := append([]string{"shell", limaInstanceName, "sudo", "-E"}, append(additionalEnv, passedEnvArgs...)...)

limaArgs = append(limaArgs, []string{nerdctlCmdName, cmdName}...)

Expand All @@ -170,10 +193,10 @@ func (nc *nerdctlCommand) run(cmdName string, args []string) error {
limaArgs = append(limaArgs, finalArgs...)

if nc.shouldReplaceForHelp(cmdName, args) {
return nc.creator.RunWithReplacingStdout([]command.Replacement{{Source: "nerdctl", Target: "finch"}}, limaArgs...)
return nc.lcc.RunWithReplacingStdout([]command.Replacement{{Source: "nerdctl", Target: "finch"}}, limaArgs...)
}

return nc.creator.Create(limaArgs...).Run()
return nc.lcc.Create(limaArgs...).Run()
}

func (nc *nerdctlCommand) assertVMIsRunning(creator command.LimaCmdCreator, logger flog.Logger) error {
Expand Down Expand Up @@ -318,6 +341,44 @@ func resolveIP(host string, logger flog.Logger) string {
return host
}

// ensureRemoteCredentials is called before any actions that may require remote resources, in order
// to ensure that fresh credentials are available inside the VM.
// For more details on how `aws configure export-credentials` works, checks the docs.
//
// [the docs]: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/configure/export-credentials.html
func ensureRemoteCredentials(
fc *config.Finch,
ecc command.Creator,
outEnv *[]string,
logger flog.Logger,
) {
if slices.Contains(fc.CredsHelpers, "ecr-login") {
out, err := ecc.Create(
"aws",
"configure",
"export-credentials",
"--format",
"process",
).CombinedOutput()
if err != nil {
logger.Debugln("failed to run `aws configure` command")
return
}

var exportCredsOut aws.Credentials
err = json.Unmarshal(out, &exportCredsOut)
if err != nil {
logger.Debugln("`aws configure export-credentials` output is unexpected, is command available? " +
"This may result in a broken ecr-credential helper experience.")
return
}

*outEnv = append(*outEnv, fmt.Sprintf("AWS_ACCESS_KEY_ID=%s", exportCredsOut.AccessKeyID))
*outEnv = append(*outEnv, fmt.Sprintf("AWS_SECRET_ACCESS_KEY=%s", exportCredsOut.SecretAccessKey))
*outEnv = append(*outEnv, fmt.Sprintf("AWS_SESSION_TOKEN=%s", exportCredsOut.SessionToken))
}
}

var nerdctlCmds = map[string]string{
"build": "Build an image from Dockerfile",
"builder": "Manage builds",
Expand Down
Loading

0 comments on commit 0525753

Please sign in to comment.