File: load.go

package info (click to toggle)
golang-github-containers-common 0.64.1%2Bds1-2
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 5,932 kB
  • sloc: makefile: 132; sh: 111
file content (179 lines) | stat: -rw-r--r-- 5,858 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//go:build !remote

package libimage

import (
	"context"
	"errors"
	"fmt"
	"time"

	dirTransport "github.com/containers/image/v5/directory"
	dockerArchiveTransport "github.com/containers/image/v5/docker/archive"
	ociArchiveTransport "github.com/containers/image/v5/oci/archive"
	ociTransport "github.com/containers/image/v5/oci/layout"
	"github.com/containers/image/v5/transports"
	"github.com/containers/image/v5/types"
	"github.com/containers/storage/pkg/fileutils"
	"github.com/sirupsen/logrus"
)

type LoadOptions struct {
	CopyOptions
}

// doLoadReference does the heavy lifting for LoadReference() and Load(),
// without adding debug messages or handling defaults.
func (r *Runtime) doLoadReference(ctx context.Context, ref types.ImageReference, options *LoadOptions) (images []string, transportName string, err error) {
	transportName = ref.Transport().Name()
	switch transportName {
	case dockerArchiveTransport.Transport.Name():
		images, err = r.loadMultiImageDockerArchive(ctx, ref, &options.CopyOptions)
	default:
		_, images, err = r.copyFromDefault(ctx, ref, &options.CopyOptions)
	}
	return images, ref.Transport().Name(), err
}

// LoadReference loads one or more images from the specified location.
func (r *Runtime) LoadReference(ctx context.Context, ref types.ImageReference, options *LoadOptions) ([]string, error) {
	logrus.Debugf("Loading image from %q", transports.ImageName(ref))

	if options == nil {
		options = &LoadOptions{}
	}
	images, _, err := r.doLoadReference(ctx, ref, options)
	return images, err
}

// Load loads one or more images (depending on the transport) from the
// specified path.  The path may point to an image the following transports:
// oci, oci-archive, dir, docker-archive.
//
// Load returns a string slice with names of recently loaded images.
// If images are unnamed in the source, it returns a string slice of image IDs instead.
func (r *Runtime) Load(ctx context.Context, path string, options *LoadOptions) ([]string, error) {
	logrus.Debugf("Loading image from %q", path)

	if options == nil {
		options = &LoadOptions{}
	}

	// we have 4 functions, so a maximum of 4 errors
	loadErrors := make([]error, 0, 4)
	for _, f := range []func() ([]string, string, error){
		// OCI
		func() ([]string, string, error) {
			logrus.Debugf("-> Attempting to load %q as an OCI directory", path)
			ref, err := ociTransport.NewReference(path, "")
			if err != nil {
				return nil, ociTransport.Transport.Name(), err
			}
			return r.doLoadReference(ctx, ref, options)
		},

		// OCI-ARCHIVE
		func() ([]string, string, error) {
			logrus.Debugf("-> Attempting to load %q as an OCI archive", path)
			ref, err := ociArchiveTransport.NewReference(path, "")
			if err != nil {
				return nil, ociArchiveTransport.Transport.Name(), err
			}
			return r.doLoadReference(ctx, ref, options)
		},

		// DOCKER-ARCHIVE
		func() ([]string, string, error) {
			logrus.Debugf("-> Attempting to load %q as a Docker archive", path)
			ref, err := dockerArchiveTransport.ParseReference(path)
			if err != nil {
				return nil, dockerArchiveTransport.Transport.Name(), err
			}
			return r.doLoadReference(ctx, ref, options)
		},

		// DIR
		func() ([]string, string, error) {
			logrus.Debugf("-> Attempting to load %q as a Docker dir", path)
			ref, err := dirTransport.NewReference(path)
			if err != nil {
				return nil, dirTransport.Transport.Name(), err
			}
			return r.doLoadReference(ctx, ref, options)
		},
	} {
		loadedImages, transportName, err := f()
		if err == nil {
			if r.eventChannel != nil {
				err = r.writeLoadEvents(path, loadedImages)
			}
			return loadedImages, err
		}
		logrus.Debugf("Error loading %s (%s): %v", path, transportName, err)
		loadErrors = append(loadErrors, fmt.Errorf("%s: %v", transportName, err))
	}

	// Give a decent error message if nothing above worked.
	// we want the colon here for the multiline error
	//nolint:revive,staticcheck
	loadError := errors.New("payload does not match any of the supported image formats:")
	for _, err := range loadErrors {
		loadError = fmt.Errorf("%v\n * %v", loadError, err)
	}

	return nil, loadError
}

// writeLoadEvents writes the events of the loaded image.
func (r *Runtime) writeLoadEvents(path string, loadedImages []string) error {
	for _, name := range loadedImages {
		image, _, err := r.LookupImage(name, nil)
		if err != nil {
			return fmt.Errorf("locating pulled image %q name in containers storage: %w", name, err)
		}
		r.writeEvent(&Event{ID: image.ID(), Name: path, Time: time.Now(), Type: EventTypeImageLoad})
	}
	return nil
}

// loadMultiImageDockerArchive loads the docker archive specified by ref.  In
// case the path@reference notation was used, only the specified image will be
// loaded.  Otherwise, all images will be loaded.
func (r *Runtime) loadMultiImageDockerArchive(ctx context.Context, ref types.ImageReference, options *CopyOptions) ([]string, error) {
	// If we cannot stat the path, it either does not exist OR the correct
	// syntax to reference an image within the archive was used, so we
	// should.
	path := ref.StringWithinTransport()
	if err := fileutils.Exists(path); err != nil {
		_, names, err := r.copyFromDockerArchive(ctx, ref, options)
		return names, err
	}

	reader, err := dockerArchiveTransport.NewReader(r.systemContextCopy(), path)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err := reader.Close(); err != nil {
			logrus.Errorf("Closing reader of docker archive: %v", err)
		}
	}()

	refLists, err := reader.List()
	if err != nil {
		return nil, err
	}

	var copiedImages []string
	for _, list := range refLists {
		for _, listRef := range list {
			_, names, err := r.copyFromDockerArchiveReaderReference(ctx, reader, listRef, options)
			if err != nil {
				return nil, err
			}
			copiedImages = append(copiedImages, names...)
		}
	}

	return copiedImages, nil
}