Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run func without HOME defined/ unaccessible .config dir #2236

Merged
merged 16 commits into from
Jun 4, 2024
3 changes: 0 additions & 3 deletions cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,6 @@ func runBuild(cmd *cobra.Command, _ []string, newClient ClientFactory) (err erro
cfg buildConfig
f fn.Function
)
if err = config.CreatePaths(); err != nil { // for possible auth.json usage
return
}
Comment on lines -162 to -164
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Glad to see these indeed are no longer necessary!

if cfg, err = newBuildConfig().Prompt(); err != nil { // gather values into a single instruction set
return
}
Expand Down
3 changes: 0 additions & 3 deletions cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,6 @@ func runDeploy(cmd *cobra.Command, newClient ClientFactory) (err error) {
cfg deployConfig
f fn.Function
)
if err = config.CreatePaths(); err != nil { // for possible auth.json usage
return
}
if cfg, err = newDeployConfig(cmd).Prompt(); err != nil {
return
}
Expand Down
33 changes: 33 additions & 0 deletions cmd/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1894,3 +1894,36 @@ func TestDeploy_NoErrorOnOldFunctionNotFound(t *testing.T) {
t.Fatal(err)
}
}

// TestDeploy_WithoutHome ensures that deploying a function without HOME &
// XDG_CONFIG_HOME defined succeeds
func TestDeploy_WithoutHome(t *testing.T) {
var (
root = FromTempDirectory(t)
ns = "myns"
)

t.Setenv("HOME", "")
t.Setenv("XDG_CONFIG_HOME", "")

// Create a basic go Function
f := fn.Function{
Runtime: "go",
Root: root,
}
_, err := fn.New().Init(f)
if err != nil {
t.Fatal(err)
}

// Deploy the function
cmd := NewDeployCmd(NewTestClient(
fn.WithDeployer(mock.NewDeployer()),
fn.WithRegistry(TestRegistry)))

cmd.SetArgs([]string{fmt.Sprintf("--namespace=%s", ns)})
err = cmd.Execute()
if err != nil {
t.Fatal(err)
}
}
2 changes: 1 addition & 1 deletion pkg/builders/buildpacks/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func (b *Builder) Build(ctx context.Context, f fn.Function, platforms []fn.Platf
if err = impl.Build(ctx, opts); err != nil {
if ctx.Err() != nil {
return // SIGINT
} else if !b.verbose {
} else if b.verbose {
err = fmt.Errorf("failed to build the function: %w", err)
fmt.Fprintln(color.Stderr(), "")
_, _ = io.Copy(color.Stderr(), &b.outBuff)
Expand Down
38 changes: 23 additions & 15 deletions pkg/docker/creds/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,24 +165,31 @@ func NewCredentialsProvider(configPath string, opts ...Opt) docker.CredentialsPr
}
}

// default credential loaders map -- load only those that should be there.
var defaultCredentialLoaders = []CredentialsCallback{}

c.authFilePath = filepath.Join(configPath, "auth.json")
sys := &containersTypes.SystemContext{
AuthFilePath: c.authFilePath,
}

home, err := os.UserHomeDir()
if err != nil {
panic(err)
if _, err := os.Stat(c.authFilePath); err == nil {
defaultCredentialLoaders = append(defaultCredentialLoaders,
func(registry string) (docker.Credentials, error) {
return getCredentialsByCredentialHelper(c.authFilePath, registry)
})
}
dockerConfigPath := filepath.Join(home, ".docker", "config.json")

var defaultCredentialLoaders = []CredentialsCallback{
func(registry string) (docker.Credentials, error) {
return getCredentialsByCredentialHelper(c.authFilePath, registry)
},
func(registry string) (docker.Credentials, error) {
return getCredentialsByCredentialHelper(dockerConfigPath, registry)
},
// add only if home dir is defined -- for .docker/config.json creds
home, err := os.UserHomeDir()
if err == nil {
dockerConfigPath := filepath.Join(home, ".docker", "config.json")
defaultCredentialLoaders = append(defaultCredentialLoaders,
func(registry string) (docker.Credentials, error) {
return getCredentialsByCredentialHelper(dockerConfigPath, registry)
})
}
defaultCredentialLoaders = append(defaultCredentialLoaders,
func(registry string) (docker.Credentials, error) {
creds, err := dockerConfig.GetCredentials(sys, registry)
if err != nil {
Expand All @@ -195,7 +202,8 @@ func NewCredentialsProvider(configPath string, opts ...Opt) docker.CredentialsPr
Username: creds.Username,
Password: creds.Password,
}, nil
},
})
defaultCredentialLoaders = append(defaultCredentialLoaders,
func(registry string) (docker.Credentials, error) {
// Fallback onto default docker config locations
emptySys := &containersTypes.SystemContext{}
Expand All @@ -207,11 +215,11 @@ func NewCredentialsProvider(configPath string, opts ...Opt) docker.CredentialsPr
Username: creds.Username,
Password: creds.Password,
}, nil
},
})
defaultCredentialLoaders = append(defaultCredentialLoaders,
func(registry string) (docker.Credentials, error) { // empty credentials provider for unsecured registries
return docker.Credentials{}, nil
},
}
})

