Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/registry-facade/pkg/registry/imagecfg.go
2499 views
1
// Copyright (c) 2020 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package registry
6
7
import (
8
"context"
9
"sync"
10
11
"github.com/containerd/containerd/errdefs"
12
lru "github.com/hashicorp/golang-lru"
13
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
14
"golang.org/x/xerrors"
15
"google.golang.org/grpc"
16
"google.golang.org/grpc/connectivity"
17
18
"github.com/gitpod-io/gitpod/registry-facade/api"
19
)
20
21
// ErrRefInvalid is returned by spec provider who cannot interpret the ref
22
var ErrRefInvalid = xerrors.Errorf("invalid ref")
23
24
// ImageSpecProvider provide the image spec for an image pull
25
// based on the ref
26
type ImageSpecProvider interface {
27
// GetSpec returns the spec for the image or a wrapped ErrRefInvalid
28
GetSpec(ctx context.Context, ref string) (*api.ImageSpec, error)
29
}
30
31
// FixedImageSpecProvider provides a single spec
32
type FixedImageSpecProvider map[string]*api.ImageSpec
33
34
func (p FixedImageSpecProvider) GetSpec(ctx context.Context, ref string) (*api.ImageSpec, error) {
35
res, ok := p[ref]
36
if !ok {
37
return nil, xerrors.Errorf("%w: %s", ErrRefInvalid, errdefs.ErrNotFound)
38
}
39
return res, nil
40
}
41
42
// NewCompositeSpecProvider aggregates multiple image spec providers
43
type CompositeSpecProvider struct {
44
providers []ImageSpecProvider
45
}
46
47
// NewCompositeSpecProvider produces a new composite image spec provider
48
func NewCompositeSpecProvider(providers ...ImageSpecProvider) *CompositeSpecProvider {
49
return &CompositeSpecProvider{
50
providers: providers,
51
}
52
}
53
54
// GetSpec returns the spec for the image or the error of the last image spec provider
55
func (csp *CompositeSpecProvider) GetSpec(ctx context.Context, ref string) (spec *api.ImageSpec, err error) {
56
if len(csp.providers) == 0 {
57
return nil, xerrors.Errorf("no image spec providers configured")
58
}
59
60
for _, p := range csp.providers {
61
spec, err = p.GetSpec(ctx, ref)
62
if err == nil {
63
return spec, nil
64
}
65
}
66
return
67
}
68
69
// RemoteSpecProvider queries a remote spec provider using gRPC
70
type RemoteSpecProvider struct {
71
addr string
72
opts []grpc.DialOption
73
conn *grpc.ClientConn
74
mu sync.RWMutex
75
}
76
77
// NewRemoteSpecProvider produces a new remote spec provider
78
func NewRemoteSpecProvider(addr string, opts []grpc.DialOption) *RemoteSpecProvider {
79
return &RemoteSpecProvider{
80
addr: addr,
81
opts: opts,
82
}
83
}
84
85
// GetSpec returns the spec for the image or a wrapped ErrRefInvalid
86
func (p *RemoteSpecProvider) GetSpec(ctx context.Context, ref string) (*api.ImageSpec, error) {
87
client, err := p.getClient(ctx)
88
if err != nil {
89
return nil, xerrors.Errorf("%w: %s", ErrRefInvalid, err.Error())
90
}
91
92
resp, err := client.GetImageSpec(ctx, &api.GetImageSpecRequest{Id: ref})
93
if err != nil {
94
return nil, xerrors.Errorf("%w: %s", ErrRefInvalid, err.Error())
95
}
96
return resp.Spec, nil
97
}
98
99
func (p *RemoteSpecProvider) getClient(ctx context.Context) (client api.SpecProviderClient, err error) {
100
isValidConn := func() bool {
101
return p.conn != nil && p.conn.GetState() != connectivity.TransientFailure
102
}
103
104
p.mu.RLock()
105
if isValidConn() {
106
defer p.mu.RUnlock()
107
return api.NewSpecProviderClient(p.conn), nil
108
}
109
p.mu.RUnlock()
110
111
p.mu.Lock()
112
defer p.mu.Unlock()
113
if isValidConn() {
114
return api.NewSpecProviderClient(p.conn), nil
115
}
116
117
p.conn, err = grpc.DialContext(ctx, p.addr, p.opts...)
118
if err != nil {
119
return nil, err
120
}
121
return api.NewSpecProviderClient(p.conn), nil
122
}
123
124
// NewCachingSpecProvider creates a new LRU caching spec provider with a max number of specs it can cache.
125
func NewCachingSpecProvider(space int, delegate ImageSpecProvider) (*CachingSpecProvider, error) {
126
cache, err := lru.New(space)
127
if err != nil {
128
return nil, err
129
}
130
return &CachingSpecProvider{
131
Cache: cache,
132
Delegate: delegate,
133
}, nil
134
}
135
136
// CachingSpecProvider caches an image spec in an LRU cache
137
type CachingSpecProvider struct {
138
Cache *lru.Cache
139
Delegate ImageSpecProvider
140
}
141
142
// GetSpec returns the spec for the image or a wrapped ErrRefInvalid
143
func (p *CachingSpecProvider) GetSpec(ctx context.Context, ref string) (*api.ImageSpec, error) {
144
res, ok := p.Cache.Get(ref)
145
if ok {
146
return res.(*api.ImageSpec), nil
147
}
148
spec, err := p.Delegate.GetSpec(ctx, ref)
149
if err != nil {
150
return nil, err
151
}
152
p.Cache.Add(ref, spec)
153
return spec, nil
154
}
155
156
// ConfigModifier modifies an image's configuration
157
type ConfigModifier func(ctx context.Context, spec *api.ImageSpec, cfg *ociv1.Image) (layer []ociv1.Descriptor, err error)
158
159
// NewConfigModifierFromLayerSource produces a config modifier from a layer source
160
func NewConfigModifierFromLayerSource(src LayerSource) ConfigModifier {
161
return func(ctx context.Context, spec *api.ImageSpec, cfg *ociv1.Image) (layer []ociv1.Descriptor, err error) {
162
addons, err := src.GetLayer(ctx, spec)
163
if err != nil {
164
return
165
}
166
envs, err := src.Envs(ctx, spec)
167
if err != nil {
168
return
169
}
170
171
for _, l := range addons {
172
layer = append(layer, l.Descriptor)
173
cfg.RootFS.DiffIDs = append(cfg.RootFS.DiffIDs, l.DiffID)
174
}
175
176
if len(envs) > 0 {
177
parsed := parseEnvs(cfg.Config.Env)
178
for _, modifyEnv := range envs {
179
modifyEnv(parsed)
180
}
181
cfg.Config.Env = parsed.serialize()
182
}
183
184
return
185
}
186
}
187
188