summaryrefslogtreecommitdiff
path: root/vendor/github.com/containers/common/libimage/import.go
blob: 552c48eae00bc006f408637944d7ecde953a421c (plain)
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
//go:build !remote

package libimage

import (
	"context"
	"errors"
	"fmt"
	"net/url"
	"os"

	"github.com/containers/common/pkg/download"
	storageTransport "github.com/containers/image/v5/storage"
	tarballTransport "github.com/containers/image/v5/tarball"
	v1 "github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/sirupsen/logrus"
)

// ImportOptions allow for customizing image imports.
type ImportOptions struct {
	CopyOptions

	// Apply the specified changes to the created image. Please refer to
	// `ImageConfigFromChanges` for supported change instructions.
	Changes []string
	// Set the commit message as a comment to created image's history.
	CommitMessage string
	// Tag the imported image with this value.
	Tag string
	// Overwrite OS of imported image.
	OS string
	// Overwrite Arch of imported image.
	Arch string
}

// Import imports a custom tarball at the specified path.  Returns the name of
// the imported image.
func (r *Runtime) Import(ctx context.Context, path string, options *ImportOptions) (string, error) {
	logrus.Debugf("Importing image from %q", path)

	if options == nil {
		options = &ImportOptions{}
	}

	ic := v1.ImageConfig{}
	if len(options.Changes) > 0 {
		config, err := ImageConfigFromChanges(options.Changes)
		if err != nil {
			return "", err
		}
		ic = config.ImageConfig
	}

	history := []v1.History{
		{Comment: options.CommitMessage},
	}

	config := v1.Image{
		Config:  ic,
		History: history,
		Platform: v1.Platform{
			OS:           options.OS,
			Architecture: options.Arch,
			Variant:      options.Variant,
		},
	}

	u, err := url.ParseRequestURI(path)
	if err == nil && u.Scheme != "" {
		// If source is a URL, download the file.
		fmt.Printf("Downloading from %q\n", path) //nolint:forbidigo
		file, err := download.FromURL(r.systemContext.BigFilesTemporaryDir, path)
		if err != nil {
			return "", err
		}
		defer os.Remove(file)
		path = file
	} else if path == "-" {
		// "-" special cases stdin
		path = os.Stdin.Name()
	}

	srcRef, err := tarballTransport.Transport.ParseReference(path)
	if err != nil {
		return "", err
	}

	updater, ok := srcRef.(tarballTransport.ConfigUpdater)
	if !ok {
		return "", errors.New("unexpected type, a tarball reference should implement tarball.ConfigUpdater")
	}
	annotations := make(map[string]string)
	if err := updater.ConfigUpdate(config, annotations); err != nil {
		return "", err
	}

	id, err := getImageID(ctx, srcRef, r.systemContextCopy())
	if err != nil {
		return "", err
	}

	destRef, err := storageTransport.Transport.ParseStoreReference(r.store, id)
	if err != nil {
		return "", err
	}

	c, err := r.newCopier(&options.CopyOptions)
	if err != nil {
		return "", err
	}
	defer c.close()

	if _, err := c.copy(ctx, srcRef, destRef); err != nil {
		return "", err
	}

	// Strip the leading @ off the id.
	name := id[1:]

	// If requested, tag the imported image.
	if options.Tag != "" {
		image, _, err := r.LookupImage(name, nil)
		if err != nil {
			return "", fmt.Errorf("looking up imported image: %w", err)
		}
		if err := image.Tag(options.Tag); err != nil {
			return "", err
		}
	}

	return "sha256:" + name, nil
}