mirror of
https://github.com/hashicorp/terraform.git
synced 2026-06-09 00:42:48 -04:00
* test: Update`MockSource` to be able to present providers as being installed via HTTP, add `newMockProviderSourceViaHTTP` for direct use in tests. Compare newMockProviderSourceViaHTTP to the existing newMockProviderSource. newMockProviderSource uses FakeInstallablePackageMeta, which creates metadata for providers that says it's being sourced from a local archive. In newMockProviderSourceViaHTTP the metadata reports the provider is downloaded via HTTP. The URL is created using a base URL that should be obtained from a test http server created in the test. * test: Add `newMockProviderSourceUsingTestHttpServer` helper, to abstract away making the http test server. Add test showing it in use. * refactor: Update test helpers to call t.Cleanup internally
306 lines
8.8 KiB
Go
306 lines
8.8 KiB
Go
// Copyright IBM Corp. 2014, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package rpcapi
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/apparentlymart/go-versions/versions"
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/hashicorp/terraform-svchost/disco"
|
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/getproviders"
|
|
"github.com/hashicorp/terraform/internal/rpcapi/terraform1"
|
|
"github.com/hashicorp/terraform/internal/rpcapi/terraform1/packages"
|
|
)
|
|
|
|
func TestPackagesServer_ProviderPackageVersions(t *testing.T) {
|
|
tcs := map[string]struct {
|
|
source string
|
|
expectedVersions []string
|
|
expectedWarnings []string
|
|
sourceFn providerSourceFn
|
|
}{
|
|
"single_version": {
|
|
source: "hashicorp/foo",
|
|
expectedVersions: []string{"0.1.0"},
|
|
sourceFn: func(_ *disco.Disco) getproviders.Source {
|
|
packages := []getproviders.PackageMeta{
|
|
{
|
|
Provider: addrs.MustParseProviderSourceString("hashicorp/foo"),
|
|
Version: versions.MustParseVersion("0.1.0"),
|
|
},
|
|
}
|
|
return getproviders.NewMockSource(packages, nil)
|
|
},
|
|
},
|
|
"multiple_versions": {
|
|
source: "hashicorp/foo",
|
|
expectedVersions: []string{"0.1.0", "0.2.0"},
|
|
sourceFn: func(_ *disco.Disco) getproviders.Source {
|
|
packages := []getproviders.PackageMeta{
|
|
{
|
|
Provider: addrs.MustParseProviderSourceString("hashicorp/foo"),
|
|
Version: versions.MustParseVersion("0.1.0"),
|
|
},
|
|
{
|
|
Provider: addrs.MustParseProviderSourceString("hashicorp/foo"),
|
|
Version: versions.MustParseVersion("0.2.0"),
|
|
},
|
|
}
|
|
return getproviders.NewMockSource(packages, nil)
|
|
},
|
|
},
|
|
"with_warnings": {
|
|
source: "hashicorp/foo",
|
|
expectedVersions: []string{"0.1.0"},
|
|
expectedWarnings: []string{"- warning one", "- warning two"},
|
|
sourceFn: func(_ *disco.Disco) getproviders.Source {
|
|
packages := []getproviders.PackageMeta{
|
|
{
|
|
Provider: addrs.MustParseProviderSourceString("hashicorp/foo"),
|
|
Version: versions.MustParseVersion("0.1.0"),
|
|
},
|
|
}
|
|
warnings := map[addrs.Provider]getproviders.Warnings{
|
|
addrs.MustParseProviderSourceString("hashicorp/foo"): {
|
|
"warning one",
|
|
"warning two",
|
|
},
|
|
}
|
|
return getproviders.NewMockSource(packages, warnings)
|
|
},
|
|
},
|
|
}
|
|
for name, tc := range tcs {
|
|
t.Run(name, func(t *testing.T) {
|
|
service := &packagesServer{
|
|
providerSourceFn: tc.sourceFn,
|
|
}
|
|
|
|
response, err := service.ProviderPackageVersions(context.Background(), &packages.ProviderPackageVersions_Request{
|
|
SourceAddr: tc.source,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(tc.expectedWarnings) > 0 {
|
|
for _, diag := range response.Diagnostics {
|
|
if diag.Severity == terraform1.Diagnostic_WARNING && diag.Summary == "Additional provider information from registry" {
|
|
expected := fmt.Sprintf("The remote registry returned warnings for %s:\n%s", tc.source, strings.Join(tc.expectedWarnings, "\n"))
|
|
if diff := cmp.Diff(expected, diag.Detail); len(diff) > 0 {
|
|
t.Error(diff)
|
|
}
|
|
}
|
|
}
|
|
|
|
// We're expecting only one diagnostic with the warnings.
|
|
if len(response.Diagnostics) > 1 {
|
|
for _, diag := range response.Diagnostics {
|
|
t.Errorf("unexpected diagnostics: %s", diag.Detail)
|
|
}
|
|
return
|
|
}
|
|
} else {
|
|
// Otherwise we're expecting no diagnostics.
|
|
if len(response.Diagnostics) > 0 {
|
|
for _, diag := range response.Diagnostics {
|
|
t.Errorf("unexpected diagnostics: %s", diag.Detail)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
if diff := cmp.Diff(tc.expectedVersions, response.Versions); len(diff) > 0 {
|
|
t.Error(diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPackagesServer_FetchProviderPackage(t *testing.T) {
|
|
providerHashes := providerHashes(t)
|
|
|
|
tcs := map[string]struct {
|
|
// source, version, platforms, and hashes are what we're going to pass
|
|
// in as the request.
|
|
source string
|
|
version string
|
|
platforms []string
|
|
hashes []string
|
|
|
|
// platformLocations, and platformHashes are what we're going to use to
|
|
// create our virtual provider metadata.
|
|
platformLocations map[string]string
|
|
platformHashes map[string][]string
|
|
|
|
// diagnostics are the expected diagnostics for each platform.
|
|
diagnostics map[string][]string
|
|
}{
|
|
"single_version_and_platform": {
|
|
source: "hashicorp/foo",
|
|
version: "0.1.0",
|
|
platforms: []string{"linux_amd64"},
|
|
platformLocations: map[string]string{
|
|
"linux_amd64": "terraform_provider_foo",
|
|
},
|
|
},
|
|
"single_version_multiple_platforms": {
|
|
source: "hashicorp/foo",
|
|
version: "0.1.0",
|
|
platforms: []string{"linux_amd64", "darwin_arm64"},
|
|
platformLocations: map[string]string{
|
|
"linux_amd64": "terraform_provider_foo",
|
|
"darwin_arm64": "terraform_provider_bar",
|
|
},
|
|
},
|
|
"single_version_and_platform_with_hashes": {
|
|
source: "hashicorp/foo",
|
|
version: "0.1.0",
|
|
platforms: []string{"linux_amd64"},
|
|
platformLocations: map[string]string{
|
|
"linux_amd64": "terraform_provider_foo",
|
|
},
|
|
platformHashes: map[string][]string{
|
|
"linux_amd64": {
|
|
"h1:dJTExJ11p+lRE8FAm4HWzTw+uMEyfE6AXXxiOgl/nB0=",
|
|
},
|
|
},
|
|
},
|
|
"single_version_and_platform_with_hashes_clash": {
|
|
source: "hashicorp/foo",
|
|
version: "0.1.0",
|
|
hashes: []string{"h1:Hod4iOH+qbXMtH4orEmCem6F3T+YRPhDSNlXmOIRNuY="},
|
|
platforms: []string{"linux_amd64"},
|
|
platformLocations: map[string]string{
|
|
"linux_amd64": "terraform_provider_foo",
|
|
},
|
|
platformHashes: map[string][]string{
|
|
"linux_amd64": {
|
|
"h1:dJTExJ11p+lRE8FAm4HWzTw+uMEyfE6AXXxiOgl/nB0=",
|
|
},
|
|
},
|
|
diagnostics: map[string][]string{
|
|
"linux_amd64": {
|
|
"the local package for registry.terraform.io/hashicorp/foo 0.1.0 doesn't match any of the checksums previously recorded in the dependency lock file",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for name, tc := range tcs {
|
|
t.Run(name, func(t *testing.T) {
|
|
service := &packagesServer{
|
|
providerSourceFn: func(_ *disco.Disco) getproviders.Source {
|
|
var providers []getproviders.PackageMeta
|
|
for _, p := range tc.platforms {
|
|
platform := parsePlatform(t, p)
|
|
|
|
var authentication getproviders.PackageAuthentication
|
|
if len(tc.platformHashes) > 0 {
|
|
authentication = getproviders.NewPackageHashAuthentication(platform, func() []getproviders.Hash {
|
|
var hashes []getproviders.Hash
|
|
for _, hash := range tc.platformHashes[p] {
|
|
hashes = append(hashes, getproviders.Hash(hash))
|
|
}
|
|
return hashes
|
|
}())
|
|
}
|
|
|
|
providers = append(providers, getproviders.PackageMeta{
|
|
Provider: addrs.MustParseProviderSourceString(tc.source),
|
|
Version: versions.MustParseVersion(tc.version),
|
|
TargetPlatform: platform,
|
|
Location: getproviders.PackageLocalDir(path.Join("testdata", "providers", tc.platformLocations[p])),
|
|
Authentication: authentication,
|
|
})
|
|
}
|
|
|
|
return getproviders.NewMockSource(providers, nil)
|
|
},
|
|
}
|
|
|
|
cacheDir := t.TempDir()
|
|
response, err := service.FetchProviderPackage(context.Background(), &packages.FetchProviderPackage_Request{
|
|
CacheDir: cacheDir,
|
|
SourceAddr: tc.source,
|
|
Version: tc.version,
|
|
Platforms: tc.platforms,
|
|
Hashes: tc.hashes,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(response.Diagnostics) > 0 {
|
|
for _, diag := range response.Diagnostics {
|
|
t.Errorf("unexpected diagnostics: %s", diag.Detail)
|
|
}
|
|
return
|
|
}
|
|
|
|
if len(response.Results) != len(tc.platforms) {
|
|
t.Fatalf("wrong number of results")
|
|
}
|
|
|
|
for ix, platform := range tc.platforms {
|
|
result := response.Results[ix]
|
|
|
|
if tc.diagnostics != nil && len(tc.diagnostics[platform]) > 0 {
|
|
if len(result.Diagnostics) != len(tc.diagnostics[platform]) {
|
|
t.Fatalf("expected %d diagnostics for %s but found %d", len(tc.diagnostics[platform]), platform, len(result.Diagnostics))
|
|
}
|
|
for ix, expected := range tc.diagnostics[platform] {
|
|
if !strings.Contains(result.Diagnostics[ix].Detail, expected) {
|
|
t.Errorf("expected: %s\nactual: %s", expected, result.Diagnostics[ix])
|
|
}
|
|
}
|
|
|
|
return
|
|
} else {
|
|
if len(result.Diagnostics) > 0 {
|
|
for _, diag := range result.Diagnostics {
|
|
t.Errorf("unexpected diagnostics for %s: %s", platform, diag.Detail)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
if diff := cmp.Diff(providerHashes[tc.platformLocations[platform]], result.Provider.Hashes); len(diff) > 0 {
|
|
t.Error(diff)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func providerHashes(t *testing.T) map[string][]string {
|
|
var hashes map[string][]string
|
|
|
|
data, err := os.ReadFile("testdata/providers/hashes.json")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := json.Unmarshal(data, &hashes); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return hashes
|
|
}
|
|
|
|
func parsePlatform(t *testing.T, raw string) getproviders.Platform {
|
|
platform, err := getproviders.ParsePlatform(raw)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return platform
|
|
}
|