// 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 ( "github.com/pulumi/pulumi/pkg/diag" "github.com/pulumi/pulumi/pkg/resource" "github.com/pulumi/pulumi/pkg/resource/graph" "github.com/pulumi/pulumi/pkg/resource/plugin" "github.com/pulumi/pulumi/pkg/tokens" "github.com/pulumi/pulumi/pkg/util/contract" ) // TODO[pulumi/pulumi#106]: plan parallelism. // Plan is the output of analyzing resource graphs and contains the steps necessary to perform an infrastructure // deployment. A plan can be generated out of whole cloth from a resource graph -- in the case of new deployments -- // however, it can alternatively be generated by diffing two resource graphs -- in the case of updates to existing // stacks (presumably more common). The plan contains step objects that can be used to drive a deployment. type Plan struct { ctx *plugin.Context // the plugin context (for provider operations). target *Target // the deployment target. prev *Snapshot // the old resource snapshot for comparison. olds map[resource.URN]*resource.State // a map of all old resources. source Source // the source of new resources. analyzers []tokens.QName // the analyzers to run during this plan's generation. preview bool // true if this plan is to be previewed rather than applied. depGraph *graph.DependencyGraph // the dependency graph of the old snapshot } // NewPlan creates a new deployment plan from a resource snapshot plus a package to evaluate. // // From the old and new states, it understands how to orchestrate an evaluation and analyze the resulting resources. // The plan may be used to simply inspect a series of operations, or actually perform them; these operations are // generated based on analysis of the old and new states. If a resource exists in new, but not old, for example, it // results in a create; if it exists in both, but is different, it results in an update; and so on and so forth. // // Note that a plan uses internal concurrency and parallelism in various ways, so it must be closed if for some reason // a plan isn't carried out to its final conclusion. This will result in cancelation and reclamation of OS resources. func NewPlan(ctx *plugin.Context, target *Target, prev *Snapshot, source Source, analyzers []tokens.QName, preview bool) *Plan { contract.Assert(ctx != nil) contract.Assert(target != nil) contract.Assert(source != nil) var depGraph *graph.DependencyGraph // Produce a map of all old resources for fast resources. olds := make(map[resource.URN]*resource.State) if prev != nil { for _, oldres := range prev.Resources { // Ignore resources that are pending deletion; these should not be recorded in the LUT. if oldres.Delete { continue } urn := oldres.URN contract.Assert(olds[urn] == nil) olds[urn] = oldres } depGraph = graph.NewDependencyGraph(prev.Resources) } return &Plan{ ctx: ctx, target: target, prev: prev, olds: olds, source: source, analyzers: analyzers, preview: preview, depGraph: depGraph, } } func (p *Plan) Ctx() *plugin.Context { return p.ctx } func (p *Plan) Target() *Target { return p.target } func (p *Plan) Diag() diag.Sink { return p.ctx.Diag } func (p *Plan) Prev() *Snapshot { return p.prev } func (p *Plan) Olds() map[resource.URN]*resource.State { return p.olds } func (p *Plan) Source() Source { return p.source } func (p *Plan) IsRefresh() bool { return p.source.IsRefresh() } // Provider fetches the provider for a given resource type, possibly lazily allocating the plugins for it. If a // provider could not be found, or an error occurred while creating it, a non-nil error is returned. func (p *Plan) Provider(pkg tokens.Package) (plugin.Provider, error) { // TODO: ideally we would flow versions on specific requests along to the underlying host function. Absent that, // we will just pass nil, which returns us the most recent version available to us. return p.ctx.Host.Provider(pkg, nil) }