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  }