Skip to content

Commit

Permalink
Merge pull request #107 from dtrudg/instrumented
Browse files Browse the repository at this point in the history
feat: instrumented Image/Index/Layer
  • Loading branch information
dtrudg authored Jan 23, 2025
2 parents 1c60954 + b34ad1d commit cdb4e81
Show file tree
Hide file tree
Showing 4 changed files with 430 additions and 0 deletions.
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

0 comments on commit cdb4e81

Please sign in to comment.