refactor ephemeral interface to better match protocol

pull/35784/head
James Bardin 2 years ago
parent 9899d59a22
commit eb184a5213

@ -27,6 +27,9 @@ type OpenEphemeralResourceRequest struct {
// Computed-only attributes are always null in the configuration, because
// they can be set only in the response.
Config cty.Value
// ClientCapabilities contains information about the client's capabilities.
ClientCapabilities ClientCapabilities
}
// OpenEphemeralResourceRequest represents the response from an OpenEphemeralResource
@ -54,7 +57,7 @@ type OpenEphemeralResourceResponse struct {
// where a final value cannot be predicted.
Result cty.Value
// InternalContext is any internal data needed by the provider to perform a
// Private is any internal data needed by the provider to perform a
// subsequent [Interface.CloseEphemeralResource] request for the same object. The
// provider may choose any encoding format to represent the needed data,
// because Terraform Core treats this field as opaque.
@ -71,16 +74,16 @@ type OpenEphemeralResourceResponse struct {
// received by exactly the same plugin instance that returned this value,
// and so it's valid for this to refer to in-memory state belonging to the
// provider instance.
InternalContext []byte
Private []byte
// Renew, if set, signals that the opened object has an inherent expration
// time and so must be "renewed" if Terraform needs to use it beyond that
// expiration time.
// RenewAt, if non-zero, signals that the opened object has an inherent
// expiration time and so must be "renewed" if Terraform needs to use it
// beyond that expiration time.
//
// If a provider sets this field then it may receive a subsequent
// [Interface.RenewEphemeralResource] call, if Terraform expects to need the
// Interface.RenewEphemeralResource call, if Terraform expects to need the
// object beyond the expiration time.
Renew *EphemeralRenew
RenewAt time.Time
// Diagnostics describes any problems encountered while opening the
// ephemeral resource. If this contains errors then the other response
@ -91,11 +94,11 @@ type OpenEphemeralResourceResponse struct {
// EphemeralRenew describes when and how Terraform Core must request renewal
// of an ephemeral resource instance in order to continue using it.
type EphemeralRenew struct {
// ExpireTime is the deadline before which Terraform must renew the
// RenewAt is the deadline before which Terraform must renew the
// ephemeral resource instance.
ExpireTime time.Time
RenewAt time.Time
// InternalContext is any internal data needed by the provider to
// Private is any internal data needed by the provider to
// perform a subsequent [Interface.RenewEphemeralResource] request. The provider
// may choose any encoding format to represent the needed data, because
// Terraform Core treats this field as opaque.
@ -113,7 +116,7 @@ type EphemeralRenew struct {
// the OpenEphemeralResource or RenewEphemeralResource request that produced this internal
// context, and so it's valid for this to refer to in-memory state in the
// provider object.
InternalContext []byte
Private []byte
}
// RenewEphemeralResourceRequest represents the arguments for the RenewEphemeralResource
@ -124,22 +127,28 @@ type RenewEphemeralResourceRequest struct {
// [OpenEphemeralResourceRequest].
TypeName string
// InternalContext echoes verbatim the value from the field of the same
// Private echoes verbatim the value from the field of the same
// name from the most recent [EphemeralRenew] object, received from either
// an [OpenEphemeralResourceResponse] or a [RenewEphemeralResourceResponse] object.
InternalContext []byte
Private []byte
}
// RenewEphemeralResourceRequest represents the response from a RenewEphemeralResource
// operation on a provider.
type RenewEphemeralResourceResponse struct {
// RenewAgain, if set, describes a new expiration deadline for the
// RenewAt, if non-zero, describes a new expiration deadline for the
// object, possibly causing a further call to [Interface.RenewEphemeralResource]
// if Terraform needs to exceed the updated deadline.
//
// If this is not set then Terraform Core will not make any further
// renewal requests for the remaining life of the object.
RenewAgain *EphemeralRenew
RenewAt time.Time
// Private is any internal data needed by the provider to
// perform a subsequent [Interface.RenewEphemeralResource] request. The provider
// may choose any encoding format to represent the needed data, because
// Terraform Core treats this field as opaque.
Private []byte
// Diagnostics describes any problems encountered while renewing the
// ephemeral resource instance. If this contains errors then the other
@ -163,9 +172,9 @@ type CloseEphemeralResourceRequest struct {
// [OpenEphemeralResourceRequest].
TypeName string
// InternalContext echoes verbatim the value from the field of the same
// Private echoes verbatim the value from the field of the same
// name from the corresponding [OpenEphemeralResourceResponse] object.
InternalContext []byte
Private []byte
}
// CloseEphemeralResourceRequest represents the response from a CloseEphemeralResource

@ -65,16 +65,16 @@ func TestResources(t *testing.T) {
Value: cty.ObjectVal(map[string]cty.Value{
"test": cty.StringVal("ephemeral.test.a[0]"),
}),
Impl: testA0,
FirstRenewal: &providers.EphemeralRenew{ExpireTime: time.Now().Add(10 * time.Millisecond)},
Impl: testA0,
RenewAt: time.Now().Add(10 * time.Millisecond),
})
resources.RegisterInstance(ctx, ephemA1, ResourceInstanceRegistration{
Value: cty.ObjectVal(map[string]cty.Value{
"test": cty.StringVal("ephemeral.test.a[1]"),
}),
Impl: testA1,
FirstRenewal: &providers.EphemeralRenew{ExpireTime: time.Now().Add(10 * time.Millisecond)},
Impl: testA1,
RenewAt: time.Now().Add(10 * time.Millisecond),
})
resources.RegisterInstance(ctx, ephemB, ResourceInstanceRegistration{
@ -194,16 +194,16 @@ func TestResourcesCancellation(t *testing.T) {
Value: cty.ObjectVal(map[string]cty.Value{
"test": cty.StringVal("ephemeral.test.a[0]"),
}),
Impl: testA0,
FirstRenewal: &providers.EphemeralRenew{ExpireTime: time.Now().Add(10 * time.Millisecond)},
Impl: testA0,
RenewAt: time.Now().Add(10 * time.Millisecond),
})
resources.RegisterInstance(ctx, ephemA1, ResourceInstanceRegistration{
Value: cty.ObjectVal(map[string]cty.Value{
"test": cty.StringVal("ephemeral.test.a[1]"),
}),
Impl: testA1,
FirstRenewal: &providers.EphemeralRenew{ExpireTime: time.Now().Add(10 * time.Millisecond)},
Impl: testA1,
RenewAt: time.Now().Add(10 * time.Millisecond),
})
resources.RegisterInstance(ctx, ephemB, ResourceInstanceRegistration{
@ -283,7 +283,7 @@ type testResourceInstance struct {
func (r *testResourceInstance) Renew(ctx context.Context, req providers.EphemeralRenew) (*providers.EphemeralRenew, tfdiags.Diagnostics) {
nextRenew := &providers.EphemeralRenew{
ExpireTime: time.Now().Add(r.renewInterval),
RenewAt: time.Now().Add(r.renewInterval),
}
r.Lock()
defer r.Unlock()

@ -40,10 +40,11 @@ func NewResources() *Resources {
}
type ResourceInstanceRegistration struct {
Value cty.Value
ConfigBody hcl.Body
Impl ResourceInstance
FirstRenewal *providers.EphemeralRenew
Value cty.Value
ConfigBody hcl.Body
Impl ResourceInstance
RenewAt time.Time
Private []byte
}
func (r *Resources) RegisterInstance(ctx context.Context, addr addrs.AbsResourceInstance, reg ResourceInstanceRegistration) {
@ -64,12 +65,17 @@ func (r *Resources) RegisterInstance(ctx context.Context, addr addrs.AbsResource
impl: reg.Impl,
renewCancel: noopCancel,
}
if reg.FirstRenewal != nil {
if !reg.RenewAt.IsZero() {
ctx, cancel := context.WithCancel(ctx)
ri.renewCancel = cancel
renewal := &providers.EphemeralRenew{
RenewAt: reg.RenewAt,
Private: reg.Private,
}
r.wg.Add(1)
go ri.handleRenewal(ctx, &r.wg, reg.FirstRenewal)
go ri.handleRenewal(ctx, &r.wg, renewal)
}
r.active.Get(configAddr).Put(addr, ri)
}
@ -220,7 +226,7 @@ func (r *resourceInstanceInternal) close(ctx context.Context) tfdiags.Diagnostic
func (r *resourceInstanceInternal) handleRenewal(ctx context.Context, wg *sync.WaitGroup, firstRenewal *providers.EphemeralRenew) {
defer wg.Done()
t := time.NewTimer(time.Until(firstRenewal.ExpireTime))
t := time.NewTimer(time.Until(firstRenewal.RenewAt))
nextRenew := firstRenewal
for {
select {
@ -242,7 +248,7 @@ func (r *resourceInstanceInternal) handleRenewal(ctx context.Context, wg *sync.W
return
}
nextRenew = anotherRenew
t.Reset(time.Until(anotherRenew.ExpireTime))
t.Reset(time.Until(anotherRenew.RenewAt))
r.renewMu.Unlock()
case <-ctx.Done():
// If we're cancelled then we'll halt renewing immediately.

@ -122,14 +122,15 @@ func ephemeralResourceOpen(ctx EvalContext, inp ephemeralResourceInput) tfdiags.
impl := &ephemeralResourceInstImpl{
addr: inp.addr,
provider: provider,
internal: resp.InternalContext,
internal: resp.Private,
}
ephemerals.RegisterInstance(ctx.StopCtx(), inp.addr, ephemeral.ResourceInstanceRegistration{
Value: resultVal,
ConfigBody: config.Config,
Impl: impl,
FirstRenewal: resp.Renew,
Value: resultVal,
ConfigBody: config.Config,
Impl: impl,
RenewAt: resp.RenewAt,
Private: resp.Private,
})
return diags
@ -185,8 +186,8 @@ var _ ephemeral.ResourceInstance = (*ephemeralResourceInstImpl)(nil)
func (impl *ephemeralResourceInstImpl) Close(ctx context.Context) tfdiags.Diagnostics {
log.Printf("[TRACE] ephemeralResourceInstImpl: closing %s", impl.addr)
resp := impl.provider.CloseEphemeralResource(providers.CloseEphemeralResourceRequest{
TypeName: impl.addr.Resource.Resource.Type,
InternalContext: impl.internal,
TypeName: impl.addr.Resource.Resource.Type,
Private: impl.internal,
})
return resp.Diagnostics
}
@ -195,8 +196,14 @@ func (impl *ephemeralResourceInstImpl) Close(ctx context.Context) tfdiags.Diagno
func (impl *ephemeralResourceInstImpl) Renew(ctx context.Context, req providers.EphemeralRenew) (nextRenew *providers.EphemeralRenew, diags tfdiags.Diagnostics) {
log.Printf("[TRACE] ephemeralResourceInstImpl: renewing %s", impl.addr)
resp := impl.provider.RenewEphemeralResource(providers.RenewEphemeralResourceRequest{
TypeName: impl.addr.Resource.Resource.Type,
InternalContext: req.InternalContext,
TypeName: impl.addr.Resource.Resource.Type,
Private: req.Private,
})
return resp.RenewAgain, resp.Diagnostics
if !resp.RenewAt.IsZero() {
nextRenew.RenewAt = resp.RenewAt
nextRenew.Private = resp.Private
}
return nextRenew, resp.Diagnostics
}

Loading…
Cancel
Save