podman
220 строк · 7.6 Кб
1package buildah
2
3import (
4"context"
5"fmt"
6"io"
7"time"
8
9"github.com/containers/buildah/define"
10"github.com/containers/buildah/internal/mkcw"
11"github.com/containers/image/v5/docker/reference"
12"github.com/containers/image/v5/types"
13encconfig "github.com/containers/ocicrypt/config"
14"github.com/containers/storage"
15"github.com/containers/storage/pkg/archive"
16"github.com/opencontainers/go-digest"
17"github.com/sirupsen/logrus"
18)
19
20// CWConvertImageOptions provides both required and optional bits of
21// configuration for CWConvertImage().
22type CWConvertImageOptions struct {
23// Required parameters.
24InputImage string
25
26// If supplied, we'll tag the resulting image with the specified name.
27Tag string
28OutputImage types.ImageReference
29
30// If supplied, we'll register the workload with this server.
31// Practically necessary if DiskEncryptionPassphrase is not set, in
32// which case we'll generate one and throw it away after.
33AttestationURL string
34
35// Used to measure the environment. If left unset (0), defaults will be applied.
36CPUs int
37Memory int
38
39// Can be manually set. If left unset ("", false, nil), reasonable values will be used.
40TeeType define.TeeType
41IgnoreAttestationErrors bool
42WorkloadID string
43DiskEncryptionPassphrase string
44Slop string
45FirmwareLibrary string
46BaseImage string
47Logger *logrus.Logger
48ExtraImageContent map[string]string
49
50// Passed through to BuilderOptions. Most settings won't make
51// sense to be made available here because we don't launch a process.
52ContainerSuffix string
53PullPolicy PullPolicy
54BlobDirectory string
55SignaturePolicyPath string
56ReportWriter io.Writer
57IDMappingOptions *IDMappingOptions
58Format string
59MaxPullRetries int
60PullRetryDelay time.Duration
61OciDecryptConfig *encconfig.DecryptConfig
62MountLabel string
63}
64
65// CWConvertImage takes the rootfs and configuration from one image, generates a
66// LUKS-encrypted disk image that more or less includes them both, and puts the
67// result into a new container image.
68// Returns the new image's ID and digest on success, along with a canonical
69// reference for it if a repository name was specified.
70func CWConvertImage(ctx context.Context, systemContext *types.SystemContext, store storage.Store, options CWConvertImageOptions) (string, reference.Canonical, digest.Digest, error) {
71// Apply our defaults if some options aren't set.
72logger := options.Logger
73if logger == nil {
74logger = logrus.StandardLogger()
75}
76
77// Now create the target working container, pulling the base image if
78// there is one and it isn't present.
79builderOptions := BuilderOptions{
80FromImage: options.BaseImage,
81SystemContext: systemContext,
82Logger: logger,
83
84ContainerSuffix: options.ContainerSuffix,
85PullPolicy: options.PullPolicy,
86BlobDirectory: options.BlobDirectory,
87SignaturePolicyPath: options.SignaturePolicyPath,
88ReportWriter: options.ReportWriter,
89IDMappingOptions: options.IDMappingOptions,
90Format: options.Format,
91MaxPullRetries: options.MaxPullRetries,
92PullRetryDelay: options.PullRetryDelay,
93OciDecryptConfig: options.OciDecryptConfig,
94MountLabel: options.MountLabel,
95}
96target, err := NewBuilder(ctx, store, builderOptions)
97if err != nil {
98return "", nil, "", fmt.Errorf("creating container from target image: %w", err)
99}
100defer func() {
101if err := target.Delete(); err != nil {
102logrus.Warnf("deleting target container: %v", err)
103}
104}()
105targetDir, err := target.Mount("")
106if err != nil {
107return "", nil, "", fmt.Errorf("mounting target container: %w", err)
108}
109defer func() {
110if err := target.Unmount(); err != nil {
111logrus.Warnf("unmounting target container: %v", err)
112}
113}()
114
115// Mount the source image, pulling it first if necessary.
116builderOptions = BuilderOptions{
117FromImage: options.InputImage,
118SystemContext: systemContext,
119Logger: logger,
120
121ContainerSuffix: options.ContainerSuffix,
122PullPolicy: options.PullPolicy,
123BlobDirectory: options.BlobDirectory,
124SignaturePolicyPath: options.SignaturePolicyPath,
125ReportWriter: options.ReportWriter,
126IDMappingOptions: options.IDMappingOptions,
127Format: options.Format,
128MaxPullRetries: options.MaxPullRetries,
129PullRetryDelay: options.PullRetryDelay,
130OciDecryptConfig: options.OciDecryptConfig,
131MountLabel: options.MountLabel,
132}
133source, err := NewBuilder(ctx, store, builderOptions)
134if err != nil {
135return "", nil, "", fmt.Errorf("creating container from source image: %w", err)
136}
137defer func() {
138if err := source.Delete(); err != nil {
139logrus.Warnf("deleting source container: %v", err)
140}
141}()
142sourceInfo := GetBuildInfo(source)
143if err != nil {
144return "", nil, "", fmt.Errorf("retrieving info about source image: %w", err)
145}
146sourceImageID := sourceInfo.FromImageID
147sourceSize, err := store.ImageSize(sourceImageID)
148if err != nil {
149return "", nil, "", fmt.Errorf("computing size of source image: %w", err)
150}
151sourceDir, err := source.Mount("")
152if err != nil {
153return "", nil, "", fmt.Errorf("mounting source container: %w", err)
154}
155defer func() {
156if err := source.Unmount(); err != nil {
157logrus.Warnf("unmounting source container: %v", err)
158}
159}()
160
161// Generate the image contents.
162archiveOptions := mkcw.ArchiveOptions{
163AttestationURL: options.AttestationURL,
164CPUs: options.CPUs,
165Memory: options.Memory,
166TempDir: targetDir,
167TeeType: options.TeeType,
168IgnoreAttestationErrors: options.IgnoreAttestationErrors,
169ImageSize: sourceSize,
170WorkloadID: options.WorkloadID,
171DiskEncryptionPassphrase: options.DiskEncryptionPassphrase,
172Slop: options.Slop,
173FirmwareLibrary: options.FirmwareLibrary,
174Logger: logger,
175GraphOptions: store.GraphOptions(),
176ExtraImageContent: options.ExtraImageContent,
177}
178rc, workloadConfig, err := mkcw.Archive(sourceDir, &source.OCIv1, archiveOptions)
179if err != nil {
180return "", nil, "", fmt.Errorf("generating encrypted image content: %w", err)
181}
182if err = archive.Untar(rc, targetDir, &archive.TarOptions{}); err != nil {
183if err = rc.Close(); err != nil {
184logger.Warnf("cleaning up: %v", err)
185}
186return "", nil, "", fmt.Errorf("saving encrypted image content: %w", err)
187}
188if err = rc.Close(); err != nil {
189return "", nil, "", fmt.Errorf("cleaning up: %w", err)
190}
191
192// Commit the image. Clear out most of the configuration (if there is any — we default
193// to scratch as a base) so that an engine that doesn't or can't set up a TEE will just
194// run the static entrypoint. The rest of the configuration which the runtime consults
195// is in the .krun_config.json file in the encrypted filesystem.
196logger.Log(logrus.DebugLevel, "committing disk image")
197target.ClearAnnotations()
198target.ClearEnv()
199target.ClearLabels()
200target.ClearOnBuild()
201target.ClearPorts()
202target.ClearVolumes()
203target.SetCmd(nil)
204target.SetCreatedBy(fmt.Sprintf(": convert %q for use with %q", sourceImageID, workloadConfig.Type))
205target.SetDomainname("")
206target.SetEntrypoint([]string{"/entrypoint"})
207target.SetHealthcheck(nil)
208target.SetHostname("")
209target.SetMaintainer("")
210target.SetShell(nil)
211target.SetUser("")
212target.SetWorkDir("")
213commitOptions := CommitOptions{
214SystemContext: systemContext,
215}
216if options.Tag != "" {
217commitOptions.AdditionalTags = append(commitOptions.AdditionalTags, options.Tag)
218}
219return target.Commit(ctx, options.OutputImage, commitOptions)
220}
221