github.com/hashicorp/packer@v1.14.3/command/fix.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package command 5 6 import ( 7 "bytes" 8 "context" 9 "encoding/json" 10 "fmt" 11 "log" 12 "os" 13 "strings" 14 15 "github.com/hashicorp/packer-plugin-sdk/template" 16 "github.com/hashicorp/packer/fix" 17 18 "github.com/posener/complete" 19 ) 20 21 type FixCommand struct { 22 Meta 23 } 24 25 func (c *FixCommand) Run(args []string) int { 26 ctx, cleanup := handleTermInterrupt(c.Ui) 27 defer cleanup() 28 29 cfg, ret := c.ParseArgs(args) 30 if ret != 0 { 31 return ret 32 } 33 34 return c.RunContext(ctx, cfg) 35 } 36 37 func (c *FixCommand) ParseArgs(args []string) (*FixArgs, int) { 38 var cfg FixArgs 39 flags := c.Meta.FlagSet("fix") 40 flags.Usage = func() { c.Ui.Say(c.Help()) } 41 cfg.AddFlagSets(flags) 42 if err := flags.Parse(args); err != nil { 43 return &cfg, 1 44 } 45 46 args = flags.Args() 47 if len(args) != 1 { 48 flags.Usage() 49 return &cfg, 1 50 } 51 cfg.Path = args[0] 52 return &cfg, 0 53 } 54 55 func (c *FixCommand) RunContext(ctx context.Context, cla *FixArgs) int { 56 if hcl2, _ := isHCLLoaded(cla.Path); hcl2 { 57 c.Ui.Error("packer fix only works with JSON files for now.") 58 return 1 59 } 60 // Read the file for decoding 61 tplF, err := os.Open(cla.Path) 62 if err != nil { 63 c.Ui.Error(fmt.Sprintf("Error opening template: %s", err)) 64 return 1 65 } 66 defer tplF.Close() 67 68 // Decode the JSON into a generic map structure 69 var templateData map[string]interface{} 70 decoder := json.NewDecoder(tplF) 71 if err := decoder.Decode(&templateData); err != nil { 72 c.Ui.Error(fmt.Sprintf("Error parsing template: %s", err)) 73 return 1 74 } 75 76 // Close the file since we're done with that 77 tplF.Close() 78 79 input := templateData 80 for _, name := range fix.FixerOrder { 81 var err error 82 fixer, ok := fix.Fixers[name] 83 if !ok { 84 panic("fixer not found: " + name) 85 } 86 87 log.Printf("Running fixer: %s", name) 88 input, err = fixer.Fix(input) 89 if err != nil { 90 c.Ui.Error(fmt.Sprintf("Error fixing: %s", err)) 91 return 1 92 } 93 } 94 95 var output bytes.Buffer 96 encoder := json.NewEncoder(&output) 97 if err := encoder.Encode(input); err != nil { 98 c.Ui.Error(fmt.Sprintf("Error encoding: %s", err)) 99 return 1 100 } 101 102 var indented bytes.Buffer 103 if err := json.Indent(&indented, output.Bytes(), "", " "); err != nil { 104 c.Ui.Error(fmt.Sprintf("Error encoding: %s", err)) 105 return 1 106 } 107 108 result := indented.String() 109 result = strings.Replace(result, `\u003c`, "<", -1) 110 result = strings.Replace(result, `\u003e`, ">", -1) 111 c.Ui.Say(result) 112 113 if cla.Validate == false { 114 return 0 115 } 116 117 // Attempt to parse and validate the template 118 tpl, err := template.Parse(strings.NewReader(result)) 119 if err != nil { 120 c.Ui.Error(fmt.Sprintf( 121 "Error! Fixed template fails to parse: %s\n\n"+ 122 "This is usually caused by an error in the input template.\n"+ 123 "Please fix the error and try again.", 124 err)) 125 return 1 126 } 127 if err := tpl.Validate(); err != nil { 128 c.Ui.Error(fmt.Sprintf( 129 "Error! Fixed template failed to validate: %s\n\n"+ 130 "This is usually caused by an error in the input template.\n"+ 131 "Please fix the error and try again.", 132 err)) 133 return 1 134 } 135 136 return 0 137 } 138 139 func (*FixCommand) Help() string { 140 helpText := ` 141 Usage: packer fix [options] TEMPLATE 142 143 Reads the JSON template and attempts to fix known backwards 144 incompatibilities. The fixed template will be outputted to standard out. 145 146 If the template cannot be fixed due to an error, the command will exit 147 with a non-zero exit status. Error messages will appear on standard error. 148 149 Fixes that are run (in order): 150 151 ` 152 153 for _, name := range fix.FixerOrder { 154 helpText += fmt.Sprintf( 155 " %-27s%s\n", name, fix.Fixers[name].Synopsis()) 156 } 157 158 helpText += ` 159 Options: 160 161 -validate=true If true (default), validates the fixed template. 162 ` 163 164 return strings.TrimSpace(helpText) 165 } 166 167 func (c *FixCommand) Synopsis() string { 168 return "fixes templates from old versions of packer" 169 } 170 171 func (c *FixCommand) AutocompleteArgs() complete.Predictor { 172 return complete.PredictNothing 173 } 174 175 func (c *FixCommand) AutocompleteFlags() complete.Flags { 176 return complete.Flags{ 177 "-validate": complete.PredictNothing, 178 } 179 }