mirror of
https://github.com/hashicorp/packer.git
synced 2026-09-25 09:14:01 -04:00
Modify external provisioner
This commit is contained in:
@@ -16,7 +16,6 @@ import (
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -64,7 +63,8 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||
}
|
||||
|
||||
func (p *Provisioner) Provision(
|
||||
ctx context.Context, ui packersdk.Ui, comm packersdk.Communicator, generatedData map[string]interface{},
|
||||
ctx context.Context, ui packersdk.Ui, comm packersdk.Communicator,
|
||||
generatedData map[string]interface{},
|
||||
) error {
|
||||
ui.Say(
|
||||
fmt.Sprintf("Starting to provision with hcp-sbom using source: %s",
|
||||
@@ -77,13 +77,15 @@ func (p *Provisioner) Provision(
|
||||
}
|
||||
p.config.ctx.Data = generatedData
|
||||
|
||||
// Download the file
|
||||
destPath, downloadErr := p.downloadSBOM(ui, comm)
|
||||
// defer os.Remove(destPath)
|
||||
// Download the file for Packer
|
||||
destPath, downloadErr := p.downloadSBOMForPacker(ui, comm, generatedData)
|
||||
if downloadErr != nil {
|
||||
return fmt.Errorf("failed to download file: %w", downloadErr)
|
||||
}
|
||||
|
||||
// Download the file for user
|
||||
p.downloadSBOMForUser(ui, comm)
|
||||
|
||||
// Validate the file
|
||||
ui.Say(fmt.Sprintf("Validating SBOM file %s", destPath))
|
||||
validationErr := p.validateSBOM(ui, destPath)
|
||||
@@ -91,29 +93,21 @@ func (p *Provisioner) Provision(
|
||||
return fmt.Errorf("failed to validate SBOM file: %w", validationErr)
|
||||
}
|
||||
|
||||
// Compress the file
|
||||
ui.Say(fmt.Sprintf("Compressing SBOM file %s", destPath))
|
||||
_, compessionErr := p.compressFile(ui, destPath)
|
||||
if compessionErr != nil {
|
||||
return fmt.Errorf("failed to compress file: %w", compessionErr)
|
||||
}
|
||||
|
||||
// Future: send compressedData to the internal API as per RFC
|
||||
// ...
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// downloadSBOM downloads a Software Bill of Materials (SBOM) from a specified
|
||||
// source to a local destination. It works with all communicators from packersdk.
|
||||
// The method returns the path to the downloaded file or an error if any issues
|
||||
// occur during the download process.
|
||||
func (p *Provisioner) downloadSBOM(ui packersdk.Ui, comm packersdk.Communicator) (string, error) {
|
||||
// downloadSBOMForPacker downloads SBOM from a specified source to a local
|
||||
// destination set by internal SBOM provisioner. It works with all communicators
|
||||
// from packersdk.
|
||||
func (p *Provisioner) downloadSBOMForPacker(
|
||||
ui packersdk.Ui, comm packersdk.Communicator, generatedData map[string]interface{},
|
||||
) (string, error) {
|
||||
src, err := interpolate.Render(p.config.Source, &p.config.ctx)
|
||||
if err != nil {
|
||||
return p.config.Destination, fmt.Errorf("error interpolating source: %s", err)
|
||||
}
|
||||
|
||||
// FIXME:: Do we really need this?
|
||||
// Check if the source is a JSON file
|
||||
if filepath.Ext(src) != ".json" {
|
||||
return p.config.Destination, fmt.Errorf(
|
||||
@@ -121,40 +115,13 @@ func (p *Provisioner) downloadSBOM(ui packersdk.Ui, comm packersdk.Communicator)
|
||||
)
|
||||
}
|
||||
|
||||
// Determine the destination path
|
||||
dst := p.config.Destination
|
||||
if dst == "" {
|
||||
tmpFile, err := os.CreateTemp("", "packer-sbom-*.json")
|
||||
if err != nil {
|
||||
return dst, fmt.Errorf(
|
||||
"failed to create file for Packer SBOM: %s", err,
|
||||
)
|
||||
}
|
||||
dst = tmpFile.Name()
|
||||
tmpFile.Close()
|
||||
} else {
|
||||
dst, err = interpolate.Render(dst, &p.config.ctx)
|
||||
if err != nil {
|
||||
return dst, fmt.Errorf("error interpolating Packer SBOM destination: %s", err)
|
||||
}
|
||||
|
||||
if strings.HasSuffix(dst, "/") {
|
||||
info, err := os.Stat(dst)
|
||||
if err != nil {
|
||||
return dst, fmt.Errorf("failed to stat destination for Packer SBOM: %s", err)
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
tmpFile, err := os.CreateTemp(dst, "packer-sbom-*.json")
|
||||
if err != nil {
|
||||
return dst, fmt.Errorf("failed to create file for Packer SBOM: %s", err)
|
||||
}
|
||||
dst = tmpFile.Name()
|
||||
tmpFile.Close()
|
||||
}
|
||||
}
|
||||
// Download the file for Packer
|
||||
desti, ok := generatedData["dst"] // this has been set by HCPSBOMInternalProvisioner.Provision
|
||||
if !ok {
|
||||
return "", fmt.Errorf("failed to find location for Packer SBOM file")
|
||||
}
|
||||
|
||||
dst := fmt.Sprintf("%v", desti)
|
||||
// Ensure the destination directory exists
|
||||
dir := filepath.Dir(dst)
|
||||
if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
|
||||
@@ -172,37 +139,81 @@ func (p *Provisioner) downloadSBOM(ui packersdk.Ui, comm packersdk.Communicator)
|
||||
pf := io.MultiWriter(f)
|
||||
|
||||
// Download the file
|
||||
ui.Say(fmt.Sprintf("Downloading SBOM file %s => %s", src, dst))
|
||||
ui.Say(fmt.Sprintf("Downloading SBOM file %s for Packer => %s", src, dst))
|
||||
if err = comm.Download(src, pf); err != nil {
|
||||
ui.Error(fmt.Sprintf("download failed for SBOM file: %s", err))
|
||||
ui.Error(fmt.Sprintf("download failed for Packer SBOM file: %s", err))
|
||||
return dst, err
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
func (p *Provisioner) compressFile(ui packersdk.Ui, filePath string) ([]byte, error) {
|
||||
sourceFile, err := os.Open(filePath)
|
||||
// downloadSBOMForUser downloads a SBOM from a specified source to a local
|
||||
// destination given by user. It works with all communicators from packersdk.
|
||||
func (p *Provisioner) downloadSBOMForUser(
|
||||
ui packersdk.Ui, comm packersdk.Communicator,
|
||||
) {
|
||||
src, err := interpolate.Render(p.config.Source, &p.config.ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer sourceFile.Close()
|
||||
|
||||
data, err := io.ReadAll(sourceFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
ui.Say(fmt.Sprintf("error interpolating source: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
encoder, err := zstd.NewWriter(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// Determine the destination path
|
||||
dst := p.config.Destination
|
||||
if dst == "" {
|
||||
ui.Say("skipped downloading SBOM file for user because 'Destination' is not provided")
|
||||
return
|
||||
}
|
||||
defer encoder.Close()
|
||||
|
||||
compressedData := encoder.EncodeAll(data, nil)
|
||||
dst, err = interpolate.Render(dst, &p.config.ctx)
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("error interpolating SBOM file destination: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("SBOM file compressed successfully. Size: %d bytes", len(compressedData)))
|
||||
return compressedData, nil
|
||||
if strings.HasSuffix(dst, "/") {
|
||||
info, err := os.Stat(dst)
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("failed to stat destination for SBOM: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
tmpFile, err := os.CreateTemp(dst, "packer-user-sbom-*.json")
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("failed to create file for Packer SBOM: %s", err))
|
||||
return
|
||||
}
|
||||
dst = tmpFile.Name()
|
||||
tmpFile.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the destination directory exists
|
||||
dir := filepath.Dir(dst)
|
||||
if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
|
||||
ui.Say(fmt.Sprintf("failed to create destination directory for Packer SBOM: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Open the destination file
|
||||
f, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("failed to open destination file: %s", err))
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Create MultiWriter for the current progress
|
||||
pf := io.MultiWriter(f)
|
||||
|
||||
// Download the file
|
||||
ui.Say(fmt.Sprintf("Downloading SBOM file for user %s => %s", src, dst))
|
||||
if err = comm.Download(src, pf); err != nil {
|
||||
ui.Error(fmt.Sprintf("download failed for user SBOM file: %s", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
type SBOM struct {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -94,8 +93,8 @@ func TestDownloadSBOM(t *testing.T) {
|
||||
provisioner := &Provisioner{
|
||||
config: tt.config,
|
||||
}
|
||||
|
||||
destPath, err := provisioner.downloadSBOM(ui, comm)
|
||||
generatedData := map[string]interface{}{}
|
||||
destPath, err := provisioner.downloadSBOMForPacker(ui, comm, generatedData)
|
||||
if tt.expectError {
|
||||
if err == nil {
|
||||
t.Fatalf("expected error, got none")
|
||||
@@ -173,47 +172,3 @@ func TestValidateSBOM(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompressFile(t *testing.T) {
|
||||
ui := &MockUi{}
|
||||
provisioner := &Provisioner{}
|
||||
validSBOM := SBOM{
|
||||
BomFormat: "CycloneDX",
|
||||
SpecVersion: "1.0",
|
||||
}
|
||||
data, _ := json.Marshal(validSBOM)
|
||||
filePath := "data.json"
|
||||
//os.WriteFile(filePath, data, 0644)
|
||||
//defer os.Remove(filePath)
|
||||
|
||||
sourceFile, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error:%v", err)
|
||||
}
|
||||
defer sourceFile.Close()
|
||||
|
||||
data, err = io.ReadAll(sourceFile)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error:%v", err)
|
||||
}
|
||||
|
||||
compressedData, err := provisioner.compressFile(ui, filePath)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
decoder, err := zstd.NewReader(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
defer decoder.Close()
|
||||
|
||||
decompressedData, err := decoder.DecodeAll(compressedData, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
if string(decompressedData) != string(data) {
|
||||
t.Fatalf("expected decompressed data to be '%s', got %s", data, decompressedData)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user