Skip to content

Commit

Permalink
Tidy up a few things
Browse files Browse the repository at this point in the history
* Print a pretty message if the plan has nothing to do:

        "info: nothing to do -- resources are up to date"

* Add an extra validation step after reading in a snapshot,
  so that we detect more errors sooner.  For example, I've
  fed in the wrong file several times, and it just chugs
  along as though it were actually a snapshot.

* Skip printing nulls in most plan outputs.  These just
  clutter up the output.
  • Loading branch information
joeduffy committed Feb 25, 2017
1 parent 877fa13 commit 53cf9f8
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 25 deletions.
82 changes: 58 additions & 24 deletions cmd/shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/marapongo/mu/pkg/tokens"
"github.com/marapongo/mu/pkg/util/cmdutil"
"github.com/marapongo/mu/pkg/util/contract"
"github.com/marapongo/mu/pkg/util/mapper"
"github.com/marapongo/mu/pkg/workspace"
)

Expand Down Expand Up @@ -159,7 +160,9 @@ type planResult struct {

func apply(cmd *cobra.Command, args []string, existing string, opts applyOptions) {
if result := plan(cmd, args, existing, opts.Delete); result != nil {
if opts.DryRun {
if result.Plan.Empty() {
sink().Infof(diag.Message("nothing to do -- resources are up to date"))
} else if opts.DryRun {
// If no output file was requested, or "-", print to stdout; else write to that file.
if opts.Output == "" || opts.Output == "-" {
printPlan(result.Plan, opts.Detail)
Expand Down Expand Up @@ -250,23 +253,41 @@ func deleteSnapshot(file string) {

// readSnapshot reads in an existing snapshot file, issuing an error and returning nil if something goes awry.
func readSnapshot(ctx *resource.Context, file string) resource.Snapshot {
// Detect the encoding of the file so we can do our initial unmarshaling.
m, ext := encoding.Detect(file)
if m == nil {
sink().Errorf(errors.ErrorIllegalMarkupExtension, ext)
return nil
}

// Now read the whole file into a byte blog.
b, err := ioutil.ReadFile(file)
if err != nil {
sink().Errorf(errors.ErrorIO, err)
return nil
}

// Deserialize the contents into a snapshot.
var snap resource.MuglSnapshot
if err = m.Unmarshal(b, &snap); err != nil {
sink().Errorf(errors.ErrorCantReadSnapshot, file, err)
return nil
}

// Next, use the mapping infrastructure to validate the contents.
var obj mapper.Object
if err = m.Unmarshal(b, &obj); err != nil {
sink().Errorf(errors.ErrorCantReadSnapshot, file, err)
return nil
} else {
delete(obj, "resources") // remove the resources, since they require custom marshaling.
md := mapper.New(nil)
if err = md.Decode(obj, &snap); err != nil {
sink().Errorf(errors.ErrorCantReadSnapshot, file, err)
return nil
}
}

return resource.DeserializeSnapshot(ctx, &snap)
}

Expand Down Expand Up @@ -470,18 +491,24 @@ func printObject(b *bytes.Buffer, props resource.PropertyMap, indent string) {

// Now print out the values intelligently based on the type.
for _, k := range keys {
printPropertyTitle(b, k, maxkey, indent)
printPropertyValue(b, props[k], indent)
if v := props[k]; shouldPrintPropertyValue(v) {
printPropertyTitle(b, k, maxkey, indent)
printPropertyValue(b, v, indent)
}
}
}

func shouldPrintPropertyValue(v resource.PropertyValue) bool {
return !v.IsNull() // by default, don't print nulls (they just clutter up the output)
}

func printPropertyTitle(b *bytes.Buffer, k resource.PropertyKey, align int, indent string) {
b.WriteString(fmt.Sprintf("%s%-"+strconv.Itoa(align)+"s: ", indent, k))
}

func printPropertyValue(b *bytes.Buffer, v resource.PropertyValue, indent string) {
if v.IsNull() {
b.WriteString("<nil>")
b.WriteString("<null>")
} else if v.IsBool() {
b.WriteString(fmt.Sprintf("%t", v.BoolValue()))
} else if v.IsNumber() {
Expand Down Expand Up @@ -542,18 +569,22 @@ func printObjectDiff(b *bytes.Buffer, diff resource.ObjectDiff, indent string) {
for _, k := range keys {
title := func(id string) { printPropertyTitle(b, k, maxkey, id) }
if add, isadd := diff.Adds[k]; isadd {
b.WriteString(colors.SpecAdded)
title(addIndent(indent))
printPropertyValue(b, add, addIndent(indent))
b.WriteString(colors.Reset)
if shouldPrintPropertyValue(add) {
b.WriteString(colors.SpecAdded)
title(addIndent(indent))
printPropertyValue(b, add, addIndent(indent))
b.WriteString(colors.Reset)
}
} else if delete, isdelete := diff.Deletes[k]; isdelete {
b.WriteString(colors.SpecDeleted)
title(deleteIndent(indent))
printPropertyValue(b, delete, deleteIndent(indent))
b.WriteString(colors.Reset)
if shouldPrintPropertyValue(delete) {
b.WriteString(colors.SpecDeleted)
title(deleteIndent(indent))
printPropertyValue(b, delete, deleteIndent(indent))
b.WriteString(colors.Reset)
}
} else if update, isupdate := diff.Updates[k]; isupdate {
printPropertyValueDiff(b, title, update, indent)
} else {
} else if same := diff.Sames[k]; shouldPrintPropertyValue(same) {
title(indent)
printPropertyValue(b, diff.Sames[k], indent)
}
Expand Down Expand Up @@ -587,25 +618,28 @@ func printPropertyValueDiff(b *bytes.Buffer, title func(string), diff resource.V
printPropertyValue(b, a.Sames[i], newIndent)
}
}
b.WriteString(fmt.Sprintf("%s]", indent))
b.WriteString(fmt.Sprintf("%s]\n", indent))
} else if diff.Object != nil {
title(indent)
b.WriteString("{\n")
printObjectDiff(b, *diff.Object, indent+" ")
b.WriteString(fmt.Sprintf("%s}", indent))
b.WriteString(fmt.Sprintf("%s}\n", indent))
} else {
// If we ended up here, the two values either differ by type, or they have different primitive values. We will
// simply emit a deletion line followed by an addition line.
b.WriteString(colors.SpecChanged)
title(deleteIndent(indent))
printPropertyValue(b, diff.Old, deleteIndent(indent))
b.WriteString("\n")
title(addIndent(indent))
printPropertyValue(b, diff.New, addIndent(indent))
b.WriteString(colors.Reset)
if shouldPrintPropertyValue(diff.Old) {
b.WriteString(colors.SpecChanged)
title(deleteIndent(indent))
printPropertyValue(b, diff.Old, deleteIndent(indent))
b.WriteString(fmt.Sprintf("%s\n", colors.Reset))
}
if shouldPrintPropertyValue(diff.New) {
b.WriteString(colors.SpecChanged)
title(addIndent(indent))
printPropertyValue(b, diff.New, addIndent(indent))
b.WriteString(fmt.Sprintf("%s\n", colors.Reset))
}
}

b.WriteString("\n")
}

func addIndent(indent string) string { return indent[:len(indent)-2] + "+ " }
Expand Down
10 changes: 9 additions & 1 deletion pkg/resource/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
// however, it can alternatively be generated by diffing two resource graphs -- in the case of updates to existing
// environments (presumably more common). The plan contains step objects that can be used to drive a deployment.
type Plan interface {
Empty() bool // true if the plan is empty.
Steps() Step // the first step to perform, linked to the rest.
Apply(prog Progress) (error, Step, ResourceState) // performs the operations specified in this plan.
}
Expand Down Expand Up @@ -73,7 +74,14 @@ type plan struct {

var _ Plan = (*plan)(nil)

func (p *plan) Steps() Step { return p.first }
func (p *plan) Empty() bool { return p.Steps() == nil }

func (p *plan) Steps() Step {
if p.first == nil {
return nil
}
return p.first
}

// Provider fetches the provider for a given resource, 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.
Expand Down
3 changes: 3 additions & 0 deletions pkg/util/mapper/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ type Mapper interface {
}

func New(customDecoders map[reflect.Type]Decoder) Mapper {
if customDecoders == nil {
customDecoders = make(map[reflect.Type]Decoder)
}
return &mapper{customDecoders}
}

Expand Down

0 comments on commit 53cf9f8

Please sign in to comment.