github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/getproviders/mock_source.go (about)

     1  package getproviders
     2  
     3  import (
     4  	"archive/zip"
     5  	"context"
     6  	"crypto/sha256"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"os"
    11  
    12  	"github.com/hashicorp/terraform/internal/addrs"
    13  )
    14  
    15  // MockSource is an in-memory-only, statically-configured source intended for
    16  // use only in unit tests of other subsystems that consume provider sources.
    17  //
    18  // The MockSource also tracks calls to it in case a calling test wishes to
    19  // assert that particular calls were made.
    20  //
    21  // This should not be used outside of unit test code.
    22  type MockSource struct {
    23  	packages []PackageMeta
    24  	warnings map[addrs.Provider]Warnings
    25  	calls    [][]interface{}
    26  }
    27  
    28  var _ Source = (*MockSource)(nil)
    29  
    30  // NewMockSource creates and returns a MockSource with the given packages.
    31  //
    32  // The given packages don't necessarily need to refer to objects that actually
    33  // exist on disk or over the network, unless the calling test is planning to
    34  // use (directly or indirectly) the results for further provider installation
    35  // actions.
    36  func NewMockSource(packages []PackageMeta, warns map[addrs.Provider]Warnings) *MockSource {
    37  	return &MockSource{
    38  		packages: packages,
    39  		warnings: warns,
    40  	}
    41  }
    42  
    43  // AvailableVersions returns all of the versions of the given provider that
    44  // are available in the fixed set of packages that were passed to
    45  // NewMockSource when creating the receiving source.
    46  func (s *MockSource) AvailableVersions(ctx context.Context, provider addrs.Provider) (VersionList, Warnings, error) {
    47  	s.calls = append(s.calls, []interface{}{"AvailableVersions", provider})
    48  	var ret VersionList
    49  	for _, pkg := range s.packages {
    50  		if pkg.Provider == provider {
    51  			ret = append(ret, pkg.Version)
    52  		}
    53  	}
    54  	var warns []string
    55  	if s.warnings != nil {
    56  		if warnings, ok := s.warnings[provider]; ok {
    57  			warns = warnings
    58  		}
    59  	}
    60  	if len(ret) == 0 {
    61  		// In this case, we'll behave like a registry that doesn't know about
    62  		// this provider at all, rather than just returning an empty result.
    63  		return nil, warns, ErrRegistryProviderNotKnown{provider}
    64  	}
    65  	ret.Sort()
    66  	return ret, warns, nil
    67  }
    68  
    69  // PackageMeta returns the first package from the list given to NewMockSource
    70  // when creating the receiver that has the given provider, version, and
    71  // target platform.
    72  //
    73  // If none of the packages match, it returns ErrPlatformNotSupported to
    74  // simulate the situation where a provider release isn't available for a
    75  // particular platform.
    76  //
    77  // Note that if the list of packages passed to NewMockSource contains more
    78  // than one with the same provider, version, and target this function will
    79  // always return the first one in the list, which may not match the behavior
    80  // of other sources in an equivalent situation because it's a degenerate case
    81  // with undefined results.
    82  func (s *MockSource) PackageMeta(ctx context.Context, provider addrs.Provider, version Version, target Platform) (PackageMeta, error) {
    83  	s.calls = append(s.calls, []interface{}{"PackageMeta", provider, version, target})
    84  
    85  	for _, pkg := range s.packages {
    86  		if pkg.Provider != provider {
    87  			continue
    88  		}
    89  		if pkg.Version != version {
    90  			// (We're using strict equality rather than precedence here,
    91  			// because this is an exact version specification. The caller
    92  			// should consider precedence when selecting a version in the
    93  			// AvailableVersions response, and pass the exact selected
    94  			// version here.)
    95  			continue
    96  		}
    97  		if pkg.TargetPlatform != target {
    98  			continue
    99  		}
   100  		return pkg, nil
   101  	}
   102  
   103  	// If we fall out here then nothing matched at all, so we'll treat that
   104  	// as "platform not supported" for consistency with RegistrySource.
   105  	return PackageMeta{}, ErrPlatformNotSupported{
   106  		Provider: provider,
   107  		Version:  version,
   108  		Platform: target,
   109  	}
   110  }
   111  
   112  // CallLog returns a list of calls to other methods of the receiever that have
   113  // been called since it was created, in case a calling test wishes to verify
   114  // a particular sequence of operations.
   115  //
   116  // The result is a slice of slices where the first element of each inner slice
   117  // is the name of the method that was called, and then any subsequent elements
   118  // are positional arguments passed to that method.
   119  //
   120  // Callers are forbidden from modifying any objects accessible via the returned
   121  // value.
   122  func (s *MockSource) CallLog() [][]interface{} {
   123  	return s.calls
   124  }
   125  
   126  // FakePackageMeta constructs and returns a PackageMeta that carries the given
   127  // metadata but has fake location information that is likely to fail if
   128  // attempting to install from it.
   129  func FakePackageMeta(provider addrs.Provider, version Version, protocols VersionList, target Platform) PackageMeta {
   130  	return PackageMeta{
   131  		Provider:         provider,
   132  		Version:          version,
   133  		ProtocolVersions: protocols,
   134  		TargetPlatform:   target,
   135  
   136  		// Some fake but somewhat-realistic-looking other metadata. This
   137  		// points nowhere, so will fail if attempting to actually use it.
   138  		Filename: fmt.Sprintf("terraform-provider-%s_%s_%s.zip", provider.Type, version.String(), target.String()),
   139  		Location: PackageHTTPURL(fmt.Sprintf("https://fake.invalid/terraform-provider-%s_%s.zip", provider.Type, version.String())),
   140  	}
   141  }
   142  
   143  // FakeInstallablePackageMeta constructs and returns a PackageMeta that points
   144  // to a temporary archive file that could actually be installed in principle.
   145  //
   146  // Installing it will not produce a working provider though: just a fake file
   147  // posing as an executable. The filename for the executable defaults to the
   148  // standard terraform-provider-NAME_X.Y.Z format, but can be overridden with
   149  // the execFilename argument.
   150  //
   151  // It's the caller's responsibility to call the close callback returned
   152  // alongside the result in order to clean up the temporary file. The caller
   153  // should call the callback even if this function returns an error, because
   154  // some error conditions leave a partially-created file on disk.
   155  func FakeInstallablePackageMeta(provider addrs.Provider, version Version, protocols VersionList, target Platform, execFilename string) (PackageMeta, func(), error) {
   156  	f, err := ioutil.TempFile("", "terraform-getproviders-fake-package-")
   157  	if err != nil {
   158  		return PackageMeta{}, func() {}, err
   159  	}
   160  
   161  	// After this point, all of our return paths should include this as the
   162  	// close callback.
   163  	close := func() {
   164  		f.Close()
   165  		os.Remove(f.Name())
   166  	}
   167  
   168  	if execFilename == "" {
   169  		execFilename = fmt.Sprintf("terraform-provider-%s_%s", provider.Type, version.String())
   170  		if target.OS == "windows" {
   171  			// For a little more (technically unnecessary) realism...
   172  			execFilename += ".exe"
   173  		}
   174  	}
   175  
   176  	zw := zip.NewWriter(f)
   177  	fw, err := zw.Create(execFilename)
   178  	if err != nil {
   179  		return PackageMeta{}, close, fmt.Errorf("failed to add %s to mock zip file: %s", execFilename, err)
   180  	}
   181  	fmt.Fprintf(fw, "This is a fake provider package for %s %s, not a real provider.\n", provider, version)
   182  	err = zw.Close()
   183  	if err != nil {
   184  		return PackageMeta{}, close, fmt.Errorf("failed to close the mock zip file: %s", err)
   185  	}
   186  
   187  	// Compute the SHA256 checksum of the generated file, to allow package
   188  	// authentication code to be exercised.
   189  	f.Seek(0, io.SeekStart)
   190  	h := sha256.New()
   191  	io.Copy(h, f)
   192  	checksum := [32]byte{}
   193  	h.Sum(checksum[:0])
   194  
   195  	meta := PackageMeta{
   196  		Provider:         provider,
   197  		Version:          version,
   198  		ProtocolVersions: protocols,
   199  		TargetPlatform:   target,
   200  
   201  		Location: PackageLocalArchive(f.Name()),
   202  
   203  		// This is a fake filename that mimics what a real registry might
   204  		// indicate as a good filename for this package, in case some caller
   205  		// intends to use it to name a local copy of the temporary file.
   206  		// (At the time of writing, no caller actually does that, but who
   207  		// knows what the future holds?)
   208  		Filename: fmt.Sprintf("terraform-provider-%s_%s_%s.zip", provider.Type, version.String(), target.String()),
   209  
   210  		Authentication: NewArchiveChecksumAuthentication(target, checksum),
   211  	}
   212  	return meta, close, nil
   213  }
   214  
   215  func (s *MockSource) ForDisplay(provider addrs.Provider) string {
   216  	return "mock source"
   217  }