initial import of checksum post-processor (#3492)
Signed-off-by: Vasiliy Tolstov <v.tolstov@selfip.ru>
This commit is contained in:
parent
c1e7caf53c
commit
d64603e137
|
@ -33,6 +33,7 @@ import (
|
|||
amazonimportpostprocessor "github.com/mitchellh/packer/post-processor/amazon-import"
|
||||
artificepostprocessor "github.com/mitchellh/packer/post-processor/artifice"
|
||||
atlaspostprocessor "github.com/mitchellh/packer/post-processor/atlas"
|
||||
checksumpostprocessor "github.com/mitchellh/packer/post-processor/checksum"
|
||||
compresspostprocessor "github.com/mitchellh/packer/post-processor/compress"
|
||||
dockerimportpostprocessor "github.com/mitchellh/packer/post-processor/docker-import"
|
||||
dockerpushpostprocessor "github.com/mitchellh/packer/post-processor/docker-push"
|
||||
|
@ -101,6 +102,7 @@ var PostProcessors = map[string]packer.PostProcessor{
|
|||
"amazon-import": new(amazonimportpostprocessor.PostProcessor),
|
||||
"artifice": new(artificepostprocessor.PostProcessor),
|
||||
"atlas": new(atlaspostprocessor.PostProcessor),
|
||||
"checksum": new(checksumpostprocessor.PostProcessor),
|
||||
"compress": new(compresspostprocessor.PostProcessor),
|
||||
"docker-import": new(dockerimportpostprocessor.PostProcessor),
|
||||
"docker-push": new(dockerpushpostprocessor.PostProcessor),
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Vasiliy Tolstov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,48 @@
|
|||
package checksum
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const BuilderId = "packer.post-processor.checksum"
|
||||
|
||||
type Artifact struct {
|
||||
files []string
|
||||
}
|
||||
|
||||
func NewArtifact(files []string) *Artifact {
|
||||
return &Artifact{files: files}
|
||||
}
|
||||
|
||||
func (a *Artifact) BuilderId() string {
|
||||
return BuilderId
|
||||
}
|
||||
|
||||
func (a *Artifact) Files() []string {
|
||||
return a.files
|
||||
}
|
||||
|
||||
func (a *Artifact) Id() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (a *Artifact) String() string {
|
||||
files := strings.Join(a.files, ", ")
|
||||
return fmt.Sprintf("Created artifact from files: %s", files)
|
||||
}
|
||||
|
||||
func (a *Artifact) State(name string) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Artifact) Destroy() error {
|
||||
for _, f := range a.files {
|
||||
err := os.RemoveAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
package checksum
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/mitchellh/packer/common"
|
||||
"github.com/mitchellh/packer/helper/config"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"github.com/mitchellh/packer/template/interpolate"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
|
||||
Keep bool `mapstructure:"keep_input_artifact"`
|
||||
ChecksumTypes []string `mapstructure:"checksum_types"`
|
||||
OutputPath string `mapstructure:"output"`
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
||||
type PostProcessor struct {
|
||||
config Config
|
||||
}
|
||||
|
||||
func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||||
err := config.Decode(&p.config, &config.DecodeOpts{
|
||||
Interpolate: true,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
Exclude: []string{},
|
||||
},
|
||||
}, raws...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.config.ChecksumTypes == nil {
|
||||
p.config.ChecksumTypes = []string{"md5"}
|
||||
}
|
||||
|
||||
if p.config.OutputPath == "" {
|
||||
p.config.OutputPath = "packer_{{.BuildName}}_{{.BuilderType}}" + ".checksum"
|
||||
}
|
||||
|
||||
errs := new(packer.MultiError)
|
||||
|
||||
if err = interpolate.Validate(p.config.OutputPath, &p.config.ctx); err != nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Error parsing target template: %s", err))
|
||||
}
|
||||
|
||||
if len(errs.Errors) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getHash(t string) hash.Hash {
|
||||
var h hash.Hash
|
||||
switch t {
|
||||
case "md5":
|
||||
h = md5.New()
|
||||
case "sha1":
|
||||
h = sha1.New()
|
||||
case "sha224":
|
||||
h = sha256.New224()
|
||||
case "sha256":
|
||||
h = sha256.New()
|
||||
case "sha384":
|
||||
h = sha512.New384()
|
||||
case "sha512":
|
||||
h = sha512.New()
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
|
||||
files := artifact.Files()
|
||||
var h hash.Hash
|
||||
var checksumFile string
|
||||
|
||||
newartifact := NewArtifact(artifact.Files())
|
||||
|
||||
for _, ct := range p.config.ChecksumTypes {
|
||||
h = getHash(ct)
|
||||
|
||||
for _, art := range files {
|
||||
if len(artifact.Files()) > 1 {
|
||||
checksumFile = filepath.Join(filepath.Dir(art), ct+"sums")
|
||||
} else if p.config.OutputPath != "" {
|
||||
checksumFile = p.config.OutputPath
|
||||
} else {
|
||||
checksumFile = fmt.Sprintf("%s.%s", art, ct+"sum")
|
||||
}
|
||||
if _, err := os.Stat(checksumFile); err != nil {
|
||||
newartifact.files = append(newartifact.files, checksumFile)
|
||||
}
|
||||
|
||||
fw, err := os.OpenFile(checksumFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0644))
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("unable to create file %s: %s", checksumFile, err.Error())
|
||||
}
|
||||
fr, err := os.Open(art)
|
||||
if err != nil {
|
||||
fw.Close()
|
||||
return nil, false, fmt.Errorf("unable to open file %s: %s", art, err.Error())
|
||||
}
|
||||
|
||||
if _, err = io.Copy(h, fr); err != nil {
|
||||
fr.Close()
|
||||
fw.Close()
|
||||
return nil, false, fmt.Errorf("unable to compute %s hash for %s", ct, art)
|
||||
}
|
||||
fr.Close()
|
||||
fw.WriteString(fmt.Sprintf("%x\t%s\n", h.Sum(nil), filepath.Base(art)))
|
||||
fw.Close()
|
||||
}
|
||||
}
|
||||
|
||||
return newartifact, true, nil
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package checksum
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mitchellh/packer/builder/file"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"github.com/mitchellh/packer/template"
|
||||
)
|
||||
|
||||
const expectedFileContents = "Hello world!"
|
||||
|
||||
func TestChecksumSHA1(t *testing.T) {
|
||||
const config = `
|
||||
{
|
||||
"post-processors": [
|
||||
{
|
||||
"type": "checksum",
|
||||
"checksum_types": ["sha1"],
|
||||
"output": "sha1sums"
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
artifact := testChecksum(t, config)
|
||||
defer artifact.Destroy()
|
||||
|
||||
f, err := os.Open("sha1sums")
|
||||
if err != nil {
|
||||
t.Errorf("Unable to read checksum file: %s", err)
|
||||
}
|
||||
if buf, _ := ioutil.ReadAll(f); !bytes.Equal(buf, []byte("d3486ae9136e7856bc42212385ea797094475802\tpackage.txt\n")) {
|
||||
t.Errorf("Failed to compate checksum: %s\n%s", buf, "d3486ae9136e7856bc42212385ea797094475802 package.txt")
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
}
|
||||
|
||||
// Test Helpers
|
||||
|
||||
func setup(t *testing.T) (packer.Ui, packer.Artifact, error) {
|
||||
// Create fake UI and Cache
|
||||
ui := packer.TestUi(t)
|
||||
cache := &packer.FileCache{CacheDir: os.TempDir()}
|
||||
|
||||
// Create config for file builder
|
||||
const fileConfig = `{"builders":[{"type":"file","target":"package.txt","content":"Hello world!"}]}`
|
||||
tpl, err := template.Parse(strings.NewReader(fileConfig))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Unable to parse setup configuration: %s", err)
|
||||
}
|
||||
|
||||
// Prepare the file builder
|
||||
builder := file.Builder{}
|
||||
warnings, err := builder.Prepare(tpl.Builders["file"].Config)
|
||||
if len(warnings) > 0 {
|
||||
for _, warn := range warnings {
|
||||
return nil, nil, fmt.Errorf("Configuration warning: %s", warn)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Invalid configuration: %s", err)
|
||||
}
|
||||
|
||||
// Run the file builder
|
||||
artifact, err := builder.Run(ui, nil, cache)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Failed to build artifact: %s", err)
|
||||
}
|
||||
|
||||
return ui, artifact, err
|
||||
}
|
||||
|
||||
func testChecksum(t *testing.T, config string) packer.Artifact {
|
||||
ui, artifact, err := setup(t)
|
||||
if err != nil {
|
||||
t.Fatalf("Error bootstrapping test: %s", err)
|
||||
}
|
||||
if artifact != nil {
|
||||
defer artifact.Destroy()
|
||||
}
|
||||
|
||||
tpl, err := template.Parse(strings.NewReader(config))
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to parse test config: %s", err)
|
||||
}
|
||||
|
||||
checksum := PostProcessor{}
|
||||
checksum.Configure(tpl.PostProcessors[0][0].Config)
|
||||
|
||||
// I get the feeling these should be automatically available somewhere, but
|
||||
// some of the post-processors construct this manually.
|
||||
checksum.config.ctx.BuildName = "chocolate"
|
||||
checksum.config.PackerBuildName = "vanilla"
|
||||
checksum.config.PackerBuilderType = "file"
|
||||
|
||||
artifactOut, _, err := checksum.PostProcess(ui, artifact)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to checksum artifact: %s", err)
|
||||
}
|
||||
|
||||
return artifactOut
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
description: |
|
||||
The checksum post-processor computes specified checksum for the artifact list
|
||||
from an upstream builder or post-processor. All downstream post-processors will
|
||||
see the new artifacts. The primary use-case is compute checksum for artifacts
|
||||
allows to verify it later.
|
||||
So firstly this post-processor get artifact, compute it checksum and pass to
|
||||
next post-processor original artifacts and checksum files.
|
||||
layout: docs
|
||||
page_title: 'Checksum Post-Processor'
|
||||
...
|
||||
|
||||
# Checksum Post-Processor
|
||||
|
||||
Type: `checksum`
|
||||
|
||||
The checksum post-processor computes specified checksum for the artifact list
|
||||
from an upstream builder or post-processor. All downstream post-processors will
|
||||
see the new artifacts. The primary use-case is compute checksum for artifact to
|
||||
verify it later.
|
||||
|
||||
After computes checksum for artifacts, you can use new artifacts with other
|
||||
post-processors like
|
||||
[artifice](https://www.packer.io/docs/post-processors/artifice.html),
|
||||
[compress](https://www.packer.io/docs/post-processors/compress.html),
|
||||
[docker-push](https://www.packer.io/docs/post-processors/docker-push.html),
|
||||
[atlas](https://www.packer.io/docs/post-processors/atlas.html), or a third-party
|
||||
post-processor.
|
||||
|
||||
## Basic example
|
||||
|
||||
The example below is fully functional.
|
||||
|
||||
``` {.javascript}
|
||||
{
|
||||
"type": "checksum"
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
Optional parameters:
|
||||
|
||||
- `checksum_types` (array of strings) - An array of strings of checksum types
|
||||
to compute. Allowed values are md5, sha1, sha224, sha256, sha384, sha512.
|
||||
- `output` (string) - Specify filename to store checksums.
|
|
@ -75,6 +75,7 @@
|
|||
<li><a href="/docs/post-processors/artifice.html">Artifice</a></li>
|
||||
<li><a href="/docs/post-processors/atlas.html">Atlas</a></li>
|
||||
<li><a href="/docs/post-processors/compress.html">compress</a></li>
|
||||
<li><a href="/docs/post-processors/checksum.html">checksum</a></li>
|
||||
<li><a href="/docs/post-processors/docker-import.html">docker-import</a></li>
|
||||
<li><a href="/docs/post-processors/docker-push.html">docker-push</a></li>
|
||||
<li><a href="/docs/post-processors/docker-save.html">docker-save</a></li>
|
||||
|
|
Loading…
Reference in New Issue