Skip to content

Commit

Permalink
Changed the type of Layer DiffID from string to digest.Digest in …
Browse files Browse the repository at this point in the history
…order to preserve the hashing algorithm used. This change also facilitates usage of `ChainID` function for base image identification.

PiperOrigin-RevId: 715787917
  • Loading branch information
Mario Leyva authored and copybara-github committed Jan 15, 2025
1 parent b98597c commit f10186f
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 20 deletions.
3 changes: 2 additions & 1 deletion artifact/image/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/osv-scalibr/artifact/image/require"
"github.com/google/osv-scalibr/artifact/image/unpack"
"github.com/opencontainers/go-digest"

scalibrfs "github.com/google/osv-scalibr/fs"
)
Expand All @@ -43,7 +44,7 @@ type Layer interface {
// produced by the FS method.
IsEmpty() bool
// DiffID is the hash of the uncompressed layer. Will be an empty string if the layer is empty.
DiffID() string
DiffID() digest.Digest
// Command is the specific command that produced the layer.
Command() string
// Uncompressed gives the uncompressed tar as a file reader.
Expand Down
14 changes: 11 additions & 3 deletions artifact/image/layerscanning/image/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,16 @@ func FromV1Image(v1Image v1.Image, config *Config) (*Image, error) {
// Add the root directory to each chain layer. If this is not done, then the virtual paths won't
// be rooted, and traversal in the virtual filesystem will be broken.
for _, chainLayer := range chainLayers {
var layerDigest string
if chainLayer.latestLayer.IsEmpty() {
layerDigest = ""
} else {
layerDigest = chainLayer.latestLayer.DiffID().Encoded()
}

err := chainLayer.fileNodeTree.Insert("/", &fileNode{
extractDir: outputImage.ExtractDir,
originLayerID: chainLayer.latestLayer.DiffID(),
originLayerID: layerDigest,
virtualPath: "/",
isWhiteout: false,
mode: fs.ModeDir,
Expand All @@ -176,14 +183,15 @@ func FromV1Image(v1Image v1.Image, config *Config) (*Image, error) {
continue
}

originLayerID := chainLayer.latestLayer.DiffID().Encoded()

// Create the chain layer directory if it doesn't exist.
dirPath := path.Join(tempPath, chainLayer.latestLayer.DiffID())
dirPath := path.Join(tempPath, originLayerID)
if err := os.Mkdir(dirPath, dirPermission); err != nil && !errors.Is(err, fs.ErrExist) {
return &outputImage, fmt.Errorf("failed to create chain layer directory: %w", err)
}

chainLayersToFill := chainLayers[i:]
originLayerID := chainLayer.latestLayer.DiffID()
layerReader, err := chainLayer.latestLayer.Uncompressed()
if err != nil {
return &outputImage, err
Expand Down
7 changes: 4 additions & 3 deletions artifact/image/layerscanning/image/layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/google/osv-scalibr/artifact/image"
"github.com/google/osv-scalibr/artifact/image/pathtree"
scalibrfs "github.com/google/osv-scalibr/fs"
"github.com/opencontainers/go-digest"
)

var (
Expand All @@ -48,7 +49,7 @@ var (

// Layer implements the Layer interface.
type Layer struct {
diffID string
diffID digest.Digest
buildCommand string
isEmpty bool
uncompressed io.ReadCloser
Expand All @@ -65,7 +66,7 @@ func (layer *Layer) IsEmpty() bool {
}

// DiffID returns the diff id of the layer.
func (layer *Layer) DiffID() string {
func (layer *Layer) DiffID() digest.Digest {
return layer.diffID
}

Expand Down Expand Up @@ -94,7 +95,7 @@ func convertV1Layer(v1Layer v1.Layer, command string, isEmpty bool) (*Layer, err
}

return &Layer{
diffID: diffID.Hex,
diffID: digest.FromString(diffID.String()),
buildCommand: command,
isEmpty: isEmpty,
uncompressed: uncompressed,
Expand Down
3 changes: 2 additions & 1 deletion artifact/image/layerscanning/image/layer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ func TestConvertV1Layer(t *testing.T) {
command: "ADD file",
isEmpty: false,
wantLayer: &Layer{
diffID: "abc123",
// The diffID is the encoded string: digest.FromString("abc123").
diffID: "sha256:27f2fb5c8ba6b6d955cbc3cd78e89a898cac60a0442260129235683f4cc74e90",
buildCommand: "ADD file",
isEmpty: false,
uncompressed: reader,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,22 @@ import (

"github.com/google/osv-scalibr/artifact/image"
scalibrfs "github.com/google/osv-scalibr/fs"
"github.com/opencontainers/go-digest"
)

// FakeChainLayer is a fake implementation of the image.ChainLayer and scalibrfs.FS interface for
// testing purposes.
type FakeChainLayer struct {
index int
diffID string
command string
layer image.Layer
testDir string
diffID digest.Digest
layer image.Layer
files map[string]string
}

// New creates a new FakeChainLayer.
func New(testDir string, index int, diffID string, command string, layer image.Layer, files map[string]string) (*FakeChainLayer, error) {
func New(testDir string, index int, diffID digest.Digest, command string, layer image.Layer, files map[string]string) (*FakeChainLayer, error) {
for name, contents := range files {
filename := filepath.Join(testDir, name)
file, err := os.Create(filename)
Expand Down
7 changes: 4 additions & 3 deletions artifact/image/layerscanning/testing/fakelayer/fake_layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,17 @@ import (
"io"

scalibrfs "github.com/google/osv-scalibr/fs"
"github.com/opencontainers/go-digest"
)

// FakeLayer is a fake implementation of the image.Layer interface for testing purposes.
type FakeLayer struct {
diffID string
diffID digest.Digest
buildCommand string
}

// New creates a new FakeLayer.
func New(diffID string, buildCommand string) *FakeLayer {
func New(diffID digest.Digest, buildCommand string) *FakeLayer {
return &FakeLayer{
diffID: diffID,
buildCommand: buildCommand,
Expand All @@ -43,7 +44,7 @@ func (fakeLayer *FakeLayer) FS() scalibrfs.FS {
}

// DiffID returns the diffID of the layer.
func (fakeLayer *FakeLayer) DiffID() string {
func (fakeLayer *FakeLayer) DiffID() digest.Digest {
return fakeLayer.diffID
}

Expand Down
9 changes: 8 additions & 1 deletion artifact/image/layerscanning/trace/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,16 @@ func PopulateLayerDetails(ctx context.Context, inventory []*extractor.Inventory,

// Create list of layer details struct to be referenced by inventory.
for i, chainLayer := range chainLayers {
var diffID string
if chainLayer.Layer().IsEmpty() {
diffID = ""
} else {
diffID = chainLayer.Layer().DiffID().Encoded()
}

chainLayerDetailsList = append(chainLayerDetailsList, &extractor.LayerDetails{
Index: i,
DiffID: chainLayer.Layer().DiffID(),
DiffID: diffID,
Command: chainLayer.Layer().Command(),
InBaseImage: false,
})
Expand Down
15 changes: 10 additions & 5 deletions artifact/image/layerscanning/trace/trace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ import (
"github.com/google/osv-scalibr/extractor/filesystem"
"github.com/google/osv-scalibr/stats"
"github.com/google/osv-scalibr/testing/fakeextractor"
"github.com/opencontainers/go-digest"
)

func setupFakeChainLayer(t *testing.T, testDir string, index int, diffID string, command string, fileContents map[string]string) *fakechainlayer.FakeChainLayer {
func setupFakeChainLayer(t *testing.T, testDir string, index int, diffID digest.Digest, command string, fileContents map[string]string) *fakechainlayer.FakeChainLayer {
t.Helper()

layer := fakelayer.New(diffID, command)
Expand All @@ -56,7 +57,8 @@ func TestPopulateLayerDetails(t *testing.T) {
// Chain Layer 1: Start with foo and bar packages.
// - foo.txt
// - bar.txt
fakeChainLayer1 := setupFakeChainLayer(t, t.TempDir(), 0, "diff-id-1", "command-1", map[string]string{
digest1 := digest.NewDigestFromEncoded(digest.SHA256, "diff-id-1")
fakeChainLayer1 := setupFakeChainLayer(t, t.TempDir(), 0, digest1, "command-1", map[string]string{
fooFile: fooPackage,
barFile: barPackage,
})
Expand All @@ -71,7 +73,8 @@ func TestPopulateLayerDetails(t *testing.T) {

// Chain Layer 2: Deletes bar package.
// - foo.txt
fakeChainLayer2 := setupFakeChainLayer(t, t.TempDir(), 1, "diff-id-2", "command-2", map[string]string{
digest2 := digest.NewDigestFromEncoded(digest.SHA256, "diff-id-2")
fakeChainLayer2 := setupFakeChainLayer(t, t.TempDir(), 1, digest2, "command-2", map[string]string{
fooFile: fooPackage,
})
fakeExtractor2 := fakeextractor.New("fake-extractor-2", 1, []string{fooFile}, map[string]fakeextractor.NamesErr{
Expand All @@ -83,7 +86,8 @@ func TestPopulateLayerDetails(t *testing.T) {
// Chain Layer 3: Adds baz package.
// - foo.txt
// - baz.txt
fakeChainLayer3 := setupFakeChainLayer(t, t.TempDir(), 2, "diff-id-3", "command-3", map[string]string{
digest3 := digest.NewDigestFromEncoded(digest.SHA256, "diff-id-3")
fakeChainLayer3 := setupFakeChainLayer(t, t.TempDir(), 2, digest3, "command-3", map[string]string{
fooFile: fooPackage,
bazFile: bazPackage,
})
Expand All @@ -100,7 +104,8 @@ func TestPopulateLayerDetails(t *testing.T) {
// - foo.txt
// - bar.txt
// - baz.txt
fakeChainLayer4 := setupFakeChainLayer(t, t.TempDir(), 3, "diff-id-4", "command-4", map[string]string{
digest4 := digest.NewDigestFromEncoded(digest.SHA256, "diff-id-4")
fakeChainLayer4 := setupFakeChainLayer(t, t.TempDir(), 3, digest4, "command-4", map[string]string{
fooFile: fooPackage,
barFile: barPackage,
bazFile: bazPackage,
Expand Down

0 comments on commit f10186f

Please sign in to comment.