Skip to content

feat: instrumented Image/Index/Layer #107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 23, 2025
Merged
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 165 additions & 0 deletions pkg/instrumented/image.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// Copyright 2024-2025 Sylabs Inc. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0

package instrumented

import (
"log/slog"
"time"

v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/partial"
"github.com/google/go-containerregistry/pkg/v1/types"
)

type wrappedImage struct {
inner v1.Image
log *slog.Logger
}

// Image returns a wrapped Image that outputs instrumentation to log.
func Image(img v1.Image, log *slog.Logger) (v1.Image, error) {
h, err := img.Digest()
if err != nil {
return nil, err
}

return &wrappedImage{
inner: img,
log: log.With(slog.String("image", h.Hex)),
}, nil
}

// Descriptor returns a Descriptor for the image manifest.
func (img *wrappedImage) Descriptor() (*v1.Descriptor, error) {
defer func(t time.Time) {
img.log.Info("Descriptor()", slog.Duration("dur", time.Since(t)))
}(time.Now())

return partial.Descriptor(img.inner)
}

// MediaType of this image's manifest.
func (img *wrappedImage) MediaType() (types.MediaType, error) {
defer func(t time.Time) {
img.log.Info("MediaType()", slog.Duration("dur", time.Since(t)))
}(time.Now())

return img.inner.MediaType()
}

// Size returns the size of the manifest.
func (img *wrappedImage) Size() (int64, error) {
defer func(t time.Time) {
img.log.Info("Size()", slog.Duration("dur", time.Since(t)))
}(time.Now())

return img.inner.Size()
}

// Digest returns the sha256 of this image's manifest.
func (img *wrappedImage) Digest() (v1.Hash, error) {
defer func(t time.Time) {
img.log.Info("Digest()", slog.Duration("dur", time.Since(t)))
}(time.Now())

return img.inner.Digest()
}

// Manifest returns this image's Manifest object.
func (img *wrappedImage) Manifest() (*v1.Manifest, error) {
defer func(t time.Time) {
img.log.Info("Manifest()", slog.Duration("dur", time.Since(t)))
}(time.Now())

return img.inner.Manifest()
}

// RawManifest returns the serialized bytes of Manifest().
func (img *wrappedImage) RawManifest() ([]byte, error) {
defer func(t time.Time) {
img.log.Info("RawManifest()", slog.Duration("dur", time.Since(t)))
}(time.Now())

return img.inner.RawManifest()
}

// ConfigName returns the hash of the image's config file, also known as the Image ID.
func (img *wrappedImage) ConfigName() (v1.Hash, error) {
defer func(t time.Time) {
img.log.Info("ConfigName()", slog.Duration("dur", time.Since(t)))
}(time.Now())

return img.inner.ConfigName()
}

// ConfigFile returns this image's config file.
func (img *wrappedImage) ConfigFile() (*v1.ConfigFile, error) {
defer func(t time.Time) {
img.log.Info("ConfigFile()", slog.Duration("dur", time.Since(t)))
}(time.Now())

return img.inner.ConfigFile()
}

// RawConfigFile returns the serialized bytes of ConfigFile().
func (img *wrappedImage) RawConfigFile() ([]byte, error) {
defer func(t time.Time) {
img.log.Info("RawConfigFile()", slog.Duration("dur", time.Since(t)))
}(time.Now())

return img.inner.RawConfigFile()
}

// Layers returns the ordered collection of filesystem layers that comprise this image.
func (img *wrappedImage) Layers() ([]v1.Layer, error) {
defer func(t time.Time) {
img.log.Info("Layers()", slog.Duration("dur", time.Since(t)))
}(time.Now())

ls, err := img.inner.Layers()
if err != nil {
return nil, err
}

for i, l := range ls {
l, err := Layer(l, img.log)
if err != nil {
return nil, err
}

ls[i] = l
}

return ls, nil
}

// LayerByDigest returns a Layer for interacting with a particular layer of the image, looking it
// up by "digest" (the compressed hash).
func (img *wrappedImage) LayerByDigest(h v1.Hash) (v1.Layer, error) {
defer func(t time.Time) {
img.log.Info("LayerByDigest()", slog.Duration("dur", time.Since(t)))
}(time.Now())

l, err := img.inner.LayerByDigest(h)
if err != nil {
return nil, err
}

return Layer(l, img.log)
}

// LayerByDiffID is an analog to LayerByDigest, looking up by "diff id" (the uncompressed hash).
func (img *wrappedImage) LayerByDiffID(h v1.Hash) (v1.Layer, error) {
defer func(t time.Time) {
img.log.Info("LayerByDiffID()", slog.Duration("dur", time.Since(t)))
}(time.Now())

l, err := img.inner.LayerByDiffID(h)
if err != nil {
return nil, err
}

return Layer(l, img.log)
}
104 changes: 104 additions & 0 deletions pkg/instrumented/index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright 2024-2025 Sylabs Inc. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0

package instrumented

import (
"log/slog"
"time"

v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/types"
)

type wrappedIndex struct {
inner v1.ImageIndex
log *slog.Logger
}

// Index returns a wrapped ImageIndex that outputs instrumentation to log.
func Index(ii v1.ImageIndex, log *slog.Logger) (v1.ImageIndex, error) {
h, err := ii.Digest()
if err != nil {
return nil, err
}

return &wrappedIndex{
inner: ii,
log: log.With(slog.String("index", h.Hex)),
}, nil
}

