crossplane

Форк
0
158 строк · 4.5 Кб
1
/*
2
Copyright 2023 The Crossplane Authors.
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package xpkg
18

19
import (
20
	"archive/tar"
21
	"bytes"
22
	"fmt"
23
	"io"
24
	"os"
25

26
	v1 "github.com/google/go-containerregistry/pkg/v1"
27
	"github.com/google/go-containerregistry/pkg/v1/empty"
28
	"github.com/google/go-containerregistry/pkg/v1/mutate"
29
	"github.com/google/go-containerregistry/pkg/v1/tarball"
30

31
	"github.com/crossplane/crossplane-runtime/pkg/errors"
32
)
33

34
// Error strings.
35
const (
36
	errLayer  = "cannot get image layers"
37
	errDigest = "cannot get image digest"
38
)
39

40
// Layer creates a v1.Layer that represents the layer contents for the xpkg and
41
// adds a corresponding label to the image Config for the layer.
42
func Layer(r io.Reader, fileName, annotation string, fileSize int64, mode os.FileMode, cfg *v1.Config) (v1.Layer, error) {
43
	tarBuf := new(bytes.Buffer)
44
	tw := tar.NewWriter(tarBuf)
45

46
	exHdr := &tar.Header{
47
		Name: fileName,
48
		Mode: int64(mode),
49
		Size: fileSize,
50
	}
51

52
	if err := writeLayer(tw, exHdr, r); err != nil {
53
		return nil, err
54
	}
55

56
	// TODO(hasheddan): we currently return a new reader every time here in
57
	// order to calculate digest, then subsequently write contents to disk. We
58
	// can greatly improve performance during package build by avoiding reading
59
	// every layer into memory.
60
	layer, err := tarball.LayerFromOpener(func() (io.ReadCloser, error) {
61
		return io.NopCloser(bytes.NewReader(tarBuf.Bytes())), nil
62
	})
63
	if err != nil {
64
		return nil, errors.Wrap(err, errLayerFromTar)
65
	}
66

67
	d, err := layer.Digest()
68
	if err != nil {
69
		return nil, errors.Wrap(err, errDigestInvalid)
70
	}
71

72
	// Add annotation label to config if a non-empty label is specified. This is
73
	// an intermediary step. AnnotateLayers must be called on an image for it to
74
	// have valid layer annotations. It propagates these labels to annotations
75
	// on the layers.
76
	if annotation != "" {
77
		cfg.Labels[Label(d.String())] = annotation
78
	}
79

80
	return layer, nil
81
}
82

83
func writeLayer(tw *tar.Writer, hdr *tar.Header, buf io.Reader) error {
84
	if err := tw.WriteHeader(hdr); err != nil {
85
		return errors.Wrap(err, errTarFromStream)
86
	}
87

88
	if _, err := io.Copy(tw, buf); err != nil {
89
		return errors.Wrap(err, errTarFromStream)
90
	}
91
	if err := tw.Close(); err != nil {
92
		return errors.Wrap(err, errTarFromStream)
93
	}
94
	return nil
95
}
96

97
// Label constructs a specially formated label using the annotationKey.
98
func Label(annotation string) string {
99
	return fmt.Sprintf("%s:%s", AnnotationKey, annotation)
100
}
101

102
// NOTE(negz): AnnotateLayers originated in upbound/up. I was confused why we
103
// store layer annotations as labels in the OCI config file when we build a
104
// package, then propagate them to OCI layer annotations when we push one. I
105
// believe this is because an xpkg file is really an OCI image tarball, and the
106
// tarball format doesn't support layer annotations (or may just lose them in
107
// some circumstances?), so we're using the config file to store them.
108
// See https://github.com/upbound/up/pull/177#discussion_r866776584.
109

110
// AnnotateLayers propagates labels from the supplied image's config file to
111
// annotations on its layers.
112
func AnnotateLayers(i v1.Image) (v1.Image, error) {
113
	cfgFile, err := i.ConfigFile()
114
	if err != nil {
115
		return nil, errors.Wrap(err, errConfigFile)
116
	}
117

118
	layers, err := i.Layers()
119
	if err != nil {
120
		return nil, errors.Wrap(err, errLayer)
121
	}
122

123
	addendums := make([]mutate.Addendum, 0)
124

125
	for _, l := range layers {
126
		d, err := l.Digest()
127
		if err != nil {
128
			return nil, errors.Wrap(err, errDigest)
129
		}
130
		if annotation, ok := cfgFile.Config.Labels[Label(d.String())]; ok {
131
			addendums = append(addendums, mutate.Addendum{
132
				Layer: l,
133
				Annotations: map[string]string{
134
					AnnotationKey: annotation,
135
				},
136
			})
137
			continue
138
		}
139
		addendums = append(addendums, mutate.Addendum{
140
			Layer: l,
141
		})
142
	}
143

144
	// we didn't find any annotations, return original image
145
	if len(addendums) == 0 {
146
		return i, nil
147
	}
148

149
	img := empty.Image
150
	for _, a := range addendums {
151
		img, err = mutate.Append(img, a)
152
		if err != nil {
153
			return nil, errors.Wrap(err, errBuildImage)
154
		}
155
	}
156

157
	return mutate.ConfigFile(img, cfgFile)
158
}
159

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.