fix pre-commit and cleanup

This commit is contained in:
Kjeld Schouten
2024-10-19 15:18:05 +02:00
parent 2acb59c9dc
commit 4ab41d42f2
203 changed files with 8456 additions and 8484 deletions
+112 -112
View File
@@ -1,187 +1,187 @@
package image
import (
"fmt"
"regexp"
"strings"
"fmt"
"regexp"
"strings"
"github.com/rs/zerolog/log"
"github.com/rs/zerolog/log"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation"
)
var (
// Valid SemVer format (Major.Minor.Patch)
semVerPattern = regexp.MustCompile(`^(\d+)\.(\d+)\.(\d+)$`)
// Matches tags like "RELEASE.2023-11-20T22-40-07Z"
releasePattern = regexp.MustCompile(`^RELEASE\.[0-9]{4}-[0-9]{2}-[0-9]{2}T`)
// Matches tags like "x64-1.2.3" and "arm64-1.2.3" followed by a numeric version
archPattern = regexp.MustCompile(`^[a-zA-Z0-9]+-[0-9]+\.[0-9]+`)
// Matches tags like "latest-2023-12-18"
prefixYearMonthDayPattern = regexp.MustCompile(`^[a-zA-Z0-9]+-[0-9]{4}-[0-9]{2}-[0-9]{2}$`)
// Matches dates like "2023-11-15" and "2022-04"
yearMonthDayPattern = regexp.MustCompile(`^[0-9]{4}-[0-9]{2}(-[0-9]{2})?$`)
// Matches tags like "1.2.3.4" "1.2" and "1"
incompleteSemVerPattern = regexp.MustCompile(`^[0-9]+(\.[0-9]+)*$`)
// Matches tags like "something-abcdefg" (only chars before dash and exactly 7 characters after the dash)
shortCommitHashSuffixPattern = regexp.MustCompile(`^[a-zA-Z]+-[a-zA-Z0-9]{7}$`)
// Matches tags like "v1.2.3", "V1.2.3, #1.2.3, $1.2.3, etc"
leadingSymbolPattern = regexp.MustCompile(`^version|Version|[vV]|^[^a-zA-Z0-9]+`)
// Valid SemVer format (Major.Minor.Patch)
semVerPattern = regexp.MustCompile(`^(\d+)\.(\d+)\.(\d+)$`)
// Matches tags like "RELEASE.2023-11-20T22-40-07Z"
releasePattern = regexp.MustCompile(`^RELEASE\.[0-9]{4}-[0-9]{2}-[0-9]{2}T`)
// Matches tags like "x64-1.2.3" and "arm64-1.2.3" followed by a numeric version
archPattern = regexp.MustCompile(`^[a-zA-Z0-9]+-[0-9]+\.[0-9]+`)
// Matches tags like "latest-2023-12-18"
prefixYearMonthDayPattern = regexp.MustCompile(`^[a-zA-Z0-9]+-[0-9]{4}-[0-9]{2}-[0-9]{2}$`)
// Matches dates like "2023-11-15" and "2022-04"
yearMonthDayPattern = regexp.MustCompile(`^[0-9]{4}-[0-9]{2}(-[0-9]{2})?$`)
// Matches tags like "1.2.3.4" "1.2" and "1"
incompleteSemVerPattern = regexp.MustCompile(`^[0-9]+(\.[0-9]+)*$`)
// Matches tags like "something-abcdefg" (only chars before dash and exactly 7 characters after the dash)
shortCommitHashSuffixPattern = regexp.MustCompile(`^[a-zA-Z]+-[a-zA-Z0-9]{7}$`)
// Matches tags like "v1.2.3", "V1.2.3, #1.2.3, $1.2.3, etc"
leadingSymbolPattern = regexp.MustCompile(`^version|Version|[vV]|^[^a-zA-Z0-9]+`)
)
func CleanTag(tag string) (string, error) {
tag = strings.TrimSpace(tag)
tag = strings.TrimSpace(tag)
if tag == "" {
return "", fmt.Errorf("tag is empty")
}
if tag == "" {
return "", fmt.Errorf("tag is empty")
}
// Do basic cleaning
tag = cleanSha(tag)
tag = cleanLeadingSymbol(tag)
// Do basic cleaning
tag = cleanSha(tag)
tag = cleanLeadingSymbol(tag)
// Return early if the tag is already in SemVer format
if semVerPattern.MatchString(tag) {
return tag, nil
}
// Return early if the tag is already in SemVer format
if semVerPattern.MatchString(tag) {
return tag, nil
}
switch {
case releasePattern.MatchString(tag):
tag = cleanRelease(tag)
case archPattern.MatchString(tag):
tag = cleanArch(tag)
case prefixYearMonthDayPattern.MatchString(tag):
tag = cleanPrefixYearMonthDay(tag)
case yearMonthDayPattern.MatchString(tag):
tag = cleanYearMonthDay(tag)
case incompleteSemVerPattern.MatchString(tag):
tag = cleanIncompleteSemVer(tag)
case shortCommitHashSuffixPattern.MatchString(tag):
tag = keepShortCommitHashSuffix(tag)
case leadingSymbolPattern.MatchString(tag):
tag = cleanLeadingSymbol(tag)
}
switch {
case releasePattern.MatchString(tag):
tag = cleanRelease(tag)
case archPattern.MatchString(tag):
tag = cleanArch(tag)
case prefixYearMonthDayPattern.MatchString(tag):
tag = cleanPrefixYearMonthDay(tag)
case yearMonthDayPattern.MatchString(tag):
tag = cleanYearMonthDay(tag)
case incompleteSemVerPattern.MatchString(tag):
tag = cleanIncompleteSemVer(tag)
case shortCommitHashSuffixPattern.MatchString(tag):
tag = keepShortCommitHashSuffix(tag)
case leadingSymbolPattern.MatchString(tag):
tag = cleanLeadingSymbol(tag)
}
// If string contains `-` the second part is usually
// either a commit hash or things like "debian" or "alpine"
// Make sure the first part is some kind of versioning and strip the rest
if strings.Contains(tag, "-") {
split := strings.Split(tag, "-")
switch {
case semVerPattern.MatchString(split[0]):
tag = split[0]
case incompleteSemVerPattern.MatchString(split[0]):
tag = split[0]
}
}
// If string contains `-` the second part is usually
// either a commit hash or things like "debian" or "alpine"
// Make sure the first part is some kind of versioning and strip the rest
if strings.Contains(tag, "-") {
split := strings.Split(tag, "-")
switch {
case semVerPattern.MatchString(split[0]):
tag = split[0]
case incompleteSemVerPattern.MatchString(split[0]):
tag = split[0]
}
}
// Re-check for incomplete SemVer after cleaning
if incompleteSemVerPattern.MatchString(tag) {
tag = cleanIncompleteSemVer(tag)
}
// Re-check for incomplete SemVer after cleaning
if incompleteSemVerPattern.MatchString(tag) {
tag = cleanIncompleteSemVer(tag)
}
if err := checkValidLabelValue(tag); err != nil {
return "", err
}
if err := checkValidLabelValue(tag); err != nil {
return "", err
}
if !semVerPattern.MatchString(tag) {
log.Warn().Msgf("Could not produce a valid SemVer tag for tag [%s]", tag)
}
if !semVerPattern.MatchString(tag) {
log.Warn().Msgf("Could not produce a valid SemVer tag for tag [%s]", tag)
}
// Build and return the updated SemVer string
return tag, nil
// Build and return the updated SemVer string
return tag, nil
}
func Clean(tag string) error {
newTag, err := CleanTag(tag)
if err != nil {
log.Fatal().Err(err).Msgf("Failed to clean tag [%s]", tag)
}
newTag, err := CleanTag(tag)
if err != nil {
log.Fatal().Err(err).Msgf("Failed to clean tag [%s]", tag)
}
log.Info().Msgf("Tag [%s] cleaned to [%s]", tag, newTag)
return nil
log.Info().Msgf("Tag [%s] cleaned to [%s]", tag, newTag)
return nil
}
func checkValidLabelValue(tag string) error {
if errs := validation.IsValidLabelValue(tag); len(errs) > 0 {
return fmt.Errorf("tag [%s] is not valid for label use. error: %s", tag, (strings.Join(errs, ", ")))
}
return nil
if errs := validation.IsValidLabelValue(tag); len(errs) > 0 {
return fmt.Errorf("tag [%s] is not valid for label use. error: %s", tag, (strings.Join(errs, ", ")))
}
return nil
}
// keepShortCommitHashSuffix keeps the last 7 characters of a tag
// eg "something-abcdefg" -> "abcdefg"
func keepShortCommitHashSuffix(tag string) string {
return strings.Split(tag, "-")[1]
return strings.Split(tag, "-")[1]
}
// cleanRelease Transforms release pattern format
// eg "RELEASE.2023-11-20T22-40-07Z" -> "2023.11.20"
func cleanRelease(tag string) string {
tag = strings.Split(tag, ".")[1]
tag = strings.Split(tag, "T")[0]
tag = strings.ReplaceAll(tag, "-", ".")
tag = strings.Split(tag, ".")[1]
tag = strings.Split(tag, "T")[0]
tag = strings.ReplaceAll(tag, "-", ".")
return tag
return tag
}
// cleanArch removes arch prefixes
// eg "x64-1.2.3" -> "1.2.3"
func cleanArch(tag string) string {
tag = strings.Split(tag, "-")[1]
tag = strings.Split(tag, "-")[1]
return tag
return tag
}
// cleanYearMonthDay Transforms date versions
// eg "2023-11-15" -> "2023.11.15" and "2022-04" -> "2022.4"
func cleanYearMonthDay(tag string) string {
tag = strings.ReplaceAll(tag, "-", ".")
parts := strings.Split(tag, ".")
for idx := range parts {
parts[idx] = strings.TrimPrefix(parts[idx], "0")
}
for len(parts) < 3 {
parts = append(parts, "0")
}
tag = strings.Join(parts, ".")
tag = strings.ReplaceAll(tag, "-", ".")
parts := strings.Split(tag, ".")
for idx := range parts {
parts[idx] = strings.TrimPrefix(parts[idx], "0")
}
for len(parts) < 3 {
parts = append(parts, "0")
}
tag = strings.Join(parts, ".")
return tag
return tag
}
// cleanIncompleteSemVer Transforms incomplete SemVer strings
// eg "1.2" -> "1.2.0" and "1" -> "1.0.0"
// versions with more parts are left as-is
func cleanIncompleteSemVer(tag string) string {
parts := strings.Split(tag, ".")
switch {
case len(parts) == 2:
tag = tag + ".0"
case len(parts) == 1:
tag = tag + ".0.0"
}
parts := strings.Split(tag, ".")
switch {
case len(parts) == 2:
tag = tag + ".0"
case len(parts) == 1:
tag = tag + ".0.0"
}
return tag
return tag
}
// cleanLeadingSymbol Trims leading 'v' or non-alphanumeric characters
// e.g "v1.2.3" -> "1.2.3"
func cleanLeadingSymbol(tag string) string {
return leadingSymbolPattern.ReplaceAllString(tag, "")
return leadingSymbolPattern.ReplaceAllString(tag, "")
}
// cleanSha Strips everything after '@'
// e.g "v1.2.3@sha256:abc123" -> "v1.2.3"
func cleanSha(tag string) string {
return strings.Split(tag, "@")[0]
return strings.Split(tag, "@")[0]
}
// cleanPrefixYearMonthDay Transforms date versions with prefix
// eg "latest-2023-12-18" -> "2023.12.18"
func cleanPrefixYearMonthDay(tag string) string {
calVer := strings.Split(tag, "-")[1:]
tag = strings.Join(calVer, ".")
calVer := strings.Split(tag, "-")[1:]
tag = strings.Join(calVer, ".")
return tag
return tag
}
+259 -259
View File
@@ -1,277 +1,277 @@
package image
import (
"strings"
"testing"
"strings"
"testing"
)
type args struct {
tag string
tag string
}
type testdata struct {
name string
args args
want string
wantErr bool
name string
args args
want string
wantErr bool
}
func TestCleanTag(t *testing.T) {
tests := []testdata{
// No match with any pattern tests
{
name: "Test valid SemVer format",
args: args{
tag: "1.2.3",
},
want: "1.2.3",
wantErr: false,
},
{
name: "Test pattern that cannot be converted to SemVer",
args: args{
tag: "latest",
},
want: "latest",
wantErr: false,
},
{
name: "Test empty tag",
args: args{
tag: "",
},
want: "",
wantErr: true,
},
{
name: "Test tag with only whitespace",
args: args{
tag: " ",
},
want: "",
wantErr: true,
},
{
name: "Test full tag with digest",
args: args{
tag: "1.2.3@sha256:abc123",
},
want: "1.2.3",
},
{
name: "Test tag with longer version and `-suffix`",
args: args{
tag: "1.2.3.4-suffix",
},
want: "1.2.3.4",
},
{
name: "Test tag with semver and `-suffix`",
args: args{
tag: "1.2.3-abc12367",
},
want: "1.2.3",
},
{
name: "Test tag with calver and `-suffix`",
args: args{
tag: "2023.11.2-abc12367",
},
want: "2023.11.2",
},
{
name: "Test with invalid label format",
args: args{
tag: strings.Repeat("a", 300),
},
want: "",
wantErr: true,
},
// cleanSha tests
{
name: "Test cleanSha",
args: args{
tag: "1.2.3@sha256:abc123",
},
want: "1.2.3",
},
// cleanLeadingSymbol tests
{
name: "Test cleanLeadingSymbol ($)",
args: args{
tag: "$1.2.3",
},
want: "1.2.3",
},
{
name: "Test cleanLeadingSymbol (v)",
args: args{
tag: "v1.2.3",
},
want: "1.2.3",
},
{
name: "Test cleanLeadingSymbol (version)",
args: args{
tag: "version-a78f38c1",
},
want: "a78f38c1",
},
// keepShortCommitHashSuffix tests
{
name: "Test keepShortCommitHashSuffix (something-hash)",
args: args{
tag: "something-abcd123",
},
want: "abcd123",
},
{
name: "Test keepShortCommitHashSuffix (version-hash)",
args: args{
tag: "version-abcd123",
},
want: "abcd123",
},
// cleanIncompleteSemVer tests
{
name: "Test cleanIncompleteSemVer (2 parts)",
args: args{
tag: "1.2",
},
want: "1.2.0",
},
{
name: "Test cleanIncompleteSemVer (1 part)",
args: args{
tag: "1",
},
want: "1.0.0",
},
{
name: "Test cleanIncompleteSemVer (more than 3 parts)",
args: args{
tag: "1.2.3.4.5",
},
want: "1.2.3.4.5",
},
{
name: "Test cleanIncompleteSemVer (with suffix)",
args: args{
tag: "2.440-jdk17",
},
want: "2.440.0",
},
// cleanYearMonthDay tests
{
name: "Test cleanYearMonthDay (year-month-day)",
args: args{
tag: "2023-11-15",
},
want: "2023.11.15",
},
{
name: "Test cleanYearMonthDay (year-month)",
args: args{
tag: "2022-04",
},
want: "2022.4.0",
},
{
name: "Test cleanYearMonthDay (with prefix)",
args: args{
tag: "latest-2023-12-18",
},
want: "2023.12.18",
},
// cleanPrefix tests
{
name: "Test cleanPrefix (random prefix)",
args: args{
tag: "abc123-v1.2.3",
},
want: "1.2.3",
},
{
name: "Test cleanPrefix (version prefix)",
args: args{
tag: "version-1.2.3",
},
want: "1.2.3",
},
// cleanArch tests
{
name: "Test cleanArch",
args: args{
tag: "x64-1.2.3",
},
want: "1.2.3",
},
// cleanRelease tests
{
name: "Test cleanRelease",
args: args{
tag: "RELEASE.2023-11-20T22-40-07Z",
},
want: "2023.11.20",
},
// cleanStupidSemVerLike tests
{
name: "Test cleanStupidSemVerLike",
args: args{
tag: "v.1.2.3",
},
want: "1.2.3",
},
{
name: "Test cleanStupidSemVerLike (2)",
args: args{
tag: "V.1.2.3",
},
want: "1.2.3",
},
}
tests := []testdata{
// No match with any pattern tests
{
name: "Test valid SemVer format",
args: args{
tag: "1.2.3",
},
want: "1.2.3",
wantErr: false,
},
{
name: "Test pattern that cannot be converted to SemVer",
args: args{
tag: "latest",
},
want: "latest",
wantErr: false,
},
{
name: "Test empty tag",
args: args{
tag: "",
},
want: "",
wantErr: true,
},
{
name: "Test tag with only whitespace",
args: args{
tag: " ",
},
want: "",
wantErr: true,
},
{
name: "Test full tag with digest",
args: args{
tag: "1.2.3@sha256:abc123",
},
want: "1.2.3",
},
{
name: "Test tag with longer version and `-suffix`",
args: args{
tag: "1.2.3.4-suffix",
},
want: "1.2.3.4",
},
{
name: "Test tag with semver and `-suffix`",
args: args{
tag: "1.2.3-abc12367",
},
want: "1.2.3",
},
{
name: "Test tag with calver and `-suffix`",
args: args{
tag: "2023.11.2-abc12367",
},
want: "2023.11.2",
},
{
name: "Test with invalid label format",
args: args{
tag: strings.Repeat("a", 300),
},
want: "",
wantErr: true,
},
// cleanSha tests
{
name: "Test cleanSha",
args: args{
tag: "1.2.3@sha256:abc123",
},
want: "1.2.3",
},
// cleanLeadingSymbol tests
{
name: "Test cleanLeadingSymbol ($)",
args: args{
tag: "$1.2.3",
},
want: "1.2.3",
},
{
name: "Test cleanLeadingSymbol (v)",
args: args{
tag: "v1.2.3",
},
want: "1.2.3",
},
{
name: "Test cleanLeadingSymbol (version)",
args: args{
tag: "version-a78f38c1",
},
want: "a78f38c1",
},
// keepShortCommitHashSuffix tests
{
name: "Test keepShortCommitHashSuffix (something-hash)",
args: args{
tag: "something-abcd123",
},
want: "abcd123",
},
{
name: "Test keepShortCommitHashSuffix (version-hash)",
args: args{
tag: "version-abcd123",
},
want: "abcd123",
},
// cleanIncompleteSemVer tests
{
name: "Test cleanIncompleteSemVer (2 parts)",
args: args{
tag: "1.2",
},
want: "1.2.0",
},
{
name: "Test cleanIncompleteSemVer (1 part)",
args: args{
tag: "1",
},
want: "1.0.0",
},
{
name: "Test cleanIncompleteSemVer (more than 3 parts)",
args: args{
tag: "1.2.3.4.5",
},
want: "1.2.3.4.5",
},
{
name: "Test cleanIncompleteSemVer (with suffix)",
args: args{
tag: "2.440-jdk17",
},
want: "2.440.0",
},
// cleanYearMonthDay tests
{
name: "Test cleanYearMonthDay (year-month-day)",
args: args{
tag: "2023-11-15",
},
want: "2023.11.15",
},
{
name: "Test cleanYearMonthDay (year-month)",
args: args{
tag: "2022-04",
},
want: "2022.4.0",
},
{
name: "Test cleanYearMonthDay (with prefix)",
args: args{
tag: "latest-2023-12-18",
},
want: "2023.12.18",
},
// cleanPrefix tests
{
name: "Test cleanPrefix (random prefix)",
args: args{
tag: "abc123-v1.2.3",
},
want: "1.2.3",
},
{
name: "Test cleanPrefix (version prefix)",
args: args{
tag: "version-1.2.3",
},
want: "1.2.3",
},
// cleanArch tests
{
name: "Test cleanArch",
args: args{
tag: "x64-1.2.3",
},
want: "1.2.3",
},
// cleanRelease tests
{
name: "Test cleanRelease",
args: args{
tag: "RELEASE.2023-11-20T22-40-07Z",
},
want: "2023.11.20",
},
// cleanStupidSemVerLike tests
{
name: "Test cleanStupidSemVerLike",
args: args{
tag: "v.1.2.3",
},
want: "1.2.3",
},
{
name: "Test cleanStupidSemVerLike (2)",
args: args{
tag: "V.1.2.3",
},
want: "1.2.3",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := CleanTag(tt.args.tag)
// If we expected an error, but didn't get one, fail the test
if (err != nil) != tt.wantErr {
t.Errorf("CleanTag() error = %v, wantErr %t", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("CleanTag() got = %v, want %v", got, tt.want)
}
})
}
got, err := CleanTag(tt.args.tag)
// If we expected an error, but didn't get one, fail the test
if (err != nil) != tt.wantErr {
t.Errorf("CleanTag() error = %v, wantErr %t", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("CleanTag() got = %v, want %v", got, tt.want)
}
})
}
}
func TestCheckValidLabelValue(t *testing.T) {
tests := []testdata{
{
name: "Test invalid label format",
args: args{
tag: "1.2.3@sha256:abc123",
},
wantErr: true,
},
{
name: "Test valid label format",
args: args{
tag: "1.2.3",
},
wantErr: false,
},
}
tests := []testdata{
{
name: "Test invalid label format",
args: args{
tag: "1.2.3@sha256:abc123",
},
wantErr: true,
},
{
name: "Test valid label format",
args: args{
tag: "1.2.3",
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := checkValidLabelValue(tt.args.tag)
// If we expected an error, but didn't get one, fail the test
if (err != nil) != tt.wantErr {
t.Errorf("checkValidLabelValue() error = %v, wantErr %t", err, tt.wantErr)
return
}
})
}
err := checkValidLabelValue(tt.args.tag)
// If we expected an error, but didn't get one, fail the test
if (err != nil) != tt.wantErr {
t.Errorf("checkValidLabelValue() error = %v, wantErr %t", err, tt.wantErr)
return
}
})
}
}
+107 -107
View File
@@ -1,146 +1,146 @@
package image
import (
"fmt"
"regexp"
"strings"
"fmt"
"regexp"
"strings"
"github.com/knadh/koanf/parsers/yaml"
"github.com/knadh/koanf/providers/file"
"github.com/knadh/koanf/v2"
"github.com/rs/zerolog/log"
"github.com/knadh/koanf/parsers/yaml"
"github.com/knadh/koanf/providers/file"
"github.com/knadh/koanf/v2"
"github.com/rs/zerolog/log"
)
// Images represents the structure of values.yaml.
type Images struct {
ImagesMap map[string]ImageDetails
K *koanf.Koanf
ImagesMap map[string]ImageDetails
K *koanf.Koanf
}
// ImageDetails represents details for each image.
type ImageDetails struct {
Repository string `yaml:"repository"`
Tag string `yaml:"tag"`
Version string
Link string
// Add other fields as needed
Repository string `yaml:"repository"`
Tag string `yaml:"tag"`
Version string
Link string
// Add other fields as needed
}
var imageRegex = regexp.MustCompile(`^image|[a-zA-Z0-9]+Image$`)
func (i *Images) LoadValuesFile(filename string) error {
// Initialize koanf instance
i.K = koanf.New(".")
// Initialize koanf instance
i.K = koanf.New(".")
// Load YAML file using koanf
if err := i.K.Load(file.Provider(filename), yaml.Parser()); err != nil {
return err
}
// Load YAML file using koanf
if err := i.K.Load(file.Provider(filename), yaml.Parser()); err != nil {
return err
}
// List only root-level keys that match the criteria
keys := getFilteredRootLevelKeys(i.K)
i.ImagesMap = make(map[string]ImageDetails)
for _, key := range keys {
// Extract relevant fields from the loaded configuration
var img ImageDetails
if err := i.K.Unmarshal(key, &img); err != nil {
return err
}
// List only root-level keys that match the criteria
keys := getFilteredRootLevelKeys(i.K)
i.ImagesMap = make(map[string]ImageDetails)
for _, key := range keys {
// Extract relevant fields from the loaded configuration
var img ImageDetails
if err := i.K.Unmarshal(key, &img); err != nil {
return err
}
// Set the Link field based on the repository
img.Link = constructLink(img.Repository)
// Set the Link field based on the repository
img.Link = constructLink(img.Repository)
// Set the Version field based on the tag
version, err := CleanTag(img.Tag)
if err != nil {
log.Error().Err(err).Msg("❌ Failed to clean tag")
}
// Set the Version field based on the tag
version, err := CleanTag(img.Tag)
if err != nil {
log.Error().Err(err).Msg("❌ Failed to clean tag")
}
img.Version = version
img.Version = version
// Save the extracted values to the struct
i.ImagesMap[key] = img
}
// Save the extracted values to the struct
i.ImagesMap[key] = img
}
return nil
return nil
}
func getFilteredRootLevelKeys(k *koanf.Koanf) []string {
filteredKeys := []string{}
filteredKeys := []string{}
// k.Raw() returns a map[string]interface{} with all the keys and their values
// This means the keys will only be the root-level keys, we can drill into the
// values later if we want the nested keys.
for key := range k.Raw() {
if key == "imageSelector" {
log.Error().Msg("❌ Found [imageSelector] in top level keys, this is not supported.")
continue
}
// Filter keys that match the regex
if imageRegex.MatchString(key) {
filteredKeys = append(filteredKeys, key)
}
}
// k.Raw() returns a map[string]interface{} with all the keys and their values
// This means the keys will only be the root-level keys, we can drill into the
// values later if we want the nested keys.
for key := range k.Raw() {
if key == "imageSelector" {
log.Error().Msg("❌ Found [imageSelector] in top level keys, this is not supported.")
continue
}
// Filter keys that match the regex
if imageRegex.MatchString(key) {
filteredKeys = append(filteredKeys, key)
}
}
return filteredKeys
return filteredKeys
}
// constructLink constructs a link based on the repository using the logic from the main function.
func constructLink(repository string) string {
prefix := ""
prefix := ""
switch {
case strings.HasPrefix(repository, "lscr.io/linuxserver/"):
prefix = "https://fleet.linuxserver.io/image?name="
repository = strings.TrimPrefix(repository, "lscr.io/")
case strings.HasPrefix(repository, "tccr.io/tccr/"):
prefix = "https://github.com/truecharts/containers/tree/master/apps/"
repository = strings.TrimPrefix(repository, "tccr.io/tccr/")
case strings.HasPrefix(repository, "mcr.microsoft.com/"):
prefix = "https://mcr.microsoft.com/en-us/product/"
repository = strings.TrimPrefix(repository, "mcr.microsoft.com/")
case strings.HasPrefix(repository, "public.ecr.aws/"):
prefix = "https://gallery.ecr.aws/"
repository = strings.TrimPrefix(repository, "public.ecr.aws/")
case strings.HasPrefix(repository, "ghcr.io/"):
prefix = "https://"
case strings.HasPrefix(repository, "quay.io/"):
prefix = "https://"
case strings.HasPrefix(repository, "gcr.io/"):
prefix = "https://"
case strings.Contains(repository, ".azurecr.io/"):
reg := fmt.Sprintf(`%s.azurecr.io/`, strings.Split(repository, ".")[0])
prefix = fmt.Sprintf("https://%s", reg)
repository = strings.TrimPrefix(repository, reg)
case strings.Contains(repository, ".ocir.io/"):
prefix = ""
default:
// Docker Hub or unknown registry
prefix = "https://hub.docker.com/r/"
repository = strings.TrimPrefix(repository, "docker.io/")
repository = strings.TrimPrefix(repository, "index.docker.io/")
repository = strings.TrimPrefix(repository, "registry-1.docker.io/")
repository = strings.TrimPrefix(repository, "registry.hub.docker.com/")
switch {
case strings.HasPrefix(repository, "lscr.io/linuxserver/"):
prefix = "https://fleet.linuxserver.io/image?name="
repository = strings.TrimPrefix(repository, "lscr.io/")
case strings.HasPrefix(repository, "tccr.io/tccr/"):
prefix = "https://github.com/truecharts/containers/tree/master/apps/"
repository = strings.TrimPrefix(repository, "tccr.io/tccr/")
case strings.HasPrefix(repository, "mcr.microsoft.com/"):
prefix = "https://mcr.microsoft.com/en-us/product/"
repository = strings.TrimPrefix(repository, "mcr.microsoft.com/")
case strings.HasPrefix(repository, "public.ecr.aws/"):
prefix = "https://gallery.ecr.aws/"
repository = strings.TrimPrefix(repository, "public.ecr.aws/")
case strings.HasPrefix(repository, "ghcr.io/"):
prefix = "https://"
case strings.HasPrefix(repository, "quay.io/"):
prefix = "https://"
case strings.HasPrefix(repository, "gcr.io/"):
prefix = "https://"
case strings.Contains(repository, ".azurecr.io/"):
reg := fmt.Sprintf(`%s.azurecr.io/`, strings.Split(repository, ".")[0])
prefix = fmt.Sprintf("https://%s", reg)
repository = strings.TrimPrefix(repository, reg)
case strings.Contains(repository, ".ocir.io/"):
prefix = ""
default:
// Docker Hub or unknown registry
prefix = "https://hub.docker.com/r/"
repository = strings.TrimPrefix(repository, "docker.io/")
repository = strings.TrimPrefix(repository, "index.docker.io/")
repository = strings.TrimPrefix(repository, "registry-1.docker.io/")
repository = strings.TrimPrefix(repository, "registry.hub.docker.com/")
// Check for Docker Official Image
if strings.Count(repository, "/") == 0 || strings.HasPrefix(repository, "library/") {
prefix = "https://hub.docker.com/_/"
repository = strings.TrimPrefix(repository, "library/")
}
// Check for Docker Official Image
if strings.Count(repository, "/") == 0 || strings.HasPrefix(repository, "library/") {
prefix = "https://hub.docker.com/_/"
repository = strings.TrimPrefix(repository, "library/")
}
// Avoid creating a bad link if the image name has more than 1 slash
slashes := strings.Count(repository, "/")
if slashes > 1 {
prefix = ""
log.Warn().Msgf("WARNING: Could not determine source repository url for [%s]", repository)
}
}
// Avoid creating a bad link if the image name has more than 1 slash
slashes := strings.Count(repository, "/")
if slashes > 1 {
prefix = ""
log.Warn().Msgf("WARNING: Could not determine source repository url for [%s]", repository)
}
}
if prefix == "" {
log.Warn().Msgf("WARNING: Could not determine source repository url for [%s]", repository)
return ""
}
if prefix == "" {
log.Warn().Msgf("WARNING: Could not determine source repository url for [%s]", repository)
return ""
}
containerURL := fmt.Sprintf("%s%s", prefix, repository)
return containerURL
containerURL := fmt.Sprintf("%s%s", prefix, repository)
return containerURL
}
+161 -161
View File
@@ -1,171 +1,171 @@
package image
import (
"fmt"
"reflect"
"testing"
"fmt"
"reflect"
"testing"
)
func TestLoadValuesFile(t *testing.T) {
type TestData struct {
name string
valuesFile string
expected map[string]ImageDetails
wantErr bool
}
testDataPath := "../../testdata/values_yaml"
tests := []TestData{
{
name: "Test malformed file",
valuesFile: "malformedValues.yaml",
expected: nil,
wantErr: true,
},
{
name: "Test empty file",
valuesFile: "emptyValues.yaml",
expected: nil,
wantErr: false,
},
{
name: "Test single image file",
valuesFile: "singleImageValues.yaml",
expected: map[string]ImageDetails{
"image": {
Repository: "nginx",
Tag: "1.15.8",
Link: "https://hub.docker.com/_/nginx",
Version: "1.15.8",
},
},
},
{
name: "Test multiple image file",
valuesFile: "multiImageValues.yaml",
expected: map[string]ImageDetails{
"image": {
Repository: "author/image",
Tag: "1.0.0",
Link: "https://hub.docker.com/r/author/image",
Version: "1.0.0",
},
"dockerHub1Image": {
Repository: "docker.io/author/image",
Tag: "1.0.0",
Link: "https://hub.docker.com/r/author/image",
Version: "1.0.0",
},
"dockerHub2Image": {
Repository: "index.docker.io/author/image",
Tag: "1.0.0",
Link: "https://hub.docker.com/r/author/image",
Version: "1.0.0",
},
"dockerHub3Image": {
Repository: "registry-1.docker.io/author/image",
Tag: "1.0.0",
Link: "https://hub.docker.com/r/author/image",
Version: "1.0.0",
},
"dockerHub4Image": {
Repository: "registry.hub.docker.com/author/image",
Tag: "1.0.0",
Link: "https://hub.docker.com/r/author/image",
Version: "1.0.0",
},
"dockerHub5Image": {
Repository: "image",
Tag: "1.0.0",
Link: "https://hub.docker.com/_/image",
Version: "1.0.0",
},
"dockerHub6Image": {
Repository: "library/image",
Tag: "1.0.0",
Link: "https://hub.docker.com/_/image",
Version: "1.0.0",
},
"lscrImage": {
Repository: "lscr.io/linuxserver/image",
Tag: "1.0.0",
Link: "https://fleet.linuxserver.io/image?name=linuxserver/image",
Version: "1.0.0",
},
"tccrImage": {
Repository: "tccr.io/tccr/image",
Tag: "1.0.0",
Link: "https://github.com/truecharts/containers/tree/master/apps/image",
Version: "1.0.0",
},
"mcrImage": {
Repository: "mcr.microsoft.com/author/image",
Tag: "1.0.0",
Link: "https://mcr.microsoft.com/en-us/product/author/image",
Version: "1.0.0",
},
"ecrImage": {
Repository: "public.ecr.aws/author/image",
Tag: "1.0.0",
Link: "https://gallery.ecr.aws/author/image",
Version: "1.0.0",
},
"ghcrImage": {
Repository: "ghcr.io/author/image",
Tag: "1.0.0",
Link: "https://ghcr.io/author/image",
Version: "1.0.0",
},
"quayImage": {
Repository: "quay.io/author/image",
Tag: "1.0.0",
Link: "https://quay.io/author/image",
Version: "1.0.0",
},
"gcrImage": {
Repository: "gcr.io/author/image",
Tag: "1.0.0",
Link: "https://gcr.io/author/image",
Version: "1.0.0",
},
"azurecrImage": {
Repository: "author.azurecr.io/image",
Tag: "1.0.0",
Link: "https://author.azurecr.io/image",
Version: "1.0.0",
},
"ocirImage": {
Repository: "author.ocir.io/image",
Tag: "1.0.0",
Link: "",
Version: "1.0.0",
},
"unknownImage": {
Repository: "unknown.io/author/image",
Tag: "1.0.0",
Link: "",
Version: "1.0.0",
},
},
},
}
type TestData struct {
name string
valuesFile string
expected map[string]ImageDetails
wantErr bool
}
testDataPath := "../../testdata/values_yaml"
tests := []TestData{
{
name: "Test malformed file",
valuesFile: "malformedValues.yaml",
expected: nil,
wantErr: true,
},
{
name: "Test empty file",
valuesFile: "emptyValues.yaml",
expected: nil,
wantErr: false,
},
{
name: "Test single image file",
valuesFile: "singleImageValues.yaml",
expected: map[string]ImageDetails{
"image": {
Repository: "nginx",
Tag: "1.15.8",
Link: "https://hub.docker.com/_/nginx",
Version: "1.15.8",
},
},
},
{
name: "Test multiple image file",
valuesFile: "multiImageValues.yaml",
expected: map[string]ImageDetails{
"image": {
Repository: "author/image",
Tag: "1.0.0",
Link: "https://hub.docker.com/r/author/image",
Version: "1.0.0",
},
"dockerHub1Image": {
Repository: "docker.io/author/image",
Tag: "1.0.0",
Link: "https://hub.docker.com/r/author/image",
Version: "1.0.0",
},
"dockerHub2Image": {
Repository: "index.docker.io/author/image",
Tag: "1.0.0",
Link: "https://hub.docker.com/r/author/image",
Version: "1.0.0",
},
"dockerHub3Image": {
Repository: "registry-1.docker.io/author/image",
Tag: "1.0.0",
Link: "https://hub.docker.com/r/author/image",
Version: "1.0.0",
},
"dockerHub4Image": {
Repository: "registry.hub.docker.com/author/image",
Tag: "1.0.0",
Link: "https://hub.docker.com/r/author/image",
Version: "1.0.0",
},
"dockerHub5Image": {
Repository: "image",
Tag: "1.0.0",
Link: "https://hub.docker.com/_/image",
Version: "1.0.0",
},
"dockerHub6Image": {
Repository: "library/image",
Tag: "1.0.0",
Link: "https://hub.docker.com/_/image",
Version: "1.0.0",
},
"lscrImage": {
Repository: "lscr.io/linuxserver/image",
Tag: "1.0.0",
Link: "https://fleet.linuxserver.io/image?name=linuxserver/image",
Version: "1.0.0",
},
"tccrImage": {
Repository: "tccr.io/tccr/image",
Tag: "1.0.0",
Link: "https://github.com/truecharts/containers/tree/master/apps/image",
Version: "1.0.0",
},
"mcrImage": {
Repository: "mcr.microsoft.com/author/image",
Tag: "1.0.0",
Link: "https://mcr.microsoft.com/en-us/product/author/image",
Version: "1.0.0",
},
"ecrImage": {
Repository: "public.ecr.aws/author/image",
Tag: "1.0.0",
Link: "https://gallery.ecr.aws/author/image",
Version: "1.0.0",
},
"ghcrImage": {
Repository: "ghcr.io/author/image",
Tag: "1.0.0",
Link: "https://ghcr.io/author/image",
Version: "1.0.0",
},
"quayImage": {
Repository: "quay.io/author/image",
Tag: "1.0.0",
Link: "https://quay.io/author/image",
Version: "1.0.0",
},
"gcrImage": {
Repository: "gcr.io/author/image",
Tag: "1.0.0",
Link: "https://gcr.io/author/image",
Version: "1.0.0",
},
"azurecrImage": {
Repository: "author.azurecr.io/image",
Tag: "1.0.0",
Link: "https://author.azurecr.io/image",
Version: "1.0.0",
},
"ocirImage": {
Repository: "author.ocir.io/image",
Tag: "1.0.0",
Link: "",
Version: "1.0.0",
},
"unknownImage": {
Repository: "unknown.io/author/image",
Tag: "1.0.0",
Link: "",
Version: "1.0.0",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var images Images
err := images.LoadValuesFile(fmt.Sprintf("%s/%s", testDataPath, tt.valuesFile))
if (err != nil) != tt.wantErr {
t.Errorf("LoadValuesFile() error = %v, wantErr %v", err, tt.wantErr)
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var images Images
err := images.LoadValuesFile(fmt.Sprintf("%s/%s", testDataPath, tt.valuesFile))
if (err != nil) != tt.wantErr {
t.Errorf("LoadValuesFile() error = %v, wantErr %v", err, tt.wantErr)
}
if tt.expected == nil && len(images.ImagesMap) > 0 {
t.Errorf("LoadValuesFile() expected = %+v, got %+v", tt.expected, images.ImagesMap)
}
if tt.expected == nil && len(images.ImagesMap) > 0 {
t.Errorf("LoadValuesFile() expected = %+v, got %+v", tt.expected, images.ImagesMap)
}
if tt.expected != nil {
if !reflect.DeepEqual(images.ImagesMap, tt.expected) {
t.Errorf("LoadValuesFile() expected = %+v, got %+v", tt.expected, images.ImagesMap)
}
}
})
}
if tt.expected != nil {
if !reflect.DeepEqual(images.ImagesMap, tt.expected) {
t.Errorf("LoadValuesFile() expected = %+v, got %+v", tt.expected, images.ImagesMap)
}
}
})
}
}