github.com/hashicorp/packer@v1.14.3/post-processor/checksum/post-processor.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 //go:generate packer-sdc mapstructure-to-hcl2 -type Config 5 6 package checksum 7 8 import ( 9 "context" 10 "crypto/md5" 11 "crypto/sha1" 12 "crypto/sha256" 13 "crypto/sha512" 14 "fmt" 15 "hash" 16 "io" 17 "os" 18 "path/filepath" 19 20 "github.com/hashicorp/hcl/v2/hcldec" 21 "github.com/hashicorp/packer-plugin-sdk/common" 22 packersdk "github.com/hashicorp/packer-plugin-sdk/packer" 23 "github.com/hashicorp/packer-plugin-sdk/template/config" 24 "github.com/hashicorp/packer-plugin-sdk/template/interpolate" 25 ) 26 27 type Config struct { 28 common.PackerConfig `mapstructure:",squash"` 29 30 ChecksumTypes []string `mapstructure:"checksum_types"` 31 OutputPath string `mapstructure:"output"` 32 ctx interpolate.Context 33 } 34 35 type PostProcessor struct { 36 config Config 37 } 38 39 func getHash(t string) hash.Hash { 40 var h hash.Hash 41 switch t { 42 case "md5": 43 h = md5.New() 44 case "sha1": 45 h = sha1.New() 46 case "sha224": 47 h = sha256.New224() 48 case "sha256": 49 h = sha256.New() 50 case "sha384": 51 h = sha512.New384() 52 case "sha512": 53 h = sha512.New() 54 } 55 return h 56 } 57 58 func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } 59 60 func (p *PostProcessor) Configure(raws ...interface{}) error { 61 err := config.Decode(&p.config, &config.DecodeOpts{ 62 PluginType: "checksum", 63 Interpolate: true, 64 InterpolateContext: &p.config.ctx, 65 InterpolateFilter: &interpolate.RenderFilter{ 66 Exclude: []string{"output"}, 67 }, 68 }, raws...) 69 if err != nil { 70 return err 71 } 72 errs := new(packersdk.MultiError) 73 74 if p.config.ChecksumTypes == nil { 75 p.config.ChecksumTypes = []string{"md5"} 76 } 77 78 for _, k := range p.config.ChecksumTypes { 79 if h := getHash(k); h == nil { 80 errs = packersdk.MultiErrorAppend(errs, 81 fmt.Errorf("Unrecognized checksum type: %s", k)) 82 } 83 } 84 85 if p.config.OutputPath == "" { 86 p.config.OutputPath = "packer_{{.BuildName}}_{{.BuilderType}}_{{.ChecksumType}}.checksum" 87 } 88 89 if err = interpolate.Validate(p.config.OutputPath, &p.config.ctx); err != nil { 90 errs = packersdk.MultiErrorAppend( 91 errs, fmt.Errorf("Error parsing target template: %s", err)) 92 } 93 94 if len(errs.Errors) > 0 { 95 return errs 96 } 97 98 return nil 99 } 100 101 func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, artifact packersdk.Artifact) (packersdk.Artifact, bool, bool, error) { 102 files := artifact.Files() 103 var h hash.Hash 104 105 var generatedData map[interface{}]interface{} 106 stateData := artifact.State("generated_data") 107 if stateData != nil { 108 // Make sure it's not a nil map so we can assign to it later. 109 generatedData = stateData.(map[interface{}]interface{}) 110 } 111 // If stateData has a nil map generatedData will be nil 112 // and we need to make sure it's not 113 if generatedData == nil { 114 generatedData = make(map[interface{}]interface{}) 115 } 116 generatedData["BuildName"] = p.config.PackerBuildName 117 generatedData["BuilderType"] = p.config.PackerBuilderType 118 119 newartifact := NewArtifact(artifact.Files()) 120 121 for _, ct := range p.config.ChecksumTypes { 122 h = getHash(ct) 123 generatedData["ChecksumType"] = ct 124 p.config.ctx.Data = generatedData 125 126 for _, art := range files { 127 checksumFile, err := interpolate.Render(p.config.OutputPath, &p.config.ctx) 128 if err != nil { 129 return nil, false, true, err 130 } 131 132 if _, err := os.Stat(checksumFile); err != nil { 133 newartifact.files = append(newartifact.files, checksumFile) 134 } 135 if err := os.MkdirAll(filepath.Dir(checksumFile), os.FileMode(0755)); err != nil { 136 return nil, false, true, fmt.Errorf("unable to create dir: %s", err.Error()) 137 } 138 fw, err := os.OpenFile(checksumFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0644)) 139 if err != nil { 140 return nil, false, true, fmt.Errorf("unable to create file %s: %s", checksumFile, err.Error()) 141 } 142 fr, err := os.Open(art) 143 if err != nil { 144 fw.Close() 145 return nil, false, true, fmt.Errorf("unable to open file %s: %s", art, err.Error()) 146 } 147 148 if _, err = io.Copy(h, fr); err != nil { 149 fr.Close() 150 fw.Close() 151 return nil, false, true, fmt.Errorf("unable to compute %s hash for %s", ct, art) 152 } 153 fr.Close() 154 _, _ = fw.WriteString(fmt.Sprintf("%x\t%s\n", h.Sum(nil), filepath.Base(art))) 155 fw.Close() 156 h.Reset() 157 } 158 } 159 160 // sets keep and forceOverride to true because we don't want to accidentally 161 // delete the very artifact we're checksumming. 162 return newartifact, true, true, nil 163 }