pulumi/pkg/resource/deploy/source_fixed.go
Sean Gillespie 9a5f4044fa
Serialize SourceEvents coming from the refresh source (#1725)
* Serialize SourceEvents coming from the refresh source

The engine requires that a source event coming from a source be "ready
to execute" at the moment that it is sent to the engine. Since the
refresh source sent all goal states eagerly through its source iterator,
the engine assumed that it was legal to execute them all in parallel and
did so. This is a problem for the snapshot, since the snapshot expects
to be in an order that is a legal topological ordering of the dependency
DAG.

This PR fixes the issue by sending refresh source events one-at-a-time
through the refresh source iterator, only unblocking to send the next
step as soon as the previous step completes.

* Fix deadlock in refresh test

* Fix an issue where the engine "completed" steps too early

By signalling that a step is done before committing the step's results
to the snapshot, the engine was left with a race where dependent
resources could find themselves completely executed and committed before
a resource that they depend on has been committed.

Fixes pulumi/pulumi#1726

* Fix an issue with Replace steps at the end of a plan

If the last step that was executed successfully was a Replace, we could
end up in a situation where we unintentionally left the snapshot
invalid.

* Add a test

* CR: pass context.Context as first parameter to Iterate

* CR: null->nil
2018-08-08 13:45:48 -07:00

65 lines
2 KiB
Go

// Copyright 2016-2018, 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 deploy
import (
"context"
"github.com/pulumi/pulumi/pkg/tokens"
"github.com/pulumi/pulumi/pkg/util/contract"
)
// NewFixedSource returns a valid planning source that is comprised of a list of pre-computed steps.
func NewFixedSource(ctx tokens.PackageName, steps []SourceEvent) Source {
return &fixedSource{ctx: ctx, steps: steps}
}
// A fixedSource just returns from a fixed set of resource states.
type fixedSource struct {
ctx tokens.PackageName
steps []SourceEvent
}
func (src *fixedSource) Close() error { return nil }
func (src *fixedSource) Project() tokens.PackageName { return src.ctx }
func (src *fixedSource) Info() interface{} { return nil }
func (src *fixedSource) IsRefresh() bool { return false }
func (src *fixedSource) Iterate(ctx context.Context, opts Options, providers ProviderSource) (SourceIterator, error) {
contract.Ignore(ctx) // TODO[pulumi/pulumi#1714]
return &fixedSourceIterator{
src: src,
current: -1,
}, nil
}
// fixedSourceIterator always returns nil, nil in response to Next, indicating that it is done.
type fixedSourceIterator struct {
src *fixedSource
current int
}
func (iter *fixedSourceIterator) Close() error {
return nil // nothing to do.
}
func (iter *fixedSourceIterator) Next() (SourceEvent, error) {
iter.current++
if iter.current >= len(iter.src.steps) {
return nil, nil
}
return iter.src.steps[iter.current], nil
}