Fix: Global provider schema cache is never used (#719)

Signed-off-by: RLRabinowitz <rlrabinowitz2@gmail.com>
This commit is contained in:
RLRabinowitz 2023-10-16 13:21:33 +03:00 committed by GitHub
parent f661d47a29
commit 03c8f6cebd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 207 additions and 6 deletions

View File

@ -78,8 +78,8 @@ func (p *GRPCProvider) GetProviderSchema() (resp providers.GetProviderSchemaResp
p.mu.Lock()
defer p.mu.Unlock()
// check the global cache if we can
if !p.Addr.IsZero() && resp.ServerCapabilities.GetProviderSchemaOptional {
// check the global cache
if !p.Addr.IsZero() {
if resp, ok := providers.SchemaCache.Get(p.Addr); ok {
return resp
}
@ -141,7 +141,7 @@ func (p *GRPCProvider) GetProviderSchema() (resp providers.GetProviderSchemaResp
}
// set the global cache if we can
if !p.Addr.IsZero() {
if !p.Addr.IsZero() && resp.ServerCapabilities.GetProviderSchemaOptional {
providers.SchemaCache.Set(p.Addr, resp)
}

View File

@ -8,6 +8,8 @@ import (
"fmt"
"testing"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
"github.com/opentofu/opentofu/internal/configs/hcl2shim"
@ -125,6 +127,100 @@ func TestGRPCProvider_GetSchema_GRPCError(t *testing.T) {
checkDiagsHasError(t, resp.Diagnostics)
}
func TestGRPCProvider_GetSchema_GlobalCacheEnabled(t *testing.T) {
ctrl := gomock.NewController(t)
client := mockproto.NewMockProviderClient(ctrl)
// The SchemaCache is global and is saved between test runs
providers.SchemaCache = providers.NewMockSchemaCache()
providerAddr := addrs.Provider{
Namespace: "namespace",
Type: "type",
}
mockedProviderResponse := &proto.Schema{Version: 2, Block: &proto.Schema_Block{}}
client.EXPECT().GetSchema(
gomock.Any(),
gomock.Any(),
gomock.Any(),
).Times(1).Return(&proto.GetProviderSchema_Response{
Provider: mockedProviderResponse,
ServerCapabilities: &proto.GetProviderSchema_ServerCapabilities{GetProviderSchemaOptional: true},
}, nil)
// Run GetProviderTwice, expect GetSchema to be called once
// Re-initialize the provider before each run to avoid usage of the local cache
p := &GRPCProvider{
client: client,
Addr: providerAddr,
}
resp := p.GetProviderSchema()
checkDiags(t, resp.Diagnostics)
if !cmp.Equal(resp.Provider.Version, mockedProviderResponse.Version) {
t.Fatal(cmp.Diff(resp.Provider.Version, mockedProviderResponse.Version))
}
p = &GRPCProvider{
client: client,
Addr: providerAddr,
}
resp = p.GetProviderSchema()
checkDiags(t, resp.Diagnostics)
if !cmp.Equal(resp.Provider.Version, mockedProviderResponse.Version) {
t.Fatal(cmp.Diff(resp.Provider.Version, mockedProviderResponse.Version))
}
}
func TestGRPCProvider_GetSchema_GlobalCacheDisabled(t *testing.T) {
ctrl := gomock.NewController(t)
client := mockproto.NewMockProviderClient(ctrl)
// The SchemaCache is global and is saved between test runs
providers.SchemaCache = providers.NewMockSchemaCache()
providerAddr := addrs.Provider{
Namespace: "namespace",
Type: "type",
}
mockedProviderResponse := &proto.Schema{Version: 2, Block: &proto.Schema_Block{}}
client.EXPECT().GetSchema(
gomock.Any(),
gomock.Any(),
gomock.Any(),
).Times(2).Return(&proto.GetProviderSchema_Response{
Provider: mockedProviderResponse,
ServerCapabilities: &proto.GetProviderSchema_ServerCapabilities{GetProviderSchemaOptional: false},
}, nil)
// Run GetProviderTwice, expect GetSchema to be called once
// Re-initialize the provider before each run to avoid usage of the local cache
p := &GRPCProvider{
client: client,
Addr: providerAddr,
}
resp := p.GetProviderSchema()
checkDiags(t, resp.Diagnostics)
if !cmp.Equal(resp.Provider.Version, mockedProviderResponse.Version) {
t.Fatal(cmp.Diff(resp.Provider.Version, mockedProviderResponse.Version))
}
p = &GRPCProvider{
client: client,
Addr: providerAddr,
}
resp = p.GetProviderSchema()
checkDiags(t, resp.Diagnostics)
if !cmp.Equal(resp.Provider.Version, mockedProviderResponse.Version) {
t.Fatal(cmp.Diff(resp.Provider.Version, mockedProviderResponse.Version))
}
}
// Ensure that provider error diagnostics are returned early.
// Reference: https://github.com/hashicorp/terraform/issues/31047
func TestGRPCProvider_GetSchema_ResponseErrorDiagnostic(t *testing.T) {

View File

@ -78,8 +78,8 @@ func (p *GRPCProvider) GetProviderSchema() (resp providers.GetProviderSchemaResp
p.mu.Lock()
defer p.mu.Unlock()
// check the global cache if we can
if !p.Addr.IsZero() && resp.ServerCapabilities.GetProviderSchemaOptional {
// check the global cache
if !p.Addr.IsZero() {
if resp, ok := providers.SchemaCache.Get(p.Addr); ok {
return resp
}
@ -141,7 +141,7 @@ func (p *GRPCProvider) GetProviderSchema() (resp providers.GetProviderSchemaResp
}
// set the global cache if we can
if !p.Addr.IsZero() {
if !p.Addr.IsZero() && resp.ServerCapabilities.GetProviderSchemaOptional {
providers.SchemaCache.Set(p.Addr, resp)
}

View File

@ -8,6 +8,8 @@ import (
"fmt"
"testing"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
@ -163,6 +165,100 @@ func TestGRPCProvider_GetSchema_ResponseErrorDiagnostic(t *testing.T) {
checkDiagsHasError(t, resp.Diagnostics)
}
func TestGRPCProvider_GetSchema_GlobalCacheEnabled(t *testing.T) {
ctrl := gomock.NewController(t)
client := mockproto.NewMockProviderClient(ctrl)
// The SchemaCache is global and is saved between test runs
providers.SchemaCache = providers.NewMockSchemaCache()
providerAddr := addrs.Provider{
Namespace: "namespace",
Type: "type",
}
mockedProviderResponse := &proto.Schema{Version: 2, Block: &proto.Schema_Block{}}
client.EXPECT().GetProviderSchema(
gomock.Any(),
gomock.Any(),
gomock.Any(),
).Times(1).Return(&proto.GetProviderSchema_Response{
Provider: mockedProviderResponse,
ServerCapabilities: &proto.GetProviderSchema_ServerCapabilities{GetProviderSchemaOptional: true},
}, nil)
// Run GetProviderTwice, expect GetSchema to be called once
// Re-initialize the provider before each run to avoid usage of the local cache
p := &GRPCProvider{
client: client,
Addr: providerAddr,
}
resp := p.GetProviderSchema()
checkDiags(t, resp.Diagnostics)
if !cmp.Equal(resp.Provider.Version, mockedProviderResponse.Version) {
t.Fatal(cmp.Diff(resp.Provider.Version, mockedProviderResponse.Version))
}
p = &GRPCProvider{
client: client,
Addr: providerAddr,
}
resp = p.GetProviderSchema()
checkDiags(t, resp.Diagnostics)
if !cmp.Equal(resp.Provider.Version, mockedProviderResponse.Version) {
t.Fatal(cmp.Diff(resp.Provider.Version, mockedProviderResponse.Version))
}
}
func TestGRPCProvider_GetSchema_GlobalCacheDisabled(t *testing.T) {
ctrl := gomock.NewController(t)
client := mockproto.NewMockProviderClient(ctrl)
// The SchemaCache is global and is saved between test runs
providers.SchemaCache = providers.NewMockSchemaCache()
providerAddr := addrs.Provider{
Namespace: "namespace",
Type: "type",
}
mockedProviderResponse := &proto.Schema{Version: 2, Block: &proto.Schema_Block{}}
client.EXPECT().GetProviderSchema(
gomock.Any(),
gomock.Any(),
gomock.Any(),
).Times(2).Return(&proto.GetProviderSchema_Response{
Provider: mockedProviderResponse,
ServerCapabilities: &proto.GetProviderSchema_ServerCapabilities{GetProviderSchemaOptional: false},
}, nil)
// Run GetProviderTwice, expect GetSchema to be called once
// Re-initialize the provider before each run to avoid usage of the local cache
p := &GRPCProvider{
client: client,
Addr: providerAddr,
}
resp := p.GetProviderSchema()
checkDiags(t, resp.Diagnostics)
if !cmp.Equal(resp.Provider.Version, mockedProviderResponse.Version) {
t.Fatal(cmp.Diff(resp.Provider.Version, mockedProviderResponse.Version))
}
p = &GRPCProvider{
client: client,
Addr: providerAddr,
}
resp = p.GetProviderSchema()
checkDiags(t, resp.Diagnostics)
if !cmp.Equal(resp.Provider.Version, mockedProviderResponse.Version) {
t.Fatal(cmp.Diff(resp.Provider.Version, mockedProviderResponse.Version))
}
}
func TestGRPCProvider_PrepareProviderConfig(t *testing.T) {
client := mockProviderClient(t)
p := &GRPCProvider{

View File

@ -0,0 +1,9 @@
package providers
import "github.com/opentofu/opentofu/internal/addrs"
func NewMockSchemaCache() *schemaCache {
return &schemaCache{
m: make(map[addrs.Provider]ProviderSchema),
}
}