Skip to content

Commit

Permalink
🧹 split loading container images from opening connections
Browse files Browse the repository at this point in the history
Signed-off-by: Ivan Milchev <[email protected]>
  • Loading branch information
imilchev committed Feb 7, 2024
1 parent 4d9e59e commit e5be52f
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 33 deletions.
17 changes: 12 additions & 5 deletions providers/os/connection/container/image/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,20 +67,27 @@ func writeCompressedTarImage(img v1.Image, digest string) (*os.File, error) {
}
filename := f.Name()

ref, err := name.ParseReference(digest, name.WeakValidation)
if err != nil {
if err := WriteCompressedTarImageToFile(img, digest, f); err != nil {
os.Remove(filename)
return nil, err

}
return f, nil
}

func WriteCompressedTarImageToFile(img v1.Image, digest string, f *os.File) error {
ref, err := name.ParseReference(digest, name.WeakValidation)
if err != nil {
return err
}

err = tarball.Write(ref, img, f)
if err != nil {
os.Remove(filename)
return nil, err
return err
}

// Rewind, to later read the complete file for uncompress
f.Seek(0, io.SeekStart)

return f, nil
return nil
}
19 changes: 5 additions & 14 deletions providers/os/connection/container/image/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package image
import (
"crypto/tls"
"fmt"
"io"
"net"
"net/http"
"strings"
Expand Down Expand Up @@ -74,14 +73,14 @@ func GetImageDescriptor(ref name.Reference, opts ...Option) (*remote.Descriptor,
return remote.Get(ref, remote.WithAuth(o.auth))
}

func LoadImageFromRegistry(ref name.Reference, opts ...Option) (v1.Image, io.ReadCloser, error) {
func LoadImageFromRegistry(ref name.Reference, opts ...Option) (v1.Image, error) {
o := &options{
insecure: false,
}

for _, option := range opts {
if err := option(o); err != nil {
return nil, nil, err
return nil, err
}
}

Expand All @@ -98,7 +97,7 @@ func LoadImageFromRegistry(ref name.Reference, opts ...Option) (v1.Image, io.Rea
auth, err := kc.Resolve(ref.Context())
if err != nil {
fmt.Printf("getting creds for %q: %v", ref, err)
return nil, nil, err
return nil, err
}
o.auth = auth
}
Expand Down Expand Up @@ -126,15 +125,7 @@ func LoadImageFromRegistry(ref name.Reference, opts ...Option) (v1.Image, io.Rea

img, err := remote.Image(ref, remote.WithAuth(o.auth), remote.WithTransport(tr))
if err != nil {
return nil, nil, err
}

// write image to disk (conmpressed, unflattened)
// Otherwise we can not later recognize it as a valid image
f, err := writeCompressedTarImage(img, ref.String())
if err != nil {
return nil, nil, err
return nil, err
}

return img, f, nil
return img, nil
}
23 changes: 13 additions & 10 deletions providers/os/connection/docker_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"context"
"errors"
"io"
"os"
"strconv"
"strings"

Expand Down Expand Up @@ -186,30 +185,38 @@ func NewContainerRegistryImage(id uint32, conf *inventory.Config, asset *invento
registryOpts = append(registryOpts, remoteOpts[i])
}

var conn *TarConnection
var img v1.Image
var rc io.ReadCloser
loadedImage := false
if asset.Connections[0].Options != nil {
if _, ok := asset.Connections[0].Options[COMPRESSED_IMAGE]; ok {
var rc io.ReadCloser
// read image from disk
img, rc, err = image.LoadImageFromDisk(asset.Connections[0].Options[COMPRESSED_IMAGE])
if err != nil {
return nil, err
}

conn, err = NewWithReader(id, conf, asset, rc)
if err != nil {
return nil, err
}
loadedImage = true
}
}
if !loadedImage {
img, rc, err = image.LoadImageFromRegistry(ref, registryOpts...)
img, err = image.LoadImageFromRegistry(ref, registryOpts...)
if err != nil {
return nil, err
}
if asset.Connections[0].Options == nil {
asset.Connections[0].Options = map[string]string{}
}
osFile := rc.(*os.File)
filename := osFile.Name()
asset.Connections[0].Options[COMPRESSED_IMAGE] = filename

conn, err = NewTarConnectionForContainer(id, conf, asset, img)
if err != nil {
return nil, err
}
}

var identifier string
Expand All @@ -218,10 +225,6 @@ func NewContainerRegistryImage(id uint32, conf *inventory.Config, asset *invento
identifier = containerid.MondooContainerImageID(hash.String())
}

conn, err := NewWithReader(id, conf, asset, rc)
if err != nil {
return nil, err
}
conn.PlatformIdentifier = identifier
conn.Metadata.Name = containerid.ShortContainerImageID(hash.String())

Expand Down
59 changes: 56 additions & 3 deletions providers/os/connection/tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"errors"
"io"
"os"
"sync"

v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/mutate"
Expand All @@ -32,9 +33,11 @@ const (
var _ shared.Connection = (*TarConnection)(nil)

type TarConnection struct {
id uint32
asset *inventory.Asset
conf *inventory.Config
id uint32
asset *inventory.Asset
conf *inventory.Config
fetchFn func() (string, error)
fetchOnce sync.Once

Fs *provider_tar.FS
CloseFN func()
Expand Down Expand Up @@ -83,7 +86,24 @@ func (p *TarConnection) RunCommand(command string) (*shared.Command, error) {
return &res, nil
}

func (p *TarConnection) EnsureLoaded() {
if p.fetchFn != nil {
p.fetchOnce.Do(func() {
f, err := p.fetchFn()
if err != nil {
log.Error().Err(err).Msg("tar> could not fetch tar file")
return
}
if err := p.LoadFile(f); err != nil {
log.Error().Err(err).Msg("tar> could not load tar file")
return
}
})
}
}

func (p *TarConnection) FileSystem() afero.Fs {
p.EnsureLoaded()
return p.Fs
}

Expand Down Expand Up @@ -155,10 +175,43 @@ func (c *TarConnection) Runtime() string {
return c.PlatformRuntime
}

func NewTarConnectionForContainer(id uint32, conf *inventory.Config, asset *inventory.Asset, img v1.Image) (*TarConnection, error) {
f, err := cache.RandomFile()
if err != nil {
return nil, err
}

return &TarConnection{
id: id,
asset: asset,
Fs: provider_tar.NewFs(""),
fetchFn: func() (string, error) {
err = cache.StreamToTmpFile(mutate.Extract(img), f)
if err != nil {
os.Remove(f.Name())
return "", err
}
log.Warn().Msg("tar> extracted image to temporary file")
f.Seek(0, io.SeekStart)

Check failure on line 195 in providers/os/connection/tar.go

View workflow job for this annotation

GitHub Actions / golangci-lint

Error return value of `f.Seek` is not checked (errcheck)
asset.Connections[0].Options[FLATTENED_IMAGE] = f.Name()
return f.Name(), nil
},
CloseFN: func() {
log.Warn().Str("tar", f.Name()).Msg("tar> remove temporary tar file on connection close")
os.Remove(f.Name())
},
PlatformKind: conf.Type,
PlatformRuntime: conf.Runtime,
conf: conf,
}, nil
}

// TODO: this one is used by plain tar connection
func NewTarConnection(id uint32, conf *inventory.Config, asset *inventory.Asset) (*TarConnection, error) {
return NewWithClose(id, conf, asset, nil)
}

// Used with docker snapshots
// NewWithReader provides a tar provider from a container image stream
func NewWithReader(id uint32, conf *inventory.Config, asset *inventory.Asset, rc io.ReadCloser) (*TarConnection, error) {
filename := ""
Expand Down
2 changes: 1 addition & 1 deletion providers/os/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ func (s *Service) Shutdown(req *plugin.ShutdownReq) (*plugin.ShutdownRes, error)
for i := range s.runtimes {
runtime := s.runtimes[i]
if x, ok := runtime.Connection.(*connection.TarConnection); ok {
x.CloseFN()
x.Close()
}
}
return &plugin.ShutdownRes{}, nil
Expand Down

0 comments on commit e5be52f

Please sign in to comment.