Ensure configuration round-trips in Huskfiles

This commit is contained in:
joeduffy 2017-02-28 15:43:46 -08:00
parent c77329129a
commit 6a2edc9159
10 changed files with 192 additions and 153 deletions

View file

@ -34,16 +34,17 @@ func newEvalCmd() *cobra.Command {
// Perform the compilation and, if non-nil is returned, output the graph.
if result := compile(cmd, args, nil); result != nil {
// Serialize that evaluation graph so that it's suitable for printing/serializing.
g := result.Heap.G
if dotOutput {
// Convert the output to a DOT file.
if err := dotconv.Print(result.Heap.G, os.Stdout); err != nil {
if err := dotconv.Print(g, os.Stdout); err != nil {
fmt.Fprintf(os.Stderr, "error: failed to write DOT file to output: %v\n", err)
os.Exit(-1)
}
} else {
// Just print a very basic, yet (hopefully) aesthetically pleasinge, ascii-ization of the graph.
shown := make(map[graph.Vertex]bool)
for _, root := range result.Heap.G.Objs() {
for _, root := range g.Objs() {
printVertex(root.ToObj(), shown, "")
}
}

View file

@ -70,37 +70,39 @@ func initHuskCmd(cmd *cobra.Command, args []string) *huskCmdInfo {
fmt.Fprintf(os.Stderr, "fatal: missing required husk name\n")
os.Exit(-1)
}
husk := tokens.QName(args[0])
// Read in the deployment information, bailing if an IO error occurs.
dep, old := readHusk(ctx, husk)
if dep == nil {
name := tokens.QName(args[0])
huskfile, husk, old := readHusk(ctx, name)
if husk == nil {
contract.Assert(!ctx.Diag.Success())
return nil // failure reading the husk information.
}
return &huskCmdInfo{
ctx: ctx,
husk: husk,
dep: dep,
old: old,
args: args[1:],
orig: args,
Ctx: ctx,
Husk: husk,
Huskfile: huskfile,
Old: old,
Args: args[1:],
Orig: args,
}
}
type huskCmdInfo struct {
ctx *resource.Context // the resulting context
husk tokens.QName // the husk name
dep *resource.Deployment // the husk's deployment record
old resource.Snapshot // the husk's latest deployment snapshot
args []string // the rest of the args after extracting the husk name
orig []string // the original args before extracting the husk name
Ctx *resource.Context // the resulting context
Husk *resource.Husk // the husk information
Huskfile *resource.Huskfile // the full serialized huskfile from which this came.
Old resource.Snapshot // the husk's latest deployment snapshot
Args []string // the rest of the args after extracting the husk name
Orig []string // the original args before extracting the husk name
}
// create just creates a new husk without deploying anything into it.
func create(husk tokens.QName) {
// create just creates a new empty husk without deploying anything into it.
// TODO: add the ability to configure the husk at the command line.
func create(name tokens.QName) {
husk := &resource.Husk{Name: name}
if success := saveHusk(husk, nil, "", false); success {
fmt.Printf("Coconut husk '%v' initialized; ready for deployments (see `coco husk deploy`)\n", husk)
fmt.Printf("Coconut husk '%v' initialized; ready for deployments (see `coco husk deploy`)\n", name)
}
}
@ -149,7 +151,7 @@ func prepareCompiler(cmd *cobra.Command, args []string) (compiler.Compiler, *pac
// compile just uses the standard logic to parse arguments, options, and to locate/compile a package. It returns the
// CocoGL graph that is produced, or nil if an error occurred (in which case, we would expect non-0 errors).
func compile(cmd *cobra.Command, args []string, config *resource.ConfigMap) *compileResult {
func compile(cmd *cobra.Command, args []string, config resource.ConfigMap) *compileResult {
// Prepare the compiler info and, provided it succeeds, perform the compilation.
if comp, pkg := prepareCompiler(cmd, args); comp != nil {
// Create the preexec hook if the config map is non-nil.
@ -208,29 +210,27 @@ func plan(cmd *cobra.Command, info *huskCmdInfo, delete bool) *planResult {
var result *compileResult
if !delete {
// First, compile; if that yields errors or an empty heap, exit early.
if result = compile(cmd, info.args, info.dep.Config); result == nil || result.Heap == nil {
if result = compile(cmd, info.Args, info.Husk.Config); result == nil || result.Heap == nil {
return nil
}
// Create a resource snapshot from the compiled/evaluated object graph.
var err error
new, err = resource.NewGraphSnapshot(
info.ctx, info.husk, result.Pkg.Tok, result.C.Ctx().Opts.Args, result.Heap)
info.Ctx, info.Husk.Name, result.Pkg.Tok, result.C.Ctx().Opts.Args, result.Heap)
if err != nil {
result.C.Diag().Errorf(errors.ErrorCantCreateSnapshot, err)
return nil
} else if !info.ctx.Diag.Success() {
} else if !info.Ctx.Diag.Success() {
return nil
}
}
// Generate a plan; this API handles all interesting cases (create, update, delete).
plan := resource.NewPlan(info.ctx, info.old, new)
plan := resource.NewPlan(info.Ctx, info.Old, new)
return &planResult{
compileResult: result,
Ctx: info.ctx,
Husk: info.husk,
Old: info.old,
Info: info,
New: new,
Plan: plan,
}
@ -238,18 +238,17 @@ func plan(cmd *cobra.Command, info *huskCmdInfo, delete bool) *planResult {
type planResult struct {
*compileResult
Ctx *resource.Context
Husk tokens.QName // the husk name.
Info *huskCmdInfo // plan command information.
Old resource.Snapshot // the existing snapshot (if any).
New resource.Snapshot // the new snapshot for this plan (if any).
Plan resource.Plan
Plan resource.Plan // the plan created by this command.
}
func apply(cmd *cobra.Command, info *huskCmdInfo, opts applyOptions) {
if result := plan(cmd, info, opts.Delete); result != nil {
// If we are doing an empty update, say so.
if result.Plan.Empty() && !opts.Delete {
info.ctx.Diag.Infof(diag.Message("nothing to do -- resources are up to date"))
info.Ctx.Diag.Infof(diag.Message("nothing to do -- resources are up to date"))
}
// Now based on whether a dry run was specified, or not, either print or perform the planned operations.
@ -258,7 +257,7 @@ func apply(cmd *cobra.Command, info *huskCmdInfo, opts applyOptions) {
if opts.Output == "" || opts.Output == "-" {
printPlan(result, opts)
} else {
saveHusk(info.husk, result.New, opts.Output, true /*overwrite*/)
saveHusk(info.Husk, result.New, opts.Output, true /*overwrite*/)
}
} else {
// If show unchanged was requested, print them first, along with a header.
@ -275,7 +274,7 @@ func apply(cmd *cobra.Command, info *huskCmdInfo, opts applyOptions) {
// TODO: we want richer diagnostics in the event that a plan apply fails. For instance, we want to
// know precisely what step failed, we want to know whether it was catastrophic, etc. We also
// probably want to plumb diag.Sink through apply so it can issue its own rich diagnostics.
info.ctx.Diag.Errorf(errors.ErrorPlanApplyFailed, err)
info.Ctx.Diag.Errorf(errors.ErrorPlanApplyFailed, err)
}
// Print out the total number of steps performed (and their kinds), the duration, and any summary info.
@ -291,12 +290,13 @@ func apply(cmd *cobra.Command, info *huskCmdInfo, opts applyOptions) {
// Now save the updated snapshot to the specified output file, if any, or the standard location otherwise.
// Note that if a failure has occurred, the Apply routine above will have returned a safe checkpoint.
saveHusk(result.Husk, checkpoint, opts.Output, true /*overwrite*/)
husk := result.Info.Husk
saveHusk(husk, checkpoint, opts.Output, true /*overwrite*/)
// If a deletion was requested, remove the husk; but only if no error has occurred!
if err == nil && opts.Delete {
deleteHusk(result.Husk)
fmt.Printf("Coconut husk '%v' has been destroyed!\n", result.Husk)
deleteHusk(husk)
fmt.Printf("Coconut husk '%v' has been destroyed!\n", husk.Name)
}
}
}
@ -311,41 +311,41 @@ func backupHusk(file string) {
}
// deleteHusk removes an existing snapshot file, leaving behind a backup.
func deleteHusk(husk tokens.QName) {
contract.Require(husk != "", "husk")
func deleteHusk(husk *resource.Husk) {
contract.Require(husk != nil, "husk")
// Just make a backup of the file and don't write out anything new.
file := workspace.HuskPath(husk)
file := workspace.HuskPath(husk.Name)
backupHusk(file)
}
// readHusk reads in an existing snapshot file, issuing an error and returning nil if something goes awry.
func readHusk(ctx *resource.Context, husk tokens.QName) (*resource.Deployment, resource.Snapshot) {
contract.Require(husk != "", "husk")
file := workspace.HuskPath(husk)
func readHusk(ctx *resource.Context, name tokens.QName) (*resource.Huskfile, *resource.Husk, resource.Snapshot) {
contract.Require(name != "", "name")
file := workspace.HuskPath(name)
// Detect the encoding of the file so we can do our initial unmarshaling.
m, ext := encoding.Detect(file)
if m == nil {
ctx.Diag.Errorf(errors.ErrorIllegalMarkupExtension, ext)
return nil, nil
return nil, nil, nil
}
// Now read the whole file into a byte blob.
b, err := ioutil.ReadFile(file)
if err != nil {
if os.IsNotExist(err) {
ctx.Diag.Errorf(errors.ErrorInvalidHuskName, husk)
ctx.Diag.Errorf(errors.ErrorInvalidHuskName, name)
} else {
ctx.Diag.Errorf(errors.ErrorIO, err)
}
return nil, nil
return nil, nil, nil
}
// Unmarshal the contents into a deployment structure.
var dep resource.Deployment
if err = m.Unmarshal(b, &dep); err != nil {
// Unmarshal the contents into a huskfile deployment structure.
var huskfile resource.Huskfile
if err = m.Unmarshal(b, &huskfile); err != nil {
ctx.Diag.Errorf(errors.ErrorCantReadDeployment, file, err)
return nil, nil
return nil, nil, nil
}
// Next, use the mapping infrastructure to validate the contents.
@ -353,7 +353,7 @@ func readHusk(ctx *resource.Context, husk tokens.QName) (*resource.Deployment, r
var obj mapper.Object
if err = m.Unmarshal(b, &obj); err != nil {
ctx.Diag.Errorf(errors.ErrorCantReadDeployment, file, err)
return nil, nil
return nil, nil, nil
} else {
if obj["latest"] != nil {
if latest, islatest := obj["latest"].(map[string]interface{}); islatest {
@ -361,21 +361,23 @@ func readHusk(ctx *resource.Context, husk tokens.QName) (*resource.Deployment, r
}
}
md := mapper.New(nil)
var ignore resource.Deployment // just for errors.
var ignore resource.Huskfile // just for errors.
if err = md.Decode(obj, &ignore); err != nil {
ctx.Diag.Errorf(errors.ErrorCantReadDeployment, file, err)
return nil, nil
return nil, nil, nil
}
}
return &dep, resource.DeserializeDeployment(ctx, &dep)
husk, snap := resource.DeserializeHuskfile(ctx, &huskfile)
contract.Assert(husk != nil)
return &huskfile, husk, snap
}
// saveHusk saves a new snapshot at the given location, backing up any existing ones.
func saveHusk(husk tokens.QName, snap resource.Snapshot, file string, existok bool) bool {
contract.Require(husk != "", "husk")
func saveHusk(husk *resource.Husk, snap resource.Snapshot, file string, existok bool) bool {
contract.Require(husk != nil, "husk")
if file == "" {
file = workspace.HuskPath(husk)
file = workspace.HuskPath(husk.Name)
}
// Make a serializable CocoGL data structure and then use the encoder to encode it.
@ -387,7 +389,7 @@ func saveHusk(husk tokens.QName, snap resource.Snapshot, file string, existok bo
if filepath.Ext(file) == "" {
file = file + ext
}
dep := resource.SerializeDeployment(husk, snap, "")
dep := resource.SerializeHuskfile(husk, snap, "")
b, err := m.Marshal(dep)
if err != nil {
sink().Errorf(errors.ErrorIO, err)

View file

@ -28,7 +28,7 @@ func newHuskDestroyCmd() *cobra.Command {
Run: func(cmd *cobra.Command, args []string) {
if info := initHuskCmd(cmd, args); info != nil {
if !dryRun && !yes {
fmt.Printf("This will permanently delete all resources in the '%v' husk!\n", info.husk)
fmt.Printf("This will permanently delete all resources in the '%v' husk!\n", info.Husk.Name)
fmt.Printf("Please confirm that this is what you'd like to do by typing (\"yes\"): ")
reader := bufio.NewReader(os.Stdin)
if line, _ := reader.ReadString('\n'); line != "yes\n" {

View file

@ -39,17 +39,17 @@ func newHuskLsCmd() *cobra.Command {
}
// Skip files without valid extensions (e.g., *.bak files).
huskfile := file.Name()
ext := filepath.Ext(huskfile)
huskfn := file.Name()
ext := filepath.Ext(huskfn)
if _, has := encoding.Marshalers[ext]; !has {
continue
}
// Create a new context and read in the husk information.
husk := tokens.QName(huskfile[:len(huskfile)-len(ext)])
name := tokens.QName(huskfn[:len(huskfn)-len(ext)])
ctx := resource.NewContext(sink())
dep, old := readHusk(ctx, husk)
if dep == nil {
huskfile, husk, old := readHusk(ctx, name)
if husk == nil {
contract.Assert(!ctx.Diag.Success())
continue // failure reading the husk information.
}
@ -57,13 +57,13 @@ func newHuskLsCmd() *cobra.Command {
// Now print out the name, last deployment time (if any), and resources (if any).
lastDeploy := "n/a"
resourceCount := "n/a"
if dep.Latest != nil {
lastDeploy = dep.Latest.Time.String()
if huskfile.Latest != nil {
lastDeploy = huskfile.Latest.Time.String()
}
if old != nil {
resourceCount = strconv.Itoa(len(old.Resources()))
}
fmt.Printf("%-20s %-48s %-12s\n", dep.Husk, lastDeploy, resourceCount)
fmt.Printf("%-20s %-48s %-12s\n", husk.Name, lastDeploy, resourceCount)
}
},
}

View file

@ -163,6 +163,7 @@ func (c *compiler) CompilePackage(pkg *pack.Package, preexec Preexec) (*symbols.
// Create a fresh evaluator; if there are pre-exec hooks, run them now.
e := eval.New(b.Ctx(), gg)
if preexec != nil {
glog.V(7).Infof("Invoking compiler preexec routine")
preexec(b.Ctx(), pkgsym, e)
}
if !c.Diag().Success() {

View file

@ -41,7 +41,9 @@ func (cfg *ConfigMap) ConfigApplier(vars map[tokens.Token]*rt.Object) compiler.P
// we are accessing module and class members, this routine will also trigger the relevant initialization routines.
func (cfg *ConfigMap) ApplyConfig(ctx *binder.Context, pkg *symbols.Package,
e eval.Interpreter) map[tokens.Token]*rt.Object {
// Keep track of all variables applied:
glog.V(5).Infof("Applying configuration values for package '%v'", pkg)
// Track all configuration variables that get set, for diagnostics and plumbing.
vars := make(map[tokens.Token]*rt.Object)
if cfg != nil {

View file

@ -13,13 +13,6 @@ import (
"github.com/pulumi/coconut/pkg/util/contract"
)
// Deployment is a serialized deployment target plus a record of the latest deployment.
type Deployment struct {
Husk tokens.QName `json:"husk"` // the target environment name.
Config *ConfigMap `json:"config,omitempty"` // optional configuration key/values.
Latest *DeploymentRecord `json:"latest,omitempty"` // the latest/current deployment record.
}
// DeploymentRecord is a serializable, flattened CocoGL graph structure, representing a deployment. It is similar
// to the actual Snapshot interface, except that it flattens and rearranges a few data structures for serializability.
// Over time, we also expect this to gather more information about deployments themselves.
@ -44,19 +37,6 @@ type ResourceDeployment struct {
// DeployedPropertyMap is a property map from resource key to the underlying property value.
type DeployedPropertyMap map[string]interface{}
// SerializeDeployment turns a snapshot into a CocoGL data structure suitable for serialization.
func SerializeDeployment(husk tokens.QName, snap Snapshot, reftag string) *Deployment {
// If snap is nil, that's okay, we will just create an empty deployment; otherwise, serialize the whole snapshot.
var latest *DeploymentRecord
if snap != nil {
latest = serializeDeploymentRecord(snap, reftag)
}
return &Deployment{
Husk: husk,
Latest: latest,
}
}
func serializeDeploymentRecord(snap Snapshot, reftag string) *DeploymentRecord {
// Initialize the reftag if needed, and only serialize if overridden.
var refp *string
@ -167,53 +147,6 @@ func serializeProperty(prop PropertyValue, reftag string) (interface{}, bool) {
return prop.V, true
}
// DeserializeDeploymentRecord takes a serialized deployment record and returns its associated snapshot.
func DeserializeDeployment(ctx *Context, ser *Deployment) Snapshot {
latest := ser.Latest
if latest == nil {
return nil
}
// Determine the reftag to use.
var reftag string
if latest.Reftag == nil {
reftag = DefaultDeploymentReftag
} else {
reftag = *latest.Reftag
}
// For every serialized resource vertex, create a ResourceDeployment out of it.
var resources []Resource
if latest.Resources != nil {
for _, kvp := range latest.Resources.Iter() {
// Deserialize the resources, if they exist.
res := kvp.Value
var props PropertyMap
if res.Properties == nil {
props = make(PropertyMap)
} else {
props = deserializeProperties(*res.Properties, reftag)
}
// And now just produce a resource object using the information available.
var id ID
if res.ID != nil {
id = *res.ID
}
resources = append(resources, NewResource(id, kvp.Key, res.Type, props))
}
}
// If the args are non-nil, use them.
var args core.Args
if latest.Args != nil {
args = *latest.Args
}
return NewSnapshot(ctx, ser.Husk, latest.Package, args, resources)
}
func deserializeProperties(props DeployedPropertyMap, reftag string) PropertyMap {
result := make(PropertyMap)
for k, prop := range props {

100
pkg/resource/husk.go Normal file
View file

@ -0,0 +1,100 @@
// Copyright 2016 Pulumi, Inc. All rights reserved.
package resource
import (
"github.com/pulumi/coconut/pkg/compiler/core"
"github.com/pulumi/coconut/pkg/tokens"
"github.com/pulumi/coconut/pkg/util/contract"
)
// Husk represents information about a deployment target.
type Husk struct {
Name tokens.QName // the target environment name.
Config ConfigMap // optional configuration key/values.
}
// Huskfile is a serialized deployment target plus a record of the latest deployment.
type Huskfile struct {
Husk tokens.QName `json:"husk"` // the target environment name.
Config *ConfigMap `json:"config,omitempty"` // optional configuration key/values.
Latest *DeploymentRecord `json:"latest,omitempty"` // the latest/current deployment record.
}
// SerializeHuskfile turns a snapshot into a CocoGL data structure suitable for serialization.
func SerializeHuskfile(husk *Husk, snap Snapshot, reftag string) *Huskfile {
contract.Requiref(husk != nil, "husk", "!= nil")
// If snap is nil, that's okay, we will just create an empty deployment; otherwise, serialize the whole snapshot.
var latest *DeploymentRecord
if snap != nil {
latest = serializeDeploymentRecord(snap, reftag)
}
var config *ConfigMap
if husk.Config != nil {
config = &husk.Config
}
return &Huskfile{
Husk: husk.Name,
Config: config,
Latest: latest,
}
}
// DeserializeDeployment takes a serialized deployment record and returns its associated snapshot.
func DeserializeHuskfile(ctx *Context, huskfile *Huskfile) (*Husk, Snapshot) {
contract.Require(ctx != nil, "ctx")
contract.Require(huskfile != nil, "huskfile")
var snap Snapshot
name := huskfile.Husk
if latest := huskfile.Latest; latest != nil {
// Determine the reftag to use.
var reftag string
if latest.Reftag == nil {
reftag = DefaultDeploymentReftag
} else {
reftag = *latest.Reftag
}
// For every serialized resource vertex, create a ResourceDeployment out of it.
var resources []Resource
if latest.Resources != nil {
for _, kvp := range latest.Resources.Iter() {
// Deserialize the resources, if they exist.
res := kvp.Value
var props PropertyMap
if res.Properties == nil {
props = make(PropertyMap)
} else {
props = deserializeProperties(*res.Properties, reftag)
}
// And now just produce a resource object using the information available.
var id ID
if res.ID != nil {
id = *res.ID
}
resources = append(resources, NewResource(id, kvp.Key, res.Type, props))
}
}
// If the args are non-nil, use them.
var args core.Args
if latest.Args != nil {
args = *latest.Args
}
snap = NewSnapshot(ctx, name, latest.Package, args, resources)
}
// Create husk and snapshot objects to return.
husk := &Husk{Name: name}
if huskfile.Config != nil {
husk.Config = *huskfile.Config
}
return husk, snap
}

View file

@ -61,7 +61,7 @@ func NewPlan(ctx *Context, old Snapshot, new Snapshot) Plan {
type plan struct {
ctx *Context // this plan's context.
husk tokens.QName // the husk/namespace target being deployed into.
ns tokens.QName // the husk/namespace target being deployed into.
pkg tokens.Package // the package from which this snapshot came.
args core.Args // the arguments used to compile this package.
first *step // the first step to take.
@ -159,14 +159,14 @@ func (p *plan) checkpoint(resources []Resource) Snapshot {
tops = append(tops, topvert.Data().(Resource))
}
glog.V(7).Infof("Checkpointing plan application: %v total resources", len(tops))
return NewSnapshot(p.ctx, p.husk, p.pkg, p.args, tops)
return NewSnapshot(p.ctx, p.ns, p.pkg, p.args, tops)
}
// newPlan handles all three cases: (1) a creation plan from a new snapshot when old doesn't exist (nil), (2) an update
// plan when both old and new exist, and (3) a deletion plan when old exists, but not new.
func newPlan(ctx *Context, old Snapshot, new Snapshot) *plan {
// These variables are read from either snapshot (preferred new, since it may have updated args).
var husk tokens.QName
var ns tokens.QName
var pkg tokens.Package
var args core.Args
@ -175,7 +175,7 @@ func newPlan(ctx *Context, old Snapshot, new Snapshot) *plan {
if old != nil {
oldres = old.Resources()
if new == nil {
husk = old.Husk()
ns = old.Namespace()
pkg = old.Pkg()
args = old.Args()
}
@ -183,7 +183,7 @@ func newPlan(ctx *Context, old Snapshot, new Snapshot) *plan {
var newres []Resource
if new != nil {
newres = new.Resources()
husk = new.Husk()
ns = new.Namespace()
pkg = new.Pkg()
args = new.Args()
}
@ -221,7 +221,7 @@ func newPlan(ctx *Context, old Snapshot, new Snapshot) *plan {
// Keep track of vertices for our later graph operations.
p := &plan{
ctx: ctx,
husk: husk,
ns: ns,
pkg: pkg,
args: args,
}

View file

@ -19,7 +19,7 @@ import (
// or apply an infrastructure deployment plan in order to make reality match the snapshot state.
type Snapshot interface {
Ctx() *Context // fetches the context for this snapshot.
Husk() tokens.QName // the husk/namespace target being deployed into.
Namespace() tokens.QName // the husk/namespace target being deployed into.
Pkg() tokens.Package // the package from which this snapshot came.
Args() core.Args // the arguments used to compile this package.
Resources() []Resource // a topologically sorted list of resources (based on dependencies).
@ -29,15 +29,15 @@ type Snapshot interface {
}
// NewSnapshot creates a snapshot from the given arguments. The resources must be in topologically sorted order.
func NewSnapshot(ctx *Context, husk tokens.QName, pkg tokens.Package,
func NewSnapshot(ctx *Context, ns tokens.QName, pkg tokens.Package,
args core.Args, resources []Resource) Snapshot {
return &snapshot{ctx, husk, pkg, args, resources}
return &snapshot{ctx, ns, pkg, args, resources}
}
// NewGraphSnapshot takes an object graph and produces a resource snapshot from it. It understands how to name
// resources based on their position within the graph and how to identify and record dependencies. This function can
// fail dynamically if the input graph did not satisfy the preconditions for resource graphs (like that it is a DAG).
func NewGraphSnapshot(ctx *Context, husk tokens.QName, pkg tokens.Package,
func NewGraphSnapshot(ctx *Context, ns tokens.QName, pkg tokens.Package,
args core.Args, heap *heapstate.Heap) (Snapshot, error) {
// Topologically sort the entire heapstate (in dependency order) and extract just the resource objects.
resobjs, err := topsort(ctx, heap.G)
@ -47,27 +47,27 @@ func NewGraphSnapshot(ctx *Context, husk tokens.QName, pkg tokens.Package,
// Next, name all resources, create their monikers and objects, and maps that we will use. Note that we must do
// this in DAG order (guaranteed by our topological sort above), so that referenced monikers are available.
resources, err := createResources(ctx, husk, heap, resobjs)
resources, err := createResources(ctx, ns, heap, resobjs)
if err != nil {
return nil, err
}
return NewSnapshot(ctx, husk, pkg, args, resources), nil
return NewSnapshot(ctx, ns, pkg, args, resources), nil
}
type snapshot struct {
ctx *Context // the context shared by all operations in this snapshot.
husk tokens.QName // the husk/namespace target being deployed into.
ns tokens.QName // the namespace target being deployed into.
pkg tokens.Package // the package from which this snapshot came.
args core.Args // the arguments used to compile this package.
resources []Resource // the topologically sorted linearized list of resources.
}
func (s *snapshot) Ctx() *Context { return s.ctx }
func (s *snapshot) Husk() tokens.QName { return s.husk }
func (s *snapshot) Pkg() tokens.Package { return s.pkg }
func (s *snapshot) Args() core.Args { return s.args }
func (s *snapshot) Resources() []Resource { return s.resources }
func (s *snapshot) Ctx() *Context { return s.ctx }
func (s *snapshot) Namespace() tokens.QName { return s.ns }
func (s *snapshot) Pkg() tokens.Package { return s.pkg }
func (s *snapshot) Args() core.Args { return s.args }
func (s *snapshot) Resources() []Resource { return s.resources }
func (s *snapshot) ResourceByID(id ID, t tokens.Type) Resource {
contract.Failf("TODO: not yet implemented")