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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
12 changes: 6 additions & 6 deletions common/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ require (
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-containerregistry v0.20.4-0.20250225234217-098045d5e61f // indirect
github.com/google/go-containerregistry v0.20.6 // indirect
github.com/google/go-intervals v0.0.2 // indirect
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 // indirect
github.com/google/uuid v1.6.0 // indirect
Expand Down Expand Up @@ -112,7 +112,7 @@ require (
github.com/sigstore/sigstore v1.9.5 // indirect
github.com/smallstep/pkcs7 v0.1.1 // indirect
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 // indirect
github.com/sylabs/sif/v2 v2.21.1 // indirect
github.com/sylabs/sif/v2 v2.22.0 // indirect
github.com/tchap/go-patricia/v2 v2.3.3 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect
Expand All @@ -121,10 +121,10 @@ require (
github.com/vishvananda/netns v0.0.5 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
Expand Down
36 changes: 18 additions & 18 deletions common/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-containerregistry v0.20.4-0.20250225234217-098045d5e61f h1:q+kbH7LI4wK3gNCxyvy2rFldJqAAB+Gch79/xj9/+GU=
github.com/google/go-containerregistry v0.20.4-0.20250225234217-098045d5e61f/go.mod h1:UnXV0UkKqoHbzwn49vfozmwMcLMS8XLLsVKVuhv3cGc=
github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU=
github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y=
github.com/google/go-intervals v0.0.2 h1:FGrVEiUnTRKR8yE04qzXYaJMtnIYqobR5QbblK3ixcM=
github.com/google/go-intervals v0.0.2/go.mod h1:MkaR3LNRfeKLPmqgJYs4E66z5InYjmCjbbr4TQlcT6Y=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
Expand Down Expand Up @@ -224,8 +224,8 @@ github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWN
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
github.com/sebdah/goldie/v2 v2.5.5 h1:rx1mwF95RxZ3/83sdS4Yp7t2C5TCokvWP4TBRbAyEWY=
github.com/sebdah/goldie/v2 v2.5.5/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
github.com/sebdah/goldie/v2 v2.7.1 h1:PkBHymaYdtvEkZV7TmyqKxdmn5/Vcj+8TpATWZjnG5E=
github.com/sebdah/goldie/v2 v2.7.1/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
github.com/seccomp/libseccomp-golang v0.11.1 h1:wuk4ZjSx6kyQII4rj6G6fvVzRHQaSiPvccJazDagu4g=
github.com/seccomp/libseccomp-golang v0.11.1/go.mod h1:5m1Lk8E9OwgZTTVz4bBOer7JuazaBa+xTkM895tDiWc=
github.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL5qTdn9lR8XKHf4RUyG1Sx3g=
Expand Down Expand Up @@ -260,8 +260,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/sylabs/sif/v2 v2.21.1 h1:GZ0b5//AFAqJEChd8wHV/uSKx/l1iuGYwjR8nx+4wPI=
github.com/sylabs/sif/v2 v2.21.1/go.mod h1:YoqEGQnb5x/ItV653bawXHZJOXQaEWpGwHsSD3YePJI=
github.com/sylabs/sif/v2 v2.22.0 h1:Y+xXufp4RdgZe02SR3nWEg7S6q4tPWN237WHYzkDSKA=
github.com/sylabs/sif/v2 v2.22.0/go.mod h1:W1XhWTmG1KcG7j5a3KSYdMcUIFvbs240w/MMVW627hs=
github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc=
github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=
Expand All @@ -287,22 +287,22 @@ go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.podman.io/image/v5 v5.37.0 h1:yzgQybwuWIIeK63hu+mQqna/wOh96XD5cpVc6j8Dg5M=
Expand Down
41 changes: 39 additions & 2 deletions common/libimage/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"strings"
"time"

"github.com/opencontainers/go-digest"
"github.com/sirupsen/logrus"
filtersPkg "go.podman.io/common/pkg/filters"
"go.podman.io/common/pkg/timetype"
Expand Down Expand Up @@ -481,9 +482,45 @@ func filterID(value string) filterFunc {

// filterDigest creates a digest filter for matching the specified value.
func filterDigest(value string) (filterFunc, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

A generic design question: What happens / what do we want to happen if the on-registry image is digest A, c/storage configuration is digest B, and the user filters using digest C - or some subset of that?

What is the goal of this PR?

if !strings.HasPrefix(value, "sha256:") {
return nil, fmt.Errorf("invalid value %q for digest filter", value)
// Check if it's a valid complete digest
if _, err := digest.Parse(value); err == nil {
// Valid complete digest - use it as is
return func(img *Image, _ *layerTree) (bool, error) {
return img.containsDigestPrefix(value), nil
}, nil
}

// Not a complete digest - check if it's a valid partial digest with algorithm prefix
if !strings.Contains(value, ":") {
return nil, fmt.Errorf("invalid value %q for digest filter: must have algorithm prefix (e.g., sha256:)", value)
}

parts := strings.SplitN(value, ":", 2)
if len(parts) != 2 {
return nil, fmt.Errorf("invalid value %q for digest filter: invalid format", value)
}

algorithm := parts[0]
hashPart := parts[1]

// Validate the algorithm is known
switch algorithm {
case "sha256", "sha512": // common algorithms
Copy link
Contributor

Choose a reason for hiding this comment

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

(Now that we know digests can be added), this is not maintainable.

Either add a storage/pkg/supported-digests as a centralized place hard-coding our choices, or (preferably??) use https://github.com/opencontainers/go-digest/blob/89707e38ad1aab6815bde4ad095806212ec90236/algorithm.go#L198 to parse any of them.


(Generally, are other parts of this parser applicable elsewhere? “filters” is not a natural place for “prefix of digest” parser.)

// Valid algorithm prefix
default:
return nil, fmt.Errorf("invalid value %q for digest filter: unsupported algorithm %q", value, algorithm)
}

// Validate hash part contains only hex characters
if len(hashPart) == 0 {
return nil, fmt.Errorf("invalid value %q for digest filter: empty hash part", value)
}
for _, c := range hashPart {
if (c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F') {
return nil, fmt.Errorf("invalid value %q for digest filter: hash part contains non-hex characters", value)
}
}

return func(img *Image, _ *layerTree) (bool, error) {
return img.containsDigestPrefix(value), nil
}, nil
Expand Down
25 changes: 22 additions & 3 deletions common/libimage/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ func (i *Image) removeRecursive(ctx context.Context, rmMap map[string]*RemoveIma
// error.
if referencedBy != "" && numNames != 1 {
byID := strings.HasPrefix(i.ID(), referencedBy)
byDigest := strings.HasPrefix(referencedBy, "sha256:")
byDigest := isDigestReference(referencedBy)
if !options.Force {
if byID && numNames > 1 {
return processedIDs, fmt.Errorf("unable to delete image %q by ID with more than one tag (%s): please force removal", i.ID(), i.Names())
Expand Down Expand Up @@ -577,7 +577,7 @@ var errTagDigest = errors.New("tag by digest not supported")
// Tag the image with the specified name and store it in the local containers
// storage. The name is normalized according to the rules of NormalizeName.
func (i *Image) Tag(name string) error {
if strings.HasPrefix(name, "sha256:") { // ambiguous input
if isDigestReference(name) { // ambiguous input
return fmt.Errorf("%s: %w", name, errTagDigest)
}

Expand Down Expand Up @@ -613,7 +613,7 @@ var errUntagDigest = errors.New("untag by digest not supported")
// the local containers storage. The name is normalized according to the rules
// of NormalizeName.
func (i *Image) Untag(name string) error {
if strings.HasPrefix(name, "sha256:") { // ambiguous input
if isDigestReference(name) { // ambiguous input
return fmt.Errorf("%s: %w", name, errUntagDigest)
}

Expand Down Expand Up @@ -1028,6 +1028,25 @@ func getImageID(ctx context.Context, src types.ImageReference, sys *types.System
return "@" + imageDigest.Encoded(), nil
}

// getImageDigestString creates an image object and returns the full digest string
// (with algorithm prefix) of the config blob for use in image names.
func getImageDigestString(ctx context.Context, src types.ImageReference, sys *types.SystemContext) (string, error) {
newImg, err := src.NewImage(ctx, sys)
if err != nil {
return "", err
}
defer func() {
if err := newImg.Close(); err != nil {
logrus.Errorf("Failed to close image: %q", err)
}
}()
imageDigest := newImg.ConfigInfo().Digest
if err = imageDigest.Validate(); err != nil {
return "", fmt.Errorf("getting config info: %w", err)
}
return imageDigest.String(), nil
}

// Checks whether the image matches the specified platform.
// Returns
// - 1) a matching error that can be used for logging (or returning) what does not match
Expand Down
8 changes: 7 additions & 1 deletion common/libimage/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,11 @@ func (r *Runtime) Import(ctx context.Context, path string, options *ImportOption
}
}

return "sha256:" + name, nil
// Get the proper digest string with the correct algorithm
digestString, err := getImageDigestString(ctx, srcRef, r.systemContextCopy())
if err != nil {
return "", err
}

return digestString, nil
}
16 changes: 11 additions & 5 deletions common/libimage/manifests/manifests.go
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,7 @@ type AddArtifactOptions struct {
Annotations map[string]string // optional, default is none
SubjectReference types.ImageReference // optional
ExcludeTitles bool // don't add "org.opencontainers.image.title" annotations set to file base names
DigestAlgorithm *digest.Algorithm // optional digest algorithm for content addressing, defaults to SHA256
}

// AddArtifact creates an artifact manifest describing the specified file or
Expand All @@ -705,6 +706,11 @@ type AddArtifactOptions struct {
// the image index and get the same end-result, but this should save them some
// work.
func (l *list) AddArtifact(ctx context.Context, sys *types.SystemContext, options AddArtifactOptions, files ...string) (digest.Digest, error) {
// Determine the digest algorithm to use, defaulting to SHA256 for OCI compatibility
digestAlgorithm := digest.SHA256
if options.DigestAlgorithm != nil {
digestAlgorithm = *options.DigestAlgorithm
}
// If we were given a subject, build a descriptor for it first, since
// it might be remote, and anything else we do before looking at it
// might have to get thrown away if we can't get to it for whatever
Expand Down Expand Up @@ -763,7 +769,7 @@ func (l *list) AddArtifact(ctx context.Context, sys *types.SystemContext, option
defer f.Close()

// Hang on to a copy of the first 512 bytes, but digest the whole thing.
digester := digest.Canonical.Digester()
digester := digestAlgorithm.Digester()
writeCounter := ioutils.NewWriteCounter(digester.Hash())
var detectableData bytes.Buffer
_, err = io.CopyN(writeCounter, io.TeeReader(f, &detectableData), 512)
Expand Down Expand Up @@ -853,7 +859,7 @@ func (l *list) AddArtifact(ctx context.Context, sys *types.SystemContext, option
if err != nil {
return "", fmt.Errorf("recording artifact config data file %q: %w", options.ConfigFile, err)
}
digester := digest.Canonical.Digester()
digester := digestAlgorithm.Digester()
counter := ioutils.NewWriteCounter(digester.Hash())
if err := func() error {
f, err := os.Open(filePath)
Expand All @@ -874,7 +880,7 @@ func (l *list) AddArtifact(ctx context.Context, sys *types.SystemContext, option
configFilePath = filePath
} else {
decoder := bytes.NewReader(configDescriptor.Data)
digester := digest.Canonical.Digester()
digester := digestAlgorithm.Digester()
counter := ioutils.NewWriteCounter(digester.Hash())
if _, err := io.Copy(counter, decoder); err != nil {
return "", fmt.Errorf("digesting inlined artifact config data: %w", err)
Expand All @@ -884,7 +890,7 @@ func (l *list) AddArtifact(ctx context.Context, sys *types.SystemContext, option
}
} else {
configDescriptor.Data = nil
configDescriptor.Digest = digest.Canonical.FromString("")
configDescriptor.Digest = digestAlgorithm.FromString("")
}

// Construct the manifest.
Expand Down Expand Up @@ -962,7 +968,7 @@ func LockerForImage(store storage.Store, image string) (lockfile.Locker, error)
if err != nil {
return nil, fmt.Errorf("locating image %q for locating lock: %w", image, err)
}
d := digest.NewDigestFromEncoded(digest.Canonical, img.ID)
d := digest.NewDigestFromEncoded(store.GetDigestAlgorithm(), img.ID)
if err := d.Validate(); err != nil {
return nil, fmt.Errorf("coercing image ID for %q into a digest: %w", image, err)
}
Expand Down
11 changes: 11 additions & 0 deletions common/libimage/normalize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,10 @@ func TestNormalizeName(t *testing.T) {

func TestNormalizeTaggedDigestedString(t *testing.T) {
const digestSuffix = "@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
const digestSuffix512 = "@sha512:b89d9c93e9ed3597455c90a0b88a8bbb5cb7188438f70953fede212a0c4394e0d200d73e696b8053595e5d7cc4f2b8f5e86b07b5e6d13b8fd1a68d7b8cc2f9b"

for _, test := range []struct{ input, expected string }{
// SHA256 test cases (existing)
{"$$garbage", ""},
{"fedora", "fedora"},
{"fedora:tag", "fedora:tag"},
Expand All @@ -134,6 +136,15 @@ func TestNormalizeTaggedDigestedString(t *testing.T) {
{"quay.io/repo/fedora:tag" + digestSuffix, "quay.io/repo/fedora" + digestSuffix},
{"localhost/fedora:anothertag" + digestSuffix, "localhost/fedora" + digestSuffix},
{"localhost:5000/fedora:v1.2.3.4.5" + digestSuffix, "localhost:5000/fedora" + digestSuffix},
// SHA512 test cases
{digestSuffix512, ""},
{"docker://alpine:latest" + digestSuffix512, ""},
{"alpine" + digestSuffix512, "alpine" + digestSuffix512},
{"alpine:latest" + digestSuffix512, "alpine" + digestSuffix512},
{"repo/alpine:123456" + digestSuffix512, "repo/alpine" + digestSuffix512},
{"quay.io/repo/alpine:tag" + digestSuffix512, "quay.io/repo/alpine" + digestSuffix512},
{"localhost/alpine:anothertag" + digestSuffix512, "localhost/alpine" + digestSuffix512},
{"localhost:5000/alpine:v1.2.3.4.5" + digestSuffix512, "localhost:5000/alpine" + digestSuffix512},
} {
res, named, err := normalizeTaggedDigestedString(test.input)
if test.expected == "" {
Expand Down
Loading
Loading