mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 17:59:05 -05:00
123 lines
4.4 KiB
Go
123 lines
4.4 KiB
Go
// Copyright (c) The OpenTofu Authors
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright (c) 2023 HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package providercache
|
|
|
|
import (
|
|
"log"
|
|
"path/filepath"
|
|
"sort"
|
|
|
|
"github.com/opentofu/opentofu/internal/addrs"
|
|
"github.com/opentofu/opentofu/internal/getproviders"
|
|
)
|
|
|
|
// NOTE: The functions in this file are only used in testing and are not optimized!
|
|
// The getproviders.SearchLocalDirectory call or allAvailablePackages could be
|
|
// cached if these functions are needed by non-test features.
|
|
|
|
// AllAvailablePackages returns a description of all of the packages already
|
|
// present in the directory. The cache entries are grouped by the provider
|
|
// they relate to and then sorted by version precedence, with highest
|
|
// precedence first.
|
|
//
|
|
// This function will return an empty result both when the directory is empty
|
|
// and when scanning the directory produces an error.
|
|
//
|
|
// The caller is forbidden from modifying the returned data structure in any
|
|
// way, even though the Go type system permits it.
|
|
//
|
|
// Not performant / cached as it's only used in tests!
|
|
func (d *Dir) AllAvailablePackages() map[addrs.Provider][]CachedProvider {
|
|
metaCache, err := d.allAvailablePackages()
|
|
if err != nil {
|
|
log.Printf("[WARN] Failed to scan provider cache directory %s: %s", d.baseDir, err)
|
|
return nil
|
|
}
|
|
|
|
return metaCache
|
|
}
|
|
|
|
// ProviderLatestVersion returns the cache entry for the latest
|
|
// version of the requested provider already available in the cache, or nil if
|
|
// there are no versions of that provider available.
|
|
//
|
|
// Not performant / cached as it's only used in tests!
|
|
func (d *Dir) ProviderLatestVersion(provider addrs.Provider) *CachedProvider {
|
|
metaCache, err := d.allAvailablePackages()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
entries := metaCache[provider]
|
|
if len(entries) == 0 {
|
|
return nil
|
|
}
|
|
|
|
return &entries[0]
|
|
}
|
|
|
|
// Not performant / cached as it's only used in tests!
|
|
func (d *Dir) allAvailablePackages() (map[addrs.Provider][]CachedProvider, error) {
|
|
log.Printf("[TRACE] providercache.fillMetaCache: scanning directory %s", d.baseDir)
|
|
|
|
allData, err := getproviders.SearchLocalDirectory(d.baseDir)
|
|
if err != nil {
|
|
log.Printf("[TRACE] providercache.fillMetaCache: error while scanning directory %s: %s", d.baseDir, err)
|
|
return nil, err
|
|
}
|
|
|
|
// The getproviders package just returns everything it found, but we're
|
|
// interested only in a subset of the results:
|
|
// - those that are for the current platform
|
|
// - those that are in the "unpacked" form, ready to execute
|
|
// ...so we'll filter in these ways while we're constructing our final
|
|
// map to save as the cache.
|
|
//
|
|
// We intentionally always make a non-nil map, even if it might ultimately
|
|
// be empty, because we use that to recognize that the cache is populated.
|
|
data := make(map[addrs.Provider][]CachedProvider)
|
|
|
|
for providerAddr, metas := range allData {
|
|
for _, meta := range metas {
|
|
if meta.TargetPlatform != d.targetPlatform {
|
|
log.Printf("[TRACE] providercache.fillMetaCache: ignoring %s because it is for %s, not %s", meta.Location, meta.TargetPlatform, d.targetPlatform)
|
|
continue
|
|
}
|
|
if _, ok := meta.Location.(getproviders.PackageLocalDir); !ok {
|
|
// PackageLocalDir indicates an unpacked provider package ready
|
|
// to execute.
|
|
log.Printf("[TRACE] providercache.fillMetaCache: ignoring %s because it is not an unpacked directory", meta.Location)
|
|
continue
|
|
}
|
|
|
|
packageDir := filepath.Clean(string(meta.Location.(getproviders.PackageLocalDir)))
|
|
|
|
log.Printf("[TRACE] providercache.fillMetaCache: including %s as a candidate package for %s %s", meta.Location, providerAddr, meta.Version)
|
|
data[providerAddr] = append(data[providerAddr], CachedProvider{
|
|
Provider: providerAddr,
|
|
Version: meta.Version,
|
|
PackageDir: filepath.ToSlash(packageDir),
|
|
})
|
|
}
|
|
}
|
|
|
|
// After we've built our lists per provider, we'll also sort them by
|
|
// version precedence so that the newest available version is always at
|
|
// index zero. If there are two versions that differ only in build metadata
|
|
// then it's undefined but deterministic which one we will select here,
|
|
// because we're preserving the order returned by SearchLocalDirectory
|
|
// in that case..
|
|
for _, entries := range data {
|
|
sort.SliceStable(entries, func(i, j int) bool {
|
|
// We're using GreaterThan rather than LessThan here because we
|
|
// want these in _decreasing_ order of precedence.
|
|
return entries[i].Version.GreaterThan(entries[j].Version)
|
|
})
|
|
}
|
|
|
|
return data, nil
|
|
}
|