github.com/opentofu/opentofu@v1.7.1/internal/getproviders/registry_source_test.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package getproviders 7 8 import ( 9 "context" 10 "fmt" 11 "regexp" 12 "strings" 13 "testing" 14 15 tfaddr "github.com/opentofu/registry-address" 16 17 "github.com/apparentlymart/go-versions/versions" 18 "github.com/google/go-cmp/cmp" 19 svchost "github.com/hashicorp/terraform-svchost" 20 21 "github.com/opentofu/opentofu/internal/addrs" 22 ) 23 24 func TestSourceAvailableVersions(t *testing.T) { 25 source, baseURL, close := testRegistrySource(t) 26 defer close() 27 28 tests := []struct { 29 provider string 30 wantVersions []string 31 wantErr string 32 }{ 33 // These test cases are relying on behaviors of the fake provider 34 // registry server implemented in registry_client_test.go. 35 { 36 "example.com/awesomesauce/happycloud", 37 []string{"0.1.0", "1.0.0", "1.2.0", "2.0.0"}, 38 ``, 39 }, 40 { 41 "example.com/weaksauce/no-versions", 42 nil, 43 ``, // having no versions is not an error, it's just odd 44 }, 45 { 46 "example.com/nonexist/nonexist", 47 nil, 48 `provider registry example.com does not have a provider named example.com/nonexist/nonexist`, 49 }, 50 { 51 "not.example.com/foo/bar", 52 nil, 53 `host not.example.com does not offer a OpenTofu provider registry`, 54 }, 55 { 56 "too-new.example.com/foo/bar", 57 nil, 58 `host too-new.example.com does not support the provider registry protocol required by this OpenTofu version, but may be compatible with a different OpenTofu version`, 59 }, 60 { 61 "fails.example.com/foo/bar", 62 nil, 63 `could not query provider registry for fails.example.com/foo/bar: the request failed after 2 attempts, please try again later: Get "` + baseURL + `/fails-immediately/foo/bar/versions": EOF`, 64 }, 65 } 66 67 for _, test := range tests { 68 t.Run(test.provider, func(t *testing.T) { 69 provider := addrs.MustParseProviderSourceString(test.provider) 70 gotVersions, _, err := source.AvailableVersions(context.Background(), provider) 71 72 if err != nil { 73 if test.wantErr == "" { 74 t.Fatalf("wrong error\ngot: %s\nwant: <nil>", err.Error()) 75 } 76 if got, want := err.Error(), test.wantErr; got != want { 77 t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) 78 } 79 return 80 } 81 82 if test.wantErr != "" { 83 t.Fatalf("wrong error\ngot: <nil>\nwant: %s", test.wantErr) 84 } 85 86 var gotVersionsStr []string 87 if gotVersions != nil { 88 gotVersionsStr = make([]string, len(gotVersions)) 89 for i, v := range gotVersions { 90 gotVersionsStr[i] = v.String() 91 } 92 } 93 94 if diff := cmp.Diff(test.wantVersions, gotVersionsStr); diff != "" { 95 t.Errorf("wrong result\n%s", diff) 96 } 97 }) 98 } 99 } 100 101 func TestSourceAvailableVersions_warnings(t *testing.T) { 102 source, _, close := testRegistrySource(t) 103 defer close() 104 105 provider := addrs.MustParseProviderSourceString("example.com/weaksauce/no-versions") 106 _, warnings, err := source.AvailableVersions(context.Background(), provider) 107 if err != nil { 108 t.Fatalf("unexpected error: %s", err.Error()) 109 } 110 111 if len(warnings) != 1 { 112 t.Fatalf("wrong number of warnings. Expected 1, got %d", len(warnings)) 113 } 114 115 } 116 117 func TestSourcePackageMeta(t *testing.T) { 118 source, baseURL, close := testRegistrySource(t) 119 defer close() 120 121 validMeta := PackageMeta{ 122 Provider: addrs.NewProvider( 123 svchost.Hostname("example.com"), "awesomesauce", "happycloud", 124 ), 125 Version: versions.MustParseVersion("1.2.0"), 126 ProtocolVersions: VersionList{versions.MustParseVersion("5.0.0")}, 127 TargetPlatform: Platform{"linux", "amd64"}, 128 Filename: "happycloud_1.2.0.zip", 129 Location: PackageHTTPURL(baseURL + "/pkg/awesomesauce/happycloud_1.2.0.zip"), 130 } 131 validMeta.Authentication = PackageAuthenticationAll( 132 NewMatchingChecksumAuthentication( 133 []byte("000000000000000000000000000000000000000000000000000000000000f00d happycloud_1.2.0.zip\n000000000000000000000000000000000000000000000000000000000000face happycloud_1.2.0_face.zip\n"), 134 "happycloud_1.2.0.zip", 135 [32]byte{30: 0xf0, 31: 0x0d}, 136 ), 137 NewArchiveChecksumAuthentication(Platform{"linux", "amd64"}, [32]byte{30: 0xf0, 31: 0x0d}), 138 NewSignatureAuthentication( 139 validMeta, 140 []byte("000000000000000000000000000000000000000000000000000000000000f00d happycloud_1.2.0.zip\n000000000000000000000000000000000000000000000000000000000000face happycloud_1.2.0_face.zip\n"), 141 []byte("GPG signature"), 142 []SigningKey{ 143 {ASCIIArmor: TestingPublicKey}, 144 }, 145 &tfaddr.Provider{Hostname: "example.com", Namespace: "awesomesauce", Type: "happycloud"}, 146 ), 147 ) 148 149 tests := []struct { 150 provider string 151 version string 152 os, arch string 153 want PackageMeta 154 wantHashes []Hash 155 wantErr string 156 }{ 157 // These test cases are relying on behaviors of the fake provider 158 // registry server implemented in registry_client_test.go. 159 { 160 "example.com/awesomesauce/happycloud", 161 "1.2.0", 162 "linux", "amd64", 163 validMeta, 164 []Hash{ 165 "zh:000000000000000000000000000000000000000000000000000000000000f00d", 166 "zh:000000000000000000000000000000000000000000000000000000000000face", 167 }, 168 ``, 169 }, 170 { 171 "example.com/awesomesauce/happycloud", 172 "1.2.0", 173 "nonexist", "amd64", 174 PackageMeta{}, 175 nil, 176 `provider example.com/awesomesauce/happycloud 1.2.0 is not available for nonexist_amd64`, 177 }, 178 { 179 "not.example.com/awesomesauce/happycloud", 180 "1.2.0", 181 "linux", "amd64", 182 PackageMeta{}, 183 nil, 184 `host not.example.com does not offer a OpenTofu provider registry`, 185 }, 186 { 187 "too-new.example.com/awesomesauce/happycloud", 188 "1.2.0", 189 "linux", "amd64", 190 PackageMeta{}, 191 nil, 192 `host too-new.example.com does not support the provider registry protocol required by this OpenTofu version, but may be compatible with a different OpenTofu version`, 193 }, 194 { 195 "fails.example.com/awesomesauce/happycloud", 196 "1.2.0", 197 "linux", "amd64", 198 PackageMeta{}, 199 nil, 200 `could not query provider registry for fails.example.com/awesomesauce/happycloud: the request failed after 2 attempts, please try again later: Get "http://placeholder-origin/fails-immediately/awesomesauce/happycloud/1.2.0/download/linux/amd64": EOF`, 201 }, 202 } 203 204 // Sometimes error messages contain specific HTTP endpoint URLs, but 205 // since our test server is on a random port we'd not be able to 206 // consistently match those. Instead, we'll normalize the URLs. 207 urlPattern := regexp.MustCompile(`http://[^/]+/`) 208 209 cmpOpts := cmp.Comparer(Version.Same) 210 211 for _, test := range tests { 212 t.Run(fmt.Sprintf("%s for %s_%s", test.provider, test.os, test.arch), func(t *testing.T) { 213 // TEMP: We don't yet have a function for parsing provider 214 // source addresses so we'll just fake it in here for now. 215 parts := strings.Split(test.provider, "/") 216 providerAddr := addrs.Provider{ 217 Hostname: svchost.Hostname(parts[0]), 218 Namespace: parts[1], 219 Type: parts[2], 220 } 221 222 version := versions.MustParseVersion(test.version) 223 224 got, err := source.PackageMeta(context.Background(), providerAddr, version, Platform{test.os, test.arch}) 225 226 if err != nil { 227 if test.wantErr == "" { 228 t.Fatalf("wrong error\ngot: %s\nwant: <nil>", err.Error()) 229 } 230 gotErr := urlPattern.ReplaceAllLiteralString(err.Error(), "http://placeholder-origin/") 231 if got, want := gotErr, test.wantErr; got != want { 232 t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) 233 } 234 return 235 } 236 237 if test.wantErr != "" { 238 t.Fatalf("wrong error\ngot: <nil>\nwant: %s", test.wantErr) 239 } 240 241 if diff := cmp.Diff(got, test.want, cmpOpts); diff != "" { 242 t.Errorf("wrong result\n%s", diff) 243 } 244 if diff := cmp.Diff(test.wantHashes, got.AcceptableHashes()); diff != "" { 245 t.Errorf("wrong AcceptableHashes result\n%s", diff) 246 } 247 }) 248 } 249 250 }