crossplane
146 строк · 4.4 Кб
1/*
2Copyright 2020 The Crossplane Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package xpkg
18
19import (
20"os"
21"path/filepath"
22"strings"
23
24"github.com/google/go-containerregistry/pkg/name"
25"github.com/spf13/afero"
26"sigs.k8s.io/yaml"
27)
28
29const (
30// MetaFile is the name of a Crossplane package metadata file.
31MetaFile string = "crossplane.yaml"
32
33// StreamFile is the name of the file in a Crossplane package image that
34// contains its YAML stream.
35StreamFile string = "package.yaml"
36
37// StreamFileMode determines the permissions on the stream file.
38StreamFileMode os.FileMode = 0o644
39
40// XpkgExtension is the extension for compiled Crossplane packages.
41XpkgExtension string = ".xpkg"
42
43// XpkgMatchPattern is the match pattern for identifying compiled Crossplane packages.
44XpkgMatchPattern string = "*" + XpkgExtension
45
46// XpkgExamplesFile is the name of the file in a Crossplane package image
47// that contains the examples YAML stream.
48XpkgExamplesFile string = ".up/examples.yaml"
49
50// AnnotationKey is the key value for xpkg annotations.
51AnnotationKey string = "io.crossplane.xpkg"
52
53// PackageAnnotation is the annotation value used for the package.yaml
54// layer.
55PackageAnnotation string = "base"
56
57// ExamplesAnnotation is the annotation value used for the examples.yaml
58// layer.
59// TODO(lsviben) Consider changing this to "examples".
60ExamplesAnnotation string = "upbound"
61
62// DefaultRegistry is the registry name that will be used when no registry
63// is provided.
64DefaultRegistry string = "xpkg.upbound.io"
65)
66
67const (
68// identifierDelimeters is the set of valid OCI image identifier delimeter
69// characters.
70identifierDelimeters string = ":@"
71)
72
73func truncate(str string, num int) string {
74t := str
75if len(str) > num {
76t = str[0:num]
77}
78return t
79}
80
81// FriendlyID builds a valid DNS label string made up of the name of a package
82// and its image digest.
83func FriendlyID(name, hash string) string {
84return ToDNSLabel(strings.Join([]string{truncate(name, 50), truncate(hash, 12)}, "-"))
85}
86
87// ToDNSLabel converts the string to a valid DNS label.
88func ToDNSLabel(s string) string { //nolint:gocyclo // TODO(negz): Document the conditions in this function.
89var cut strings.Builder
90for i := range s {
91b := s[i]
92if ('a' <= b && b <= 'z') || ('0' <= b && b <= '9') {
93cut.WriteByte(b)
94}
95if (b == '.' || b == '/' || b == ':' || b == '-') && (i != 0 && i != 62 && i != len(s)-1) {
96cut.WriteByte('-')
97}
98if i == 62 {
99break
100}
101}
102return strings.Trim(cut.String(), "-")
103}
104
105// BuildPath builds a path with the provided extension.
106func BuildPath(path, name, ext string) string {
107full := filepath.Join(path, name)
108existExt := filepath.Ext(full)
109return full[0:len(full)-len(existExt)] + ext
110}
111
112// ParseNameFromMeta extracts the package name from its meta file.
113func ParseNameFromMeta(fs afero.Fs, path string) (string, error) {
114bs, err := afero.ReadFile(fs, filepath.Clean(path))
115if err != nil {
116return "", err
117}
118pkgName, err := parseNameFromPackage(bs)
119if err != nil {
120return "", err
121}
122return pkgName, nil
123}
124
125// ParsePackageSourceFromReference parses a package source from an OCI image
126// reference. A source is defined as an OCI image reference with the identifier
127// (tag or digest) stripped and no other changes to the original reference
128// source. This is necessary because go-containerregistry will convert docker.io
129// to index.docker.io for backwards compatibility before pulling an image. We do
130// not want to do that in cases where we are not pulling an image because it
131// breaks comparison with dependencies defined in a Configuration manifest.
132func ParsePackageSourceFromReference(ref name.Reference) string {
133return strings.TrimRight(strings.TrimSuffix(ref.String(), ref.Identifier()), identifierDelimeters)
134}
135
136type metaPkg struct {
137Metadata struct {
138Name string `json:"name"`
139}
140}
141
142func parseNameFromPackage(bs []byte) (string, error) {
143p := &metaPkg{}
144err := yaml.Unmarshal(bs, p)
145return p.Metadata.Name, err
146}
147