[PULUMI] CA provider

This commit is contained in:
Kat Inskip 2023-03-09 15:43:39 -08:00
parent 64507a991c
commit b224bd4935
Signed by: kat
GPG key ID: 465E64DECEA8CF0F
27 changed files with 2727 additions and 313 deletions

340
integration/integration.go Normal file
View file

@ -0,0 +1,340 @@
// Copyright 2022, 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 integration
import (
"context"
"fmt"
"testing"
"github.com/blang/semver"
"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
presource "github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
"github.com/stretchr/testify/assert"
p "github.com/pulumi/pulumi-go-provider"
)
type Server interface {
GetSchema(p.GetSchemaRequest) (p.GetSchemaResponse, error)
Cancel() error
CheckConfig(p.CheckRequest) (p.CheckResponse, error)
DiffConfig(p.DiffRequest) (p.DiffResponse, error)
Configure(p.ConfigureRequest) error
Invoke(p.InvokeRequest) (p.InvokeResponse, error)
Check(p.CheckRequest) (p.CheckResponse, error)
Diff(p.DiffRequest) (p.DiffResponse, error)
Create(p.CreateRequest) (p.CreateResponse, error)
Read(p.ReadRequest) (p.ReadResponse, error)
Update(p.UpdateRequest) (p.UpdateResponse, error)
Delete(p.DeleteRequest) error
Construct(p.ConstructRequest) (p.ConstructResponse, error)
}
func NewServer(pkg string, version semver.Version, provider p.Provider) Server {
return &server{p.RunInfo{
PackageName: pkg,
Version: version.String(),
}, provider.WithDefaults(),
context.Background()}
}
type server struct {
runInfo p.RunInfo
p p.Provider
context context.Context
}
type ctx struct {
context.Context
runInfo p.RunInfo
urn presource.URN
}
func (c *ctx) Log(severity diag.Severity, msg string) {
if c.urn != "" {
fmt.Printf("Log(%s): %s", severity, msg)
return
}
fmt.Printf("%s Log(%s): %s", c.urn, severity, msg)
}
func (c *ctx) Logf(severity diag.Severity, msg string, args ...any) {
c.Log(severity, fmt.Sprintf(msg, args...))
}
func (c *ctx) LogStatus(severity diag.Severity, msg string) {
if c.urn != "" {
fmt.Printf("LogStatus(%s): %s", severity, msg)
return
}
fmt.Printf("%s LogStatus(%s): %s", c.urn, severity, msg)
}
func (c *ctx) LogStatusf(severity diag.Severity, msg string, args ...any) {
c.LogStatus(severity, fmt.Sprintf(msg, args...))
}
func (c *ctx) RuntimeInformation() p.RunInfo { return c.runInfo }
func (s *server) ctx(urn presource.URN) p.Context {
return &ctx{s.context, s.runInfo, urn}
}
func (s *server) GetSchema(req p.GetSchemaRequest) (p.GetSchemaResponse, error) {
return s.p.GetSchema(s.ctx(""), req)
}
func (s *server) Cancel() error {
return s.p.Cancel(s.ctx(""))
}
func (s *server) CheckConfig(req p.CheckRequest) (p.CheckResponse, error) {
return s.p.CheckConfig(s.ctx(""), req)
}
func (s *server) DiffConfig(req p.DiffRequest) (p.DiffResponse, error) {
return s.p.DiffConfig(s.ctx(""), req)
}
func (s *server) Configure(req p.ConfigureRequest) error {
return s.p.Configure(s.ctx(""), req)
}
func (s *server) Invoke(req p.InvokeRequest) (p.InvokeResponse, error) {
return s.p.Invoke(s.ctx(presource.URN(req.Token)), req)
}
func (s *server) Check(req p.CheckRequest) (p.CheckResponse, error) {
return s.p.Check(s.ctx(req.Urn), req)
}
func (s *server) Diff(req p.DiffRequest) (p.DiffResponse, error) {
return s.p.Diff(s.ctx(req.Urn), req)
}
func (s *server) Create(req p.CreateRequest) (p.CreateResponse, error) {
return s.p.Create(s.ctx(req.Urn), req)
}
func (s *server) Read(req p.ReadRequest) (p.ReadResponse, error) {
return s.p.Read(s.ctx(req.Urn), req)
}
func (s *server) Update(req p.UpdateRequest) (p.UpdateResponse, error) {
return s.p.Update(s.ctx(req.Urn), req)
}
func (s *server) Delete(req p.DeleteRequest) error {
return s.p.Delete(s.ctx(req.Urn), req)
}
func (s *server) Construct(req p.ConstructRequest) (p.ConstructResponse, error) {
return s.p.Construct(s.ctx(req.URN), req)
}
// TODO: Add support for diff verification
type Operation struct {
// The inputs for the operation
Inputs presource.PropertyMap
// The expected output for the operation. If ExpectedOutput is nil, no check will be made.
ExpectedOutput presource.PropertyMap
// A function called on the output of this operation.
Hook func(inputs, output presource.PropertyMap)
// If the test should expect the operation to signal an error.
ExpectFailure bool
// If CheckFailures is non-nil, expect the check step to fail with the provided output.
CheckFailures []p.CheckFailure
}
// Steps describing the lifecycle of a resource.
type LifeCycleTest struct {
Resource tokens.Type
Create Operation
Updates []Operation
}
// Run a resource through it's lifecycle asserting that its output is as expected.
// The resource is
// 1. Previewed
// 2. Created
// 2. Previewed and Updated for each update in the Updates list.
// 3. Deleted
func (l LifeCycleTest) Run(t *testing.T, server Server) {
urn := presource.NewURN("test", "provider", "", l.Resource, "test")
runCreate := func(op Operation) (p.CreateResponse, bool) {
// Here we do the create and the initial setup
checkResponse, err := server.Check(p.CheckRequest{
Urn: urn,
Olds: nil,
News: op.Inputs,
})
assert.NoError(t, err, "resource check errored")
if len(op.CheckFailures) > 0 || len(checkResponse.Failures) > 0 {
assert.ElementsMatch(t, op.CheckFailures, checkResponse.Failures,
"check failures mismatch on create")
return p.CreateResponse{}, false
}
_, err = server.Create(p.CreateRequest{
Urn: urn,
Properties: checkResponse.Inputs.Copy(),
Preview: true,
})
// We allow the failure from ExpectFailure to hit at either the preview or the Create.
if op.ExpectFailure && err != nil {
return p.CreateResponse{}, false
}
createResponse, err := server.Create(p.CreateRequest{
Urn: urn,
Properties: checkResponse.Inputs.Copy(),
})
if op.ExpectFailure {
assert.Error(t, err, "expected an error on create")
return p.CreateResponse{}, false
}
assert.NoError(t, err, "failed to run the create")
if err != nil {
return p.CreateResponse{}, false
}
if op.Hook != nil {
op.Hook(checkResponse.Inputs, createResponse.Properties.Copy())
}
if op.ExpectedOutput != nil {
assert.EqualValues(t, op.ExpectedOutput, createResponse.Properties, "create outputs")
}
return createResponse, true
}
createResponse, keepGoing := runCreate(l.Create)
if !keepGoing {
return
}
id := createResponse.ID
olds := createResponse.Properties
for i, update := range l.Updates {
// Perform the check
check, err := server.Check(p.CheckRequest{
Urn: urn,
Olds: olds,
News: update.Inputs,
})
assert.NoErrorf(t, err, "check returned an error on update %d", i)
if err != nil {
return
}
if len(update.CheckFailures) > 0 || len(check.Failures) > 0 {
assert.ElementsMatchf(t, update.CheckFailures, check.Failures,
"check failures mismatch on update %d", i)
continue
}
diff, err := server.Diff(p.DiffRequest{
ID: id,
Urn: urn,
Olds: olds,
News: check.Inputs.Copy(),
})
assert.NoErrorf(t, err, "diff failed on update %d", i)
if err != nil {
return
}
if !diff.HasChanges {
// We don't have any changes, so we can just do nothing
continue
}
isDelete := false
for _, v := range diff.DetailedDiff {
switch v.Kind {
case p.AddReplace:
fallthrough
case p.DeleteReplace:
fallthrough
case p.UpdateReplace:
isDelete = true
}
}
if isDelete {
runDelete := func() {
err = server.Delete(p.DeleteRequest{
ID: id,
Urn: urn,
Properties: olds,
})
assert.NoError(t, err, "failed to delete the resource")
}
if diff.DeleteBeforeReplace {
runDelete()
result, keepGoing := runCreate(update)
if !keepGoing {
continue
}
id = result.ID
olds = result.Properties
} else {
result, keepGoing := runCreate(update)
if !keepGoing {
continue
}
runDelete()
// Set the new block
id = result.ID
olds = result.Properties
}
} else {
// Now perform the preview
_, err = server.Update(p.UpdateRequest{
ID: id,
Urn: urn,
Olds: olds,
News: check.Inputs.Copy(),
Preview: true,
})
if update.ExpectFailure && err != nil {
continue
}
result, err := server.Update(p.UpdateRequest{
ID: id,
Urn: urn,
Olds: olds,
News: check.Inputs.Copy(),
})
if update.ExpectFailure {
assert.Errorf(t, err, "expected failure on update %d", i)
continue
}
if update.Hook != nil {
update.Hook(check.Inputs, result.Properties.Copy())
}
if update.ExpectedOutput != nil {
assert.EqualValues(t, update.ExpectedOutput, result.Properties.Copy(), "expected output on update %d", i)
}
olds = result.Properties
}
}
err := server.Delete(p.DeleteRequest{
ID: id,
Urn: urn,
Properties: olds,
})
assert.NoError(t, err, "failed to delete the resource")
}