// MediaType of this image's manifest.
func (ii *wrappedIndex) MediaType() (types.MediaType, error) {
defer func(t time.Time) {
ii.log.Info("MediaType()", slog.Duration("dur", time.Since(t)))
}(time.Now())

return ii.inner.MediaType()
}

// Digest returns the sha256 of this image's manifest.
func (ii *wrappedIndex) Digest() (v1.Hash, error) {
defer func(t time.Time) {
ii.log.Info("Digest()", slog.Duration("dur", time.Since(t)))
}(time.Now())

return ii.inner.Digest()
}

// Size returns the size of the manifest.
func (ii *wrappedIndex) Size() (int64, error) {
defer func(t time.Time) {
ii.log.Info("Size()", slog.Duration("dur", time.Since(t)))
}(time.Now())

return ii.inner.Size()
}

// IndexManifest returns this image index's manifest object.
func (ii *wrappedIndex) IndexManifest() (*v1.IndexManifest, error) {
defer func(t time.Time) {
ii.log.Info("IndexManifest()", slog.Duration("dur", time.Since(t)))
}(time.Now())

return ii.inner.IndexManifest()
}

// RawManifest returns the serialized bytes of IndexManifest().
func (ii *wrappedIndex) RawManifest() ([]byte, error) {
defer func(t time.Time) {
ii.log.Info("RawManifest()", slog.Duration("dur", time.Since(t)))
}(time.Now())

return ii.inner.RawManifest()
}

// Image returns a v1.Image that this ImageIndex references.
func (ii *wrappedIndex) Image(d v1.Hash) (v1.Image, error) {
defer func(t time.Time) {
ii.log.Info("Image()", slog.Duration("dur", time.Since(t)))
}(time.Now())

img, err := ii.inner.Image(d)
if err != nil {
return nil, err
}

return Image(img, ii.log)
}

// ImageIndex returns a v1.ImageIndex that this ImageIndex references.
func (ii *wrappedIndex) ImageIndex(d v1.Hash) (v1.ImageIndex, error) {
defer func(t time.Time) {
ii.log.Info("ImageIndex()", slog.Duration("dur", time.Since(t)))
}(time.Now())

idx, err := ii.inner.ImageIndex(d)
if err != nil {
return nil, err
}

return Index(idx, ii.log)
}
120 changes: 120 additions & 0 deletions pkg/instrumented/layer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright 2024-2025 Sylabs Inc. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0

package instrumented

import (
"io"
"log/slog"
"time"

v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/partial"
"github.com/google/go-containerregistry/pkg/v1/types"
)

type wrappedLayer struct {
inner v1.Layer
log *slog.Logger
}

// Layer returns a wrapped Layer that outputs instrumentation to log.
func Layer(l v1.Layer, log *slog.Logger) (v1.Layer, error) {
h, err := l.Digest()
if err != nil {
return nil, err
}

return &wrappedLayer{
inner: l,
log: log.With(slog.String("layer", h.Hex)),
}, nil
}

// Digest returns the Hash of the compressed layer.
func (l *wrappedLayer) Digest() (v1.Hash, error) {
defer func(t time.Time) {
l.log.Info("Digest()",
slog.Duration("dur", time.Since(t)),
)
}(time.Now())

return l.inner.Digest()
}

// DiffID implements v1.Layer.
func (l *wrappedLayer) DiffID() (v1.Hash, error) {
defer func(t time.Time) {
l.log.Info("DiffID()",
slog.Duration("dur", time.Since(t)),
)
}(time.Now())

return l.inner.DiffID()
}

// Compressed returns an io.ReadCloser for the compressed layer contents.
func (l *wrappedLayer) Compressed() (io.ReadCloser, error) {
defer func(t time.Time) {
l.log.Info("Compressed()",
slog.Duration("dur", time.Since(t)),
)
}(time.Now())

rc, err := l.inner.Compressed()
if err != nil {
return nil, err
}

return readCloser(rc, l.log.With(slog.Bool("compressed", true))), nil
}

// Uncompressed implements v1.Layer.
func (l *wrappedLayer) Uncompressed() (io.ReadCloser, error) {
defer func(t time.Time) {
l.log.Info("Uncompressed()",
slog.Duration("dur", time.Since(t)),
)
}(time.Now())

rc, err := l.inner.Uncompressed()
if err != nil {
return nil, err
}

return readCloser(rc, l.log.With(slog.Bool("compressed", false))), nil
}

// Size returns the compressed size of the Layer.
func (l *wrappedLayer) Size() (int64, error) {
defer func(t time.Time) {
l.log.Info("Size()",
slog.Duration("dur", time.Since(t)),
)
}(time.Now())

return l.inner.Size()
}

// MediaType returns the media type of the Layer.
func (l *wrappedLayer) MediaType() (types.MediaType, error) {
defer func(t time.Time) {
l.log.Info("MediaType()",
slog.Duration("dur", time.Since(t)),
)
}(time.Now())

return l.inner.MediaType()
}

// Descriptor returns a Descriptor for the layer.
func (l *wrappedLayer) Descriptor() (*v1.Descriptor, error) {
defer func(t time.Time) {
l.log.Info("Descriptor()",
slog.Duration("dur", time.Since(t)),
)
}(time.Now())

return partial.Descriptor(l.inner)
}
Loading
Loading