From a9ab1b380b029ec0af622e37fffcc202349514cb Mon Sep 17 00:00:00 2001 From: Bum-Seok Hwang Date: Tue, 28 Sep 2021 19:31:51 +0900 Subject: [PATCH] add: Support multiple version numbers pattern #11 (#12) add: Support multiple version numbers pattern #11 --- remoteRegistry/docker/docker_test.go | 81 --------------------------- remoteRegistry/docker/ecr_test.go | 47 ++++++++++++++++ remoteRegistry/docker/private_test.go | 61 ++++++++++++++++++++ util/version.go | 47 ++++++++++------ util/version_test.go | 39 +++++++++++++ 5 files changed, 178 insertions(+), 97 deletions(-) create mode 100644 remoteRegistry/docker/ecr_test.go create mode 100644 remoteRegistry/docker/private_test.go create mode 100644 util/version_test.go diff --git a/remoteRegistry/docker/docker_test.go b/remoteRegistry/docker/docker_test.go index 9d98e8b..6d0d2e0 100644 --- a/remoteRegistry/docker/docker_test.go +++ b/remoteRegistry/docker/docker_test.go @@ -1,44 +1,10 @@ package docker import ( - "fmt" - "os" "strings" "testing" - - "github.com/google/go-containerregistry/pkg/authn" - "github.com/joho/godotenv" ) -type testenv struct { - host string - image string - tag string - auth string - username string - password string -} - -var privateenv = testenv{} -var ecrenv = testenv{} - -func init() { - if err := godotenv.Load("../../.env"); err != nil { - fmt.Printf("error loading .env file - %s", err) - } - - privateenv.host = os.Getenv("TEST_DOCKER_PRIVATE_HOST") - privateenv.image = os.Getenv("TEST_DOCKER_PRIVATE_IMAGE") - privateenv.tag = os.Getenv("TEST_DOCKER_PRIVATE_TAG") - privateenv.auth = os.Getenv("TEST_DOCKER_PRIVATE_AUTH") - privateenv.username = os.Getenv("TEST_DOCKER_PRIVATE_USERNAME") - privateenv.password = os.Getenv("TEST_DOCKER_PRIVATE_PASSWORD") - - ecrenv.host = os.Getenv("TEST_DOCKER_ECR_HOST") - ecrenv.image = os.Getenv("TEST_DOCKER_ECR_IMAGE") - ecrenv.tag = os.Getenv("TEST_DOCKER_ECR_TAG") -} - func TestGetImageStringAsterisk(t *testing.T) { r := NewRemoteRegistry() @@ -50,50 +16,3 @@ func TestGetImageStringAsterisk(t *testing.T) { t.Logf("success: %s", s) } } - -func TestGetImageFromPrivateRegistry(t *testing.T) { - if os.Getenv("TEST_DOCKER_PRIVATE_SKIP") != "" { - t.Log("skipping test") - return - } - - r := NewRemoteRegistry() - - if privateenv.auth != "" { - r.WithImageAuthMap(map[string]authn.Authenticator{ - privateenv.host: NewPrivateAuthenticatorWithAuth(privateenv.host, privateenv.auth), - }) - } else if privateenv.username != "" && privateenv.password != "" { - r.WithImageAuthMap(map[string]authn.Authenticator{ - privateenv.host: NewPrivateAuthenticator(privateenv.host, privateenv.username, privateenv.password), - }) - } else { - t.Fatalf("env not set") - } - - if s, err := r.GetImageString(privateenv.host+"/"+privateenv.image, privateenv.tag, "linux/amd64"); err != nil { - t.Fatalf("err: %v", err) - } else { - t.Logf("success: %s", s) - } -} - -func TestGetImageFromECR(t *testing.T) { - - if os.Getenv("TEST_DOCKER_ECR_SKIP") != "" { - t.Log("skipping test") - return - } - - r := NewRemoteRegistry() - - if ecrenv.host == "" || ecrenv.image == "" || ecrenv.tag == "" { - t.Fatalf("env not set") - } - - if s, err := r.GetImageString(ecrenv.host+"/"+ecrenv.image, ecrenv.tag, "linux/amd64"); err != nil { - t.Fatalf("err: %v, %+v", err, ecrenv) - } else { - t.Logf("success: %s", s) - } -} diff --git a/remoteRegistry/docker/ecr_test.go b/remoteRegistry/docker/ecr_test.go new file mode 100644 index 0000000..eb4706b --- /dev/null +++ b/remoteRegistry/docker/ecr_test.go @@ -0,0 +1,47 @@ +package docker + +import ( + "fmt" + "os" + "testing" + + "github.com/joho/godotenv" +) + +type testEcrEnv struct { + host string + image string + tag string +} + +var ecrenv = testEcrEnv{} + +func init() { + if err := godotenv.Load("../../.env"); err != nil { + fmt.Printf("error loading .env file - %s", err) + } + + ecrenv.host = os.Getenv("TEST_DOCKER_ECR_HOST") + ecrenv.image = os.Getenv("TEST_DOCKER_ECR_IMAGE") + ecrenv.tag = os.Getenv("TEST_DOCKER_ECR_TAG") +} + +func TestGetImageFromECR(t *testing.T) { + + if os.Getenv("TEST_DOCKER_ECR_SKIP") != "" { + t.Log("skipping test") + return + } + + r := NewRemoteRegistry() + + if ecrenv.host == "" || ecrenv.image == "" || ecrenv.tag == "" { + t.Fatalf("env not set") + } + + if s, err := r.GetImageString(ecrenv.host+"/"+ecrenv.image, ecrenv.tag, "linux/amd64"); err != nil { + t.Fatalf("err: %v, %+v", err, ecrenv) + } else { + t.Logf("success: %s", s) + } +} diff --git a/remoteRegistry/docker/private_test.go b/remoteRegistry/docker/private_test.go new file mode 100644 index 0000000..b2f0a41 --- /dev/null +++ b/remoteRegistry/docker/private_test.go @@ -0,0 +1,61 @@ +package docker + +import ( + "fmt" + "os" + "testing" + + "github.com/google/go-containerregistry/pkg/authn" + "github.com/joho/godotenv" +) + +type testPrivateEnv struct { + host string + image string + tag string + auth string + username string + password string +} + +var privateenv = testPrivateEnv{} + +func init() { + if err := godotenv.Load("../../.env"); err != nil { + fmt.Printf("error loading .env file - %s", err) + } + + privateenv.host = os.Getenv("TEST_DOCKER_PRIVATE_HOST") + privateenv.image = os.Getenv("TEST_DOCKER_PRIVATE_IMAGE") + privateenv.tag = os.Getenv("TEST_DOCKER_PRIVATE_TAG") + privateenv.auth = os.Getenv("TEST_DOCKER_PRIVATE_AUTH") + privateenv.username = os.Getenv("TEST_DOCKER_PRIVATE_USERNAME") + privateenv.password = os.Getenv("TEST_DOCKER_PRIVATE_PASSWORD") +} + +func TestGetImageFromPrivateRegistry(t *testing.T) { + if os.Getenv("TEST_DOCKER_PRIVATE_SKIP") != "" { + t.Log("skipping test") + return + } + + r := NewRemoteRegistry() + + if privateenv.auth != "" { + r.WithImageAuthMap(map[string]authn.Authenticator{ + privateenv.host: NewPrivateAuthenticatorWithAuth(privateenv.host, privateenv.auth), + }) + } else if privateenv.username != "" && privateenv.password != "" { + r.WithImageAuthMap(map[string]authn.Authenticator{ + privateenv.host: NewPrivateAuthenticator(privateenv.host, privateenv.username, privateenv.password), + }) + } else { + t.Fatalf("env not set") + } + + if s, err := r.GetImageString(privateenv.host+"/"+privateenv.image, privateenv.tag, "linux/amd64"); err != nil { + t.Fatalf("err: %v", err) + } else { + t.Logf("success: %s", s) + } +} diff --git a/util/version.go b/util/version.go index 7ccc74c..58a3bb2 100644 --- a/util/version.go +++ b/util/version.go @@ -10,38 +10,53 @@ import ( var ErrNotFound = errors.New("not found") +// GetHighestVersionWithFilter versions는 version 목록이다. +// filter는 *(asterisk)를 숫자(\d+)로 대입하는 regexp로 변환된다. +// 예를 들어, filter가 "1.2.*"이면, 1.2.3이나 1.2.4라는 버전을 찾는다. +// 그리고 그 중 *의 위치에 해당하는 숫자가 가장 큰 버전을 반환한다. +// *은 여럿일 수 있으며 왼쪽에서 오른쪽으로 동일 위치에서 더 큰 버전을 찾는다. +// 예를 들어, filter가 "1.*.*"이면, 1.1.5와 1.2.0 중 1.2.0을 반환한다. func GetHighestVersionWithFilter(versions []string, filter string) (string, error) { - targetTag := "" - targetVer := int64(0) + highestTag := "" + highestNumbers := []int64{} + regexString := fmt.Sprintf("^%s$", strings.Replace(regexp.QuoteMeta(filter), `\*`, `(\d+)`, -1)) + patt, err := regexp.Compile(regexString) - patt, err := regexp.Compile(fmt.Sprintf("^%s$", strings.Replace(regexp.QuoteMeta(filter), "\\*", "(\\d+)", 1))) if nil != err { return "", err } - for _, v := range versions { - // fmt.Println(v) - matches := patt.FindStringSubmatch(v) + for _, tag := range versions { + matches := patt.FindStringSubmatch(tag) - if len(matches) == 0 { + if tag == "" || len(matches) < 2 { continue } - ver, err := strconv.ParseInt(matches[1], 10, 64) - if nil != err { - continue + numbers := []int64{} + + for idx, match := range matches { + if idx == 0 { // 첫번째 매치는 무시한다. + continue + } + if number, err := strconv.ParseInt(match, 10, 64); err != nil { + return "", err + } else { + numbers = append(numbers, number) + } } - if targetVer > ver { - continue + for idx, number := range numbers { // 각 자릿수를 비교해 더 큰 값이 발견되면 교체 + if idx >= len(highestNumbers) || highestNumbers[idx] <= number { + highestTag, highestNumbers = tag, numbers + break + } } - - targetTag, targetVer = v, ver } - if targetTag == "" { + if highestTag == "" { return "", ErrNotFound } - return targetTag, nil + return highestTag, nil } diff --git a/util/version_test.go b/util/version_test.go new file mode 100644 index 0000000..c3a79b0 --- /dev/null +++ b/util/version_test.go @@ -0,0 +1,39 @@ +package util + +import ( + "testing" +) + +var versions []string = []string{"v1.0.0", "v1.0.1", "v1.0.2", "v1.0.9", "v1.0.10", "v1.0.11", "v2.0.99", "v2.0.9", "v3.1.999", "v3.1.998", "v4.0.999", "v4.1.0", "v5.0.0", "v6.0.0", "v6.0.1.0"} + +func TestGetHighestVersionWithFilter(t *testing.T) { + if highestVersion, _ := GetHighestVersionWithFilter(versions, "v1.0.*"); highestVersion != "v1.0.11" { + t.Errorf("Expected: v1.0.11, Got: %s", highestVersion) + } + + if highestVersion, _ := GetHighestVersionWithFilter(versions, "v2.0.*"); highestVersion != "v2.0.99" { + t.Errorf("Expected: v2.0.99, Got: %s", highestVersion) + } + + if highestVersion, _ := GetHighestVersionWithFilter(versions, "v3.1.*"); highestVersion != "v3.1.999" { + t.Errorf("Expected: v3.1.999, Got: %s", highestVersion) + } +} + +func TestGetHighestVersionWithFilterSingleVersion(t *testing.T) { + if highestVersion, _ := GetHighestVersionWithFilter(versions, "v5.*.*"); highestVersion != "v5.0.0" { + t.Errorf("Expected: v5.0.0, Got: %s", highestVersion) + } +} + +func TestGetHighestVersionWithFilterMultipleAsterisk(t *testing.T) { + if highestVersion, _ := GetHighestVersionWithFilter(versions, "v4.*.*"); highestVersion != "v4.1.0" { + t.Errorf("Expected: v4.1.0, Got: %s", highestVersion) + } +} + +func TestGetHighestVersionWithFilterAsteriskNotMatch(t *testing.T) { + if highestVersion, _ := GetHighestVersionWithFilter(versions, "v6.*.*"); highestVersion != "v6.0.0" { + t.Errorf("Expected: v6.0.0, Got: %s", highestVersion) + } +}