// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package tofu
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log"
"reflect"
"runtime"
"sort"
"strings"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/davecgh/go-spew/spew"
"github.com/go-test/deep"
"github.com/google/go-cmp/cmp"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/gocty"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/configs"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/configs/hcl2shim"
"github.com/opentofu/opentofu/internal/lang/marks"
"github.com/opentofu/opentofu/internal/plans"
"github.com/opentofu/opentofu/internal/providers"
"github.com/opentofu/opentofu/internal/provisioners"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/tfdiags"
)
func TestContext2Apply_basic(t *testing.T) {
m := testModule(t, "apply-good")
p := testProvider("aws")
p.PlanResourceChangeFn = testDiffFn
p.ApplyResourceChangeFn = testApplyFn
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
},
})
plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
assertNoErrors(t, diags)
state, diags := ctx.Apply(plan, m)
if diags.HasErrors() {
t.Fatalf("diags: %s", diags.Err())
}
mod := state.RootModule()
if len(mod.Resources) < 2 {
t.Fatalf("bad: %#v", mod.Resources)
}
actual := strings.TrimSpace(state.String())
expected := strings.TrimSpace(testTofuApplyStr)
if actual != expected {
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
}
}
func TestContext2Apply_stop(t *testing.T) {
t.Parallel()
m := testModule(t, "apply-stop")
stopCh := make(chan struct{})
waitCh := make(chan struct{})
stoppedCh := make(chan struct{})
stopCalled := uint32(0)
applyStopped := uint32(0)
p := &MockProvider{
GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{
ResourceTypes: map[string]providers.Schema{
"indefinite": {
Version: 1,
Block: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"result": {
Type: cty.String,
Computed: true,
},
},
},
},
},
},
PlanResourceChangeFn: func(prcr providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
log.Printf("[TRACE] TestContext2Apply_stop: no-op PlanResourceChange")
return providers.PlanResourceChangeResponse{
PlannedState: cty.ObjectVal(map[string]cty.Value{
"result": cty.UnknownVal(cty.String),
}),
}
},
ApplyResourceChangeFn: func(arcr providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
// This will unblock the main test code once we reach this
// point, so that it'll then be guaranteed to call Stop
// while we're waiting in here.
close(waitCh)
log.Printf("[TRACE] TestContext2Apply_stop: ApplyResourceChange waiting for Stop call")
// This will block until StopFn closes this channel below.
<-stopCh
atomic.AddUint32(&applyStopped, 1)
// This unblocks StopFn below, thereby acknowledging the request
// to stop.
close(stoppedCh)
return providers.ApplyResourceChangeResponse{
NewState: cty.ObjectVal(map[string]cty.Value{
"result": cty.StringVal("complete"),
}),
}
},
StopFn: func() error {
// Closing this channel will unblock the channel read in
// ApplyResourceChangeFn above.
log.Printf("[TRACE] TestContext2Apply_stop: Stop called")
atomic.AddUint32(&stopCalled, 1)
close(stopCh)
// This will block until ApplyResourceChange has reacted to
// being stopped.
log.Printf("[TRACE] TestContext2Apply_stop: Waiting for ApplyResourceChange to react to being stopped")
<-stoppedCh
log.Printf("[TRACE] TestContext2Apply_stop: Stop is completing")
return nil
},
}
hook := &testHook{}
ctx := testContext2(t, &ContextOpts{
Hooks: []Hook{hook},
Providers: map[addrs.Provider]providers.Factory{
addrs.MustParseProviderSourceString("terraform.io/test/indefinite"): testProviderFuncFixed(p),
},
})
plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
assertNoErrors(t, diags)
// We'll reset the hook events before we apply because we only care about
// the apply-time events.
hook.Calls = hook.Calls[:0]
// We'll apply in the background so that we can call Stop in the foreground.
stateCh := make(chan *states.State)
go func(plan *plans.Plan) {
state, _ := ctx.Apply(plan, m)
stateCh <- state
}(plan)
// We'll wait until the provider signals that we've reached the
// ApplyResourceChange function, so we can guarantee the expected
// order of operations so our hook events below will always match.
t.Log("waiting for the apply phase to get started")
<-waitCh
// This will block until the apply operation has unwound, so we should
// be able to observe all of the apply side-effects afterwards.
t.Log("waiting for ctx.Stop to return")
ctx.Stop()
t.Log("waiting for apply goroutine to return state")
state := <-stateCh
t.Log("apply is all complete")
if state == nil {
t.Fatalf("final state is nil")
}
if got, want := atomic.LoadUint32(&stopCalled), uint32(1); got != want {
t.Errorf("provider's Stop method was not called")
}
if got, want := atomic.LoadUint32(&applyStopped), uint32(1); got != want {
// This should not happen if things are working correctly but this is
// to catch weird situations such as if a bug in this test causes us
// to inadvertently stop OpenTofu before it reaches te apply phase,
// or if the apply operation fails in a way that causes it not to reach
// the ApplyResourceChange function.
t.Errorf("somehow provider's ApplyResourceChange didn't react to being stopped")
}
// Because we interrupted the apply phase while applying the resource,
// we should have halted immediately after we finished visiting that
// resource. We don't visit indefinite.bar at all.
gotEvents := hook.Calls
wantEvents := []*testHookCall{
{"PreDiff", "indefinite.foo"},
{"PostDiff", "indefinite.foo"},
{"PreApply", "indefinite.foo"},
{"PostApply", "indefinite.foo"},
{"PostStateUpdate", ""}, // State gets updated one more time to include the apply result.
}
// The "Stopping" event gets sent to the hook asynchronously from the others
// because it is triggered in the ctx.Stop call above, rather than from
// the goroutine where ctx.Apply was running, and therefore it doesn't
// appear in a guaranteed position in gotEvents. We already checked above
// that the provider's Stop method was called, so we'll just strip that
// event out of our gotEvents.
seenStopped := false
for i, call := range gotEvents {
if call.Action == "Stopping" {
seenStopped = true
// We'll shift up everything else in the slice to create the
// effect of the Stopping event not having been present at all,
// which should therefore make this slice match "wantEvents".
copy(gotEvents[i:], gotEvents[i+1:])
gotEvents = gotEvents[:len(gotEvents)-1]
break
}
}
if diff := cmp.Diff(wantEvents, gotEvents); diff != "" {
t.Errorf("wrong hook events\n%s", diff)
}
if !seenStopped {
t.Errorf("'Stopping' event did not get sent to the hook")
}
rov := state.OutputValue(addrs.OutputValue{Name: "result"}.Absolute(addrs.RootModuleInstance))
if rov != nil && rov.Value != cty.NilVal && !rov.Value.IsNull() {
t.Errorf("'result' output value unexpectedly populated: %#v", rov.Value)
}
resourceAddr := addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "indefinite",
Name: "foo",
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
rv := state.ResourceInstance(resourceAddr)
if rv == nil || rv.Current == nil {
t.Fatalf("no state entry for %s", resourceAddr)
}
resour
没有合适的资源?快使用搜索试试~ 我知道了~
云基础设施管理:opentf
共2000个文件
go:1403个
json:382个
mdx:119个
需积分: 3 0 下载量 66 浏览量
2024-03-09
20:22:09
上传
评论
收藏 5.63MB ZIP 举报
温馨提示
主要语言:Go 项目分类:[工具] 推荐理由:一个用于安全高效地构建、更改和控制基础架构版本的工具。OpenTF 可以管理现有的流行服务提供商以及自定义的内部解决方案,帮助组织实现基础架构的版本管理和变更控制,确保系统的可靠性和稳定性。
资源推荐
资源详情
资源评论
收起资源包目录
云基础设施管理:opentf (2000个子文件)
CODEOWNERS 214B
.gitignore 19B
context_apply_test.go 358KB
plan_test.go 245KB
context_plan_test.go 200KB
tfplugin5.pb.go 199KB
context_plan2_test.go 187KB
tfplugin6.pb.go 177KB
schema_test.go 105KB
node_resource_abstract_instance.go 93KB
differ_test.go 93KB
init_test.go 92KB
installer_test.go 85KB
test_test.go 80KB
objchange_test.go 76KB
backend_complete_test.go 71KB
shims_test.go 67KB
planfile.pb.go 66KB
context_validate_test.go 63KB
resource_data_test.go 63KB
context_apply2_test.go 62KB
meta_backend.go 60KB
state.go 57KB
backend_test.go 55KB
apply_test.go 55KB
backend_apply_test.go 54KB
state_mv_test.go 53KB
meta_backend_test.go 53KB
plan_valid_test.go 50KB
init.go 50KB
schema.go 49KB
tfe_client_mock.go 49KB
context_refresh_test.go 48KB
renderer_test.go 48KB
plan_test.go 45KB
backend_apply_test.go 45KB
backend_plan_test.go 44KB
resource_diff_test.go 44KB
backend_test.go 42KB
backend.go 42KB
move_endpoint_module_test.go 41KB
meta_backend_migrate.go 41KB
eval_variable_test.go 40KB
backend.go 39KB
operation_test.go 39KB
test.go 38KB
collection_test.go 38KB
module_install.go 38KB
diff.go 38KB
context_plan.go 37KB
backend_plan_test.go 36KB
config.go 36KB
evaluate.go 35KB
state_test.go 34KB
backend.go 34KB
compatible_test.go 34KB
resource_test.go 33KB
command_test.go 33KB
module_install_test.go 32KB
installer.go 32KB
test_test.go 31KB
plan.go 31KB
show_test.go 31KB
provider_validation.go 30KB
tfplan.go 30KB
package_authentication_test.go 30KB
config_test.go 29KB
module.go 29KB
functions_test.go 29KB
state_test.go 29KB
meta.go 29KB
context_import_test.go 28KB
resource_address_test.go 28KB
login.go 28KB
version4.go 27KB
diagnostic_test.go 27KB
state_test.go 27KB
backend_plan_test.go 27KB
context_test.go 26KB
descriptions.go 26KB
move_endpoint_module.go 26KB
mock.go 26KB
resource.go 26KB
diagnostic_test.go 25KB
opentf_test.go 25KB
resource.go 25KB
refresh_test.go 24KB
import_test.go 24KB
run_test.go 24KB
node_resource_plan_instance.go 24KB
communicator_test.go 24KB
module_source_test.go 24KB
diff_test.go 24KB
context_apply_checks_test.go 23KB
state_test.go 23KB
backend_test.go 23KB
decoder_spec_test.go 22KB
communicator.go 22KB
backend.go 22KB
grpc_provider_test.go 22KB
共 2000 条
- 1
- 2
- 3
- 4
- 5
- 6
- 20
资源评论
全栈海哥
- 粉丝: 1502
- 资源: 98
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功