pulumi/pkg/resource/deploy/source_refresh.go
joeduffy bac58d7922 Respond to CR feedback
Incorporate feedback from @swgillespie and @pgavlin.
2018-04-18 11:46:37 -07:00

106 lines
3.3 KiB
Go

// Copyright 2016-2018, Pulumi Corporation. All rights reserved.
package deploy
import (
"github.com/pkg/errors"
"github.com/pulumi/pulumi/pkg/resource"
"github.com/pulumi/pulumi/pkg/resource/plugin"
"github.com/pulumi/pulumi/pkg/tokens"
"github.com/pulumi/pulumi/pkg/workspace"
)
// NewRefreshSource returns a new source that generates events based on reading an existing checkpoint state,
// combined with refreshing its associated resource state from the cloud provider.
func NewRefreshSource(plugctx *plugin.Context, proj *workspace.Project, target *Target, dryRun bool) Source {
return &refreshSource{
plugctx: plugctx,
proj: proj,
target: target,
dryRun: dryRun,
}
}
// A refreshSource refreshes resource state from the cloud provider.
type refreshSource struct {
plugctx *plugin.Context
proj *workspace.Project
target *Target
dryRun bool
}
func (src *refreshSource) Close() error { return nil }
func (src *refreshSource) Project() tokens.PackageName { return src.proj.Name }
func (src *refreshSource) Info() interface{} { return nil }
func (src *refreshSource) IsRefresh() bool { return true }
func (src *refreshSource) Iterate(opts Options) (SourceIterator, error) {
var states []*resource.State
if snap := src.target.Snapshot; snap != nil {
states = snap.Resources
}
return &refreshSourceIterator{
plugctx: src.plugctx,
states: states,
current: -1,
}, nil
}
// refreshSourceIterator returns state from an existing snapshot, augmented by consulting the resource provider.
type refreshSourceIterator struct {
plugctx *plugin.Context
states []*resource.State
current int
}
func (iter *refreshSourceIterator) Close() error {
return nil // nothing to do.
}
func (iter *refreshSourceIterator) Next() (SourceEvent, error) {
for {
iter.current++
if iter.current >= len(iter.states) {
return nil, nil
}
goal, err := iter.newRefreshGoal(iter.states[iter.current])
if err != nil {
return nil, err
} else if goal != nil {
return &refreshSourceEvent{goal: goal}, nil
}
// If the goal was nil, it means the resource was deleted, and we should keep going.
}
}
// newRefreshGoal refreshes the state, if appropriate, and returns a new goal state.
func (iter *refreshSourceIterator) newRefreshGoal(s *resource.State) (*resource.Goal, error) {
// If this is a custom resource, go ahead and load up its plugin, and ask it to refresh the state.
if s.Custom {
provider, err := iter.plugctx.Host.Provider(s.Type.Package(), nil)
if err != nil {
return nil, errors.Wrapf(err, "fetching provider to refresh %s", s.URN)
}
refreshed, err := provider.Read(s.URN, s.ID, s.Outputs)
if err != nil {
return nil, errors.Wrapf(err, "refreshing %s's state", s.URN)
} else if refreshed == nil {
return nil, nil // the resource was deleted.
}
s = resource.NewState(
s.Type, s.URN, s.Custom, s.Delete, s.ID, s.Inputs, refreshed, s.Parent, s.Protect, s.Dependencies)
}
// Now just return the actual state as the goal state.
return resource.NewGoal(s.Type, s.URN.Name(), s.Custom, s.Outputs, s.Parent, s.Protect, s.Dependencies), nil
}
type refreshSourceEvent struct {
goal *resource.Goal
}
func (rse *refreshSourceEvent) event() {}
func (rse *refreshSourceEvent) Goal() *resource.Goal { return rse.goal }
func (rse *refreshSourceEvent) Done(result *RegisterResult) {}