func (p *Plan) Apply(ctx context.Context, r Runner, diff Diff) (bool, error) {
	validity := p.ApplyResourceGraph(ctx, p.endpoints(), &diff, r)
	statusCheck := func(status Validity) bool {
		return any(validity, func(vtree ValidityTree) bool { return vtree.ValidityStatus == status })
	}
	if statusCheck(Invalid) {
		return false, buildErrorResultTree(p.id, validity, Invalid, ChildInvalid)
	}
	if statusCheck(Inconclusive) {
		return false, buildErrorResultTree(p.id, validity, Inconclusive, ChildInconclusive)
	}
	return any(validity, func(vtree ValidityTree) bool { return vtree.Updated }), nil
}
Apply implements Resource.
Apply applies this plan.
The diff contains:
1) either a state roll-up of contained resource states or nil
2) a set of updated resources (of which only direct dependencies will be handed to
individual resources
package plan
import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"reflect"
	"sort"
	"strings"
	"github.com/weaveworks/cluster-api-provider-existinginfra/pkg/utilities/object"
	"github.com/fatih/structs"
	log "github.com/sirupsen/logrus"
)
type Runner interface {
	RunCommand(ctx context.Context, cmd string, stdin io.Reader) (stdouterr string, err error)
}
type Resource interface {
	State() State
	QueryState(ctx context.Context, runner Runner) (State, error)
	Apply(ctx context.Context, runner Runner, diff Diff) (propagate bool, err error)
	Undo(ctx context.Context, runner Runner, current State) error
}
type RunError struct {
	ExitCode int
}
func (e *RunError) Error() string {
	return fmt.Sprintf("command exited with %d", e.ExitCode)
}
type Plan struct {
	id            string
	resources     map[string]Resource
	graph         *graph
	undoCondition func(Runner, State) bool
}
var (
	dummyPlan    Resource = RegisterResource(&Plan{})
	planTypeName          = extractResourceTypeName(dummyPlan)
)
type Diff struct {
	CurrentState    State
	InvalidatedDeps []Resource
}
type Validity int
const (
	Valid Validity = iota
	Invalid
	Inconclusive
)
type InvalidityReason int
const (
	None InvalidityReason = iota
	ApplyError
	QueryError
	ChildInvalid
	ChildInconclusive
	DependencyInvalid
	DependencyInconclusive
)
type connectors struct {
	inCount int
	in      chan ValidityTree
	outs    []chan ValidityTree
}
type ValidityTree struct {
	ResourceID     string           `json:"resource"`
	ValidityStatus Validity         `json:"status,omitempty"`
	Reason         InvalidityReason `json:"reason,omitempty"`
	Updated        bool             `json:"updated,omitempty"`
	ObservedError  string           `json:"error,omitempty"`
	Dependencies   []ValidityTree   `json:"dependencies,omitempty"`
	Children       []ValidityTree   `json:"children,omitempty"`
}
func EmptyDiff() Diff {
	return Diff{
		CurrentState:    nil,
		InvalidatedDeps: []Resource{}}
}
func (v Validity) String() string {
	return toValidityString[v]
}
var toValidityString = map[Validity]string{
	Valid:        "Valid",
	Invalid:      "Invalid",
	Inconclusive: "Inconclusive",
}
var toValidityID = map[string]Validity{
	"Valid":        Valid,
	"Invalid":      Invalid,
	"Inconclusive": Inconclusive,
}
func (v Validity) MarshalJSON() ([]byte, error) {
	buffer := bytes.NewBufferString(`"`)
	buffer.WriteString(toValidityString[v])
	buffer.WriteString(`"`)
	return buffer.Bytes(), nil
}
func (v *Validity) UnmarshalJSON(b []byte) error {
	var j string
	err := json.Unmarshal(b, &j)
	if err != nil {
		return err
	}
	*v = toValidityID[j]
	return nil
}
func (r InvalidityReason) String() string {
	return toReasonString[r]
}
var toReasonString = map[InvalidityReason]string{
	None:                   "None",
	ApplyError:             "ApplyError",
	QueryError:             "QueryError",
	ChildInvalid:           "ChildInvalid",
	ChildInconclusive:      "ChildInconclusive",
	DependencyInvalid:      "DependencyInvalid",
	DependencyInconclusive: "DependencyInconclusive",
}
var toReasonID = map[string]InvalidityReason{
	"None":                   None,
	"ApplyError":             ApplyError,
	"QueryError":             QueryError,
	"ChildInvalid":           ChildInvalid,
	"ChildInconclusive":      ChildInconclusive,
	"DependencyInvalid":      DependencyInvalid,
	"DependencyInconclusive": DependencyInconclusive,
}
func (r InvalidityReason) MarshalJSON() ([]byte, error) {
	buffer := bytes.NewBufferString(`"`)
	buffer.WriteString(toReasonString[r])
	buffer.WriteString(`"`)
	return buffer.Bytes(), nil
}
func (r *InvalidityReason) UnmarshalJSON(b []byte) error {
	var j string
	err := json.Unmarshal(b, &j)
	if err != nil {
		return err
	}
	*r = toReasonID[j]
	return nil
}
func (v ValidityTree) ToJSON() string {
	str, _ := json.MarshalIndent(v, "", " ")
	return string(str)
}
func (v ValidityTree) prune(seen map[string]bool) ValidityTree {
	if seen[v.ResourceID] {
		return ValidityTree{ResourceID: v.ResourceID}
	}
	seen[v.ResourceID] = true
	copy := v
	copy.Children = []ValidityTree{}
	copy.Dependencies = []ValidityTree{}
	for _, child := range v.Children {
		copy.Children = append(copy.Children, child.prune(seen))
	}
	for _, dep := range v.Dependencies {
		copy.Dependencies = append(copy.Dependencies, dep.prune(seen))
	}
	return copy
}
func (v ValidityTree) ToExplanation() ValidityTree {
	copy := v
	newChildren := []ValidityTree{}
	newDependencies := []ValidityTree{}
	for _, child := range copy.Children {
		if child.ValidityStatus != Valid {
			newChildren = append(newChildren, child.ToExplanation())
		}
	}
	for _, dep := range copy.Dependencies {
		if dep.ValidityStatus != Valid {
			newDependencies = append(newDependencies, dep.ToExplanation())
		}
	}
	copy.Children = newChildren
	copy.Dependencies = newDependencies
	return copy.prune(make(map[string]bool))
}
func (v ValidityTree) ObservedErrorString() string {
	if v.ObservedError == "" {
		return "an unspecified error"
	}
	return v.ObservedError
}
var errorStrings = map[InvalidityReason]string{
	QueryError:             "the current state could not be determined",
	ChildInvalid:           "a child failed",
	ChildInconclusive:      "the current state of a child could not be determined",
	DependencyInvalid:      "a dependency failed",
	DependencyInconclusive: "the current state of a dependency could not be determined",
}
func (v ValidityTree) Error() string {
	prefix := "Apply failed because "
	if v.Reason == ApplyError {
		return prefix + "of: " + v.ObservedErrorString()
	}
	return prefix + errorStrings[v.Reason]
}
func newPlan() *Plan {
	return &Plan{
		resources: make(map[string]Resource),
		graph:     newGraph(16),
	}
}
func (p *Plan) addResource(id string, r Resource, options *addOptions) error {
	assertRegistered(r)
	if _, ok := p.resources[id]; ok {
		return fmt.Errorf("plan: resource %q already in the plan", id)
	}
	if plan, ok := r.(*Plan); ok {
		plan.id = id
	}
	p.resources[id] = r
	p.graph.AddNode(id)
	for _, dep := range options.deps {
		p.graph.AddEdge(dep, id)
	}
	return nil
}
func (p *Plan) GetResource(name string) Resource {
	return p.resources[name]
}
type resourceType struct {
	baseType reflect.Type
	isPtr    bool
}
func (p *Plan) ToJSON() string {
	s := displayableState(p.toState())
	json, _ := json.Marshal(s)
	return string(json)
}
func (p *Plan) ToHumanReadableJSON() string {
	s := displayableState(p.toState())
	json, _ := json.MarshalIndent(s, "", " ")
	return string(json)
}
func (p *Plan) ToState() State {
	return displayableState(p.toState())
}
func (p *Plan) ToDOT() string {
	var dot bytes.Buffer
	dot.WriteString("digraph wks {\n")
	dot.WriteString("\t node [shape=box, style=bold]\n")
	dot.WriteString("\t ranksep=\"1.0 equally\"\n")
	p.toDOTBody(&dot)
	p.childrenToDOT(&dot)
	dot.WriteString("}")
	return dot.String()
}
func (p *Plan) childrenToDOT(dot *bytes.Buffer) {
	children, _ := p.graph.Toposort()
	for _, child := range children {
		dot.WriteString(fmt.Sprintf("\t\"%s\" -> \"%s\" [style=dashed color=red]\n", child, p.id))
	}
}
func (p *Plan) toDOTBody(dot *bytes.Buffer) {
	resDeps := extractDependencies(p.graph)
	for name, res := range p.resources {
		pres, isPlan := res.(*Plan)
		dot.WriteString(fmt.Sprintf("\t\"%s\"", name))
		k := reflect.ValueOf(res).Kind()
		if k == reflect.Interface || k == reflect.Ptr {
			resElem := reflect.ValueOf(res).Elem()
			dot.WriteString(fmt.Sprintf(" [label=<<TABLE BORDER=\"0\"><TR><TD COLSPAN=\"2\" align=\"center\">%s<BR/>%s</TD></TR>",
				name, resElem.Type()))
			style := "rounded"
			if isPlan {
				style = "bold"
			}
			dot.WriteString(fmt.Sprintf("</TABLE>> style=%s]\n", style))
			if isPlan {
				pres.toDOTBody(dot)
			}
		}
		deps := resDeps[name]
		for _, d := range deps {
			dot.WriteString(fmt.Sprintf("\t\"%s\" -> \"%s\" [style=bold color=blue]\n", d, name))
		}
		if isPlan {
			pres.childrenToDOT(dot)
		}
		dot.WriteString("\n")
	}
}
func NewPlanFromJSON(r io.Reader) (Plan, error) {
	s, err := NewStateFromJSON(r)
	if err != nil {
		return *newPlan(), err
	}
	return s.toPlan(), nil
}
var resourcePackageName = filepath.Join(reflect.ValueOf(EmptyState).Type().PkgPath(), "resource")
var resourceTypes = make(map[string]resourceType)
func RegisterResource(r Resource) Resource {
	resourceTypes[extractResourceTypeName(r)] = resourceBaseType(r)
	return r
}
func assertRegistered(r Resource) {
	if _, ok := resourceTypes[extractResourceTypeName(r)]; !ok {
		log.Fatalf("Resource type: '%v' not registered. "+
			"Please insert a line such as the following in your resource's source file: \""+
			"var _ plan.Resource = plan.RegisterResource(<resource instance>)\"", reflect.TypeOf(r))
	}
}
func resourceTypeByName(name string) resourceType {
	return resourceTypes[name]
}
func EqualPlans(p1, p2 Plan) bool {
	return p1.ToJSON() == p2.ToJSON()
}
func (p *Plan) toState() State {
	planData := make(map[string]interface{})
	deps := extractDependencies(p.graph)
	for id, r := range p.resources {
		typename := extractResourceTypeName(r)
		depends := deps[id]
		if depends == nil {
			depends = []string{}
		}
		meta := map[string]interface{}{"dependsOn": depends}
		resData := structs.Map(r)
		if planResource, ok := r.(*Plan); ok {
			resData = planResource.toState()
		}
		planData[id] = map[string]interface{}{
			"meta":   meta,
			typename: resData,
		}
	}
	return State(planData)
}
func displayableState(planState State) State {
	m := map[string]interface{}{}
	for id, resval := range planState {
		resmap := resval.(map[string]interface{})
		m[id] = displayableResourceState(resmap)
	}
	return State(m)
}
func displayableResourceState(stateEntry map[string]interface{}) map[string]interface{} {
	resourceMap := map[string]interface{}{}
	resourceMap["meta"] = stateEntry["meta"]
	for typeName, valstate := range stateEntry {
		if typeName != "meta" {
			resourceMap[typeName] = displayableResourceData(typeName, valstate)
			break
		}
	}
	return resourceMap
}
func displayableResourceData(typeName string, stateData interface{}) State {
	if typeName == planTypeName {
		return displayableState(State(stateData.(map[string]interface{})))
	}
	rtype := resourceTypeByName(typeName)
	rmap := map[string]interface{}{}
	for i := 0; i < rtype.baseType.NumField(); i++ {
		f := rtype.baseType.Field(i)
		planTag := f.Tag.Get("plan")
		if planTag == "hide" {
			continue
		}
		key := f.Name
		structsTag := f.Tag.Get("structs")
		if structsTag != "" {
			tagItems := strings.Split(structsTag, ",")
			key = tagItems[0]
		}
		val, ok := stateData.(map[string]interface{})[key]
		if ok {
			rmap[key] = val
		}
	}
	return State(rmap)
}
func resourceBaseType(r Resource) resourceType {
	val := reflect.ValueOf(r)
	isPtr := (val.Kind() == reflect.Ptr)
	if isPtr {
		val = reflect.Indirect(val)
	}
	return resourceType{val.Type(), isPtr}
}
func extractResourceTypeName(r Resource) string {
	var typename string
	btype := resourceBaseType(r).baseType
	ppath := btype.PkgPath()
	if ppath != resourcePackageName {
		typename = filepath.Join(ppath, btype.Name())
	} else {
		typename = btype.Name()
	}
	return typename
}
func extractDependencies(graphval *graph) map[string][]string {
	dependsOn := make(map[string][]string)
	for from, tos := range graphval.edges {
		for to := range tos {
			dependencies := dependsOn[to]
			if dependencies == nil {
				dependencies = make([]string, 0)
			}
			dependencies = append(dependencies, from)
			sort.Strings(dependencies)
			dependsOn[to] = dependencies
		}
	}
	return dependsOn
}
func sortTreeDeps(deps []ValidityTree) {
	sort.Slice(deps,
		func(i, j int) bool {
			return strings.Compare(deps[i].ResourceID, deps[j].ResourceID) < 0
		})
}
func any(depTrees map[string]ValidityTree, pred func(ValidityTree) bool) bool {
	for _, vtree := range depTrees {
		if pred(vtree) {
			return true
		}
	}
	return false
}
func buildErrorResultTree(
	id string,
	vmap map[string]ValidityTree,
	status Validity,
	reason InvalidityReason) ValidityTree {
	childValidities := make([]ValidityTree, 0)
	for _, vtree := range vmap {
		childValidities = append(childValidities, vtree)
	}
	sortTreeDeps(childValidities)
	return ValidityTree{
		ResourceID:     id,
		ValidityStatus: status,
		Reason:         reason,
		ObservedError:  "Apply failed because " + errorStrings[reason],
		Children:       childValidities,
		Dependencies:   make([]ValidityTree, 0)}
}
func (p *Plan) endpoints() []string {
	endpoints := make([]string, 0)
	dependents := extractDependencies(p.graph.Invert())
	for rid := range p.resources {
		if len(dependents[rid]) == 0 {
			endpoints = append(endpoints, rid)
		}
	}
	return endpoints
}