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 }