Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 32 additions & 16 deletions cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
pack "knative.dev/func/pkg/builders/buildpacks"
"knative.dev/func/pkg/builders/s2i"
"knative.dev/func/pkg/config"
"knative.dev/func/pkg/docker"
fn "knative.dev/func/pkg/functions"
"knative.dev/func/pkg/oci"
)
Expand All @@ -29,7 +30,7 @@ SYNOPSIS
{{rootCmdUse}} build [-r|--registry] [--builder] [--builder-image]
[--push] [--username] [--password] [--token]
[--platform] [-p|--path] [-c|--confirm] [-v|--verbose]
[--build-timestamp] [--registry-insecure]
[--build-timestamp] [--registry-insecure] [--registry-authfile]

DESCRIPTION

Expand Down Expand Up @@ -69,7 +70,7 @@ EXAMPLES
SuggestFor: []string{"biuld", "buidl", "built"},
PreRunE: bindEnv("image", "path", "builder", "registry", "confirm",
"push", "builder-image", "base-image", "platform", "verbose",
"build-timestamp", "registry-insecure", "username", "password", "token"),
"build-timestamp", "registry-insecure", "registry-authfile", "username", "password", "token"),
RunE: func(cmd *cobra.Command, args []string) error {
return runBuild(cmd, args, newClient)
},
Expand Down Expand Up @@ -102,6 +103,7 @@ EXAMPLES
cmd.Flags().StringP("registry", "r", cfg.Registry,
"Container registry + registry namespace. (ex 'ghcr.io/myuser'). The full image name is automatically determined using this along with function name. ($FUNC_REGISTRY)")
cmd.Flags().Bool("registry-insecure", cfg.RegistryInsecure, "Skip TLS certificate verification when communicating in HTTPS with the registry ($FUNC_REGISTRY_INSECURE)")
cmd.Flags().String("registry-authfile", "", "Path to a authentication file containing registry credentials ($FUNC_REGISTRY_AUTHFILE)")

// Function-Context Flags:
// Options whose value is available on the function with context only
Expand Down Expand Up @@ -293,6 +295,9 @@ type buildConfig struct {
// Build with the current timestamp as the created time for docker image.
// This is only useful for buildpacks builder.
WithTimestamp bool

// RegistryAuthfile is the path to a docker-config file containing registry credentials.
RegistryAuthfile string
}

// newBuildConfig gathers options into a single build request.
Expand All @@ -305,16 +310,17 @@ func newBuildConfig() buildConfig {
Verbose: viper.GetBool("verbose"),
RegistryInsecure: viper.GetBool("registry-insecure"),
},
BuilderImage: viper.GetString("builder-image"),
BaseImage: viper.GetString("base-image"),
Image: viper.GetString("image"),
Path: viper.GetString("path"),
Platform: viper.GetString("platform"),
Push: viper.GetBool("push"),
Username: viper.GetString("username"),
Password: viper.GetString("password"),
Token: viper.GetString("token"),
WithTimestamp: viper.GetBool("build-timestamp"),
BuilderImage: viper.GetString("builder-image"),
BaseImage: viper.GetString("base-image"),
Image: viper.GetString("image"),
Path: viper.GetString("path"),
Platform: viper.GetString("platform"),
Push: viper.GetBool("push"),
Username: viper.GetString("username"),
Password: viper.GetString("password"),
Token: viper.GetString("token"),
WithTimestamp: viper.GetBool("build-timestamp"),
RegistryAuthfile: viper.GetString("registry-authfile"),
}
}

