generated from kubernetes/kubernetes-template-project
-
Notifications
You must be signed in to change notification settings - Fork 80
/
Copy pathwalkimages.go
133 lines (117 loc) · 4.05 KB
/
walkimages.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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"fmt"
"net/http"
"golang.org/x/sync/errgroup"
"k8s.io/klog/v2"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/google"
"github.com/google/go-containerregistry/pkg/v1/partial"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/types"
)
// WalkImageLAyersFunc is used to visit an image
type WalkImageLayersFunc func(ref name.Reference, layers []v1.Layer) error
// Unfortunately this is only doable on GCP currently.
//
// TODO: To support other registries in the meantime, we could require a list of
// image names as an input and plumb that through, then list tags and get something
// close to this. The _catalog endpoint + tag listing could also work in some cases.
//
// However, even then, this is more complete because it lists all manifests, not just tags.
// It's also simpler and more efficient.
//
// See: https://github.com/opencontainers/distribution-spec/issues/222
func WalkImageLayersGCP(transport http.RoundTripper, repo name.Repository, walkImageLayers WalkImageLayersFunc, skipImage func(string) bool) error {
g := new(errgroup.Group)
// TODO: This is really just an approximation to avoid exceeding typical socket limits
// See also quota limits:
// https://cloud.google.com/artifact-registry/quotas
g.SetLimit(1000)
g.Go(func() error {
return google.Walk(repo, func(r name.Repository, tags *google.Tags, err error) error {
if err != nil {
return err
}
for digest, metadata := range tags.Manifests {
digest := digest
// google.Walk already walks the child manifests
if metadata.MediaType == string(types.DockerManifestList) || metadata.MediaType == string(types.OCIImageIndex) {
continue
}
ref, err := name.ParseReference(fmt.Sprintf("%s@%s", r, digest))
if err != nil {
return err
}
g.Go(func() error {
if skipImage(digest) {
klog.V(4).Infof("Skipping already-uploaded: %s", ref)
return nil
}
return walkManifestLayers(transport, ref, walkImageLayers)
})
}
return nil
}, google.WithTransport(transport))
})
return g.Wait()
}
func walkManifestLayers(transport http.RoundTripper, ref name.Reference, walkImageLayers WalkImageLayersFunc) error {
desc, err := remote.Get(ref, remote.WithTransport(transport))
if err != nil {
return err
}
// google.Walk already resolves these to individual manifests
if desc.MediaType.IsIndex() {
klog.Warningf("Skipping Index: %s", ref.String())
return nil
}
// Specially handle schema 1
// https://github.com/google/go-containerregistry/issues/377
if desc.MediaType == types.DockerManifestSchema1 || desc.MediaType == types.DockerManifestSchema1Signed {
layers, err := layersForV1(transport, ref, desc)
if err != nil {
return err
}
return walkImageLayers(ref, layers)
}
// we don't expect anything other than index, or image ...
if !desc.MediaType.IsImage() {
klog.Warningf("Un-handled type: %s for %s", desc.MediaType, ref.String())
return nil
}
// Handle normal images
image, err := desc.Image()
if err != nil {
return err
}
layers, err := imageToLayers(image)
if err != nil {
return err
}
return walkImageLayers(ref, layers)
}
func imageToLayers(image v1.Image) ([]v1.Layer, error) {
layers, err := image.Layers()
if err != nil {
return nil, err
}
configLayer, err := partial.ConfigLayer(image)
if err != nil {
return nil, err
}
return append(layers, configLayer), nil
}