c.credentialLoaders = append(c.credentialLoaders, defaultCredentialLoaders...)

Expand Down
80 changes: 79 additions & 1 deletion pkg/docker/creds/credentials_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"net/http"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"sync"
Expand Down Expand Up @@ -284,7 +285,6 @@ func startServer(t *testing.T, uname, pwd string) (addr, addrTLS string) {
panic(err)
}
}()

// make the testing CA trusted by default HTTP transport/client
oldDefaultTransport := http.DefaultTransport
newDefaultTransport := http.DefaultTransport.(*http.Transport).Clone()
Expand Down Expand Up @@ -549,6 +549,77 @@ func TestCredentialsProviderSavingFromUserInput(t *testing.T) {
}
}

// TestCredentialsWithoutHome tests different scenarios when HOME is not set
func TestCredentialsWithoutHome(t *testing.T) {
type args struct {
promptUser creds.CredentialsCallback
verifyCredentials creds.VerifyCredentialsCallback
registry string
setUpEnv setUpEnv
}
tests := []struct {
name string
testHomePathEmpty bool
args args
want Credentials
}{
{
name: "empty home with correct user prompt",
testHomePathEmpty: true,
args: args{
promptUser: correctPwdCallback, // user inputs correct credentials
verifyCredentials: correctVerifyCbk,
registry: "docker.io",
setUpEnv: setEmptyHome,
},
want: Credentials{Username: dockerIoUser, Password: dockerIoUserPwd},
},
{
name: "empty config with user prompt",
args: args{
promptUser: correctPwdCallback,
verifyCredentials: correctVerifyCbk,
registry: "docker.io",
},
want: Credentials{Username: dockerIoUser, Password: dockerIoUserPwd},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resetHomeDir(t)
if tt.args.setUpEnv != nil {
tt.args.setUpEnv(t)
}

// prepare config path for credentials provider
var configPath string
if tt.testHomePathEmpty {
configPath = ""
} else {
configPath = testConfigPath(t)
}

credentialsProvider := creds.NewCredentialsProvider(
configPath,
creds.WithPromptForCredentials(tt.args.promptUser),
creds.WithVerifyCredentials(tt.args.verifyCredentials),
)

got, err := credentialsProvider(context.Background(), tt.args.registry+"/someorg/someimage:sometag")
if err != nil {
t.Errorf("unexpected error: %v", err)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("got: %v, want: %v", got, tt.want)
}
})
}
}

// ********************** helper functions below **************************** \\

func resetHomeDir(t *testing.T) {
t.TempDir()
if err := os.RemoveAll(homeTempDir); err != nil {
Expand Down Expand Up @@ -903,3 +974,10 @@ func (i *inMemoryHelper) Delete(serverURL string) error {

return credentials.NewErrCredentialsNotFound()
}

// set home variables to empty values
func setEmptyHome(t *testing.T) {
t.Helper()
t.Setenv("HOME", "")
t.Setenv("XDG_CONFIG_HOME", "")
}
47 changes: 47 additions & 0 deletions pkg/functions/client_int_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/docker/docker/client"

"knative.dev/func/pkg/builders/buildpacks"
"knative.dev/func/pkg/builders/s2i"
"knative.dev/func/pkg/docker"
fn "knative.dev/func/pkg/functions"
"knative.dev/func/pkg/knative"
Expand Down Expand Up @@ -545,6 +546,31 @@ func Handle(ctx context.Context, w http.ResponseWriter, req *http.Request) {
}
}

// TestDeployWithoutHome ensures that running client.New works without
// home
func TestDeployWithoutHome(t *testing.T) {
root, cleanup := Mktemp(t)
defer cleanup()

t.Setenv("HOME", "")
t.Setenv("XDG_CONFIG_HOME", "")
verbose := false
name := "test-deploy-no-home"

f := fn.Function{Runtime: "node", Name: name, Root: root, Namespace: DefaultNamespace}

// client with s2i builder because pack needs HOME
client := newClientWithS2i(verbose)

// expect to succeed
_, _, err := client.New(context.Background(), f)
if err != nil {
t.Fatalf("expected no errors but got %v", err)
}

defer del(t, client, name, DefaultNamespace)
}

// ***********
// Helpers
// ***********
Expand All @@ -571,6 +597,27 @@ func newClient(verbose bool) *fn.Client {
)
}

// copy of newClient just builder is s2i instead of buildpacks
func newClientWithS2i(verbose bool) *fn.Client {
builder := s2i.NewBuilder(s2i.WithVerbose(verbose))
pusher := docker.NewPusher(docker.WithVerbose(verbose))
deployer := knative.NewDeployer(knative.WithDeployerVerbose(verbose))
describer := knative.NewDescriber(verbose)
remover := knative.NewRemover(verbose)
lister := knative.NewLister(verbose)

return fn.New(
fn.WithRegistry(DefaultRegistry),
fn.WithVerbose(verbose),
fn.WithBuilder(builder),
fn.WithPusher(pusher),
fn.WithDeployer(deployer),
fn.WithDescriber(describer),
fn.WithRemover(remover),
fn.WithLister(lister),
)
}

// Del cleans up after a test by removing a function by name.
// (test fails if the named function does not exist)
//
Expand Down