Skip to content

Commit

Permalink
Use embedded hashes for our well-known assets
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
justinsb committed Mar 30, 2024
1 parent 311a1fa commit 349849c
Show file tree
Hide file tree
Showing 17 changed files with 197 additions and 475 deletions.
2 changes: 1 addition & 1 deletion cmd/kops/get_assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func RunGetAssets(ctx context.Context, f *util.Factory, out io.Writer, options *
file := File{
Canonical: fileAsset.CanonicalURL.String(),
Download: fileAsset.DownloadURL.String(),
SHA: fileAsset.SHAValue,
SHA: fileAsset.SHAValue.Hex(),
}
if !seen[file.Canonical] {
result.Files = append(result.Files, &file)
Expand Down
74 changes: 30 additions & 44 deletions pkg/assets/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"k8s.io/klog/v2"
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/apis/kops/util"
"k8s.io/kops/pkg/assets/assetdata"
"k8s.io/kops/pkg/featureflag"
"k8s.io/kops/pkg/kubemanifest"
"k8s.io/kops/pkg/values"
Expand Down Expand Up @@ -97,7 +98,7 @@ type FileAsset struct {
// CanonicalURL is the canonical location of the asset, for example as distributed by the kops project
CanonicalURL *url.URL
// SHAValue is the SHA hash of the FileAsset.
SHAValue string
SHAValue *hashing.Hash
}

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

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

fileAsset := &FileAsset{
DownloadURL: fileURL,
CanonicalURL: fileURL,
DownloadURL: canonicalURL,
CanonicalURL: canonicalURL,
}

if a.AssetsLocation != nil && a.AssetsLocation.FileRepository != nil {

normalizedFile, err := a.remapURL(fileURL)
normalizedFile, err := a.remapURL(canonicalURL)
if err != nil {
return nil, nil, err
return nil, err
}

if fileURL.Host != normalizedFile.Host {
if canonicalURL.Host != normalizedFile.Host {
fileAsset.DownloadURL = normalizedFile
klog.V(4).Infof("adding remapped file: %q", fileAsset.DownloadURL.String())
}
}

h, err := a.findHash(fileAsset)
if err != nil {
return nil, nil, err
}
fileAsset.SHAValue = h.Hex()

klog.V(8).Infof("adding file: %+v", fileAsset)
a.FileAssets = append(a.FileAssets, fileAsset)

return fileAsset.DownloadURL, h, nil
}

// RemapFileAndSHAValue returns a remapped URL for the file without a SHA file in object storage, if AssetsLocation is defined.
func (a *AssetBuilder) RemapFileAndSHAValue(fileURL *url.URL, shaValue string) (*url.URL, error) {
if fileURL == nil {
return nil, fmt.Errorf("unable to remap a nil URL")
}

fileAsset := &FileAsset{
DownloadURL: fileURL,
CanonicalURL: fileURL,
SHAValue: shaValue,
}

if a.AssetsLocation != nil && a.AssetsLocation.FileRepository != nil {
normalizedFile, err := a.remapURL(fileURL)
if knownHash == nil {
h, err := a.findHash(fileAsset)
if err != nil {
return nil, err
}
if fileURL.Host != normalizedFile.Host {
fileAsset.DownloadURL = normalizedFile
klog.V(4).Infof("adding remapped file: %q", fileAsset.DownloadURL.String())
}
knownHash = h
}

fileAsset.SHAValue = knownHash

klog.V(8).Infof("adding file: %+v", fileAsset)
a.FileAssets = append(a.FileAssets, fileAsset)

return fileAsset.DownloadURL, nil
return fileAsset, nil
}

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

knownHash, found, err := assetdata.GetHash(file.CanonicalURL)
if err != nil {
return nil, err
}
if found {
return knownHash, nil
}

klog.Infof("asset %q is not well-known, downloading hash", file.CanonicalURL)

// We now prefer sha256 hashes
for backoffSteps := 1; backoffSteps <= 3; backoffSteps++ {
// We try first with a short backoff, so we don't
Expand All @@ -338,7 +324,7 @@ func (a *AssetBuilder) findHash(file *FileAsset) (*hashing.Hash, error) {
for _, ext := range []string{".sha256", ".sha256sum"} {
for _, mirror := range FindURLMirrors(u.String()) {
hashURL := mirror + ext
klog.V(3).Infof("Trying to read hash fie: %q", hashURL)
klog.V(3).Infof("Trying to read hash file: %q", hashURL)
b, err := a.vfsContext.ReadFile(hashURL, vfs.WithBackoff(backoff))
if err != nil {
// Try to log without being too alarming - issue #7550
Expand Down
2 changes: 1 addition & 1 deletion pkg/assets/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func Copy(imageAssets []*ImageAsset, fileAssets []*FileAsset, vfsContext *vfs.VF
Name: fileAsset.CanonicalURL.String(),
TargetFile: fileAsset.DownloadURL.String(),
SourceFile: fileAsset.CanonicalURL.String(),
SHA: fileAsset.SHAValue,
SHA: fileAsset.SHAValue.Hex(),
VFSContext: vfsContext,
Cluster: cluster,
}
Expand Down
9 changes: 5 additions & 4 deletions pkg/assets/mirrored_asset.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ limitations under the License.
package assets

import (
"net/url"
"strings"

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

// BuildMirroredAsset checks to see if this is a file under the standard base location, and if so constructs some mirror locations
func BuildMirroredAsset(u *url.URL, hash *hashing.Hash) *MirroredAsset {
func BuildMirroredAsset(asset *FileAsset) *MirroredAsset {
u := asset.DownloadURL

a := &MirroredAsset{
Hash: hash,
Hash: asset.SHAValue,
}

if hash == nil {
if asset.SHAValue == nil {
klog.Warningf("not using mirrors for asset %s as it does not have a known hash", u)
a.Locations = []string{u.String()}
} else {
Expand Down
52 changes: 30 additions & 22 deletions pkg/nodemodel/fileassets.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ func (c *FileAssets) AddFileAssets(assetBuilder *assets.AssetBuilder) error {
}
k.Path = path.Join(k.Path, an)

u, hash, err := assetBuilder.RemapFileAndSHA(k)
asset, err := buildMirroredAsset(assetBuilder, k)
if err != nil {
return err
}
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(u, hash))
c.Assets[arch] = append(c.Assets[arch], asset)
}

kubernetesVersion, _ := util.ParseKubernetesVersion(c.Cluster.Spec.KubernetesVersion)
Expand All @@ -106,70 +106,69 @@ func (c *FileAssets) AddFileAssets(assetBuilder *assets.AssetBuilder) error {
if err != nil {
return fmt.Errorf("unable to parse auth-provider-gcp binary asset hash %q: %v", hashes[arch], err)
}
u, err := assetBuilder.RemapFileAndSHAValue(k, hashes[arch])
asset, err := assetBuilder.RemapFile(k, hash)
if err != nil {
return err
}

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

k, err := url.Parse(fmt.Sprintf("%s/linux/%s/ecr-credential-provider-linux-%s", *binaryLocation, arch, arch))
u, err := url.Parse(fmt.Sprintf("%s/linux/%s/ecr-credential-provider-linux-%s", *binaryLocation, arch, arch))
if err != nil {
return err
}
u, hash, err := assetBuilder.RemapFileAndSHA(k)
asset, err := buildMirroredAsset(assetBuilder, u)
if err != nil {
return err
}

c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(u, hash))
c.Assets[arch] = append(c.Assets[arch], asset)
}
}

{
cniAsset, cniAssetHash, err := wellknownassets.FindCNIAssets(c.Cluster, assetBuilder, arch)
cniAsset, err := wellknownassets.FindCNIAssets(c.Cluster, assetBuilder, arch)
if err != nil {
return err
}
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(cniAsset, cniAssetHash))
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(cniAsset))
}

if c.Cluster.Spec.Containerd == nil || !c.Cluster.Spec.Containerd.SkipInstall {
containerdAssetUrl, containerdAssetHash, err := wellknownassets.FindContainerdAsset(c.Cluster, assetBuilder, arch)
containerdAsset, err := wellknownassets.FindContainerdAsset(c.Cluster, assetBuilder, arch)
if err != nil {
return err
}
if containerdAssetUrl != nil && containerdAssetHash != nil {
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(containerdAssetUrl, containerdAssetHash))
if containerdAsset != nil {
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(containerdAsset))
}

runcAssetUrl, runcAssetHash, err := wellknownassets.FindRuncAsset(c.Cluster, assetBuilder, arch)
runcAsset, err := wellknownassets.FindRuncAsset(c.Cluster, assetBuilder, arch)
if err != nil {
return err
}
if runcAssetUrl != nil && runcAssetHash != nil {
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(runcAssetUrl, runcAssetHash))
if runcAsset != nil {
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(runcAsset))
}
nerdctlAssetUrl, nerdctlAssetHash, err := wellknownassets.FindNerdctlAsset(c.Cluster, assetBuilder, arch)
nerdctlAsset, err := wellknownassets.FindNerdctlAsset(c.Cluster, assetBuilder, arch)
if err != nil {
return err
}
if nerdctlAssetUrl != nil && nerdctlAssetHash != nil {
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(nerdctlAssetUrl, nerdctlAssetHash))
if nerdctlAsset != nil {
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(nerdctlAsset))
}
}

crictlAssetUrl, crictlAssetHash, err := wellknownassets.FindCrictlAsset(c.Cluster, assetBuilder, arch)
crictlAsset, err := wellknownassets.FindCrictlAsset(c.Cluster, assetBuilder, arch)
if err != nil {
return err
}
if crictlAssetUrl != nil && crictlAssetHash != nil {
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(crictlAssetUrl, crictlAssetHash))
if crictlAsset != nil {
c.Assets[arch] = append(c.Assets[arch], assets.BuildMirroredAsset(crictlAsset))
}

asset, err := wellknownassets.NodeUpAsset(assetBuilder, arch)
Expand All @@ -193,3 +192,12 @@ func needsMounterAsset(c *kops.Cluster) bool {
return false
}
}

func buildMirroredAsset(assetBuilder *assets.AssetBuilder, canonicalURL *url.URL) (*assets.MirroredAsset, error) {
asset, err := assetBuilder.RemapFile(canonicalURL, nil)
if err != nil {
return nil, err
}
mirroredAsset := assets.BuildMirroredAsset(asset)
return mirroredAsset, nil
}
18 changes: 9 additions & 9 deletions pkg/nodemodel/nodeupconfigbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,14 @@ func NewNodeUpConfigBuilder(cluster *kops.Cluster, assetBuilder *assets.AssetBui

baseURL.Path = path.Join(baseURL.Path, "/bin/linux", string(arch), component+".tar")

u, hash, err := assetBuilder.RemapFileAndSHA(baseURL)
asset, err := assetBuilder.RemapFile(baseURL, nil)
if err != nil {
return nil, err
}

image := &nodeup.Image{
Sources: []string{u.String()},
Hash: hash.Hex(),
Sources: []string{asset.DownloadURL.String()},
Hash: asset.SHAValue.Hex(),
}
images[role][arch] = append(images[role][arch], image)
}
Expand All @@ -143,14 +143,14 @@ func NewNodeUpConfigBuilder(cluster *kops.Cluster, assetBuilder *assets.AssetBui

baseURL.Path = path.Join(baseURL.Path, "/images/"+name+"-"+string(arch)+".tar.gz")

u, hash, err := assetBuilder.RemapFileAndSHA(baseURL)
asset, err := assetBuilder.RemapFile(baseURL, nil)
if err != nil {
return nil, err
}

image := &nodeup.Image{
Sources: []string{u.String()},
Hash: hash.Hex(),
Sources: []string{asset.DownloadURL.String()},
Hash: asset.SHAValue.Hex(),
}
images[role][arch] = append(images[role][arch], image)
}
Expand All @@ -166,14 +166,14 @@ func NewNodeUpConfigBuilder(cluster *kops.Cluster, assetBuilder *assets.AssetBui

baseURL.Path = path.Join(baseURL.Path, "/images/"+name+"-"+string(arch)+".tar.gz")

u, hash, err := assetBuilder.RemapFileAndSHA(baseURL)
asset, err := assetBuilder.RemapFile(baseURL, nil)
if err != nil {
return nil, err
}

image := &nodeup.Image{
Sources: []string{u.String()},
Hash: hash.Hex(),
Sources: []string{asset.DownloadURL.String()},
Hash: asset.SHAValue.Hex(),
}
images[role][arch] = append(images[role][arch], image)
}
Expand Down

0 comments on commit 349849c

Please sign in to comment.