File: transport.go

package info (click to toggle)
golang-github-containers-image 5.36.1-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 5,152 kB
  • sloc: sh: 267; makefile: 100
file content (209 lines) | stat: -rw-r--r-- 9,259 bytes parent folder | download
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
package archive

import (
	"context"
	"errors"
	"fmt"
	"strconv"
	"strings"

	"github.com/containers/image/v5/docker/internal/tarfile"
	"github.com/containers/image/v5/docker/reference"
	ctrImage "github.com/containers/image/v5/internal/image"
	"github.com/containers/image/v5/transports"
	"github.com/containers/image/v5/types"
)

func init() {
	transports.Register(Transport)
}

// Transport is an ImageTransport for local Docker archives.
var Transport = archiveTransport{}

type archiveTransport struct{}

func (t archiveTransport) Name() string {
	return "docker-archive"
}

// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an ImageReference.
func (t archiveTransport) ParseReference(reference string) (types.ImageReference, error) {
	return ParseReference(reference)
}

// ValidatePolicyConfigurationScope checks that scope is a valid name for a signature.PolicyTransportScopes keys
// (i.e. a valid PolicyConfigurationIdentity() or PolicyConfigurationNamespaces() return value).
// It is acceptable to allow an invalid value which will never be matched, it can "only" cause user confusion.
// scope passed to this function will not be "", that value is always allowed.
func (t archiveTransport) ValidatePolicyConfigurationScope(scope string) error {
	// See the explanation in archiveReference.PolicyConfigurationIdentity.
	return errors.New(`docker-archive: does not support any scopes except the default "" one`)
}

// archiveReference is an ImageReference for Docker images.
type archiveReference struct {
	path string
	// May be nil to read the only image in an archive, or to create an untagged image.
	ref reference.NamedTagged
	// If not -1, a zero-based index of the image in the manifest. Valid only for sources.
	// Must not be set if ref is set.
	sourceIndex int
	// If not nil, must have been created from path (but archiveReader.path may point at a temporary
	// file, not necessarily path precisely).
	archiveReader *tarfile.Reader
	// If not nil, must have been created for path
	writer *Writer
}

// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an Docker ImageReference.
func ParseReference(refString string) (types.ImageReference, error) {
	if refString == "" {
		return nil, fmt.Errorf("docker-archive reference %s isn't of the form <path>[:<reference>]", refString)
	}

	path, tagOrIndex, gotTagOrIndex := strings.Cut(refString, ":")
	var nt reference.NamedTagged
	sourceIndex := -1

	if gotTagOrIndex {
		// A :tag or :@index was specified.
		if len(tagOrIndex) > 0 && tagOrIndex[0] == '@' {
			i, err := strconv.Atoi(tagOrIndex[1:])
			if err != nil {
				return nil, fmt.Errorf("Invalid source index %s: %w", tagOrIndex, err)
			}
			if i < 0 {
				return nil, fmt.Errorf("Invalid source index @%d: must not be negative", i)
			}
			sourceIndex = i
		} else {
			ref, err := reference.ParseNormalizedNamed(tagOrIndex)
			if err != nil {
				return nil, fmt.Errorf("docker-archive parsing reference: %w", err)
			}
			ref = reference.TagNameOnly(ref)
			refTagged, isTagged := ref.(reference.NamedTagged)
			if !isTagged { // If ref contains a digest, TagNameOnly does not change it
				return nil, fmt.Errorf("reference does not include a tag: %s", ref.String())
			}
			nt = refTagged
		}
	}

	return newReference(path, nt, sourceIndex, nil, nil)
}

// NewReference returns a Docker archive reference for a path and an optional reference.
func NewReference(path string, ref reference.NamedTagged) (types.ImageReference, error) {
	return newReference(path, ref, -1, nil, nil)
}

// NewIndexReference returns a Docker archive reference for a path and a zero-based source manifest index.
func NewIndexReference(path string, sourceIndex int) (types.ImageReference, error) {
	if sourceIndex < 0 {
		return nil, fmt.Errorf("invalid call to NewIndexReference with negative index %d", sourceIndex)
	}
	return newReference(path, nil, sourceIndex, nil, nil)
}

