Skip to content

Commit 5b33d16

Browse files
committed
add type BlobCompression to reduce code duplication
1 parent 201365f commit 5b33d16

2 files changed

Lines changed: 73 additions & 37 deletions

File tree

internal/models/blob.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,16 @@
44
package models
55

66
import (
7+
"compress/gzip"
8+
"errors"
9+
"fmt"
10+
"io"
711
"time"
812

13+
"github.com/klauspost/compress/zstd"
914
"github.com/opencontainers/go-digest"
15+
v1 "github.com/opencontainers/image-spec/specs-go/v1"
16+
"go.podman.io/image/v5/manifest"
1017
. "go.xyrillian.de/gg/option"
1118
)
1219

@@ -42,6 +49,55 @@ func (b Blob) SafeMediaType() string {
4249
return b.MediaType
4350
}
4451

52+
// BlobCompression is an enum indicating the compression format used within the blob.
53+
// This information is derived from the blob's MediaType, and only reported when the MediaType is understood by Keppel.
54+
type BlobCompression string
55+
56+
const (
57+
// BlobCompressionUnknown indicates that we do not recognize the blob's MediaType, and thus do not know whether it is uncompressed.
58+
BlobCompressionUnknown BlobCompression = "unknown"
59+
// BlobCompressionNone indicates that we recognize the blob's MediaType and thus know that the payload is uncompressed.
60+
BlobCompressionNone BlobCompression = "none"
61+
BlobCompressionGzip BlobCompression = "gzip"
62+
BlobCompressionZstd BlobCompression = "zstd"
63+
)
64+
65+
// Compression reports the compression format used within the blob,
66+
// or None if the blob's MediaType is not understood by Keppel.
67+
func (b Blob) Compression() BlobCompression {
68+
switch b.MediaType {
69+
case manifest.DockerV2SchemaLayerMediaTypeUncompressed, v1.MediaTypeImageLayer:
70+
return BlobCompressionNone
71+
case manifest.DockerV2Schema2LayerMediaType, v1.MediaTypeImageLayerGzip:
72+
return BlobCompressionGzip
73+
case manifest.DockerV2SchemaLayerMediaTypeZstd, v1.MediaTypeImageLayerZstd:
74+
return BlobCompressionZstd
75+
default:
76+
return BlobCompressionUnknown
77+
}
78+
}
79+
80+
// Reader wraps a reader for compressed data in this format with the respective
81+
// decompressor, returning a reader that yields uncompressed data.
82+
func (c BlobCompression) Reader(r io.Reader) (io.ReadCloser, error) {
83+
switch c {
84+
case BlobCompressionNone:
85+
return io.NopCloser(r), nil
86+
case BlobCompressionGzip:
87+
return gzip.NewReader(r)
88+
case BlobCompressionZstd:
89+
zr, err := zstd.NewReader(r)
90+
if err != nil {
91+
return nil, err
92+
}
93+
return zr.IOReadCloser(), nil
94+
case BlobCompressionUnknown:
95+
return nil, errors.New("do not know how to handle read data with unknown compression format")
96+
default:
97+
panic(fmt.Sprintf("unexpected BlobCompression value: %q", c))
98+
}
99+
}
100+
45101
const (
46102
// BlobValidationInterval is how often each blob will be validated by BlobValidationJob.
47103
// This is here instead of near the job because package processor also needs to know it.

internal/tasks/manifests.go

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
package tasks
55

66
import (
7-
"compress/gzip"
87
"context"
98
"database/sql"
109
"errors"
@@ -17,7 +16,6 @@ import (
1716
"time"
1817

1918
"github.com/go-gorp/gorp/v3"
20-
"github.com/klauspost/compress/zstd"
2119
"github.com/opencontainers/go-digest"
2220
imagespecs "github.com/opencontainers/image-spec/specs-go/v1"
2321
"github.com/prometheus/client_golang/prometheus"
@@ -815,15 +813,12 @@ func (j *Janitor) checkPreConditionsForTrivy(ctx context.Context, account models
815813

816814
// filter media types that trivy is known to support
817815
for _, blob := range layerBlobs {
818-
if blob.MediaType == imageManifest.DockerV2SchemaLayerMediaTypeUncompressed || blob.MediaType == imageManifest.DockerV2Schema2LayerMediaType || blob.MediaType == imageManifest.DockerV2SchemaLayerMediaTypeZstd ||
819-
blob.MediaType == imagespecs.MediaTypeImageLayer || blob.MediaType == imagespecs.MediaTypeImageLayerGzip || blob.MediaType == imagespecs.MediaTypeImageLayerZstd {
820-
continue
816+
if blob.Compression() == models.BlobCompressionUnknown { // None = unknown compression method because we don't recognize `blob.MediaType`
817+
securityInfo.VulnerabilityStatus = models.UnsupportedVulnerabilityStatus
818+
securityInfo.Message = fmt.Sprintf("vulnerability scanning is not supported for blob layers with media type %q", blob.MediaType)
819+
securityInfo.NextCheckAt = Some(j.timeNow().Add(j.addJitter(24 * time.Hour)))
820+
return false, layerBlobs, nil
821821
}
822-
823-
securityInfo.VulnerabilityStatus = models.UnsupportedVulnerabilityStatus
824-
securityInfo.Message = fmt.Sprintf("vulnerability scanning is not supported for blob layers with media type %q", blob.MediaType)
825-
securityInfo.NextCheckAt = Some(j.timeNow().Add(j.addJitter(24 * time.Hour)))
826-
return false, layerBlobs, nil
827822
}
828823

829824
// can only validate when all blobs are present in the storage
@@ -845,47 +840,32 @@ func (j *Janitor) checkPreConditionsForTrivy(ctx context.Context, account models
845840
}
846841

847842
if blob.BlocksVulnScanning.IsNone() {
848-
isUncompressed := blob.MediaType == imageManifest.DockerV2SchemaLayerMediaTypeUncompressed || blob.MediaType == imagespecs.MediaTypeImageLayer
849-
isGzip := blob.MediaType == imageManifest.DockerV2Schema2LayerMediaType || blob.MediaType == imagespecs.MediaTypeImageLayerGzip
850-
isZstd := blob.MediaType == imageManifest.DockerV2SchemaLayerMediaTypeZstd || blob.MediaType == imagespecs.MediaTypeImageLayerZstd
843+
compression := blob.Compression()
851844

852845
// when measuring uncompressed size, use LimitReader as a simple but
853846
// effective guard against zip bombs
854847
limitBytes := int64(1 << 30 * blobUncompressedSizeTooBigGiB)
855848
var numberBytes int64
856849

857-
if isUncompressed {
850+
if compression == models.BlobCompressionNone {
858851
numberBytes = int64(blob.SizeBytes) //nolint:gosec
859-
} else if isGzip || isZstd {
852+
} else {
860853
// uncompress the blob to check if it's too large for Trivy to handle within its allotted timeout
861854
reader, _, err := j.sd.ReadBlob(ctx, account, blob.StorageID)
862855
if err != nil {
863856
return false, layerBlobs, fmt.Errorf("cannot read blob %s: %w", blob.Digest, err)
864857
}
865858
defer reader.Close()
866859

867-
if isGzip {
868-
compressedReader, err := gzip.NewReader(reader)
869-
if err != nil {
870-
return false, layerBlobs, fmt.Errorf("cannot create gzip reader for blob %s: %w", blob.Digest, err)
871-
}
872-
defer compressedReader.Close()
873-
874-
numberBytes, err = io.Copy(io.Discard, io.LimitReader(compressedReader, limitBytes+1))
875-
if err != nil {
876-
return false, layerBlobs, fmt.Errorf("cannot decompress gzip blob %s: %w", blob.Digest, err)
877-
}
878-
} else if isZstd {
879-
compressedReader, err := zstd.NewReader(reader)
880-
if err != nil {
881-
return false, layerBlobs, fmt.Errorf("cannot create zstd reader for blob %s: %w", blob.Digest, err)
882-
}
883-
defer compressedReader.Close()
884-
885-
numberBytes, err = io.Copy(io.Discard, io.LimitReader(compressedReader, limitBytes+1))
886-
if err != nil {
887-
return false, layerBlobs, fmt.Errorf("cannot decompress zstd blob %s: %w", blob.Digest, err)
888-
}
860+
compressedReader, err := compression.Reader(reader)
861+
if err != nil {
862+
return false, layerBlobs, fmt.Errorf("cannot create %s reader for blob %s: %w", compression, blob.Digest, err)
863+
}
864+
defer compressedReader.Close()
865+
866+
numberBytes, err = io.Copy(io.Discard, io.LimitReader(compressedReader, limitBytes+1))
867+
if err != nil {
868+
return false, layerBlobs, fmt.Errorf("cannot decompress %s blob %s: %w", compression, blob.Digest, err)
889869
}
890870
}
891871

0 commit comments

Comments
 (0)