summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
authorjannfis <jann@mistrust.net>2020-11-23 20:02:48 +0100
committerGitHub <noreply@github.com>2020-11-23 20:02:48 +0100
commite3b13f16bfc543ffe98fac6b84b309fc8bf719ff (patch)
treed8311564105e01002bdb053a89b1b9c0c80ac812 /pkg
parent6723a25cfcd3f117f0d234b5449db7a6cc68bc2d (diff)
feat: Support for getting pull creds from external scripts (#121)
* feat: Support for getting pull creds from external scripts * spelling * be more verbose on error
Diffstat (limited to 'pkg')
-rw-r--r--pkg/image/credentials.go37
-rw-r--r--pkg/image/credentials_test.go67
2 files changed, 104 insertions, 0 deletions
diff --git a/pkg/image/credentials.go b/pkg/image/credentials.go
index 4410ccb..cccbb1d 100644
--- a/pkg/image/credentials.go
+++ b/pkg/image/credentials.go
@@ -5,7 +5,11 @@ import (
"encoding/json"
"fmt"
"os"
+ "os/exec"
"strings"
+ "time"
+
+ argoexec "github.com/argoproj/pkg/exec"
"github.com/argoproj-labs/argocd-image-updater/pkg/client"
"github.com/argoproj-labs/argocd-image-updater/pkg/log"
@@ -18,6 +22,7 @@ const (
CredentialSourcePullSecret CredentialSourceType = 1
CredentialSourceSecret CredentialSourceType = 2
CredentialSourceEnv CredentialSourceType = 3
+ CredentialSourceExt CredentialSourceType = 4
)
type CredentialSource struct {
@@ -27,6 +32,7 @@ type CredentialSource struct {
SecretName string
SecretField string
EnvName string
+ ScriptPath string
}
type Credential struct {
@@ -70,6 +76,9 @@ func ParseCredentialSource(credentialSource string, requirePrefix bool) (*Creden
case "env":
err = src.parseEnvDefinition(tokens[1])
src.Type = CredentialSourceEnv
+ case "ext":
+ err = src.parseExtDefinition(tokens[1])
+ src.Type = CredentialSourceExt
default:
err = fmt.Errorf("unknown credential source: %s", tokens[0])
}
@@ -121,6 +130,27 @@ func (src *CredentialSource) FetchCredentials(registryURL string, kubeclient *cl
return nil, err
}
return &creds, nil
+ case CredentialSourceExt:
+ if !strings.HasPrefix(src.ScriptPath, "/") {
+ return nil, fmt.Errorf("path to script must be absolute, but is '%s'", src.ScriptPath)
+ }
+ _, err := os.Stat(src.ScriptPath)
+ if err != nil {
+ return nil, fmt.Errorf("could not stat %s: %v", src.ScriptPath, err)
+ }
+ cmd := exec.Command(src.ScriptPath)
+ out, err := argoexec.RunCommandExt(cmd, argoexec.CmdOpts{Timeout: 10 * time.Second})
+ if err != nil {
+ return nil, fmt.Errorf("error executing %s: %v", src.ScriptPath, err)
+ }
+ tokens := strings.SplitN(out, ":", 2)
+ if len(tokens) != 2 {
+ return nil, fmt.Errorf("invalid script output, must be single line with syntax <username>:<password>")
+ }
+ creds.Username = tokens[0]
+ creds.Password = tokens[1]
+ return &creds, nil
+
default:
return nil, fmt.Errorf("unknown credential type")
}
@@ -164,6 +194,13 @@ func (src *CredentialSource) parseEnvDefinition(definition string) error {
return nil
}
+// Parse an external script definition
+// nolint:unparam
+func (src *CredentialSource) parseExtDefinition(definition string) error {
+ src.ScriptPath = definition
+ return nil
+}
+
// This unmarshals & parses Docker's config.json file, returning username and
// password for given registry URL
func parseDockerConfigJson(registryURL string, jsonSource string) (string, string, error) {
diff --git a/pkg/image/credentials_test.go b/pkg/image/credentials_test.go
index 9e2a056..8c2682d 100644
--- a/pkg/image/credentials_test.go
+++ b/pkg/image/credentials_test.go
@@ -2,6 +2,7 @@ package image
import (
"os"
+ "path"
"testing"
"github.com/argoproj-labs/argocd-image-updater/pkg/client"
@@ -84,6 +85,21 @@ func Test_ParseCredentialAnnotation(t *testing.T) {
assert.Error(t, err)
assert.Nil(t, src)
})
+
+ t.Run("Parse valid credentials definition from environment", func(t *testing.T) {
+ src, err := ParseCredentialSource("env:DUMMY_SECRET", false)
+ require.NoError(t, err)
+ require.NotNil(t, src)
+ assert.Equal(t, "DUMMY_SECRET", src.EnvName)
+ })
+
+ t.Run("Parse valid credentials definition from environment", func(t *testing.T) {
+ src, err := ParseCredentialSource("env:DUMMY_SECRET", false)
+ require.NoError(t, err)
+ require.NotNil(t, src)
+ assert.Equal(t, "DUMMY_SECRET", src.EnvName)
+ })
+
}
func Test_ParseCredentialReference(t *testing.T) {
@@ -198,6 +214,57 @@ func Test_FetchCredentialsFromEnv(t *testing.T) {
})
}
+func Test_FetchCredentialsFromExt(t *testing.T) {
+ t.Run("Fetch credentials from external script - valid output", func(t *testing.T) {
+ pwd, err := os.Getwd()
+ require.NoError(t, err)
+ credSrc := &CredentialSource{
+ Type: CredentialSourceExt,
+ Registry: "https://registry-1.docker.io/v2",
+ ScriptPath: path.Join(pwd, "..", "..", "test", "testdata", "scripts", "get-credentials-valid.sh"),
+ }
+ creds, err := credSrc.FetchCredentials("https://registry-1.docker.io", nil)
+ require.NoError(t, err)
+ require.NotNil(t, creds)
+ assert.Equal(t, "username", creds.Username)
+ assert.Equal(t, "password", creds.Password)
+ })
+ t.Run("Fetch credentials from external script - invalid script output", func(t *testing.T) {
+ pwd, err := os.Getwd()
+ require.NoError(t, err)
+ credSrc := &CredentialSource{
+ Type: CredentialSourceExt,
+ Registry: "https://registry-1.docker.io/v2",
+ ScriptPath: path.Join(pwd, "..", "..", "test", "testdata", "scripts", "get-credentials-invalid.sh"),
+ }
+ creds, err := credSrc.FetchCredentials("https://registry-1.docker.io", nil)
+ require.Errorf(t, err, "invalid script output")
+ require.Nil(t, creds)
+ })
+ t.Run("Fetch credentials from external script - script does not exist", func(t *testing.T) {
+ pwd, err := os.Getwd()
+ require.NoError(t, err)
+ credSrc := &CredentialSource{
+ Type: CredentialSourceExt,
+ Registry: "https://registry-1.docker.io/v2",
+ ScriptPath: path.Join(pwd, "..", "..", "test", "testdata", "scripts", "get-credentials-notexist.sh"),
+ }
+ creds, err := credSrc.FetchCredentials("https://registry-1.docker.io", nil)
+ require.Errorf(t, err, "no such file or directory")
+ require.Nil(t, creds)
+ })
+ t.Run("Fetch credentials from external script - relative path", func(t *testing.T) {
+ credSrc := &CredentialSource{
+ Type: CredentialSourceExt,
+ Registry: "https://registry-1.docker.io/v2",
+ ScriptPath: "get-credentials-notexist.sh",
+ }
+ creds, err := credSrc.FetchCredentials("https://registry-1.docker.io", nil)
+ require.Errorf(t, err, "path to script must be absolute")
+ require.Nil(t, creds)
+ })
+}
+
func Test_ParseDockerConfig(t *testing.T) {
t.Run("Parse valid Docker configuration with matching registry", func(t *testing.T) {
config := fixture.MustReadFile("../../test/testdata/docker/valid-config.json")