Skip to content

Commit 14c8e69

Browse files
committed
Use embedded hashes for our well-known assets
Rather than downloading the hash every time, we can record the hashes for our well-known assets and bake them into the kOps binary. If the hash is not baked in, we will continue to fall-back to downloading it, this is important for new k8s versions, or where the user specifies a version of one of our well-known assets (such as containerd).
1 parent a83cea7 commit 14c8e69

20 files changed

+214
-479
lines changed

cmd/kops/get_assets.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ func RunGetAssets(ctx context.Context, f *util.Factory, out io.Writer, options *
132132
file := File{
133133
Canonical: fileAsset.CanonicalURL.String(),
134134
Download: fileAsset.DownloadURL.String(),
135-
SHA: fileAsset.SHAValue,
135+
SHA: fileAsset.SHAValue.Hex(),
136136
}
137137
if !seen[file.Canonical] {
138138
result.Files = append(result.Files, &file)

hack/generate-asset-hashes.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ function generate_runc_hashes() {
6262
# This file is generated by generate-asset-hashes.sh
6363
6464
filestores:
65-
- base: https://dl.k8s.io/release/
65+
- base: https://github.com/opencontainers/runc/releases/download/
6666
6767
files:
6868
EOF

pkg/assets/assetdata/data_test.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,18 @@ func TestGetHash(t *testing.T) {
2626
Name string
2727
Hash string
2828
}{
29-
{Name: "https://dl.k8s.io/release/v1.26.0/bin/linux/amd64/kubelet", Hash: "sha256:b64949fe696c77565edbe4100a315b6bf8f0e2325daeb762f7e865f16a6e54b5"},
29+
{
30+
Name: "https://dl.k8s.io/release/v1.26.0/bin/linux/amd64/kubelet",
31+
Hash: "b64949fe696c77565edbe4100a315b6bf8f0e2325daeb762f7e865f16a6e54b5",
32+
},
33+
{
34+
Name: "https://github.com/opencontainers/runc/releases/download/v1.1.0/runc.amd64",
35+
Hash: "ab1c67fbcbdddbe481e48a55cf0ef9a86b38b166b5079e0010737fd87d7454bb",
36+
},
37+
{
38+
Name: "https://github.com/opencontainers/runc/releases/download/v1.1.0/runc.arm64",
39+
Hash: "9ec8e68feabc4e7083a4cfa45ebe4d529467391e0b03ee7de7ddda5770b05e68",
40+
},
3041
}
3142

3243
for _, g := range grid {
@@ -41,7 +52,7 @@ func TestGetHash(t *testing.T) {
4152
if !found {
4253
t.Fatalf("hash for %q was not found", g.Name)
4354
}
44-
got := h.String()
55+
got := h.Hex()
4556
want := g.Hash
4657
if got != g.Hash {
4758
t.Errorf("unexpected hash for %q; got %q, want %q", g.Name, got, want)

pkg/assets/assetdata/runc-1.1.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# This file is generated by generate-asset-hashes.sh
22

33
filestores:
4-
- base: https://dl.k8s.io/release/
4+
- base: https://github.com/opencontainers/runc/releases/download/
55

66
files:
77
# runc 1.1.0

pkg/assets/builder.go

+30-44
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"k8s.io/klog/v2"
3333
"k8s.io/kops/pkg/apis/kops"
3434
"k8s.io/kops/pkg/apis/kops/util"
35+
"k8s.io/kops/pkg/assets/assetdata"
3536
"k8s.io/kops/pkg/featureflag"
3637
"k8s.io/kops/pkg/kubemanifest"
3738
"k8s.io/kops/pkg/values"
@@ -97,7 +98,7 @@ type FileAsset struct {
9798
// CanonicalURL is the canonical location of the asset, for example as distributed by the kops project
9899
CanonicalURL *url.URL
99100
// SHAValue is the SHA hash of the FileAsset.
100-
SHAValue string
101+
SHAValue *hashing.Hash
101102
}
102103

103104
// NewAssetBuilder creates a new AssetBuilder.
@@ -235,70 +236,45 @@ func (a *AssetBuilder) RemapImage(image string) (string, error) {
235236
return image + "@" + digest, nil
236237
}
237238

238-
// RemapFileAndSHA returns a remapped URL for the file, if AssetsLocation is defined.
239-
// It also returns the SHA hash of the file.
240-
func (a *AssetBuilder) RemapFileAndSHA(fileURL *url.URL) (*url.URL, *hashing.Hash, error) {
241-
if fileURL == nil {
242-
return nil, nil, fmt.Errorf("unable to remap a nil URL")
239+
// RemapFile returns a remapped URL for the file, if AssetsLocation is defined.
240+
// It also returns the SHA hash of the file; which is knownHash is provided,
241+
// and otherwise will be found via download.
242+
func (a *AssetBuilder) RemapFile(canonicalURL *url.URL, knownHash *hashing.Hash) (*FileAsset, error) {
243+
if canonicalURL == nil {
244+
return nil, fmt.Errorf("unable to remap a nil URL")
243245
}
244246

245247
fileAsset := &FileAsset{
246-
DownloadURL: fileURL,
247-
CanonicalURL: fileURL,
248+
DownloadURL: canonicalURL,
249+
CanonicalURL: canonicalURL,
248250
}
249251

250252
if a.AssetsLocation != nil && a.AssetsLocation.FileRepository != nil {
251-
252-
normalizedFile, err := a.remapURL(fileURL)
253+
normalizedFile, err := a.remapURL(canonicalURL)
253254
if err != nil {
254-
return nil, nil, err
255+
return nil, err
255256
}
256257

257-
if fileURL.Host != normalizedFile.Host {
258+
if canonicalURL.Host != normalizedFile.Host {
258259
fileAsset.DownloadURL = normalizedFile
259260
klog.V(4).Infof("adding remapped file: %q", fileAsset.DownloadURL.String())
260261
}
261262
}
262263

263-
h, err := a.findHash(fileAsset)
264-
if err != nil {
265-
return nil, nil, err
266-
}
267-
fileAsset.SHAValue = h.Hex()
268-
269-
klog.V(8).Infof("adding file: %+v", fileAsset)
270-
a.FileAssets = append(a.FileAssets, fileAsset)
271-
272-
return fileAsset.DownloadURL, h, nil
273-
}
274-
275-
// RemapFileAndSHAValue returns a remapped URL for the file without a SHA file in object storage, if AssetsLocation is defined.
276-
func (a *AssetBuilder) RemapFileAndSHAValue(fileURL *url.URL, shaValue string) (*url.URL, error) {
277-
if fileURL == nil {
278-
return nil, fmt.Errorf("unable to remap a nil URL")
279-
}
280-
281-
fileAsset := &FileAsset{
282-
DownloadURL: fileURL,
283-
CanonicalURL: fileURL,
284-
SHAValue: shaValue,
285-
}
286-
287-
if a.AssetsLocation != nil && a.AssetsLocation.FileRepository != nil {
288-
normalizedFile, err := a.remapURL(fileURL)
264+
if knownHash == nil {
265+
h, err := a.findHash(fileAsset)
289266
if err != nil {
290267
return nil, err
291268
}
292-
if fileURL.Host != normalizedFile.Host {
293-
fileAsset.DownloadURL = normalizedFile
294-
klog.V(4).Infof("adding remapped file: %q", fileAsset.DownloadURL.String())
295-
}
269+
knownHash = h
296270
}
297271

272+
fileAsset.SHAValue = knownHash
273+
298274
klog.V(8).Infof("adding file: %+v", fileAsset)
299275
a.FileAssets = append(a.FileAssets, fileAsset)
300276

301-
return fileAsset.DownloadURL, nil
277+
return fileAsset, nil
302278
}
303279

304280
// FindHash returns the hash value of a FileAsset.
@@ -324,6 +300,16 @@ func (a *AssetBuilder) findHash(file *FileAsset) (*hashing.Hash, error) {
324300
return nil, fmt.Errorf("file url is not defined")
325301
}
326302

303+
knownHash, found, err := assetdata.GetHash(file.CanonicalURL)
304+
if err != nil {
305+
return nil, err
306+
}
307+
if found {
308+
return knownHash, nil
309+
}
310+
311+
klog.Infof("asset %q is not well-known, downloading hash", file.CanonicalURL)
312+
327313
// We now prefer sha256 hashes
328314
for backoffSteps := 1; backoffSteps <= 3; backoffSteps++ {
329315
// We try first with a short backoff, so we don't
@@ -338,7 +324,7 @@ func (a *AssetBuilder) findHash(file *FileAsset) (*hashing.Hash, error) {
338324
for _, ext := range []string{".sha256", ".sha256sum"} {
339325
for _, mirror := range FindURLMirrors(u.String()) {
340326
hashURL := mirror + ext
341-
klog.V(3).Infof("Trying to read hash fie: %q", hashURL)
327+
klog.V(3).Infof("Trying to read hash file: %q", hashURL)
342328
b, err := a.vfsContext.ReadFile(hashURL, vfs.WithBackoff(backoff))
343329
if err != nil {
344330
// Try to log without being too alarming - issue #7550

pkg/assets/copy.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func Copy(imageAssets []*ImageAsset, fileAssets []*FileAsset, vfsContext *vfs.VF
5656
Name: fileAsset.CanonicalURL.String(),
5757
TargetFile: fileAsset.DownloadURL.String(),
5858
SourceFile: fileAsset.CanonicalURL.String(),
59-
SHA: fileAsset.SHAValue,
59+
SHA: fileAsset.SHAValue.Hex(),
6060
VFSContext: vfsContext,
6161
Cluster: cluster,
6262
}

pkg/assets/mirrored_asset.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ limitations under the License.
1717
package assets
1818

1919
import (
20-
"net/url"
2120
"strings"
2221

2322
"k8s.io/klog/v2"
@@ -30,12 +29,14 @@ type MirroredAsset struct {
3029
}
3130

3231
// BuildMirroredAsset checks to see if this is a file under the standard base location, and if so constructs some mirror locations
33-
func BuildMirroredAsset(u *url.URL, hash *hashing.Hash) *MirroredAsset {
32+
func BuildMirroredAsset(asset *FileAsset) *MirroredAsset {
33+
u := asset.DownloadURL
34+
3435
a := &MirroredAsset{
35-
Hash: hash,
36+
Hash: asset.SHAValue,
3637
}
3738

38-
if hash == nil {
39+
if asset.SHAValue == nil {
3940
klog.Warningf("not using mirrors for asset %s as it does not have a known hash", u)
4041
a.Locations = []string{u.String()}
4142
} else {

pkg/nodemodel/fileassets.go

+32-22
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,11 @@ func (c *FileAssets) AddFileAssets(assetBuilder *assets.AssetBuilder) error {
7575
}
7676
k.Path = path.Join(k.Path, an)
7777

78-
u, hash, err := assetBuilder.RemapFileAndSHA(k)
78+
asset, err := buildMirroredAsset(assetBuilder, k)
7979
if err != nil {
8080
return err
8181
}
82-
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(u, hash))
82+
c.Assets[arch] = append(c.Assets[arch], asset)
8383
}
8484

8585
kubernetesVersion, _ := util.ParseKubernetesVersion(c.Cluster.Spec.KubernetesVersion)
@@ -98,6 +98,7 @@ func (c *FileAssets) AddFileAssets(assetBuilder *assets.AssetBuilder) error {
9898
return err
9999
}
100100

101+
// TODO: Move these hashes to assetdata
101102
hashes := map[architectures.Architecture]string{
102103
"amd64": "827d558953d861b81a35c3b599191a73f53c1f63bce42c61e7a3fee21a717a89",
103104
"arm64": "f1617c0ef77f3718e12a3efc6f650375d5b5e96eebdbcbad3e465e89e781bdfa",
@@ -106,70 +107,69 @@ func (c *FileAssets) AddFileAssets(assetBuilder *assets.AssetBuilder) error {
106107
if err != nil {
107108
return fmt.Errorf("unable to parse auth-provider-gcp binary asset hash %q: %v", hashes[arch], err)
108109
}
109-
u, err := assetBuilder.RemapFileAndSHAValue(k, hashes[arch])
110+
asset, err := assetBuilder.RemapFile(k, hash)
110111
if err != nil {
111112
return err
112113
}
113114

114-
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(u, hash))
115+
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(asset))
115116
case kops.CloudProviderAWS:
116117
binaryLocation := c.Cluster.Spec.CloudProvider.AWS.BinariesLocation
117118
if binaryLocation == nil {
118119
binaryLocation = fi.PtrTo("https://artifacts.k8s.io/binaries/cloud-provider-aws/v1.27.1")
119120
}
120121

121-
k, err := url.Parse(fmt.Sprintf("%s/linux/%s/ecr-credential-provider-linux-%s", *binaryLocation, arch, arch))
122+
u, err := url.Parse(fmt.Sprintf("%s/linux/%s/ecr-credential-provider-linux-%s", *binaryLocation, arch, arch))
122123
if err != nil {
123124
return err
124125
}
125-
u, hash, err := assetBuilder.RemapFileAndSHA(k)
126+
asset, err := buildMirroredAsset(assetBuilder, u)
126127
if err != nil {
127128
return err
128129
}
129-
130-
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(u, hash))
130+
c.Assets[arch] = append(c.Assets[arch], asset)
131131
}
132132
}
133133

134134
{
135-
cniAsset, cniAssetHash, err := wellknownassets.FindCNIAssets(c.Cluster, assetBuilder, arch)
135+
cniAsset, err := wellknownassets.FindCNIAssets(c.Cluster, assetBuilder, arch)
136136
if err != nil {
137137
return err
138138
}
139-
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(cniAsset, cniAssetHash))
139+
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(cniAsset))
140140
}
141141

142142
if c.Cluster.Spec.Containerd == nil || !c.Cluster.Spec.Containerd.SkipInstall {
143-
containerdAssetUrl, containerdAssetHash, err := wellknownassets.FindContainerdAsset(c.Cluster, assetBuilder, arch)
143+
containerdAsset, err := wellknownassets.FindContainerdAsset(c.Cluster, assetBuilder, arch)
144144
if err != nil {
145145
return err
146146
}
147-
if containerdAssetUrl != nil && containerdAssetHash != nil {
148-
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(containerdAssetUrl, containerdAssetHash))
147+
if containerdAsset != nil {
148+
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(containerdAsset))
149149
}
150150

151-
runcAssetUrl, runcAssetHash, err := wellknownassets.FindRuncAsset(c.Cluster, assetBuilder, arch)
151+
runcAsset, err := wellknownassets.FindRuncAsset(c.Cluster, assetBuilder, arch)
152152
if err != nil {
153153
return err
154154
}
155-
if runcAssetUrl != nil && runcAssetHash != nil {
156-
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(runcAssetUrl, runcAssetHash))
155+
if runcAsset != nil {
156+
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(runcAsset))
157157
}
158-
nerdctlAssetUrl, nerdctlAssetHash, err := wellknownassets.FindNerdctlAsset(c.Cluster, assetBuilder, arch)
158+
nerdctlAsset, err := wellknownassets.FindNerdctlAsset(c.Cluster, assetBuilder, arch)
159159
if err != nil {
160160
return err
161161
}
162-
if nerdctlAssetUrl != nil && nerdctlAssetHash != nil {
163-
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(nerdctlAssetUrl, nerdctlAssetHash))
162+
if nerdctlAsset != nil {
163+
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(nerdctlAsset))
164164
}
165165
}
166166

167-
crictlAssetUrl, crictlAssetHash, err := wellknownassets.FindCrictlAsset(c.Cluster, assetBuilder, arch)
167+
crictlAsset, err := wellknownassets.FindCrictlAsset(c.Cluster, assetBuilder, arch)
168168
if err != nil {
169169
return err
170170
}
171-
if crictlAssetUrl != nil && crictlAssetHash != nil {
172-
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(crictlAssetUrl, crictlAssetHash))
171+
if crictlAsset != nil {
172+
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(crictlAsset))
173173
}
174174

175175
asset, err := wellknownassets.NodeUpAsset(assetBuilder, arch)
@@ -193,3 +193,13 @@ func needsMounterAsset(c *kops.Cluster) bool {
193193
return false
194194
}
195195
}
196+
197+
// buildMirroredAsset is a simple wrapper around RemapFile & BuildMirroredAsset.
198+
func buildMirroredAsset(assetBuilder *assets.AssetBuilder, canonicalURL *url.URL) (*assets.MirroredAsset, error) {
199+
asset, err := assetBuilder.RemapFile(canonicalURL, nil)
200+
if err != nil {
201+
return nil, err
202+
}
203+
mirroredAsset := assets.BuildMirroredAsset(asset)
204+
return mirroredAsset, nil
205+
}

0 commit comments

Comments
 (0)