-
Notifications
You must be signed in to change notification settings - Fork 30
/
parse.go
117 lines (101 loc) · 3.44 KB
/
parse.go
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
package manifest
import (
"context"
"errors"
"fmt"
"io"
"io/fs"
"os"
"path"
"path/filepath"
"regexp"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/crane"
containerregistryv1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/kyma-project/lifecycle-manager/api/v1beta2"
"github.com/kyma-project/lifecycle-manager/internal/manifest/filemutex"
"github.com/kyma-project/lifecycle-manager/pkg/ocmextensions"
)
var ErrImageLayerPull = errors.New("failed to pull layer")
type PathExtractor struct {
fileMutexCache *filemutex.MutexCache
}
func NewPathExtractor(cache *filemutex.MutexCache) *PathExtractor {
if cache == nil {
return &PathExtractor{fileMutexCache: filemutex.NewMutexCache(nil)}
}
return &PathExtractor{fileMutexCache: cache}
}
func (p PathExtractor) GetPathFromRawManifest(ctx context.Context,
imageSpec v1beta2.ImageSpec,
keyChain authn.Keychain,
) (string, error) {
imageRef := fmt.Sprintf("%s/%s@%s", imageSpec.Repo, imageSpec.Name, imageSpec.Ref)
installPath := getFsChartPath(imageSpec)
manifestPath := path.Join(installPath, v1beta2.RawManifestLayerName+".yaml")
fileMutex, err := p.fileMutexCache.GetLocker(installPath)
if err != nil {
return "", fmt.Errorf("failed to load locker from cache: %w", err)
}
fileMutex.Lock()
defer fileMutex.Unlock()
dir, err := os.Open(manifestPath)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return "", fmt.Errorf("opening dir for installs caused an error %s: %w", imageRef, err)
}
if dir != nil {
return manifestPath, nil
}
// pull image layer
layer, err := pullLayer(ctx, imageRef, keyChain)
if err != nil {
return "", err
}
// copy uncompressed manifest to install path
blobReadCloser, err := layer.Uncompressed()
if err != nil {
return "", fmt.Errorf("failed fetching blob for layer %s: %w", imageRef, err)
}
defer blobReadCloser.Close()
// create dir for uncompressed manifest
if err := os.MkdirAll(installPath, fs.ModePerm); err != nil {
return "", fmt.Errorf(
"failure while creating installPath directory for layer %s: %w",
imageRef, err,
)
}
outFile, err := os.Create(manifestPath)
if err != nil {
return "", fmt.Errorf("file create failed for layer %s: %w", imageRef, err)
}
if _, err := io.Copy(outFile, blobReadCloser); err != nil {
return "", fmt.Errorf("file copy storage failed for layer %s: %w", imageRef, err)
}
err = io.Closer(outFile).Close()
if err != nil {
return manifestPath, fmt.Errorf("failed to close io: %w", err)
}
return manifestPath, nil
}
func pullLayer(ctx context.Context, imageRef string, keyChain authn.Keychain) (containerregistryv1.Layer, error) {
noSchemeImageRef := ocmextensions.NoSchemeURL(imageRef)
isInsecureLayer, err := regexp.MatchString("^http://", imageRef)
if err != nil {
return nil, fmt.Errorf("invalid imageRef: %w", err)
}
if isInsecureLayer {
imgLayer, err := crane.PullLayer(noSchemeImageRef, crane.Insecure, crane.WithAuthFromKeychain(keyChain))
if err != nil {
return nil, fmt.Errorf("%s due to: %w", ErrImageLayerPull.Error(), err)
}
return imgLayer, nil
}
imgLayer, err := crane.PullLayer(noSchemeImageRef, crane.WithAuthFromKeychain(keyChain), crane.WithContext(ctx))
if err != nil {
return nil, fmt.Errorf("%s due to: %w", ErrImageLayerPull.Error(), err)
}
return imgLayer, nil
}
func getFsChartPath(imageSpec v1beta2.ImageSpec) string {
return filepath.Join(os.TempDir(), fmt.Sprintf("%s-%s", imageSpec.Name, imageSpec.Ref))
}