Expand Down Expand Up @@ -479,10 +485,12 @@ func (c buildConfig) Validate(cmd *cobra.Command) (err error) {
// deployment is not the contiainer, but rather the running service.
func (c buildConfig) clientOptions() ([]fn.Option, error) {
o := []fn.Option{fn.WithRegistry(c.Registry)}

t := newTransport(c.RegistryInsecure)
creds := newCredentialsProvider(config.Dir(), t, c.RegistryAuthfile)

switch c.Builder {
case builders.Host:
t := newTransport(c.RegistryInsecure) // may provide a custom impl which proxies
creds := newCredentialsProvider(config.Dir(), t)
o = append(o,
fn.WithBuilder(oci.NewBuilder(builders.Host, c.Verbose)),
fn.WithPusher(oci.NewPusher(c.RegistryInsecure, false, c.Verbose,
Expand All @@ -495,12 +503,20 @@ func (c buildConfig) clientOptions() ([]fn.Option, error) {
fn.WithBuilder(pack.NewBuilder(
pack.WithName(builders.Pack),
pack.WithTimestamp(c.WithTimestamp),
pack.WithVerbose(c.Verbose))))
pack.WithVerbose(c.Verbose))),
fn.WithPusher(docker.NewPusher(
docker.WithCredentialsProvider(creds),
docker.WithTransport(t),
docker.WithVerbose(c.Verbose))))
case builders.S2I:
o = append(o,
fn.WithBuilder(s2i.NewBuilder(
s2i.WithName(builders.S2I),
s2i.WithVerbose(c.Verbose))))
s2i.WithVerbose(c.Verbose))),
fn.WithPusher(docker.NewPusher(
docker.WithCredentialsProvider(creds),
docker.WithTransport(t),
docker.WithVerbose(c.Verbose))))
default:
return o, builders.ErrUnknownBuilder{Name: c.Builder, Known: KnownBuilders()}
}
Expand Down
14 changes: 10 additions & 4 deletions cmd/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ func NewTestClient(options ...fn.Option) ClientFactory {
// 'Verbose' indicates the system should write out a higher amount of logging.
func NewClient(cfg ClientConfig, options ...fn.Option) (*fn.Client, func()) {
var (
t = newTransport(cfg.InsecureSkipVerify) // may provide a custom impl which proxies
c = newCredentialsProvider(config.Dir(), t) // for accessing registries
d = newKnativeDeployer(cfg.Verbose) // default deployer (can be overridden via options)
t = newTransport(cfg.InsecureSkipVerify) // may provide a custom impl which proxies
c = newCredentialsProvider(config.Dir(), t, "") // for accessing registries
d = newKnativeDeployer(cfg.Verbose) // default deployer (can be overridden via options)
pp = newTektonPipelinesProvider(c, cfg.Verbose)
o = []fn.Option{ // standard (shared) options for all commands
fn.WithVerbose(cfg.Verbose),
Expand Down Expand Up @@ -101,7 +101,8 @@ func newTransport(insecureSkipVerify bool) fnhttp.RoundTripCloser {
// newCredentialsProvider returns a credentials provider which possibly
// has cluster-flavor specific additional credential loaders to take advantage
// of features or configuration nuances of cluster variants.
func newCredentialsProvider(configPath string, t http.RoundTripper) oci.CredentialsProvider {
// If authFilePath is provided (non-empty), it will be used as the primary auth file.
func newCredentialsProvider(configPath string, t http.RoundTripper, authFilePath string) oci.CredentialsProvider {
additionalLoaders := append(k8s.GetOpenShiftDockerCredentialLoaders(), k8s.GetGoogleCredentialLoader()...)
additionalLoaders = append(additionalLoaders, k8s.GetECRCredentialLoader()...)
additionalLoaders = append(additionalLoaders, k8s.GetACRCredentialLoader()...)
Expand All @@ -112,6 +113,11 @@ func newCredentialsProvider(configPath string, t http.RoundTripper) oci.Credenti
creds.WithAdditionalCredentialLoaders(additionalLoaders...),
}

// If a custom auth file path is provided, use it
if authFilePath != "" {
options = append(options, creds.WithAuthFilePath(authFilePath))
}

// Other cluster variants can be supported here
return creds.NewCredentialsProvider(configPath, options...)
}
Expand Down
15 changes: 12 additions & 3 deletions cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ SYNOPSIS
[-b|--build] [--builder] [--builder-image] [-p|--push]
[--domain] [--platform] [--build-timestamp] [--pvc-size]
[--service-account] [-c|--confirm] [-v|--verbose]
[--registry-insecure] [--remote-storage-class]
[--registry-insecure] [--registry-authfile] [--remote-storage-class]

DESCRIPTION

Expand Down Expand Up @@ -132,8 +132,9 @@ EXAMPLES
PreRunE: bindEnv("build", "build-timestamp", "builder", "builder-image",
"base-image", "confirm", "domain", "env", "git-branch", "git-dir",
"git-url", "image", "namespace", "path", "platform", "push", "pvc-size",
"service-account", "deployer", "registry", "registry-insecure", "remote",
"username", "password", "token", "verbose", "remote-storage-class"),
"service-account", "deployer", "registry", "registry-insecure",
"registry-authfile", "remote", "username", "password", "token", "verbose",
"remote-storage-class"),
RunE: func(cmd *cobra.Command, args []string) error {
return runDeploy(cmd, newClient)
},
Expand Down Expand Up @@ -161,6 +162,7 @@ EXAMPLES
cmd.Flags().StringP("registry", "r", cfg.Registry,
"Container registry + registry namespace. (ex 'ghcr.io/myuser'). The full image name is automatically determined using this along with function name. ($FUNC_REGISTRY)")
cmd.Flags().Bool("registry-insecure", cfg.RegistryInsecure, "Skip TLS certificate verification when communicating in HTTPS with the registry ($FUNC_REGISTRY_INSECURE)")
cmd.Flags().String("registry-authfile", "", "Path to a authentication file containing registry credentials ($FUNC_REGISTRY_AUTHFILE)")

// Function-Context Flags:
// Options whose value is available on the function with context only
Expand Down Expand Up @@ -918,6 +920,13 @@ func (c deployConfig) clientOptions() ([]fn.Option, error) {
return o, err
}

t := newTransport(c.RegistryInsecure)
creds := newCredentialsProvider(config.Dir(), t, c.RegistryAuthfile)

// Override the pipelines provider to use custom credentials
// This is needed for remote builds (deploy --remote)
o = append(o, fn.WithPipelinesProvider(newTektonPipelinesProvider(creds, c.Verbose)))

// Add the appropriate deployer based on deploy type
deployer := c.Deployer
if deployer == "" {
Expand Down
29 changes: 15 additions & 14 deletions docs/reference/func_build.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ SYNOPSIS
func build [-r|--registry] [--builder] [--builder-image]
[--push] [--username] [--password] [--token]
[--platform] [-p|--path] [-c|--confirm] [-v|--verbose]
[--build-timestamp] [--registry-insecure]
[--build-timestamp] [--registry-insecure] [--registry-authfile]

DESCRIPTION

Expand Down Expand Up @@ -57,19 +57,20 @@ func build
### Options

```
--base-image string Override the base image for your function (host builder only)
--build-timestamp Use the actual time as the created time for the docker image. This is only useful for buildpacks builder.
-b, --builder string Builder to use when creating the function's container. Currently supported builders are "host", "pack" and "s2i". ($FUNC_BUILDER) (default "pack")
--builder-image string Specify a custom builder image for use by the builder other than its default. ($FUNC_BUILDER_IMAGE)
-c, --confirm Prompt to confirm options interactively ($FUNC_CONFIRM)
-h, --help help for build
-i, --image string Full image name in the form [registry]/[namespace]/[name]:[tag] (optional). This option takes precedence over --registry ($FUNC_IMAGE)
-p, --path string Path to the function. Default is current directory ($FUNC_PATH)
--platform string Optionally specify a target platform, for example "linux/amd64" when using the s2i build strategy
-u, --push Attempt to push the function image to the configured registry after being successfully built
-r, --registry string Container registry + registry namespace. (ex 'ghcr.io/myuser'). The full image name is automatically determined using this along with function name. ($FUNC_REGISTRY)
--registry-insecure Skip TLS certificate verification when communicating in HTTPS with the registry ($FUNC_REGISTRY_INSECURE)
-v, --verbose Print verbose logs ($FUNC_VERBOSE)
--base-image string Override the base image for your function (host builder only)
--build-timestamp Use the actual time as the created time for the docker image. This is only useful for buildpacks builder.
-b, --builder string Builder to use when creating the function's container. Currently supported builders are "host", "pack" and "s2i". ($FUNC_BUILDER) (default "pack")
--builder-image string Specify a custom builder image for use by the builder other than its default. ($FUNC_BUILDER_IMAGE)
-c, --confirm Prompt to confirm options interactively ($FUNC_CONFIRM)
-h, --help help for build
-i, --image string Full image name in the form [registry]/[namespace]/[name]:[tag] (optional). This option takes precedence over --registry ($FUNC_IMAGE)
-p, --path string Path to the function. Default is current directory ($FUNC_PATH)
--platform string Optionally specify a target platform, for example "linux/amd64" when using the s2i build strategy
-u, --push Attempt to push the function image to the configured registry after being successfully built
-r, --registry string Container registry + registry namespace. (ex 'ghcr.io/myuser'). The full image name is automatically determined using this along with function name. ($FUNC_REGISTRY)
--registry-authfile string Path to a authentication file containing registry credentials ($FUNC_REGISTRY_AUTHFILE)
--registry-insecure Skip TLS certificate verification when communicating in HTTPS with the registry ($FUNC_REGISTRY_INSECURE)
-v, --verbose Print verbose logs ($FUNC_VERBOSE)
```

### SEE ALSO
Expand Down
3 changes: 2 additions & 1 deletion docs/reference/func_deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ SYNOPSIS
[-b|--build] [--builder] [--builder-image] [-p|--push]
[--domain] [--platform] [--build-timestamp] [--pvc-size]
[--service-account] [-c|--confirm] [-v|--verbose]
[--registry-insecure] [--remote-storage-class]
[--registry-insecure] [--registry-authfile] [--remote-storage-class]

DESCRIPTION

Expand Down Expand Up @@ -133,6 +133,7 @@ func deploy
-u, --push Push the function image to registry before deploying. ($FUNC_PUSH) (default true)
--pvc-size string When triggering a remote deployment, set a custom volume size to allocate for the build operation ($FUNC_PVC_SIZE)
-r, --registry string Container registry + registry namespace. (ex 'ghcr.io/myuser'). The full image name is automatically determined using this along with function name. ($FUNC_REGISTRY)
--registry-authfile string Path to a authentication file containing registry credentials ($FUNC_REGISTRY_AUTHFILE)
--registry-insecure Skip TLS certificate verification when communicating in HTTPS with the registry ($FUNC_REGISTRY_INSECURE)
-R, --remote Trigger a remote deployment. Default is to deploy and build from the local system ($FUNC_REMOTE)
--remote-storage-class string Specify a storage class to use for the volume on-cluster during remote builds
Expand Down
13 changes: 12 additions & 1 deletion pkg/creds/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ type credentialsProvider struct {

type Opt func(opts *credentialsProvider)

// WithAuthFilePath sets a custom path to a docker-config file containing registry credentials.
// If not specified, the default path (configPath/auth.json) will be used.
func WithAuthFilePath(path string) Opt {
return func(opts *credentialsProvider) {
opts.authFilePath = path
}
}

// WithPromptForCredentials sets custom callback that is supposed to
// interactively ask for credentials in case the credentials cannot be found in configuration files.
// The callback may be called multiple times in case incorrect credentials were returned before.
Expand Down Expand Up @@ -187,7 +195,10 @@ func NewCredentialsProvider(configPath string, opts ...Opt) oci.CredentialsProvi
return oci.Credentials{}, ErrCredentialsNotFound
})

c.authFilePath = filepath.Join(configPath, "auth.json")
// Set authFilePath if not already set by WithAuthFilePath option
if c.authFilePath == "" {
c.authFilePath = filepath.Join(configPath, "auth.json")
}
sys := &containersTypes.SystemContext{
AuthFilePath: c.authFilePath,
}
Expand Down
85 changes: 79 additions & 6 deletions pkg/creds/credentials_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -642,11 +642,9 @@ func TestCredentialsWithoutHome(t *testing.T) {
)

got, err := credentialsProvider(context.Background(), tt.args.registry+"/someorg/someimage:sometag")

// ASSERT
if err != nil {
t.Errorf("%v", err)
return
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("got: %v, want: %v", got, tt.want)
Expand Down Expand Up @@ -803,8 +801,7 @@ func TestCredentialsHomePermissions(t *testing.T) {

got, err := credentialsProvider(context.Background(), tt.args.registry+"/someorg/someimage:sometag")
if err != nil {
t.Errorf("%v", err)
return
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("got: %v, want: %v", got, tt.want)
Expand All @@ -815,10 +812,85 @@ func TestCredentialsHomePermissions(t *testing.T) {
}
}

func TestCredentialsFromAuthfile(t *testing.T) {
tests := []struct {
name string
verifyCredentials creds.VerifyCredentialsCallback
authFileContent string
image string
want Credentials
}{
{
name: "Single registry auth file",
verifyCredentials: correctVerifyCbk,
authFileContent: fmt.Sprintf(`
{
"auths": {
"docker.io": {
"auth": "%s"
}
}
}
`, base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", dockerIoUser, dockerIoUserPwd)))),
image: "docker.io/someorg/someimage:sometag",
want: Credentials{Username: dockerIoUser, Password: dockerIoUserPwd},
},
{
name: "Auth file with multiple registries",
verifyCredentials: correctVerifyCbk,
authFileContent: fmt.Sprintf(`
{
"auths": {
"docker.io": {
"auth": "%s"
},
"quay.io": {
"auth": "%s"
}
}
}
`, base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", dockerIoUser, dockerIoUserPwd))),
base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", quayIoUser, quayIoUserPwd)))),
image: "quay.io/someorg/someimage:sometag",
want: Credentials{Username: quayIoUser, Password: quayIoUserPwd},
},
}

// reset HOME to the original value after tests since they may change it
defer func() {
os.Setenv("HOME", homeTempDir)
}()

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resetHomeDir(t)

authFile := fmt.Sprintf("%s/authfile.json", t.TempDir())
if err := os.WriteFile(authFile, []byte(tt.authFileContent), 06444); err != nil {
t.Fatalf("failed to write auth file: %s", err)
}

credentialsProvider := creds.NewCredentialsProvider(
testConfigPath(t),
creds.WithVerifyCredentials(tt.verifyCredentials),
creds.WithAuthFilePath(authFile),
)

got, err := credentialsProvider(context.Background(), tt.image)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("got: %v, want: %v", got, tt.want)
}
})
}
}

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

func resetHomeDir(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Right below that line in 906 is t.TempDir() which probably should be t.Helper(). And the function right below resetHomePermissions is missing the t.Helper() too.

Copy link
Member Author

Choose a reason for hiding this comment

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

👍

t.TempDir()
t.Helper()
if err := os.RemoveAll(homeTempDir); err != nil {
t.Fatal(err)
}
Expand All @@ -829,6 +901,7 @@ func resetHomeDir(t *testing.T) {

// resetHomePermissions resets the HOME perms to 0700 (same as resetHomeDir(t))
func resetHomePermissions(t *testing.T) {
t.Helper()
if err := os.Chmod(homeTempDir, 0700); err != nil {
t.Fatal(err)
}
Expand Down
Loading