Repository: aws-cloudformation/rain
Pull request #40 (2020-10-27 17:08:18)

Fix reference check
*Issue #, if available:* #39 

*Description of changes:* Do not force formatting of template on deploy. This avoids the reference check. In addition, do not panic on reference check failure. Added option to format without sorting to `fmt` command.


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.



Patch:
diff --git a/cfn/cfn.go b/cfn/cfn.go
index 715697c5..dd0ab4e5 100644
--- a/cfn/cfn.go
+++ b/cfn/cfn.go
@@ -4,11 +4,11 @@
 package cfn
 
 import (
-	"fmt"
 	"strings"
 
 	"github.com/aws-cloudformation/rain/cfn/diff"
 	"github.com/aws-cloudformation/rain/cfn/graph"
+	"github.com/aws-cloudformation/rain/config"
 )
 
 const pseudoParameterType = "Parameter"
@@ -80,7 +80,8 @@ func (t Template) Graph() graph.Graph {
 						if strings.HasPrefix(toName, "AWS::") {
 							toType = "Parameters"
 						} else {
-							panic(fmt.Errorf("template has unresolved dependency '%s' at %s: %s", toName, typeName, fromName))
+							config.Debugf("template has unresolved dependency '%s' at %s: %s", toName, typeName, fromName)
+							continue
 						}
 					}
 
diff --git a/cfn/format/encoder.go b/cfn/format/encoder.go
index d1f03b03..c725285f 100644
--- a/cfn/format/encoder.go
+++ b/cfn/format/encoder.go
@@ -113,7 +113,12 @@ func (p encoder) formatMap(data map[string]interface{}) string {
 		return "{}"
 	}
 
-	keys := p.sortKeys()
+	var keys []string
+	if p.Unsorted {
+		keys = p.keys()
+	} else {
+		keys = p.sortKeys()
+	}
 
 	parts := make([]string, len(keys))
 
@@ -240,6 +245,15 @@ func (p encoder) formatList(data []interface{}) string {
 	return strings.Join(parts, "\n")
 }
 
+func (p *encoder) keys() []string {
+	keys := make([]string, 0)
+	for key := range p.currentValue.Value().(map[string]interface{}) {
+		keys = append(keys, key)
+	}
+
+	return keys
+}
+
 func (p encoder) format() string {
 	switch v := p.currentValue.Value().(type) {
 	case cfn.Template:
diff --git a/cfn/format/format.go b/cfn/format/format.go
index 4f4afc07..3b3755da 100644
--- a/cfn/format/format.go
+++ b/cfn/format/format.go
@@ -25,6 +25,7 @@ type Options struct {
 	Style    Style
 	Compact  bool
 	Comments map[string]interface{}
+	Unsorted bool
 }
 
 // Value returns a string representation of any given value
diff --git a/client/cfn/cfn.go b/client/cfn/cfn.go
index ea7432ff..f62ead7b 100644
--- a/client/cfn/cfn.go
+++ b/client/cfn/cfn.go
@@ -193,7 +193,9 @@ func makeTags(tags map[string]string) []*types.Tag {
 }
 
 func checkTemplate(template cfn.Template) (string, error) {
-	templateBody := format.Template(template, format.Options{})
+	templateBody := format.Template(template, format.Options{
+		Unsorted: true,
+	})
 
 	if len(templateBody) > 460800 {
 		return "", fmt.Errorf("Template is too large to deploy")
diff --git a/cmd/fmt.go b/cmd/fmt.go
index 68775f06..f60500da 100644
--- a/cmd/fmt.go
+++ b/cmd/fmt.go
@@ -13,6 +13,7 @@ var compactFlag bool
 var jsonFlag bool
 var verifyFlag bool
 var writeFlag bool
+var unsortedFlag bool
 
 var fmtCmd = &cobra.Command{
 	Use:                   "fmt <filename>",
@@ -38,8 +39,9 @@ var fmtCmd = &cobra.Command{
 
 		// Format the output
 		options := format.Options{
-			Style:   format.YAML,
-			Compact: compactFlag,
+			Style:    format.YAML,
+			Compact:  compactFlag,
+			Unsorted: unsortedFlag,
 		}
 
 		if jsonFlag {
@@ -76,5 +78,6 @@ func init() {
 	fmtCmd.Flags().BoolVarP(&jsonFlag, "json", "j", false, "Output the template as JSON (default format: YAML).")
 	fmtCmd.Flags().BoolVarP(&verifyFlag, "verify", "v", false, "Check if the input is already correctly formatted and exit.\nThe exit status will be 0 if so and 1 if not.")
 	fmtCmd.Flags().BoolVarP(&writeFlag, "write", "w", false, "Write the output back to the file rather than to stdout.")
+	fmtCmd.Flags().BoolVarP(&unsortedFlag, "unsorted", "u", false, "Do not sort the template's properties.")
 	Rain.AddCommand(fmtCmd)
 }


Test patch:
diff --git a/cmd/fmt_test.go b/cmd/fmt_test.go
index d6a9913a..38e33b15 100644
--- a/cmd/fmt_test.go
+++ b/cmd/fmt_test.go
@@ -24,12 +24,13 @@ func Example_fmt_help() {
 	//   fmt, format
 	//
 	// Flags:
-	//   -c, --compact   Produce more compact output.
-	//   -h, --help      help for fmt
-	//   -j, --json      Output the template as JSON (default format: YAML).
-	//   -v, --verify    Check if the input is already correctly formatted and exit.
-	//                   The exit status will be 0 if so and 1 if not.
-	//   -w, --write     Write the output back to the file rather than to stdout.
+	//   -c, --compact    Produce more compact output.
+	//   -h, --help       help for fmt
+	//   -j, --json       Output the template as JSON (default format: YAML).
+	//   -u, --unsorted   Do not sort the template's properties.
+	//   -v, --verify     Check if the input is already correctly formatted and exit.
+	//                    The exit status will be 0 if so and 1 if not.
+	//   -w, --write      Write the output back to the file rather than to stdout.
 	//
 	// Global Flags:
 	//       --debug            Output debugging information