mirror of https://github.com/pulumi/pulumi.git
671 lines
22 KiB
Go
671 lines
22 KiB
Go
// Copyright 2024-2024, Pulumi Corporation.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package lifecycletest
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/blang/semver"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
. "github.com/pulumi/pulumi/pkg/v3/engine" //nolint:revive
|
|
lt "github.com/pulumi/pulumi/pkg/v3/engine/lifecycletest/framework"
|
|
"github.com/pulumi/pulumi/pkg/v3/resource/deploy/deploytest"
|
|
"github.com/pulumi/pulumi/pkg/v3/resource/deploy/providers"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/promise"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/config"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
|
|
pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go"
|
|
)
|
|
|
|
// TestPackageRef tests we can request a package ref from the engine and then use that, instead of Version,
|
|
// PackageDownloadURL etc.
|
|
func TestPackageRef(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
loaders := []*deploytest.ProviderLoader{
|
|
deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
|
|
return &deploytest.Provider{
|
|
CreateF: func(_ context.Context, req plugin.CreateRequest) (plugin.CreateResponse, error) {
|
|
return plugin.CreateResponse{
|
|
ID: "0",
|
|
Properties: req.Properties,
|
|
Status: resource.StatusOK,
|
|
}, nil
|
|
},
|
|
}, nil
|
|
}),
|
|
deploytest.NewProviderLoader("pkgA", semver.MustParse("2.0.0"), func() (plugin.Provider, error) {
|
|
return &deploytest.Provider{
|
|
CreateF: func(_ context.Context, req plugin.CreateRequest) (plugin.CreateResponse, error) {
|
|
return plugin.CreateResponse{
|
|
ID: "1",
|
|
Properties: req.Properties,
|
|
Status: resource.StatusOK,
|
|
}, nil
|
|
},
|
|
}, nil
|
|
}),
|
|
}
|
|
|
|
programF := deploytest.NewLanguageRuntimeF(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
|
pkg1Ref, err := monitor.RegisterPackage("pkgA", "1.0.0", "", nil, nil)
|
|
require.NoError(t, err)
|
|
pkg2Ref, err := monitor.RegisterPackage("pkgA", "2.0.0", "", nil, nil)
|
|
require.NoError(t, err)
|
|
|
|
// If we register the "same" provider in parallel, we should get the same ref.
|
|
promises := []*promise.Promise[string]{}
|
|
for i := 0; i < 100; i++ {
|
|
var pcs promise.CompletionSource[string]
|
|
promises = append(promises, pcs.Promise())
|
|
go func() {
|
|
ref, err := monitor.RegisterPackage("pkgB", "1.0.0", "downloadUrl", nil, nil)
|
|
require.NoError(t, err)
|
|
pcs.MustFulfill(ref)
|
|
}()
|
|
}
|
|
ctx := context.Background()
|
|
expected, err := promises[0].Result(ctx)
|
|
require.NoError(t, err)
|
|
for i := 1; i < 100; i++ {
|
|
got, err := promises[i].Result(ctx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, expected, got)
|
|
}
|
|
|
|
// Now register some resources using the UUID for the provider, instead of a normal provider ref.
|
|
resp, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
|
|
PackageRef: pkg1Ref,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, resource.ID("0"), resp.ID)
|
|
|
|
resp, err = monitor.RegisterResource("pkgA:m:typA", "resB", true, deploytest.ResourceOptions{
|
|
PackageRef: pkg2Ref,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, resource.ID("1"), resp.ID)
|
|
|
|
return err
|
|
})
|
|
|
|
hostF := deploytest.NewPluginHostF(nil, nil, programF, loaders...)
|
|
p := <.TestPlan{
|
|
Options: lt.TestUpdateOptions{T: t, HostF: hostF},
|
|
}
|
|
|
|
snap, err := lt.TestOp(Update).
|
|
RunStep(p.GetProject(), p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil, "0")
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, snap)
|
|
|
|
assert.Len(t, snap.Resources, 4)
|
|
assert.Equal(t, string(snap.Resources[0].URN)+"::"+string(snap.Resources[0].ID), snap.Resources[1].Provider)
|
|
assert.Equal(t, string(snap.Resources[2].URN)+"::"+string(snap.Resources[2].ID), snap.Resources[3].Provider)
|
|
}
|
|
|
|
// TestReplacementParameterizedProvider tests that we can register a parameterized provider that replaces a base
|
|
// provider.
|
|
func TestReplacementParameterizedProvider(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
loadCount := 0
|
|
loaders := []*deploytest.ProviderLoader{
|
|
deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
|
|
loadCount++
|
|
|
|
var param string
|
|
|
|
return &deploytest.Provider{
|
|
ParameterizeF: func(
|
|
ctx context.Context, req plugin.ParameterizeRequest,
|
|
) (plugin.ParameterizeResponse, error) {
|
|
value := req.Parameters.(*plugin.ParameterizeValue)
|
|
|
|
param = string(value.Value)
|
|
|
|
return plugin.ParameterizeResponse{
|
|
Name: value.Name,
|
|
Version: value.Version,
|
|
}, nil
|
|
},
|
|
CreateF: func(_ context.Context, req plugin.CreateRequest) (plugin.CreateResponse, error) {
|
|
if req.URN.Type() == "pkgExt:m:typA" {
|
|
assert.Equal(t, "replacement", param)
|
|
}
|
|
|
|
return plugin.CreateResponse{
|
|
ID: "id",
|
|
Properties: req.Properties,
|
|
Status: resource.StatusOK,
|
|
}, nil
|
|
},
|
|
InvokeF: func(_ context.Context, req plugin.InvokeRequest) (plugin.InvokeResponse, error) {
|
|
assert.Equal(t, "pkgExt:index:func", req.Tok.String())
|
|
assert.Equal(t, resource.NewStringProperty("in"), req.Args["input"])
|
|
|
|
return plugin.InvokeResponse{
|
|
Properties: resource.PropertyMap{
|
|
"output": resource.NewStringProperty("in " + param),
|
|
},
|
|
}, nil
|
|
},
|
|
ReadF: func(_ context.Context, req plugin.ReadRequest) (plugin.ReadResponse, error) {
|
|
if param == "" {
|
|
assert.Equal(t, tokens.Type("pkgA:m:typA"), req.URN.Type())
|
|
} else {
|
|
assert.Equal(t, tokens.Type("pkgExt:m:typA"), req.URN.Type())
|
|
}
|
|
|
|
return plugin.ReadResponse{
|
|
ReadResult: plugin.ReadResult{
|
|
ID: req.ID,
|
|
Inputs: req.Inputs,
|
|
Outputs: req.State,
|
|
},
|
|
Status: resource.StatusOK,
|
|
}, nil
|
|
},
|
|
CallF: func(_ context.Context, req plugin.CallRequest, _ *deploytest.ResourceMonitor) (plugin.CallResponse, error) {
|
|
assert.Equal(t, "pkgExt:index:call", req.Tok.String())
|
|
assert.Equal(t, resource.NewStringProperty("in"), req.Args["input"])
|
|
assert.Equal(t, map[resource.PropertyKey][]resource.URN{
|
|
"input": {"urn:pulumi:stack::m::typA::resB"},
|
|
}, req.Options.ArgDependencies)
|
|
|
|
return plugin.CallResponse{
|
|
Return: resource.PropertyMap{
|
|
"output": resource.NewStringProperty("output"),
|
|
},
|
|
ReturnDependencies: map[resource.PropertyKey][]resource.URN{
|
|
"output": {"urn:pulumi:stack::m::typA::resB"},
|
|
},
|
|
Failures: nil,
|
|
}, nil
|
|
},
|
|
ConstructF: func(
|
|
_ context.Context,
|
|
req plugin.ConstructRequest,
|
|
_ *deploytest.ResourceMonitor,
|
|
) (plugin.ConstructResponse, error) {
|
|
if param == "" {
|
|
assert.Equal(t, tokens.Type("pkgA:m:typA"), req.Type)
|
|
assert.Equal(t, "mlcA", req.Name)
|
|
} else {
|
|
assert.Equal(t, tokens.Type("pkgExt:m:typA"), req.Type)
|
|
assert.Equal(t, "mlcB", req.Name)
|
|
}
|
|
|
|
return plugin.ConstructResponse{
|
|
URN: resource.NewURN("", "", "", req.Type, req.Name),
|
|
Outputs: resource.PropertyMap{
|
|
"output": resource.NewStringProperty("output"),
|
|
},
|
|
OutputDependencies: map[resource.PropertyKey][]resource.URN{
|
|
"output": {"urn:pulumi:stack::m::typA::resB"},
|
|
},
|
|
}, nil
|
|
},
|
|
}, nil
|
|
}),
|
|
}
|
|
|
|
programF := deploytest.NewLanguageRuntimeF(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
|
pkgRef, err := monitor.RegisterPackage("pkgA", "1.0.0", "", nil, nil)
|
|
require.NoError(t, err)
|
|
|
|
// Register a resource using that base provider
|
|
_, err = monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
|
|
PackageRef: pkgRef,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Register a multi-language component with the base provider
|
|
mlcA, err := monitor.RegisterResource("pkgA:m:typA", "mlcA", true, deploytest.ResourceOptions{
|
|
PackageRef: pkgRef,
|
|
Remote: true,
|
|
})
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "mlcA", mlcA.URN.Name())
|
|
assert.Equal(t, resource.PropertyMap{
|
|
"output": resource.NewStringProperty("output"),
|
|
}, mlcA.Outputs)
|
|
assert.Equal(t, map[resource.PropertyKey][]resource.URN{
|
|
"output": {"urn:pulumi:stack::m::typA::resB"},
|
|
}, mlcA.Dependencies)
|
|
|
|
// Now register a replacement provider
|
|
extRef, err := monitor.RegisterPackage("pkgA", "1.0.0", "", nil, &pulumirpc.Parameterization{
|
|
Name: "pkgExt",
|
|
Version: "0.5.0",
|
|
Value: []byte("replacement"),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Test registering a resource with the replacement provider
|
|
_, err = monitor.RegisterResource("pkgExt:m:typA", "resB", true, deploytest.ResourceOptions{
|
|
PackageRef: extRef,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Register a multi-language component with the replacement provider
|
|
mlcB, err := monitor.RegisterResource("pkgExt:m:typA", "mlcB", true, deploytest.ResourceOptions{
|
|
PackageRef: extRef,
|
|
Remote: true,
|
|
})
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "mlcB", mlcB.URN.Name())
|
|
assert.Equal(t, resource.PropertyMap{
|
|
"output": resource.NewStringProperty("output"),
|
|
}, mlcB.Outputs)
|
|
assert.Equal(t, map[resource.PropertyKey][]resource.URN{
|
|
"output": {"urn:pulumi:stack::m::typA::resB"},
|
|
}, mlcB.Dependencies)
|
|
|
|
// Test invoking a function on the replacement provider
|
|
result, _, err := monitor.Invoke("pkgExt:index:func", resource.PropertyMap{
|
|
"input": resource.NewStringProperty("in"),
|
|
}, "", "", extRef)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, resource.PropertyMap{
|
|
"output": resource.NewStringProperty("in replacement"),
|
|
}, result)
|
|
|
|
// Test reading a resource on the replacement provider
|
|
_, _, err = monitor.ReadResource("pkgExt:m:typA", "resC", "id", "", resource.PropertyMap{}, "", "", "", extRef)
|
|
require.NoError(t, err)
|
|
|
|
// Test calling a function on the replacement provider
|
|
callOuts, callDeps, callFailures, err := monitor.Call(
|
|
"pkgExt:index:call",
|
|
resource.PropertyMap{
|
|
"input": resource.NewStringProperty("in"),
|
|
},
|
|
map[resource.PropertyKey][]resource.URN{
|
|
"input": {"urn:pulumi:stack::m::typA::resB"},
|
|
},
|
|
"", /*provider*/
|
|
"", /*version*/
|
|
extRef,
|
|
)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, resource.PropertyMap{
|
|
"output": resource.NewStringProperty("output"),
|
|
}, callOuts)
|
|
assert.Equal(t, map[resource.PropertyKey][]resource.URN{
|
|
"output": {"urn:pulumi:stack::m::typA::resB"},
|
|
}, callDeps)
|
|
assert.Nil(t, callFailures)
|
|
|
|
// Test that we can create an explicit replacement provider and can use it
|
|
prov, err := monitor.RegisterResource("pulumi:providers:pkgExt", "provider", true, deploytest.ResourceOptions{
|
|
PackageRef: extRef,
|
|
})
|
|
assert.NoError(t, err)
|
|
provID := prov.ID
|
|
|
|
if provID == "" {
|
|
provID = providers.UnknownID
|
|
}
|
|
provRef, err := providers.NewReference(prov.URN, provID)
|
|
assert.NoError(t, err)
|
|
|
|
_, err = monitor.RegisterResource("pkgExt:m:typA", "resD", true, deploytest.ResourceOptions{
|
|
Provider: provRef.String(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
return err
|
|
})
|
|
|
|
hostF := deploytest.NewPluginHostF(nil, nil, programF, loaders...)
|
|
p := <.TestPlan{
|
|
Options: lt.TestUpdateOptions{T: t, HostF: hostF},
|
|
}
|
|
|
|
snap, err := lt.TestOp(Update).RunStep(
|
|
p.GetProject(), p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil, "up")
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, snap)
|
|
assert.Len(t, snap.Resources, 7)
|
|
|
|
// Check that we loaded the provider thrice
|
|
assert.Equal(t, 3, loadCount)
|
|
|
|
// Check the state of the parameterized provider is what we expect
|
|
prov := snap.Resources[2]
|
|
assert.Equal(t, tokens.Type("pulumi:providers:pkgExt"), prov.Type)
|
|
assert.Equal(t, "default_0_5_0", prov.URN.Name())
|
|
assert.Equal(t, resource.NewPropertyMapFromMap(map[string]any{
|
|
"version": "0.5.0",
|
|
"__internal": map[string]any{
|
|
"name": "pkgA",
|
|
"version": "1.0.0",
|
|
"parameterization": "cmVwbGFjZW1lbnQ=",
|
|
},
|
|
}), prov.Inputs)
|
|
|
|
snap, err = lt.TestOp(Refresh).RunStep(
|
|
p.GetProject(), p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil, "refresh")
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, snap)
|
|
assert.Len(t, snap.Resources, 7)
|
|
|
|
snap, err = lt.TestOp(Destroy).RunStep(
|
|
p.GetProject(), p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil, "destroy")
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, snap)
|
|
assert.Len(t, snap.Resources, 0)
|
|
}
|
|
|
|
// TestReplacementParameterizedProviderConfig tests that we can register a parameterized provider that uses config keys
|
|
// like "name" without clashing against the internal state the engine tracks for parameterization. c.f.
|
|
// https://github.com/pulumi/pulumi/issues/16757.
|
|
func TestReplacementParameterizedProviderConfig(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
loaders := []*deploytest.ProviderLoader{
|
|
deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
|
|
var param string
|
|
return &deploytest.Provider{
|
|
ConfigureF: func(_ context.Context, req plugin.ConfigureRequest) (plugin.ConfigureResponse, error) {
|
|
// Ensure that the provider configuration is what we expect.
|
|
var expected resource.PropertyMap
|
|
if param == "replacement" {
|
|
expected = resource.NewPropertyMapFromMap(map[string]any{
|
|
"version": "0.5.0",
|
|
"name": "testingExt",
|
|
})
|
|
} else {
|
|
expected = resource.NewPropertyMapFromMap(map[string]any{
|
|
"version": "1.0.0",
|
|
"name": "testingBase",
|
|
})
|
|
}
|
|
|
|
if !req.Inputs.DeepEquals(expected) {
|
|
return plugin.ConfigureResponse{},
|
|
fmt.Errorf("expected provider configuration to be %v, got %v", expected, req.Inputs)
|
|
}
|
|
return plugin.ConfigureResponse{}, nil
|
|
},
|
|
ParameterizeF: func(
|
|
ctx context.Context, req plugin.ParameterizeRequest,
|
|
) (plugin.ParameterizeResponse, error) {
|
|
value := req.Parameters.(*plugin.ParameterizeValue)
|
|
|
|
param = string(value.Value)
|
|
|
|
return plugin.ParameterizeResponse{
|
|
Name: value.Name,
|
|
Version: value.Version,
|
|
}, nil
|
|
},
|
|
CreateF: func(_ context.Context, req plugin.CreateRequest) (plugin.CreateResponse, error) {
|
|
if req.URN.Type() == "pkgExt:m:typA" {
|
|
assert.Equal(t, "replacement", param)
|
|
}
|
|
|
|
return plugin.CreateResponse{
|
|
ID: "id",
|
|
Properties: req.Properties,
|
|
Status: resource.StatusOK,
|
|
}, nil
|
|
},
|
|
}, nil
|
|
}),
|
|
}
|
|
|
|
programF := deploytest.NewLanguageRuntimeF(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
|
pkgRef, err := monitor.RegisterPackage("pkgA", "1.0.0", "http://example.com", nil, nil)
|
|
require.NoError(t, err)
|
|
|
|
// Register a resource using that base provider
|
|
_, err = monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
|
|
PackageRef: pkgRef,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Now register a replacement provider
|
|
extRef, err := monitor.RegisterPackage("pkgA", "1.0.0", "", nil, &pulumirpc.Parameterization{
|
|
Name: "pkgExt",
|
|
Version: "0.5.0",
|
|
Value: []byte("replacement"),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
_, err = monitor.RegisterResource("pkgExt:m:typA", "resB", true, deploytest.ResourceOptions{
|
|
PackageRef: extRef,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
return err
|
|
})
|
|
|
|
hostF := deploytest.NewPluginHostF(nil, nil, programF, loaders...)
|
|
p := <.TestPlan{
|
|
Options: lt.TestUpdateOptions{T: t, HostF: hostF},
|
|
Config: config.Map{
|
|
config.MustParseKey("pkgA:name"): config.NewValue("testingBase"),
|
|
config.MustParseKey("pkgExt:name"): config.NewValue("testingExt"),
|
|
},
|
|
}
|
|
|
|
snap, err := lt.TestOp(Update).RunStep(
|
|
p.GetProject(), p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil, "up")
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, snap)
|
|
assert.Len(t, snap.Resources, 4)
|
|
|
|
// Check the state of the parameterized provider is what we expect
|
|
prov := snap.Resources[2]
|
|
assert.Equal(t, tokens.Type("pulumi:providers:pkgExt"), prov.Type)
|
|
assert.Equal(t, resource.NewPropertyMapFromMap(map[string]any{
|
|
"version": "0.5.0",
|
|
"name": "testingExt",
|
|
"__internal": map[string]any{
|
|
"name": "pkgA",
|
|
"version": "1.0.0",
|
|
"parameterization": "cmVwbGFjZW1lbnQ=",
|
|
},
|
|
}), prov.Inputs)
|
|
|
|
snap, err = lt.TestOp(Refresh).RunStep(
|
|
p.GetProject(), p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil, "refresh")
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, snap)
|
|
assert.Len(t, snap.Resources, 4)
|
|
|
|
snap, err = lt.TestOp(Destroy).RunStep(
|
|
p.GetProject(), p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil, "destroy")
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, snap)
|
|
assert.Len(t, snap.Resources, 0)
|
|
}
|
|
|
|
// TestReplacementParameterizedProviderImport tests that we can register a parameterized provider that replaces a base
|
|
// provider and use it to import resources. Test with both default and explicit providers
|
|
func TestReplacementParameterizedProviderImport(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
loadCount := 0
|
|
loaders := []*deploytest.ProviderLoader{
|
|
deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
|
|
loadCount++
|
|
|
|
var param string
|
|
|
|
return &deploytest.Provider{
|
|
ParameterizeF: func(
|
|
ctx context.Context, req plugin.ParameterizeRequest,
|
|
) (plugin.ParameterizeResponse, error) {
|
|
value := req.Parameters.(*plugin.ParameterizeValue)
|
|
|
|
param = string(value.Value)
|
|
|
|
return plugin.ParameterizeResponse{
|
|
Name: value.Name,
|
|
Version: value.Version,
|
|
}, nil
|
|
},
|
|
ReadF: func(_ context.Context, req plugin.ReadRequest) (plugin.ReadResponse, error) {
|
|
if param == "" {
|
|
assert.Equal(t, tokens.Type("pkgA:m:typA"), req.URN.Type())
|
|
assert.Equal(t, resource.ID("idA"), req.ID)
|
|
} else {
|
|
assert.Equal(t, tokens.Type("pkgExt:m:typA"), req.URN.Type())
|
|
assert.Equal(t, resource.ID("idB"), req.ID)
|
|
}
|
|
|
|
return plugin.ReadResponse{
|
|
ReadResult: plugin.ReadResult{
|
|
ID: req.ID,
|
|
Inputs: resource.PropertyMap{
|
|
"input": resource.NewStringProperty("input"),
|
|
},
|
|
Outputs: resource.PropertyMap{
|
|
"output": resource.NewStringProperty("output"),
|
|
},
|
|
},
|
|
Status: resource.StatusOK,
|
|
}, nil
|
|
},
|
|
}, nil
|
|
}),
|
|
}
|
|
|
|
programF := deploytest.NewLanguageRuntimeF(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
|
pkgRef, err := monitor.RegisterPackage("pkgA", "1.0.0", "", nil, nil)
|
|
require.NoError(t, err)
|
|
|
|
// Import a resource using that base provider
|
|
_, err = monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
|
|
PackageRef: pkgRef,
|
|
ImportID: "idA",
|
|
Inputs: resource.PropertyMap{
|
|
"input": resource.NewStringProperty("input"),
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Now register a replacement provider
|
|
extRef, err := monitor.RegisterPackage("pkgA", "1.0.0", "", nil, &pulumirpc.Parameterization{
|
|
Name: "pkgExt",
|
|
Version: "0.5.0",
|
|
Value: []byte("replacement"),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Test importing a resource with the replacement provider
|
|
_, err = monitor.RegisterResource("pkgExt:m:typA", "resB", true, deploytest.ResourceOptions{
|
|
PackageRef: extRef,
|
|
ImportID: "idB",
|
|
Inputs: resource.PropertyMap{
|
|
"input": resource.NewStringProperty("input"),
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Test that we can create an explicit replacement provider and can use it
|
|
prov, err := monitor.RegisterResource("pulumi:providers:pkgExt", "provider", true, deploytest.ResourceOptions{
|
|
PackageRef: extRef,
|
|
})
|
|
assert.NoError(t, err)
|
|
provID := prov.ID
|
|
|
|
if provID == "" {
|
|
provID = providers.UnknownID
|
|
}
|
|
provRef, err := providers.NewReference(prov.URN, provID)
|
|
assert.NoError(t, err)
|
|
|
|
_, err = monitor.RegisterResource("pkgExt:m:typA", "resC", true, deploytest.ResourceOptions{
|
|
Provider: provRef.String(),
|
|
ImportID: "idB",
|
|
Inputs: resource.PropertyMap{
|
|
"input": resource.NewStringProperty("input"),
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
return err
|
|
})
|
|
|
|
hostF := deploytest.NewPluginHostF(nil, nil, programF, loaders...)
|
|
p := <.TestPlan{
|
|
Options: lt.TestUpdateOptions{T: t, HostF: hostF},
|
|
}
|
|
|
|
snap, err := lt.TestOp(Update).RunStep(
|
|
p.GetProject(), p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil, "up")
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, snap)
|
|
assert.Len(t, snap.Resources, 6)
|
|
|
|
// Check that we loaded the provider thrice
|
|
assert.Equal(t, 3, loadCount)
|
|
|
|
// Check the state of the parameterized provider is what we expect
|
|
prov := snap.Resources[2]
|
|
assert.Equal(t, tokens.Type("pulumi:providers:pkgExt"), prov.Type)
|
|
assert.Equal(t, "default_0_5_0", prov.URN.Name())
|
|
assert.Equal(t, resource.NewPropertyMapFromMap(map[string]any{
|
|
"version": "0.5.0",
|
|
"__internal": map[string]any{
|
|
"name": "pkgA",
|
|
"version": "1.0.0",
|
|
"parameterization": "cmVwbGFjZW1lbnQ=",
|
|
},
|
|
}), prov.Inputs)
|
|
|
|
// Check the state of the imported resources is what we expect
|
|
resA := snap.Resources[1]
|
|
assert.Equal(t, tokens.Type("pkgA:m:typA"), resA.Type)
|
|
assert.Equal(t, "resA", resA.URN.Name())
|
|
assert.Equal(t, resource.PropertyMap{
|
|
"input": resource.NewStringProperty("input"),
|
|
}, resA.Inputs)
|
|
assert.Equal(t, resource.PropertyMap{
|
|
"output": resource.NewStringProperty("output"),
|
|
}, resA.Outputs)
|
|
|
|
resB := snap.Resources[3]
|
|
assert.Equal(t, tokens.Type("pkgExt:m:typA"), resB.Type)
|
|
assert.Equal(t, "resB", resB.URN.Name())
|
|
assert.Equal(t, resource.PropertyMap{
|
|
"input": resource.NewStringProperty("input"),
|
|
}, resB.Inputs)
|
|
assert.Equal(t, resource.PropertyMap{
|
|
"output": resource.NewStringProperty("output"),
|
|
}, resB.Outputs)
|
|
|
|
resC := snap.Resources[5]
|
|
assert.Equal(t, tokens.Type("pkgExt:m:typA"), resC.Type)
|
|
assert.Equal(t, "resC", resC.URN.Name())
|
|
assert.Equal(t, resource.PropertyMap{
|
|
"input": resource.NewStringProperty("input"),
|
|
}, resC.Inputs)
|
|
assert.Equal(t, resource.PropertyMap{
|
|
"output": resource.NewStringProperty("output"),
|
|
}, resC.Outputs)
|
|
}
|