github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/getproviders/filesystem_mirror_source.go (about) 1 package getproviders 2 3 import ( 4 "context" 5 6 "github.com/hashicorp/terraform/internal/addrs" 7 ) 8 9 // FilesystemMirrorSource is a source that reads providers and their metadata 10 // from a directory prefix in the local filesystem. 11 type FilesystemMirrorSource struct { 12 baseDir string 13 14 // allPackages caches the result of scanning the baseDir for all available 15 // packages on the first call that needs package availability information, 16 // to avoid re-scanning the filesystem on subsequent operations. 17 allPackages map[addrs.Provider]PackageMetaList 18 } 19 20 var _ Source = (*FilesystemMirrorSource)(nil) 21 22 // NewFilesystemMirrorSource constructs and returns a new filesystem-based 23 // mirror source with the given base directory. 24 func NewFilesystemMirrorSource(baseDir string) *FilesystemMirrorSource { 25 return &FilesystemMirrorSource{ 26 baseDir: baseDir, 27 } 28 } 29 30 // AvailableVersions scans the directory structure under the source's base 31 // directory for locally-mirrored packages for the given provider, returning 32 // a list of version numbers for the providers it found. 33 func (s *FilesystemMirrorSource) AvailableVersions(ctx context.Context, provider addrs.Provider) (VersionList, Warnings, error) { 34 // s.allPackages is populated if scanAllVersions succeeds 35 err := s.scanAllVersions() 36 if err != nil { 37 return nil, nil, err 38 } 39 40 // There might be multiple packages for a given version in the filesystem, 41 // but the contract here is to return distinct versions so we'll dedupe 42 // them first, then sort them, and then return them. 43 versionsMap := make(map[Version]struct{}) 44 for _, m := range s.allPackages[provider] { 45 versionsMap[m.Version] = struct{}{} 46 } 47 ret := make(VersionList, 0, len(versionsMap)) 48 for v := range versionsMap { 49 ret = append(ret, v) 50 } 51 ret.Sort() 52 return ret, nil, nil 53 } 54 55 // PackageMeta checks to see if the source's base directory contains a 56 // local copy of the distribution package for the given provider version on 57 // the given target, and returns the metadata about it if so. 58 func (s *FilesystemMirrorSource) PackageMeta(ctx context.Context, provider addrs.Provider, version Version, target Platform) (PackageMeta, error) { 59 // s.allPackages is populated if scanAllVersions succeeds 60 err := s.scanAllVersions() 61 if err != nil { 62 return PackageMeta{}, err 63 } 64 65 relevantPkgs := s.allPackages[provider].FilterProviderPlatformExactVersion(provider, target, version) 66 if len(relevantPkgs) == 0 { 67 // This is the local equivalent of a "404 Not Found" when retrieving 68 // a particular version from a registry or network mirror. Because 69 // the caller should've selected a version already found by 70 // AvailableVersions, the only discriminator that should fail here 71 // is the target platform, and so our error result assumes that, 72 // causing the caller to return an error like "This provider version is 73 // not compatible with aros_riscv". 74 return PackageMeta{}, ErrPlatformNotSupported{ 75 Provider: provider, 76 Version: version, 77 Platform: target, 78 } 79 } 80 81 // It's possible that there could be multiple copies of the same package 82 // available in the filesystem, if e.g. there's both a packed and an 83 // unpacked variant. For now we assume that the decision between them 84 // is arbitrary and just take the first one in the result. 85 return relevantPkgs[0], nil 86 } 87 88 // AllAvailablePackages scans the directory structure under the source's base 89 // directory for locally-mirrored packages for all providers, returning a map 90 // of the discovered packages with the fully-qualified provider names as 91 // keys. 92 // 93 // This is not an operation generally supported by all Source implementations, 94 // but the filesystem implementation offers it because we also use the 95 // filesystem mirror source directly to scan our auto-install plugin directory 96 // and in other automatic discovery situations. 97 func (s *FilesystemMirrorSource) AllAvailablePackages() (map[addrs.Provider]PackageMetaList, error) { 98 // s.allPackages is populated if scanAllVersions succeeds 99 err := s.scanAllVersions() 100 return s.allPackages, err 101 } 102 103 func (s *FilesystemMirrorSource) scanAllVersions() error { 104 if s.allPackages != nil { 105 // we're distinguishing nil-ness from emptiness here so we can 106 // recognize when we've scanned the directory without errors, even 107 // if we found nothing during the scan. 108 return nil 109 } 110 111 ret, err := SearchLocalDirectory(s.baseDir) 112 if err != nil { 113 return err 114 } 115 116 // As noted above, we use an explicit empty map so we can distinguish a 117 // successful-but-empty result from a failure on future calls, so we'll 118 // make sure that's what we have before we assign it here. 119 if ret == nil { 120 ret = make(map[addrs.Provider]PackageMetaList) 121 } 122 s.allPackages = ret 123 return nil 124 } 125 126 func (s *FilesystemMirrorSource) ForDisplay(provider addrs.Provider) string { 127 return s.baseDir 128 }