/
post-processor.go
138 lines (118 loc) · 4.19 KB
/
post-processor.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package manifest
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"time"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
)
type Config struct {
common.PackerConfig `mapstructure:",squash"`
OutputPath string `mapstructure:"output"`
StripPath bool `mapstructure:"strip_path"`
ctx interpolate.Context
}
type PostProcessor struct {
config Config
}
type ManifestFile struct {
Builds []Artifact `json:"builds"`
LastRunUUID string `json:"last_run_uuid"`
}
func (p *PostProcessor) Configure(raws ...interface{}) error {
err := config.Decode(&p.config, &config.DecodeOpts{
Interpolate: true,
InterpolateContext: &p.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{},
},
}, raws...)
if err != nil {
return err
}
if p.config.OutputPath == "" {
p.config.OutputPath = "packer-manifest.json"
}
if err = interpolate.Validate(p.config.OutputPath, &p.config.ctx); err != nil {
return fmt.Errorf("Error parsing target template: %s", err)
}
return nil
}
func (p *PostProcessor) PostProcess(ui packer.Ui, source packer.Artifact) (packer.Artifact, bool, error) {
artifact := &Artifact{}
var err error
var fi os.FileInfo
// Create the current artifact.
for _, name := range source.Files() {
af := ArtifactFile{}
if fi, err = os.Stat(name); err == nil {
af.Size = fi.Size()
}
if p.config.StripPath {
af.Name = filepath.Base(name)
} else {
af.Name = name
}
artifact.ArtifactFiles = append(artifact.ArtifactFiles, af)
}
artifact.ArtifactId = source.Id()
artifact.BuilderType = p.config.PackerBuilderType
artifact.BuildName = p.config.PackerBuildName
artifact.BuildTime = time.Now().Unix()
// Since each post-processor runs in a different process we need a way to
// coordinate between various post-processors in a single packer run. We do
// this by setting a UUID per run and tracking this in the manifest file.
// When we detect that the UUID in the file is the same, we know that we are
// part of the same run and we simply add our data to the list. If the UUID
// is different we will check the -force flag and decide whether to truncate
// the file before we proceed.
artifact.PackerRunUUID = os.Getenv("PACKER_RUN_UUID")
// Create a lock file with exclusive access. If this fails we will retry
// after a delay.
lockFilename := p.config.OutputPath + ".lock"
for i := 0; i < 3; i++ {
// The file should not be locked for very long so we'll keep this short.
time.Sleep((time.Duration(i) * 200 * time.Millisecond))
_, err = os.OpenFile(lockFilename, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
if err == nil {
break
}
log.Printf("Error locking manifest file for reading and writing. Will sleep and retry. %s", err)
}
defer os.Remove(lockFilename)
// Read the current manifest file from disk
contents := []byte{}
if contents, err = ioutil.ReadFile(p.config.OutputPath); err != nil && !os.IsNotExist(err) {
return source, true, fmt.Errorf("Unable to open %s for reading: %s", p.config.OutputPath, err)
}
// Parse the manifest file JSON, if we have one
manifestFile := &ManifestFile{}
if len(contents) > 0 {
if err = json.Unmarshal(contents, manifestFile); err != nil {
return source, true, fmt.Errorf("Unable to parse content from %s: %s", p.config.OutputPath, err)
}
}
// If -force is set and we are not on same run, truncate the file. Otherwise
// we will continue to add new builds to the existing manifest file.
if p.config.PackerForce && os.Getenv("PACKER_RUN_UUID") != manifestFile.LastRunUUID {
manifestFile = &ManifestFile{}
}
// Add the current artifact to the manifest file
manifestFile.Builds = append(manifestFile.Builds, *artifact)
manifestFile.LastRunUUID = os.Getenv("PACKER_RUN_UUID")
// Write JSON to disk
if out, err := json.MarshalIndent(manifestFile, "", " "); err == nil {
if err = ioutil.WriteFile(p.config.OutputPath, out, 0664); err != nil {
return source, true, fmt.Errorf("Unable to write %s: %s", p.config.OutputPath, err)
}
} else {
return source, true, fmt.Errorf("Unable to marshal JSON %s", err)
}
return source, true, nil
}