mirror of https://github.com/knative/func.git
oci: refactors pusher auth
Signed-off-by: kapil <kapilsareen584@gmail.com>
This commit is contained in:
parent
61ecd623a2
commit
b52ddfb7f2
|
@ -394,9 +394,14 @@ func (c buildConfig) clientOptions() ([]fn.Option, error) {
|
||||||
o := []fn.Option{fn.WithRegistry(c.Registry)}
|
o := []fn.Option{fn.WithRegistry(c.Registry)}
|
||||||
switch c.Builder {
|
switch c.Builder {
|
||||||
case builders.Host:
|
case builders.Host:
|
||||||
|
t := newTransport(c.RegistryInsecure) // may provide a custom impl which proxies
|
||||||
|
creds := newCredentialsProvider(config.Dir(), t)
|
||||||
o = append(o,
|
o = append(o,
|
||||||
fn.WithBuilder(oci.NewBuilder(builders.Host, c.Verbose)),
|
fn.WithBuilder(oci.NewBuilder(builders.Host, c.Verbose)),
|
||||||
fn.WithPusher(oci.NewPusher(c.RegistryInsecure, false, c.Verbose)))
|
fn.WithPusher(oci.NewPusher(c.RegistryInsecure, false, c.Verbose,
|
||||||
|
oci.WithCredentialsProvider(creds),
|
||||||
|
oci.WithVerbose(c.Verbose))),
|
||||||
|
)
|
||||||
case builders.Pack:
|
case builders.Pack:
|
||||||
o = append(o,
|
o = append(o,
|
||||||
fn.WithBuilder(pack.NewBuilder(
|
fn.WithBuilder(pack.NewBuilder(
|
||||||
|
|
|
@ -8,12 +8,13 @@ import (
|
||||||
"knative.dev/func/cmd/prompt"
|
"knative.dev/func/cmd/prompt"
|
||||||
"knative.dev/func/pkg/builders/buildpacks"
|
"knative.dev/func/pkg/builders/buildpacks"
|
||||||
"knative.dev/func/pkg/config"
|
"knative.dev/func/pkg/config"
|
||||||
|
"knative.dev/func/pkg/creds"
|
||||||
"knative.dev/func/pkg/docker"
|
"knative.dev/func/pkg/docker"
|
||||||
"knative.dev/func/pkg/docker/creds"
|
|
||||||
fn "knative.dev/func/pkg/functions"
|
fn "knative.dev/func/pkg/functions"
|
||||||
fnhttp "knative.dev/func/pkg/http"
|
fnhttp "knative.dev/func/pkg/http"
|
||||||
"knative.dev/func/pkg/k8s"
|
"knative.dev/func/pkg/k8s"
|
||||||
"knative.dev/func/pkg/knative"
|
"knative.dev/func/pkg/knative"
|
||||||
|
"knative.dev/func/pkg/oci"
|
||||||
"knative.dev/func/pkg/pipelines/tekton"
|
"knative.dev/func/pkg/pipelines/tekton"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -100,19 +101,22 @@ func newTransport(insecureSkipVerify bool) fnhttp.RoundTripCloser {
|
||||||
// newCredentialsProvider returns a credentials provider which possibly
|
// newCredentialsProvider returns a credentials provider which possibly
|
||||||
// has cluster-flavor specific additional credential loaders to take advantage
|
// has cluster-flavor specific additional credential loaders to take advantage
|
||||||
// of features or configuration nuances of cluster variants.
|
// of features or configuration nuances of cluster variants.
|
||||||
func newCredentialsProvider(configPath string, t http.RoundTripper) docker.CredentialsProvider {
|
func newCredentialsProvider(configPath string, t http.RoundTripper) oci.CredentialsProvider {
|
||||||
|
additionalLoaders := append(k8s.GetOpenShiftDockerCredentialLoaders(), k8s.GetGoogleCredentialLoader()...)
|
||||||
|
additionalLoaders = append(additionalLoaders, k8s.GetECRCredentialLoader()...)
|
||||||
|
additionalLoaders = append(additionalLoaders, k8s.GetACRCredentialLoader()...)
|
||||||
options := []creds.Opt{
|
options := []creds.Opt{
|
||||||
creds.WithPromptForCredentials(prompt.NewPromptForCredentials(os.Stdin, os.Stdout, os.Stderr)),
|
creds.WithPromptForCredentials(prompt.NewPromptForCredentials(os.Stdin, os.Stdout, os.Stderr)),
|
||||||
creds.WithPromptForCredentialStore(prompt.NewPromptForCredentialStore()),
|
creds.WithPromptForCredentialStore(prompt.NewPromptForCredentialStore()),
|
||||||
creds.WithTransport(t),
|
creds.WithTransport(t),
|
||||||
creds.WithAdditionalCredentialLoaders(k8s.GetOpenShiftDockerCredentialLoaders()...),
|
creds.WithAdditionalCredentialLoaders(additionalLoaders...),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Other cluster variants can be supported here
|
// Other cluster variants can be supported here
|
||||||
return creds.NewCredentialsProvider(configPath, options...)
|
return creds.NewCredentialsProvider(configPath, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTektonPipelinesProvider(creds docker.CredentialsProvider, verbose bool) *tekton.PipelinesProvider {
|
func newTektonPipelinesProvider(creds oci.CredentialsProvider, verbose bool) *tekton.PipelinesProvider {
|
||||||
options := []tekton.Opt{
|
options := []tekton.Opt{
|
||||||
tekton.WithCredentialsProvider(creds),
|
tekton.WithCredentialsProvider(creds),
|
||||||
tekton.WithVerbose(verbose),
|
tekton.WithVerbose(verbose),
|
||||||
|
|
|
@ -11,14 +11,14 @@ import (
|
||||||
"github.com/AlecAivazis/survey/v2/terminal"
|
"github.com/AlecAivazis/survey/v2/terminal"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
|
|
||||||
"knative.dev/func/pkg/docker"
|
"knative.dev/func/pkg/creds"
|
||||||
"knative.dev/func/pkg/docker/creds"
|
"knative.dev/func/pkg/oci"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewPromptForCredentials(in io.Reader, out, errOut io.Writer) func(repository string) (docker.Credentials, error) {
|
func NewPromptForCredentials(in io.Reader, out, errOut io.Writer) func(repository string) (oci.Credentials, error) {
|
||||||
firstTime := true
|
firstTime := true
|
||||||
return func(repository string) (docker.Credentials, error) {
|
return func(repository string) (oci.Credentials, error) {
|
||||||
var result docker.Credentials
|
var result oci.Credentials
|
||||||
if firstTime {
|
if firstTime {
|
||||||
firstTime = false
|
firstTime = false
|
||||||
fmt.Fprintf(out, "Please provide credentials for image repository '%s'.\n", repository)
|
fmt.Fprintf(out, "Please provide credentials for image repository '%s'.\n", repository)
|
||||||
|
@ -56,7 +56,7 @@ func NewPromptForCredentials(in io.Reader, out, errOut io.Writer) func(repositor
|
||||||
if isTerm {
|
if isTerm {
|
||||||
err := survey.Ask(qs, &result, survey.WithStdio(fr, out.(terminal.FileWriter), errOut))
|
err := survey.Ask(qs, &result, survey.WithStdio(fr, out.(terminal.FileWriter), errOut))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return docker.Credentials{}, err
|
return oci.Credentials{}, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
reader := bufio.NewReader(in)
|
reader := bufio.NewReader(in)
|
||||||
|
@ -64,18 +64,18 @@ func NewPromptForCredentials(in io.Reader, out, errOut io.Writer) func(repositor
|
||||||
fmt.Fprintf(out, "Username: ")
|
fmt.Fprintf(out, "Username: ")
|
||||||
u, err := reader.ReadString('\n')
|
u, err := reader.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return docker.Credentials{}, err
|
return oci.Credentials{}, err
|
||||||
}
|
}
|
||||||
u = strings.Trim(u, "\r\n")
|
u = strings.Trim(u, "\r\n")
|
||||||
|
|
||||||
fmt.Fprintf(out, "Password: ")
|
fmt.Fprintf(out, "Password: ")
|
||||||
p, err := reader.ReadString('\n')
|
p, err := reader.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return docker.Credentials{}, err
|
return oci.Credentials{}, err
|
||||||
}
|
}
|
||||||
p = strings.Trim(p, "\r\n")
|
p = strings.Trim(p, "\r\n")
|
||||||
|
|
||||||
result = docker.Credentials{Username: u, Password: p}
|
result = oci.Credentials{Username: u, Password: p}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
|
|
|
@ -12,8 +12,7 @@ import (
|
||||||
"github.com/Netflix/go-expect"
|
"github.com/Netflix/go-expect"
|
||||||
"github.com/creack/pty"
|
"github.com/creack/pty"
|
||||||
"github.com/hinshun/vt10x"
|
"github.com/hinshun/vt10x"
|
||||||
|
"knative.dev/func/pkg/oci"
|
||||||
"knative.dev/func/pkg/docker"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -21,7 +20,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_NewPromptForCredentials(t *testing.T) {
|
func Test_NewPromptForCredentials(t *testing.T) {
|
||||||
expectedCreds := docker.Credentials{
|
expectedCreds := oci.Credentials{
|
||||||
Username: "testuser",
|
Username: "testuser",
|
||||||
Password: "testpwd",
|
Password: "testpwd",
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,10 @@ import (
|
||||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||||
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
|
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
|
||||||
|
|
||||||
"knative.dev/func/pkg/docker"
|
"knative.dev/func/pkg/oci"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CredentialsCallback func(registry string) (docker.Credentials, error)
|
type CredentialsCallback func(registry string) (oci.Credentials, error)
|
||||||
|
|
||||||
var ErrUnauthorized = errors.New("bad credentials")
|
var ErrUnauthorized = errors.New("bad credentials")
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ var ErrCredentialsNotFound = errors.New("credentials not found")
|
||||||
|
|
||||||
// VerifyCredentialsCallback checks if credentials are authorized for image push.
|
// VerifyCredentialsCallback checks if credentials are authorized for image push.
|
||||||
// If credentials are incorrect this callback shall return ErrUnauthorized.
|
// If credentials are incorrect this callback shall return ErrUnauthorized.
|
||||||
type VerifyCredentialsCallback func(ctx context.Context, image string, credentials docker.Credentials) error
|
type VerifyCredentialsCallback func(ctx context.Context, image string, credentials oci.Credentials) error
|
||||||
|
|
||||||
type keyChain struct {
|
type keyChain struct {
|
||||||
user string
|
user string
|
||||||
|
@ -48,7 +48,7 @@ func (k keyChain) Resolve(resource authn.Resource) (authn.Authenticator, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckAuth verifies that credentials can be used for image push
|
// CheckAuth verifies that credentials can be used for image push
|
||||||
func CheckAuth(ctx context.Context, image string, credentials docker.Credentials, trans http.RoundTripper) error {
|
func CheckAuth(ctx context.Context, image string, credentials oci.Credentials, trans http.RoundTripper) error {
|
||||||
|
|
||||||
ref, err := name.ParseReference(image)
|
ref, err := name.ParseReference(image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -142,9 +142,8 @@ func WithAdditionalCredentialLoaders(loaders ...CredentialsCallback) Opt {
|
||||||
// The picked value will be saved in the func config.
|
// The picked value will be saved in the func config.
|
||||||
//
|
//
|
||||||
// To verify that credentials are correct custom callback can be used (see WithVerifyCredentials).
|
// To verify that credentials are correct custom callback can be used (see WithVerifyCredentials).
|
||||||
func NewCredentialsProvider(configPath string, opts ...Opt) docker.CredentialsProvider {
|
func NewCredentialsProvider(configPath string, opts ...Opt) oci.CredentialsProvider {
|
||||||
var c credentialsProvider
|
var c credentialsProvider
|
||||||
|
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&c)
|
o(&c)
|
||||||
}
|
}
|
||||||
|
@ -154,7 +153,7 @@ func NewCredentialsProvider(configPath string, opts ...Opt) docker.CredentialsPr
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.verifyCredentials == nil {
|
if c.verifyCredentials == nil {
|
||||||
c.verifyCredentials = func(ctx context.Context, registry string, credentials docker.Credentials) error {
|
c.verifyCredentials = func(ctx context.Context, registry string, credentials oci.Credentials) error {
|
||||||
return CheckAuth(ctx, registry, credentials, c.transport)
|
return CheckAuth(ctx, registry, credentials, c.transport)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,7 +174,7 @@ func NewCredentialsProvider(configPath string, opts ...Opt) docker.CredentialsPr
|
||||||
|
|
||||||
if _, err := os.Stat(c.authFilePath); err == nil {
|
if _, err := os.Stat(c.authFilePath); err == nil {
|
||||||
defaultCredentialLoaders = append(defaultCredentialLoaders,
|
defaultCredentialLoaders = append(defaultCredentialLoaders,
|
||||||
func(registry string) (docker.Credentials, error) {
|
func(registry string) (oci.Credentials, error) {
|
||||||
return getCredentialsByCredentialHelper(c.authFilePath, registry)
|
return getCredentialsByCredentialHelper(c.authFilePath, registry)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -185,40 +184,40 @@ func NewCredentialsProvider(configPath string, opts ...Opt) docker.CredentialsPr
|
||||||
if err == nil {
|
if err == nil {
|
||||||
dockerConfigPath := filepath.Join(home, ".docker", "config.json")
|
dockerConfigPath := filepath.Join(home, ".docker", "config.json")
|
||||||
defaultCredentialLoaders = append(defaultCredentialLoaders,
|
defaultCredentialLoaders = append(defaultCredentialLoaders,
|
||||||
func(registry string) (docker.Credentials, error) {
|
func(registry string) (oci.Credentials, error) {
|
||||||
return getCredentialsByCredentialHelper(dockerConfigPath, registry)
|
return getCredentialsByCredentialHelper(dockerConfigPath, registry)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
defaultCredentialLoaders = append(defaultCredentialLoaders,
|
defaultCredentialLoaders = append(defaultCredentialLoaders,
|
||||||
func(registry string) (docker.Credentials, error) {
|
func(registry string) (oci.Credentials, error) {
|
||||||
creds, err := dockerConfig.GetCredentials(sys, registry)
|
creds, err := dockerConfig.GetCredentials(sys, registry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return docker.Credentials{}, err
|
return oci.Credentials{}, err
|
||||||
}
|
}
|
||||||
if creds.Username == "" || creds.Password == "" {
|
if creds.Username == "" || creds.Password == "" {
|
||||||
return docker.Credentials{}, ErrCredentialsNotFound
|
return oci.Credentials{}, ErrCredentialsNotFound
|
||||||
}
|
}
|
||||||
return docker.Credentials{
|
return oci.Credentials{
|
||||||
Username: creds.Username,
|
Username: creds.Username,
|
||||||
Password: creds.Password,
|
Password: creds.Password,
|
||||||
}, nil
|
}, nil
|
||||||
})
|
})
|
||||||
defaultCredentialLoaders = append(defaultCredentialLoaders,
|
defaultCredentialLoaders = append(defaultCredentialLoaders,
|
||||||
func(registry string) (docker.Credentials, error) {
|
func(registry string) (oci.Credentials, error) {
|
||||||
// Fallback onto default docker config locations
|
// Fallback onto default docker config locations
|
||||||
emptySys := &containersTypes.SystemContext{}
|
emptySys := &containersTypes.SystemContext{}
|
||||||
creds, err := dockerConfig.GetCredentials(emptySys, registry)
|
creds, err := dockerConfig.GetCredentials(emptySys, registry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return docker.Credentials{}, err
|
return oci.Credentials{}, err
|
||||||
}
|
}
|
||||||
return docker.Credentials{
|
return oci.Credentials{
|
||||||
Username: creds.Username,
|
Username: creds.Username,
|
||||||
Password: creds.Password,
|
Password: creds.Password,
|
||||||
}, nil
|
}, nil
|
||||||
})
|
})
|
||||||
defaultCredentialLoaders = append(defaultCredentialLoaders,
|
defaultCredentialLoaders = append(defaultCredentialLoaders,
|
||||||
func(registry string) (docker.Credentials, error) { // empty credentials provider for unsecured registries
|
func(registry string) (oci.Credentials, error) { // empty credentials provider for unsecured registries
|
||||||
return docker.Credentials{}, nil
|
return oci.Credentials{}, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
c.credentialLoaders = append(c.credentialLoaders, defaultCredentialLoaders...)
|
c.credentialLoaders = append(c.credentialLoaders, defaultCredentialLoaders...)
|
||||||
|
@ -226,13 +225,13 @@ func NewCredentialsProvider(configPath string, opts ...Opt) docker.CredentialsPr
|
||||||
return c.getCredentials
|
return c.getCredentials
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *credentialsProvider) getCredentials(ctx context.Context, image string) (docker.Credentials, error) {
|
func (c *credentialsProvider) getCredentials(ctx context.Context, image string) (oci.Credentials, error) {
|
||||||
var err error
|
var err error
|
||||||
result := docker.Credentials{}
|
result := oci.Credentials{}
|
||||||
|
|
||||||
ref, err := name.ParseReference(image)
|
ref, err := name.ParseReference(image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return docker.Credentials{}, fmt.Errorf("cannot parse the image reference: %w", err)
|
return oci.Credentials{}, fmt.Errorf("cannot parse the image reference: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
registry := ref.Context().RegistryStr()
|
registry := ref.Context().RegistryStr()
|
||||||
|
@ -244,7 +243,7 @@ func (c *credentialsProvider) getCredentials(ctx context.Context, image string)
|
||||||
if errors.Is(err, ErrCredentialsNotFound) {
|
if errors.Is(err, ErrCredentialsNotFound) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return docker.Credentials{}, err
|
return oci.Credentials{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.verifyCredentials(ctx, image, result)
|
err = c.verifyCredentials(ctx, image, result)
|
||||||
|
@ -252,14 +251,14 @@ func (c *credentialsProvider) getCredentials(ctx context.Context, image string)
|
||||||
return result, nil
|
return result, nil
|
||||||
} else {
|
} else {
|
||||||
if !errors.Is(err, ErrUnauthorized) {
|
if !errors.Is(err, ErrUnauthorized) {
|
||||||
return docker.Credentials{}, err
|
return oci.Credentials{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.promptForCredentials == nil {
|
if c.promptForCredentials == nil {
|
||||||
return docker.Credentials{}, ErrCredentialsNotFound
|
return oci.Credentials{}, ErrCredentialsNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is [registry] / [repository]
|
// this is [registry] / [repository]
|
||||||
|
@ -271,7 +270,7 @@ func (c *credentialsProvider) getCredentials(ctx context.Context, image string)
|
||||||
// use repo here to print it out in prompt
|
// use repo here to print it out in prompt
|
||||||
result, err = c.promptForCredentials(repository)
|
result, err = c.promptForCredentials(repository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return docker.Credentials{}, err
|
return oci.Credentials{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.verifyCredentials(ctx, image, result)
|
err = c.verifyCredentials(ctx, image, result)
|
||||||
|
@ -282,21 +281,21 @@ func (c *credentialsProvider) getCredentials(ctx context.Context, image string)
|
||||||
// This shouldn't be fatal error.
|
// This shouldn't be fatal error.
|
||||||
if strings.Contains(err.Error(), "not implemented") {
|
if strings.Contains(err.Error(), "not implemented") {
|
||||||
fmt.Fprintf(os.Stderr, "the cred-helper does not support write operation (consider changing the cred-helper it in auth.json)\n")
|
fmt.Fprintf(os.Stderr, "the cred-helper does not support write operation (consider changing the cred-helper it in auth.json)\n")
|
||||||
return docker.Credentials{}, nil
|
return oci.Credentials{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !errors.Is(err, errNoCredentialHelperConfigured) {
|
if !errors.Is(err, errNoCredentialHelperConfigured) {
|
||||||
return docker.Credentials{}, err
|
return oci.Credentials{}, err
|
||||||
}
|
}
|
||||||
helpers := listCredentialHelpers()
|
helpers := listCredentialHelpers()
|
||||||
helper, err := c.promptForCredentialStore(helpers)
|
helper, err := c.promptForCredentialStore(helpers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return docker.Credentials{}, err
|
return oci.Credentials{}, err
|
||||||
}
|
}
|
||||||
helper = strings.TrimPrefix(helper, "docker-credential-")
|
helper = strings.TrimPrefix(helper, "docker-credential-")
|
||||||
err = setCredentialHelperToConfig(c.authFilePath, helper)
|
err = setCredentialHelperToConfig(c.authFilePath, helper)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return docker.Credentials{}, fmt.Errorf("faild to set the helper to the config: %w", err)
|
return oci.Credentials{}, fmt.Errorf("faild to set the helper to the config: %w", err)
|
||||||
}
|
}
|
||||||
err = setCredentialsByCredentialHelper(c.authFilePath, registry, result.Username, result.Password)
|
err = setCredentialsByCredentialHelper(c.authFilePath, registry, result.Username, result.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -304,11 +303,11 @@ func (c *credentialsProvider) getCredentials(ctx context.Context, image string)
|
||||||
// This shouldn't be fatal error.
|
// This shouldn't be fatal error.
|
||||||
if strings.Contains(err.Error(), "not implemented") {
|
if strings.Contains(err.Error(), "not implemented") {
|
||||||
fmt.Fprintf(os.Stderr, "the cred-helper does not support write operation (consider changing the cred-helper it in auth.json)\n")
|
fmt.Fprintf(os.Stderr, "the cred-helper does not support write operation (consider changing the cred-helper it in auth.json)\n")
|
||||||
return docker.Credentials{}, nil
|
return oci.Credentials{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !errors.Is(err, errNoCredentialHelperConfigured) {
|
if !errors.Is(err, errNoCredentialHelperConfigured) {
|
||||||
return docker.Credentials{}, err
|
return oci.Credentials{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -317,7 +316,7 @@ func (c *credentialsProvider) getCredentials(ctx context.Context, image string)
|
||||||
if errors.Is(err, ErrUnauthorized) {
|
if errors.Is(err, ErrUnauthorized) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return docker.Credentials{}, err
|
return oci.Credentials{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -374,8 +373,8 @@ func setCredentialHelperToConfig(confFilePath, helper string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCredentialsByCredentialHelper(confFilePath, registry string) (docker.Credentials, error) {
|
func getCredentialsByCredentialHelper(confFilePath, registry string) (oci.Credentials, error) {
|
||||||
result := docker.Credentials{}
|
result := oci.Credentials{}
|
||||||
|
|
||||||
helper, err := getCredentialHelperFromConfig(confFilePath)
|
helper, err := getCredentialHelperFromConfig(confFilePath)
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
|
@ -26,8 +26,8 @@ import (
|
||||||
|
|
||||||
"github.com/docker/docker-credential-helpers/credentials"
|
"github.com/docker/docker-credential-helpers/credentials"
|
||||||
|
|
||||||
"knative.dev/func/pkg/docker"
|
"knative.dev/func/pkg/creds"
|
||||||
"knative.dev/func/pkg/docker/creds"
|
"knative.dev/func/pkg/oci"
|
||||||
. "knative.dev/func/pkg/testing"
|
. "knative.dev/func/pkg/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ func TestCheckAuth(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
c := docker.Credentials{
|
c := oci.Credentials{
|
||||||
Username: tt.args.username,
|
Username: tt.args.username,
|
||||||
Password: tt.args.password,
|
Password: tt.args.password,
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ func TestCheckAuth(t *testing.T) {
|
||||||
func TestCheckAuthEmptyCreds(t *testing.T) {
|
func TestCheckAuthEmptyCreds(t *testing.T) {
|
||||||
|
|
||||||
localhost, _, _ := startServer(t, "", "")
|
localhost, _, _ := startServer(t, "", "")
|
||||||
err := creds.CheckAuth(context.Background(), localhost+"/someorg/someimage:sometag", docker.Credentials{}, http.DefaultTransport)
|
err := creds.CheckAuth(context.Background(), localhost+"/someorg/someimage:sometag", oci.Credentials{}, http.DefaultTransport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -334,7 +334,7 @@ const (
|
||||||
quayIoUserPwd = "goodPwd2"
|
quayIoUserPwd = "goodPwd2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Credentials = docker.Credentials
|
type Credentials = oci.Credentials
|
||||||
|
|
||||||
func TestNewCredentialsProvider(t *testing.T) {
|
func TestNewCredentialsProvider(t *testing.T) {
|
||||||
helperWithQuayIO := newInMemoryHelper()
|
helperWithQuayIO := newInMemoryHelper()
|
||||||
|
@ -460,8 +460,8 @@ func TestNewCredentialsProvider(t *testing.T) {
|
||||||
func TestNewCredentialsProviderEmptyCreds(t *testing.T) {
|
func TestNewCredentialsProviderEmptyCreds(t *testing.T) {
|
||||||
resetHomeDir(t)
|
resetHomeDir(t)
|
||||||
|
|
||||||
credentialsProvider := creds.NewCredentialsProvider(testConfigPath(t), creds.WithVerifyCredentials(func(ctx context.Context, image string, credentials docker.Credentials) error {
|
credentialsProvider := creds.NewCredentialsProvider(testConfigPath(t), creds.WithVerifyCredentials(func(ctx context.Context, image string, credentials oci.Credentials) error {
|
||||||
if image == "localhost:5555/someorg/someimage:sometag" && credentials == (docker.Credentials{}) {
|
if image == "localhost:5555/someorg/someimage:sometag" && credentials == (oci.Credentials{}) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
t.Fatal("unreachable")
|
t.Fatal("unreachable")
|
||||||
|
@ -471,7 +471,7 @@ func TestNewCredentialsProviderEmptyCreds(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
if c != (docker.Credentials{}) {
|
if c != (oci.Credentials{}) {
|
||||||
t.Error("unexpected credentials")
|
t.Error("unexpected credentials")
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
fn "knative.dev/func/pkg/functions"
|
fn "knative.dev/func/pkg/functions"
|
||||||
|
"knative.dev/func/pkg/oci"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/image"
|
"github.com/docker/docker/api/types/image"
|
||||||
"github.com/docker/docker/api/types/registry"
|
"github.com/docker/docker/api/types/registry"
|
||||||
|
@ -33,13 +34,6 @@ import (
|
||||||
|
|
||||||
type Opt func(*Pusher)
|
type Opt func(*Pusher)
|
||||||
|
|
||||||
type Credentials struct {
|
|
||||||
Username string
|
|
||||||
Password string
|
|
||||||
}
|
|
||||||
|
|
||||||
type CredentialsProvider func(ctx context.Context, image string) (Credentials, error)
|
|
||||||
|
|
||||||
// PusherDockerClient is sub-interface of client.CommonAPIClient required by pusher.
|
// PusherDockerClient is sub-interface of client.CommonAPIClient required by pusher.
|
||||||
type PusherDockerClient interface {
|
type PusherDockerClient interface {
|
||||||
daemon.Client
|
daemon.Client
|
||||||
|
@ -52,12 +46,12 @@ type PusherDockerClientFactory func() (PusherDockerClient, error)
|
||||||
// Pusher of images from local to remote registry.
|
// Pusher of images from local to remote registry.
|
||||||
type Pusher struct {
|
type Pusher struct {
|
||||||
verbose bool // verbose logging.
|
verbose bool // verbose logging.
|
||||||
credentialsProvider CredentialsProvider
|
credentialsProvider oci.CredentialsProvider
|
||||||
transport http.RoundTripper
|
transport http.RoundTripper
|
||||||
dockerClientFactory PusherDockerClientFactory
|
dockerClientFactory PusherDockerClientFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithCredentialsProvider(cp CredentialsProvider) Opt {
|
func WithCredentialsProvider(cp oci.CredentialsProvider) Opt {
|
||||||
return func(p *Pusher) {
|
return func(p *Pusher) {
|
||||||
p.credentialsProvider = cp
|
p.credentialsProvider = cp
|
||||||
}
|
}
|
||||||
|
@ -81,14 +75,10 @@ func WithVerbose(verbose bool) Opt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func EmptyCredentialsProvider(ctx context.Context, registry string) (Credentials, error) {
|
|
||||||
return Credentials{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPusher creates an instance of a docker-based image pusher.
|
// NewPusher creates an instance of a docker-based image pusher.
|
||||||
func NewPusher(opts ...Opt) *Pusher {
|
func NewPusher(opts ...Opt) *Pusher {
|
||||||
result := &Pusher{
|
result := &Pusher{
|
||||||
credentialsProvider: EmptyCredentialsProvider,
|
credentialsProvider: oci.EmptyCredentialsProvider,
|
||||||
transport: http.DefaultTransport,
|
transport: http.DefaultTransport,
|
||||||
dockerClientFactory: func() (PusherDockerClient, error) {
|
dockerClientFactory: func() (PusherDockerClient, error) {
|
||||||
c, _, err := NewClient(client.DefaultDockerHost)
|
c, _, err := NewClient(client.DefaultDockerHost)
|
||||||
|
@ -176,7 +166,7 @@ func (n *Pusher) Push(ctx context.Context, f fn.Function) (string, error) {
|
||||||
return d.String(), nil
|
return d.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Pusher) pushImage(ctx context.Context, f fn.Function, credentials Credentials) (digest string, err error) {
|
func (n *Pusher) pushImage(ctx context.Context, f fn.Function, credentials oci.Credentials) (digest string, err error) {
|
||||||
|
|
||||||
var output io.Writer
|
var output io.Writer
|
||||||
|
|
||||||
|
@ -211,7 +201,7 @@ func (n *Pusher) pushImage(ctx context.Context, f fn.Function, credentials Crede
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Pusher) daemonPush(ctx context.Context, f fn.Function, credentials Credentials, output io.Writer) (digest string, err error) {
|
func (n *Pusher) daemonPush(ctx context.Context, f fn.Function, credentials oci.Credentials, output io.Writer) (digest string, err error) {
|
||||||
cli, err := n.dockerClientFactory()
|
cli, err := n.dockerClientFactory()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to create docker api client: %w", err)
|
return "", fmt.Errorf("failed to create docker api client: %w", err)
|
||||||
|
@ -267,7 +257,7 @@ func ParseDigest(output string) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Pusher) push(ctx context.Context, f fn.Function, credentials Credentials, output io.Writer) (digest string, err error) {
|
func (n *Pusher) push(ctx context.Context, f fn.Function, credentials oci.Credentials, output io.Writer) (digest string, err error) {
|
||||||
auth := &authn.Basic{
|
auth := &authn.Basic{
|
||||||
Username: credentials.Username,
|
Username: credentials.Username,
|
||||||
Password: credentials.Password,
|
Password: credentials.Password,
|
||||||
|
|
|
@ -38,6 +38,7 @@ import (
|
||||||
|
|
||||||
"knative.dev/func/pkg/docker"
|
"knative.dev/func/pkg/docker"
|
||||||
fn "knative.dev/func/pkg/functions"
|
fn "knative.dev/func/pkg/functions"
|
||||||
|
"knative.dev/func/pkg/oci"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetRegistry(t *testing.T) {
|
func TestGetRegistry(t *testing.T) {
|
||||||
|
@ -77,8 +78,8 @@ const (
|
||||||
imageRepoDigest = "sha256:00af51d125f3092e157a7f8a717029412dc9d266c017e89cecdfeccb4cc3d7a7"
|
imageRepoDigest = "sha256:00af51d125f3092e157a7f8a717029412dc9d266c017e89cecdfeccb4cc3d7a7"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testCredProvider = docker.CredentialsProvider(func(ctx context.Context, registry string) (docker.Credentials, error) {
|
var testCredProvider = oci.CredentialsProvider(func(ctx context.Context, registry string) (oci.Credentials, error) {
|
||||||
return docker.Credentials{
|
return oci.Credentials{
|
||||||
Username: testUser,
|
Username: testUser,
|
||||||
Password: testPwd,
|
Password: testPwd,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
package k8s
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/go-containerregistry/pkg/name"
|
||||||
|
"github.com/google/go-containerregistry/pkg/v1/google"
|
||||||
|
|
||||||
|
"knative.dev/func/pkg/creds"
|
||||||
|
"knative.dev/func/pkg/oci"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetGoogleCredentialLoader() []creds.CredentialsCallback {
|
||||||
|
return []creds.CredentialsCallback{
|
||||||
|
func(registry string) (oci.Credentials, error) {
|
||||||
|
if registry != "gcr.io" {
|
||||||
|
return oci.Credentials{}, nil // skip if not GCR
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := name.NewRegistry(registry)
|
||||||
|
if err != nil {
|
||||||
|
return oci.Credentials{}, fmt.Errorf("parse registry: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticator, err := google.Keychain.Resolve(res)
|
||||||
|
if err != nil {
|
||||||
|
return oci.Credentials{}, fmt.Errorf("resolve google keychain: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
authCfg, err := authenticator.Authorization()
|
||||||
|
if err != nil {
|
||||||
|
return oci.Credentials{}, fmt.Errorf("get authorization: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return oci.Credentials{
|
||||||
|
Username: authCfg.Username,
|
||||||
|
Password: authCfg.Password,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetECRCredentialLoader() []creds.CredentialsCallback {
|
||||||
|
return []creds.CredentialsCallback{} // TODO: Implement ECR credentials loader
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetACRCredentialLoader() []creds.CredentialsCallback {
|
||||||
|
return []creds.CredentialsCallback{
|
||||||
|
func(registry string) (oci.Credentials, error) {
|
||||||
|
if !strings.HasSuffix(registry, ".azurecr.io") {
|
||||||
|
return oci.Credentials{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(path.Join(os.Getenv("HOME"), ".azure", "accessTokens.json"))
|
||||||
|
if err != nil {
|
||||||
|
return oci.Credentials{}, fmt.Errorf("open Azure access tokens: %w", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
var tokens []struct {
|
||||||
|
AccessToken string `json:"accessToken"`
|
||||||
|
Resource string `json:"resource"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.NewDecoder(f).Decode(&tokens); err != nil {
|
||||||
|
return oci.Credentials{}, fmt.Errorf("decode Azure access tokens: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
target := "https://" + registry
|
||||||
|
for _, t := range tokens {
|
||||||
|
if t.Resource == target {
|
||||||
|
return oci.Credentials{
|
||||||
|
Username: "00000000-0000-0000-0000-000000000000",
|
||||||
|
Password: t.AccessToken,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return oci.Credentials{}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,9 +14,9 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
"k8s.io/apimachinery/pkg/util/rand"
|
"k8s.io/apimachinery/pkg/util/rand"
|
||||||
|
|
||||||
"knative.dev/func/pkg/docker"
|
"knative.dev/func/pkg/creds"
|
||||||
"knative.dev/func/pkg/docker/creds"
|
|
||||||
fn "knative.dev/func/pkg/functions"
|
fn "knative.dev/func/pkg/functions"
|
||||||
|
"knative.dev/func/pkg/oci"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -106,7 +106,7 @@ func GetOpenShiftDockerCredentialLoaders() []creds.CredentialsCallback {
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var credentials docker.Credentials
|
var credentials oci.Credentials
|
||||||
|
|
||||||
if authInfo := rawConf.AuthInfos[cc.AuthInfo]; authInfo != nil {
|
if authInfo := rawConf.AuthInfos[cc.AuthInfo]; authInfo != nil {
|
||||||
credentials.Username = "openshift"
|
credentials.Username = "openshift"
|
||||||
|
@ -114,11 +114,11 @@ func GetOpenShiftDockerCredentialLoaders() []creds.CredentialsCallback {
|
||||||
}
|
}
|
||||||
|
|
||||||
return []creds.CredentialsCallback{
|
return []creds.CredentialsCallback{
|
||||||
func(registry string) (docker.Credentials, error) {
|
func(registry string) (oci.Credentials, error) {
|
||||||
if registry == openShiftRegistryHostPort {
|
if registry == openShiftRegistryHostPort {
|
||||||
return credentials, nil
|
return credentials, nil
|
||||||
}
|
}
|
||||||
return docker.Credentials{}, creds.ErrCredentialsNotFound
|
return oci.Credentials{}, creds.ErrCredentialsNotFound
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ import (
|
||||||
"github.com/google/go-containerregistry/pkg/authn"
|
"github.com/google/go-containerregistry/pkg/authn"
|
||||||
"github.com/google/go-containerregistry/pkg/name"
|
"github.com/google/go-containerregistry/pkg/name"
|
||||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||||
"github.com/google/go-containerregistry/pkg/v1/google"
|
|
||||||
"github.com/google/go-containerregistry/pkg/v1/layout"
|
"github.com/google/go-containerregistry/pkg/v1/layout"
|
||||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -22,29 +21,63 @@ import (
|
||||||
fn "knative.dev/func/pkg/functions"
|
fn "knative.dev/func/pkg/functions"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Credentials struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CredentialsProvider func(ctx context.Context, image string) (Credentials, error)
|
||||||
|
|
||||||
|
type Opt func(*Pusher)
|
||||||
|
|
||||||
// Pusher of OCI multi-arch layout directories.
|
// Pusher of OCI multi-arch layout directories.
|
||||||
type Pusher struct {
|
type Pusher struct {
|
||||||
Anonymous bool
|
Anonymous bool
|
||||||
Insecure bool
|
credentialsProvider CredentialsProvider
|
||||||
Token string
|
|
||||||
Username string
|
Insecure bool
|
||||||
Verbose bool
|
Token string
|
||||||
|
Username string
|
||||||
|
Verbose bool
|
||||||
|
|
||||||
updates chan v1.Update
|
updates chan v1.Update
|
||||||
done chan bool
|
done chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPusher(insecure, anon, verbose bool) *Pusher {
|
func EmptyCredentialsProvider(ctx context.Context, registry string) (Credentials, error) {
|
||||||
return &Pusher{
|
return Credentials{}, nil
|
||||||
Insecure: insecure,
|
}
|
||||||
Anonymous: anon,
|
|
||||||
Verbose: verbose,
|
func WithCredentialsProvider(cp CredentialsProvider) Opt {
|
||||||
updates: make(chan v1.Update, 10),
|
return func(p *Pusher) {
|
||||||
done: make(chan bool, 1),
|
p.credentialsProvider = cp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithVerbose(verbose bool) Opt {
|
||||||
|
return func(pusher *Pusher) {
|
||||||
|
pusher.Verbose = verbose
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPusher(insecure, anon, verbose bool, opts ...Opt) *Pusher {
|
||||||
|
result := &Pusher{
|
||||||
|
credentialsProvider: EmptyCredentialsProvider,
|
||||||
|
Insecure: insecure,
|
||||||
|
Anonymous: anon,
|
||||||
|
Verbose: verbose,
|
||||||
|
updates: make(chan v1.Update, 10),
|
||||||
|
done: make(chan bool, 1),
|
||||||
|
}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(result)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Pusher) Push(ctx context.Context, f fn.Function) (digest string, err error) {
|
func (p *Pusher) Push(ctx context.Context, f fn.Function) (digest string, err error) {
|
||||||
|
credentials, _ := p.credentialsProvider(ctx, f.Build.Image)
|
||||||
|
|
||||||
go p.handleUpdates(ctx)
|
go p.handleUpdates(ctx)
|
||||||
defer func() { p.done <- true }()
|
defer func() { p.done <- true }()
|
||||||
buildDir, err := getLastBuildDir(f)
|
buildDir, err := getLastBuildDir(f)
|
||||||
|
@ -67,7 +100,7 @@ func (p *Pusher) Push(ctx context.Context, f fn.Function) (digest string, err er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = p.writeIndex(ctx, ref, ii); err != nil {
|
if err = p.writeIndex(ctx, ref, ii, credentials); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
h, err := ii.Digest()
|
h, err := ii.Digest()
|
||||||
|
@ -120,7 +153,7 @@ func getLastBuildDir(f fn.Function) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeIndex to its defined registry.
|
// writeIndex to its defined registry.
|
||||||
func (p *Pusher) writeIndex(ctx context.Context, ref name.Reference, ii v1.ImageIndex) error {
|
func (p *Pusher) writeIndex(ctx context.Context, ref name.Reference, ii v1.ImageIndex, creds Credentials) error {
|
||||||
oo := []remote.Option{
|
oo := []remote.Option{
|
||||||
remote.WithContext(ctx),
|
remote.WithContext(ctx),
|
||||||
remote.WithProgress(p.updates),
|
remote.WithProgress(p.updates),
|
||||||
|
@ -135,10 +168,13 @@ func (p *Pusher) writeIndex(ctx context.Context, ref name.Reference, ii v1.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
if !p.Anonymous {
|
if !p.Anonymous {
|
||||||
a, err := p.authOption(ctx)
|
a, err := p.authOption(ctx, creds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if a == nil {
|
||||||
|
return errors.New("no authentication option provided")
|
||||||
|
}
|
||||||
oo = append(oo, a)
|
oo = append(oo, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,13 +184,14 @@ func (p *Pusher) writeIndex(ctx context.Context, ref name.Reference, ii v1.Image
|
||||||
// authOption selects an appropriate authentication option.
|
// authOption selects an appropriate authentication option.
|
||||||
// If user provided = basic auth (secret is password)
|
// If user provided = basic auth (secret is password)
|
||||||
// If only secret provided = bearer token auth
|
// If only secret provided = bearer token auth
|
||||||
// If neither are provided = Returned is a cascading keychain auth mthod
|
// If neither are provided = creds from credentials provider
|
||||||
// which performs the following in order:
|
// which performs the following in order:
|
||||||
// - Default Keychain (docker and podman config files)
|
// - Default Keychain (docker and podman config files)
|
||||||
// - Google Keychain
|
// - Google Keychain
|
||||||
// - TODO: ECR Amazon
|
// - TODO: ECR Amazon
|
||||||
// - TODO: ACR Azure
|
// - TODO: ACR Azure
|
||||||
func (p *Pusher) authOption(ctx context.Context) (remote.Option, error) {
|
// - interactive prompt for username and password
|
||||||
|
func (p *Pusher) authOption(ctx context.Context, creds Credentials) (remote.Option, error) {
|
||||||
|
|
||||||
// Basic Auth if provided
|
// Basic Auth if provided
|
||||||
username, _ := ctx.Value(fn.PushUsernameKey{}).(string)
|
username, _ := ctx.Value(fn.PushUsernameKey{}).(string)
|
||||||
|
@ -168,12 +205,10 @@ func (p *Pusher) authOption(ctx context.Context) (remote.Option, error) {
|
||||||
return remote.WithAuth(&authn.Basic{Username: username, Password: password}), nil
|
return remote.WithAuth(&authn.Basic{Username: username, Password: password}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default chain
|
// Use provided credentials if available or prompt for them
|
||||||
return remote.WithAuthFromKeychain(authn.NewMultiKeychain(
|
if creds.Username != "" && creds.Password != "" {
|
||||||
authn.DefaultKeychain, // Podman and Docker config files
|
return remote.WithAuth(&authn.Basic{Username: creds.Username, Password: creds.Password}), nil
|
||||||
google.Keychain, // Google
|
}
|
||||||
// TODO: Integrate and test ECR and ACR credential helpers:
|
|
||||||
// authn.NewKeychainFromHelper(ecr.ECRHelper{ClientFactory: api.DefaultClientFactory{}}),
|
return nil, nil
|
||||||
// authn.NewKeychainFromHelper(acr.ACRCredHelper{}),
|
|
||||||
)), nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,9 +37,9 @@ import (
|
||||||
"knative.dev/pkg/apis"
|
"knative.dev/pkg/apis"
|
||||||
|
|
||||||
"knative.dev/func/pkg/builders/buildpacks"
|
"knative.dev/func/pkg/builders/buildpacks"
|
||||||
"knative.dev/func/pkg/docker"
|
|
||||||
fn "knative.dev/func/pkg/functions"
|
fn "knative.dev/func/pkg/functions"
|
||||||
"knative.dev/func/pkg/k8s"
|
"knative.dev/func/pkg/k8s"
|
||||||
|
"knative.dev/func/pkg/oci"
|
||||||
"knative.dev/func/pkg/pipelines"
|
"knative.dev/func/pkg/pipelines"
|
||||||
"knative.dev/func/pkg/pipelines/tekton"
|
"knative.dev/func/pkg/pipelines/tekton"
|
||||||
"knative.dev/func/pkg/random"
|
"knative.dev/func/pkg/random"
|
||||||
|
@ -102,8 +102,8 @@ func TestGitlab(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
credentialsProvider := func(ctx context.Context, image string) (docker.Credentials, error) {
|
credentialsProvider := func(ctx context.Context, image string) (oci.Credentials, error) {
|
||||||
return docker.Credentials{
|
return oci.Credentials{
|
||||||
Username: "",
|
Username: "",
|
||||||
Password: "",
|
Password: "",
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"knative.dev/func/pkg/k8s"
|
"knative.dev/func/pkg/k8s"
|
||||||
"knative.dev/func/pkg/knative"
|
"knative.dev/func/pkg/knative"
|
||||||
|
"knative.dev/func/pkg/oci"
|
||||||
|
|
||||||
"knative.dev/func/pkg/builders/buildpacks"
|
"knative.dev/func/pkg/builders/buildpacks"
|
||||||
pack "knative.dev/func/pkg/builders/buildpacks"
|
pack "knative.dev/func/pkg/builders/buildpacks"
|
||||||
|
@ -34,8 +35,8 @@ import (
|
||||||
. "knative.dev/func/pkg/testing"
|
. "knative.dev/func/pkg/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testCP = func(_ context.Context, _ string) (docker.Credentials, error) {
|
var testCP = func(_ context.Context, _ string) (oci.Credentials, error) {
|
||||||
return docker.Credentials{
|
return oci.Credentials{
|
||||||
Username: "",
|
Username: "",
|
||||||
Password: "",
|
Password: "",
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -34,6 +34,7 @@ import (
|
||||||
"knative.dev/func/pkg/k8s"
|
"knative.dev/func/pkg/k8s"
|
||||||
fnlabels "knative.dev/func/pkg/k8s/labels"
|
fnlabels "knative.dev/func/pkg/k8s/labels"
|
||||||
"knative.dev/func/pkg/knative"
|
"knative.dev/func/pkg/knative"
|
||||||
|
"knative.dev/func/pkg/oci"
|
||||||
"knative.dev/pkg/apis"
|
"knative.dev/pkg/apis"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -54,11 +55,11 @@ type pacURLCallback = func() (string, error)
|
||||||
type PipelinesProvider struct {
|
type PipelinesProvider struct {
|
||||||
verbose bool
|
verbose bool
|
||||||
getPacURL pacURLCallback
|
getPacURL pacURLCallback
|
||||||
credentialsProvider docker.CredentialsProvider
|
credentialsProvider oci.CredentialsProvider
|
||||||
decorator PipelineDecorator
|
decorator PipelineDecorator
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithCredentialsProvider(credentialsProvider docker.CredentialsProvider) Opt {
|
func WithCredentialsProvider(credentialsProvider oci.CredentialsProvider) Opt {
|
||||||
return func(pp *PipelinesProvider) {
|
return func(pp *PipelinesProvider) {
|
||||||
pp.credentialsProvider = credentialsProvider
|
pp.credentialsProvider = credentialsProvider
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue