You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
terraform/internal/stacks/stackruntime/internal/stackeval/component_instance.go

701 lines
25 KiB

package stackeval
import (
"context"
"fmt"
"github.com/hashicorp/hcl/v2"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/convert"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/instances"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/promising"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
"github.com/hashicorp/terraform/internal/stacks/stackconfig/stackconfigtypes"
"github.com/hashicorp/terraform/internal/stacks/stackplan"
"github.com/hashicorp/terraform/internal/stacks/stackruntime/hooks"
"github.com/hashicorp/terraform/internal/stacks/stackstate"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/terraform"
"github.com/hashicorp/terraform/internal/tfdiags"
)
type ComponentInstance struct {
call *Component
key addrs.InstanceKey
main *Main
repetition instances.RepetitionData
moduleTreePlan promising.Once[withDiagnostics[*plans.Plan]]
}
var _ Plannable = (*ComponentInstance)(nil)
var _ ExpressionScope = (*ComponentInstance)(nil)
func newComponentInstance(call *Component, key addrs.InstanceKey, repetition instances.RepetitionData) *ComponentInstance {
return &ComponentInstance{
call: call,
key: key,
main: call.main,
repetition: repetition,
}
}
func (c *ComponentInstance) Addr() stackaddrs.AbsComponentInstance {
callAddr := c.call.Addr()
stackAddr := callAddr.Stack
return stackaddrs.AbsComponentInstance{
Stack: stackAddr,
Item: stackaddrs.ComponentInstance{
Component: callAddr.Item,
Key: c.key,
},
}
}
func (c *ComponentInstance) InputVariableValues(ctx context.Context, phase EvalPhase) cty.Value {
ret, _ := c.CheckInputVariableValues(ctx, phase)
return ret
}
func (c *ComponentInstance) CheckInputVariableValues(ctx context.Context, phase EvalPhase) (cty.Value, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
wantTy, defs := c.call.Config(ctx).InputsType(ctx)
decl := c.call.Declaration(ctx)
if wantTy == cty.NilType {
// Suggests that the target module is invalid in some way, so we'll
// just report that we don't know the input variable values and trust
// that the module's problems will be reported by some other return
// path.
return cty.DynamicVal, diags
}
v := cty.EmptyObjectVal
expr := decl.Inputs
rng := decl.DeclRange
var hclCtx *hcl.EvalContext
if expr != nil {
result, moreDiags := EvalExprAndEvalContext(ctx, expr, phase, c)
diags = diags.Append(moreDiags)
if moreDiags.HasErrors() {
return cty.DynamicVal, diags
}
expr = result.Expression
hclCtx = result.EvalContext
v = result.Value
rng = tfdiags.SourceRangeFromHCL(result.Expression.Range())
}
if defs != nil {
v = defs.Apply(v)
}
v, err := convert.Convert(v, wantTy)
if err != nil {
// A conversion failure here could either be caused by an author-provided
// expression that's invalid or by the author omitting the argument
// altogether when there's at least one required attribute, so we'll
// return slightly different messages in each case.
if expr != nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid inputs for component",
Detail: fmt.Sprintf("Invalid input variable definition object: %s.", tfdiags.FormatError(err)),
Subject: rng.ToHCL().Ptr(),
Expression: expr,
EvalContext: hclCtx,
})
} else {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing required inputs for component",
Detail: fmt.Sprintf("Must provide \"inputs\" argument to define the component's input variables: %s.", tfdiags.FormatError(err)),
Subject: rng.ToHCL().Ptr(),
})
}
return cty.DynamicVal, diags
}
for _, path := range stackconfigtypes.ProviderInstancePathsInValue(v) {
err := path.NewErrorf("cannot send provider configuration reference to Terraform module input variable")
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid inputs for component",
Detail: fmt.Sprintf(
"Invalid input variable definition object: %s.\n\nUse the separate \"providers\" argument to specify the provider configurations to use for this component's root module.",
tfdiags.FormatError(err),
),
Subject: rng.ToHCL().Ptr(),
Expression: expr,
EvalContext: hclCtx,
})
}
return v, diags
}
// inputValuesForModulesRuntime adapts the result of
// [ComponentInstance.InputVariableValues] to the representation that the
// main Terraform modules runtime expects.
func (c *ComponentInstance) inputValuesForModulesRuntime(ctx context.Context, phase EvalPhase) terraform.InputValues {
valsObj := c.InputVariableValues(ctx, phase)
if valsObj == cty.NilVal {
return nil
}
// valsObj might be an unknown value during the planning phase, in which
// case we'll return an InputValues with all of the expected variables
// defined as unknown values of their expected type constraints. To
// achieve that, we'll do our work with the configuration's object type
// constraint instead of with the value we've been given directly.
wantTy, _ := c.call.Config(ctx).InputsType(ctx)
if wantTy == cty.NilType {
// The configuration is too invalid for us to know what type we're
// expecting, so we'll just bail.
return nil
}
wantAttrs := wantTy.AttributeTypes()
ret := make(terraform.InputValues, len(wantAttrs))
for name, aty := range wantAttrs {
v := valsObj.GetAttr(name)
if !v.IsKnown() {
// We'll ensure that it has the expected type even if
// InputVariableValues didn't know what types to use.
v = cty.UnknownVal(aty)
}
ret[name] = &terraform.InputValue{
Value: v,
SourceType: terraform.ValueFromCaller,
}
}
return ret
}
// CheckProviders evaluates the "providers" argument from the component
// configuration and returns a mapping from the provider configuration
// addresses that the component's root module expect to have populated
// to the address of the [ProviderInstance] from the stack configuration
// to pass into that slot.
//
// If the second return value "valid" is true then the providers argument
// is valid and so the returned map should be complete. If "valid" is false
// then there are some problems with the providers argument and so the
// map might be incomplete, and so callers should use it only with a great
// deal of care.
func (c *ComponentInstance) Providers(ctx context.Context, phase EvalPhase) (selections map[addrs.RootProviderConfig]stackaddrs.AbsProviderConfigInstance, valid bool) {
ret, diags := c.CheckProviders(ctx, phase)
return ret, !diags.HasErrors()
}
// CheckProviders evaluates the "providers" argument from the component
// configuration and returns a mapping from the provider configuration
// addresses that the component's root module expect to have populated
// to the address of the [ProviderInstance] from the stack configuration
// to pass into that slot.
//
// If the "providers" argument is invalid then this will return error
// diagnostics along with a partial result.
func (c *ComponentInstance) CheckProviders(ctx context.Context, phase EvalPhase) (map[addrs.RootProviderConfig]stackaddrs.AbsProviderConfigInstance, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
ret := make(map[addrs.RootProviderConfig]stackaddrs.AbsProviderConfigInstance)
stack := c.call.Stack(ctx)
stackConfig := stack.StackConfig(ctx)
declConfigs := c.call.Declaration(ctx).ProviderConfigs
neededConfigs := c.call.Config(ctx).RequiredProviderInstances(ctx)
for _, inCalleeAddr := range neededConfigs {
// declConfigs is based on _local_ provider references so we'll
// need to translate based on the stack configuration's
// required_providers block.
typeAddr := inCalleeAddr.Provider
localName, ok := stackConfig.ProviderLocalName(ctx, typeAddr)
if !ok {
// TODO: We should probably catch this as a one-time error during
// validation of the component config block, rather than raising
// it separately for each instance, since the set of required
// providers for both this stack and the root module of the
// component are statically-declared.
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Component requires undeclared provider",
Detail: fmt.Sprintf(
"The root module for %s requires a configuration for provider %q, which isn't declared as a dependency of this stack configuration.\n\nDeclare this provider in the stack's required_providers block, and then assign a configuration for that provider in this component's \"providers\" argument.",
c.Addr(), typeAddr.ForDisplay(),
),
Subject: c.call.Declaration(ctx).DeclRange.ToHCL().Ptr(),
})
continue
}
localAddr := addrs.LocalProviderConfig{
LocalName: localName,
Alias: inCalleeAddr.Alias,
}
expr, ok := declConfigs[localAddr]
if !ok {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing required provider configuration",
Detail: fmt.Sprintf(
"The root module for %s requires provider configuration named %q for provider %q, which is not assigned in the component's \"providers\" argument.",
c.Addr(), localAddr.StringCompact(), typeAddr.ForDisplay(),
),
Subject: c.call.Declaration(ctx).DeclRange.ToHCL().Ptr(),
})
continue
}
// If we've got this far then expr is an expression that should
// evaluate to a special cty capsule type that acts as a reference
// to a provider configuration declared elsewhere in the tree
// of stack configurations.
result, hclDiags := EvalExprAndEvalContext(ctx, expr, phase, c)
diags = diags.Append(hclDiags)
if hclDiags.HasErrors() {
continue
}
const errSummary = "Invalid provider reference"
if actualTy := result.Value.Type(); stackconfigtypes.IsProviderConfigType(actualTy) {
actualTypeAddr := stackconfigtypes.ProviderForProviderConfigType(actualTy)
if actualTypeAddr != typeAddr {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: errSummary,
Detail: fmt.Sprintf(
"The provider configuration slot %s requires a configuration for provider %q, not for provider %q.",
localAddr.StringCompact(), typeAddr, actualTypeAddr,
),
Subject: result.Expression.Range().Ptr(),
})
continue
}
} else {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: errSummary,
Detail: fmt.Sprintf(
"The provider configuration slot %s requires a configuration for provider %q.",
localAddr.StringCompact(), typeAddr,
),
Subject: result.Expression.Range().Ptr(),
})
}
v := result.Value
// If the tests succeeded above then "v" should definitely
// be of the expected type, but might be unknown or null.
if v.IsNull() {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: errSummary,
Detail: fmt.Sprintf(
"The provider configuration slot %s is required, but this definition returned null.",
localAddr.StringCompact(),
),
Subject: result.Expression.Range().Ptr(),
})
continue
}
if !v.IsKnown() {
// TODO: Once we support deferred changes we should return
// something that lets the caller know the configuration is
// incomplete so it can defer planning the entire component.
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: errSummary,
Detail: fmt.Sprintf(
"This expression depends on values that won't be known until the apply phase, so Terraform cannot determine which provider configuration to use while planning changes for %s.",
c.Addr().String(),
),
Subject: result.Expression.Range().Ptr(),
})
continue
}
// If it's of the correct type, known, and not null then we should
// be able to retrieve a specific provider instance address that
// this value refers to.
providerInstAddr := stackconfigtypes.ProviderInstanceForValue(v)
ret[inCalleeAddr] = providerInstAddr
// The reference must be to a provider instance that's actually
// configured.
providerInstStack := c.main.Stack(ctx, providerInstAddr.Stack, phase)
if providerInstStack != nil {
provider := providerInstStack.Provider(ctx, providerInstAddr.Item.ProviderConfig)
if provider != nil {
insts := provider.Instances(ctx, phase)
if insts == nil {
// If we get here then we don't yet know which instances
// this provider has, so we'll be optimistic that it'll
// show up in a later phase.
continue
}
if _, exists := insts[providerInstAddr.Item.Key]; exists {
continue
}
}
}
// If we fall here then something on the path to the provider instance
// doesn't exist, and so effectively the provider instance doesn't exist.
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: errSummary,
Detail: fmt.Sprintf(
"Expression result refers to undefined provider instance %s.",
providerInstAddr,
),
Subject: result.Expression.Range().Ptr(),
})
}
return ret, diags
}
func (c *ComponentInstance) ModuleTreePlan(ctx context.Context) *plans.Plan {
ret, _ := c.CheckModuleTreePlan(ctx)
return ret
}
func (c *ComponentInstance) CheckModuleTreePlan(ctx context.Context) (*plans.Plan, tfdiags.Diagnostics) {
return doOnceWithDiags(
ctx, &c.moduleTreePlan, c.main,
func(ctx context.Context) (*plans.Plan, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
addr := c.Addr()
h := hooksFromContext(ctx)
seq, ctx := hookBegin(ctx, h.BeginComponentInstancePlan, h.ContextAttach, addr)
decl := c.call.Declaration(ctx)
// This is our main bridge from the stacks language into the main Terraform
// module language during the planning phase. We need to ask the main
// language runtime to plan the module tree associated with this
// component and return the result.
moduleTree := c.call.Config(ctx).ModuleTree(ctx)
if moduleTree == nil {
// Presumably the configuration is invalid in some way, so
// we can't create a plan and the relevant diagnostics will
// get reported when the plan driver visits the ComponentConfig
// object.
return nil, diags
}
providerSchemas := make(map[addrs.Provider]providers.ProviderSchema)
for _, sourceAddr := range moduleTree.ProviderTypes() {
pTy := c.main.ProviderType(ctx, sourceAddr)
if pTy == nil {
continue // not our job to report a missing provider type
}
schema, err := pTy.Schema(ctx)
if err != nil {
// FIXME: it's not technically our job to report a schema
// fetch failure, but currently there is no single other
// place that definitely does it, so we'll do it here at
// the risk of some redundant errors if we end up using
// the same provider multiple times.
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Provider initialization error",
Detail: fmt.Sprintf("Failed to fetch the provider schema for %s: %s.", sourceAddr, err),
Subject: decl.DeclRange.ToHCL().Ptr(),
})
continue // not our job to report a schema fetch failure
}
providerSchemas[sourceAddr] = schema
}
if diags.HasErrors() {
return nil, diags
}
tfCtx, err := terraform.NewContext(&terraform.ContextOpts{
Hooks: []terraform.Hook{
&componentInstanceTerraformHook{
ctx: ctx,
seq: seq,
hooks: hooksFromContext(ctx),
addr: c.Addr(),
},
},
PreloadedProviderSchemas: providerSchemas,
})
if err != nil {
// Should not get here because we should always pass a valid
// ContextOpts above.
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Failed to instantiate Terraform modules runtime",
fmt.Sprintf("Could not load the main Terraform language runtime: %s.\n\nThis is a bug in Terraform; please report it!", err),
))
return nil, diags
}
stackPlanOpts := c.main.PlanningOpts()
inputValues := c.inputValuesForModulesRuntime(ctx, PlanPhase)
if inputValues == nil {
// inputValuesForModulesRuntime uses nil (as opposed to a
// non-nil zerolen map) to represent that the definition of
// the input variables was so invalid that we cannot do
// anything with it, in which case we'll just return early
// and assume the plan walk driver will find the diagnostics
// via another return path.
return nil, diags
}
providerInstAddrs, valid := c.Providers(ctx, PlanPhase)
if !valid {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Cannot plan component",
Detail: fmt.Sprintf("Cannot generate a plan for %s because its provider configuration assignments are invalid.", c.Addr()),
Subject: decl.DeclRange.ToHCL().Ptr(),
})
return nil, diags
}
providerInsts := make(map[addrs.RootProviderConfig]providers.Interface)
for calleeAddr, callerAddr := range providerInstAddrs {
providerInstStack := c.main.Stack(ctx, callerAddr.Stack, PlanPhase)
if providerInstStack == nil {
continue
}
provider := providerInstStack.Provider(ctx, callerAddr.Item.ProviderConfig)
if provider == nil {
continue
}
insts := provider.Instances(ctx, PlanPhase)
if insts == nil {
// If we get here then we don't yet know which instances
// this provider has, so we'll be optimistic that it'll
// show up in a later phase.
continue
}
inst, exists := insts[callerAddr.Item.Key]
if !exists {
continue
}
providerInsts[calleeAddr] = inst.Client(ctx, PlanPhase)
}
// TODO: Should pass in the previous run state once we have a
// previous stack state to take it from.
// NOTE: This ComponentInstance type only deals with component
// instances currently declared in the configuration. See
// [ComponentInstanceRemoved] for the model of a component instance
// that existed in the prior state but is not currently declared
// in the configuration.
plan, moreDiags := tfCtx.Plan(moduleTree, nil, &terraform.PlanOpts{
Mode: stackPlanOpts.PlanningMode,
SetVariables: inputValues,
ExternalProviders: providerInsts,
})
diags = diags.Append(moreDiags)
if plan != nil {
for _, rsrcChange := range plan.DriftedResources {
hookMore(ctx, seq, h.ReportResourceInstanceDrift, &hooks.ResourceInstanceChange{
Addr: stackaddrs.AbsResourceInstance{
Component: addr,
Item: rsrcChange.Addr,
},
Change: rsrcChange,
})
}
for _, rsrcChange := range plan.Changes.Resources {
hookMore(ctx, seq, h.ReportResourceInstancePlanned, &hooks.ResourceInstanceChange{
Addr: stackaddrs.AbsResourceInstance{
Component: addr,
Item: rsrcChange.Addr,
},
Change: rsrcChange,
})
}
}
if diags.HasErrors() {
hookMore(ctx, seq, h.ErrorComponentInstancePlan, addr)
} else {
hookMore(ctx, seq, h.EndComponentInstancePlan, addr)
}
return plan, diags
},
)
}
// ApplyModuleTreePlan applies a plan returned by a previous call to
// [ComponentInstance.CheckModuleTreePlan].
//
// Applying a plan often has significant externally-visible side-effects, and
// so this method should be called only once for a given plan. In practice
// we currently ensure that is true by calling it only from the package-level
// [ApplyPlan] function, which arranges for this function to be called
// concurrently with the same method on other component instances and with
// a whole-tree walk to gather up results and diagnostics.
func (c *ComponentInstance) ApplyModuleTreePlan(ctx context.Context, plan *plans.Plan) (*states.State, tfdiags.Diagnostics) {
panic("unimplemented")
}
// ApplyResultState returns the new state resulting from applying a plan for
// this object using [ApplyModuleTreePlan], or nil if the apply failed and
// so there is no new state to return.
func (c *ComponentInstance) ApplyResultState(ctx context.Context) *states.State {
ret, _ := c.CheckApplyResultState(ctx)
return ret
}
// CheckApplyResultState returns the new state resulting from applying a plan for
// this object using [ApplyModuleTreePlan] and diagnostics describing any
// problems encountered when applying it.
func (c *ComponentInstance) CheckApplyResultState(ctx context.Context) (*states.State, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
changes := c.main.ApplyChangeResults()
newState, moreDiags, err := changes.ComponentInstanceResult(ctx, c.Addr())
diags = diags.Append(moreDiags)
if err != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Component instance apply not scheduled",
fmt.Sprintf("Terraform needs the result from applying changes to %s, but that apply was apparently not scheduled to run. This is a bug in Terraform.", c.Addr()),
))
}
return newState, diags
}
func (c *ComponentInstance) ResultValue(ctx context.Context, phase EvalPhase) cty.Value {
switch phase {
case PlanPhase:
plan := c.ModuleTreePlan(ctx)
if plan == nil {
// Planning seems to have failed so we cannot decide a result value yet.
// We can't do any better than DynamicVal here because in the
// modules language output values don't have statically-declared
// result types.
return cty.DynamicVal
}
// During the plan phase we use the planned output changes to construct
// our value.
outputChanges := plan.Changes.Outputs
attrs := make(map[string]cty.Value, len(outputChanges))
for _, changeSrc := range outputChanges {
name := changeSrc.Addr.OutputValue.Name
change, err := changeSrc.Decode()
if err != nil {
attrs[name] = cty.DynamicVal
}
attrs[name] = change.After
}
return cty.ObjectVal(attrs)
case ApplyPhase:
newState := c.ApplyResultState(ctx)
if newState == nil {
// Applying seems to have failed so we cannot provide a result
// value, and so we'll return a placeholder to help our caller
// unwind gracefully with its own placeholder result.
// We can't do any better than DynamicVal here because in the
// modules language output values don't have statically-declared
// result types.
return cty.DynamicVal
}
// During the apply phase we use the root module output values from
// the new state to construct our value.
outputVals := newState.RootModule().OutputValues
attrs := make(map[string]cty.Value, len(outputVals))
for _, ov := range outputVals {
name := ov.Addr.OutputValue.Name
attrs[name] = ov.Value
}
return cty.ObjectVal(attrs)
default:
// We can't produce a concrete value for any other phase.
return cty.DynamicVal
}
}
// ResolveExpressionReference implements ExpressionScope.
func (c *ComponentInstance) ResolveExpressionReference(ctx context.Context, ref stackaddrs.Reference) (Referenceable, tfdiags.Diagnostics) {
stack := c.call.Stack(ctx)
return stack.resolveExpressionReference(ctx, ref, nil, c.repetition)
}
// PlanChanges implements Plannable by validating that all of the per-instance
// arguments are suitable, and then asking the main Terraform language runtime
// to produce a plan in terms of the component's selected module.
func (c *ComponentInstance) PlanChanges(ctx context.Context) ([]stackplan.PlannedChange, tfdiags.Diagnostics) {
var changes []stackplan.PlannedChange
var diags tfdiags.Diagnostics
hookSingle(ctx, hooksFromContext(ctx).PendingComponentInstancePlan, c.Addr())
// We must always at least announce that the component instance exists,
// and that must come before any resource instance changes referring to it.
changes = append(changes, &stackplan.PlannedChangeComponentInstance{
Addr: c.Addr(),
// FIXME: Once we actually have a prior state this should vary
// depending on whether the same component instance existed in
// the prior state.
Action: plans.Create,
})
_, moreDiags := c.CheckInputVariableValues(ctx, PlanPhase)
diags = diags.Append(moreDiags)
_, moreDiags = c.CheckProviders(ctx, PlanPhase)
diags = diags.Append(moreDiags)
corePlan, moreDiags := c.CheckModuleTreePlan(ctx)
diags = diags.Append(moreDiags)
if corePlan != nil {
for _, rsrcChange := range corePlan.DriftedResources {
changes = append(changes, &stackplan.PlannedChangeResourceInstanceOutside{
ComponentInstanceAddr: c.Addr(),
ChangeSrc: rsrcChange,
})
}
for _, rsrcChange := range corePlan.Changes.Resources {
changes = append(changes, &stackplan.PlannedChangeResourceInstancePlanned{
ComponentInstanceAddr: c.Addr(),
ChangeSrc: rsrcChange,
})
}
}
return changes, diags
}
// CheckApply implements ApplyChecker.
func (c *ComponentInstance) CheckApply(ctx context.Context) ([]stackstate.AppliedChange, tfdiags.Diagnostics) {
var changes []stackstate.AppliedChange
var diags tfdiags.Diagnostics
// FIXME: We need to report AppliedChange objects for the component
// instance itself and each of the affected resource instances inside it.
// For now we're only reporting diagnostics as an initial stub.
_, moreDiags := c.CheckInputVariableValues(ctx, ApplyPhase)
diags = diags.Append(moreDiags)
_, moreDiags = c.CheckProviders(ctx, ApplyPhase)
diags = diags.Append(moreDiags)
_, moreDiags = c.CheckApplyResultState(ctx)
diags = diags.Append(moreDiags)
return changes, diags
}
func (c *ComponentInstance) tracingName() string {
return c.Addr().String()
}