// newReference returns a docker archive reference for a path, an optional reference or sourceIndex,
// and optionally a tarfile.Reader and/or a tarfile.Writer matching path.
func newReference(path string, ref reference.NamedTagged, sourceIndex int,
	archiveReader *tarfile.Reader, writer *Writer) (types.ImageReference, error) {
	if strings.Contains(path, ":") {
		return nil, fmt.Errorf("Invalid docker-archive: reference: colon in path %q is not supported", path)
	}
	if ref != nil && sourceIndex != -1 {
		return nil, fmt.Errorf("Invalid docker-archive: reference: cannot use both a tag and a source index")
	}
	if _, isDigest := ref.(reference.Canonical); isDigest {
		return nil, fmt.Errorf("docker-archive doesn't support digest references: %s", ref.String())
	}
	if sourceIndex != -1 && sourceIndex < 0 {
		return nil, fmt.Errorf("Invalid docker-archive: reference: index @%d must not be negative", sourceIndex)
	}
	return archiveReference{
		path:          path,
		ref:           ref,
		sourceIndex:   sourceIndex,
		archiveReader: archiveReader,
		writer:        writer,
	}, nil
}

func (ref archiveReference) Transport() types.ImageTransport {
	return Transport
}

// StringWithinTransport returns a string representation of the reference, which MUST be such that
// reference.Transport().ParseReference(reference.StringWithinTransport()) returns an equivalent reference.
// NOTE: The returned string is not promised to be equal to the original input to ParseReference;
// e.g. default attribute values omitted by the user may be filled in the return value, or vice versa.
// WARNING: Do not use the return value in the UI to describe an image, it does not contain the Transport().Name() prefix.
func (ref archiveReference) StringWithinTransport() string {
	switch {
	case ref.ref != nil:
		return fmt.Sprintf("%s:%s", ref.path, ref.ref.String())
	case ref.sourceIndex != -1:
		return fmt.Sprintf("%s:@%d", ref.path, ref.sourceIndex)
	default:
		return ref.path
	}
}

// DockerReference returns a Docker reference associated with this reference
// (fully explicit, i.e. !reference.IsNameOnly, but reflecting user intent,
// not e.g. after redirect or alias processing), or nil if unknown/not applicable.
func (ref archiveReference) DockerReference() reference.Named {
	return ref.ref
}

// PolicyConfigurationIdentity returns a string representation of the reference, suitable for policy lookup.
// This MUST reflect user intent, not e.g. after processing of third-party redirects or aliases;
// The value SHOULD be fully explicit about its semantics, with no hidden defaults, AND canonical
// (i.e. various references with exactly the same semantics should return the same configuration identity)
// It is fine for the return value to be equal to StringWithinTransport(), and it is desirable but
// not required/guaranteed that it will be a valid input to Transport().ParseReference().
// Returns "" if configuration identities for these references are not supported.
func (ref archiveReference) PolicyConfigurationIdentity() string {
	// Punt, the justification is similar to dockerReference.PolicyConfigurationIdentity.
	return ""
}

// PolicyConfigurationNamespaces returns a list of other policy configuration namespaces to search
// for if explicit configuration for PolicyConfigurationIdentity() is not set.  The list will be processed
// in order, terminating on first match, and an implicit "" is always checked at the end.
// It is STRONGLY recommended for the first element, if any, to be a prefix of PolicyConfigurationIdentity(),
// and each following element to be a prefix of the element preceding it.
func (ref archiveReference) PolicyConfigurationNamespaces() []string {
	// TODO
	return []string{}
}

// NewImage returns a types.ImageCloser for this reference, possibly specialized for this ImageTransport.
// The caller must call .Close() on the returned ImageCloser.
// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource,
// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage.
// WARNING: This may not do the right thing for a manifest list, see image.FromSource for details.
func (ref archiveReference) NewImage(ctx context.Context, sys *types.SystemContext) (types.ImageCloser, error) {
	return ctrImage.FromReference(ctx, sys, ref)
}

// NewImageSource returns a types.ImageSource for this reference.
// The caller must call .Close() on the returned ImageSource.
func (ref archiveReference) NewImageSource(ctx context.Context, sys *types.SystemContext) (types.ImageSource, error) {
	return newImageSource(sys, ref)
}

// NewImageDestination returns a types.ImageDestination for this reference.
// The caller must call .Close() on the returned ImageDestination.
func (ref archiveReference) NewImageDestination(ctx context.Context, sys *types.SystemContext) (types.ImageDestination, error) {
	return newImageDestination(sys, ref)
}

// DeleteImage deletes the named image from the registry, if supported.
func (ref archiveReference) DeleteImage(ctx context.Context, sys *types.SystemContext) error {
	// Not really supported, for safety reasons.
	return errors.New("Deleting images not implemented for docker-archive: images")
}