-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[POC] Provisioner for SBOM #13171
base: main
Are you sure you want to change the base?
[POC] Provisioner for SBOM #13171
Changes from 5 commits
e534589
62eaca5
f9c29ce
31af258
0272dc3
355112b
498fd1d
647056a
b0589f5
17ee3e9
00b8730
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -50,11 +50,14 @@ type CoreBuild struct { | |||||||||||||||
onError string | ||||||||||||||||
l sync.Mutex | ||||||||||||||||
prepareCalled bool | ||||||||||||||||
|
||||||||||||||||
SBOMFilesCompressed [][]byte | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
type BuildMetadata struct { | ||||||||||||||||
PackerVersion string | ||||||||||||||||
Plugins map[string]PluginDetails | ||||||||||||||||
SBOMs [][]byte | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
func (b *CoreBuild) getPluginsMetadata() map[string]PluginDetails { | ||||||||||||||||
|
@@ -88,6 +91,7 @@ func (b *CoreBuild) GetMetadata() BuildMetadata { | |||||||||||||||
metadata := BuildMetadata{ | ||||||||||||||||
PackerVersion: version.FormattedVersion(), | ||||||||||||||||
Plugins: b.getPluginsMetadata(), | ||||||||||||||||
SBOMs: b.SBOMFilesCompressed, | ||||||||||||||||
} | ||||||||||||||||
return metadata | ||||||||||||||||
} | ||||||||||||||||
|
@@ -300,6 +304,16 @@ func (b *CoreBuild) Run(ctx context.Context, originalUi packersdk.Ui) ([]packers | |||||||||||||||
return nil, err | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
if len(b.Provisioners) > 0 { | ||||||||||||||||
for _, p := range b.Provisioners { | ||||||||||||||||
sbomInternalProvisioner, ok := p.Provisioner.(*SBOMInternalProvisioner) | ||||||||||||||||
if !ok { | ||||||||||||||||
continue | ||||||||||||||||
} | ||||||||||||||||
b.SBOMFilesCompressed = append(b.SBOMFilesCompressed, sbomInternalProvisioner.CompressedData) | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given that the alternative has only one instruction, you can probably invert this logic here
Suggested change
|
||||||||||||||||
} | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
// If there was no result, don't worry about running post-processors | ||||||||||||||||
// because there is nothing they can do, just return. | ||||||||||||||||
if builderArtifact == nil { | ||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,12 @@ package packer | |
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"log" | ||
"os" | ||
|
||
"github.com/klauspost/compress/zstd" | ||
|
||
"time" | ||
|
||
"github.com/hashicorp/hcl/v2/hcldec" | ||
|
@@ -234,3 +239,80 @@ func (p *DebuggedProvisioner) Provision(ctx context.Context, ui packersdk.Ui, co | |
|
||
return p.Provisioner.Provision(ctx, ui, comm, generatedData) | ||
} | ||
|
||
// SBOMInternalProvisioner is a Provisioner implementation that waits until a key | ||
// press before the provisioner is actually run. | ||
type SBOMInternalProvisioner struct { | ||
Provisioner packersdk.Provisioner | ||
TempFileLoc string | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason why we keep this as part of the provisioner? Since we only create the file during |
||
CompressedData []byte | ||
} | ||
|
||
func (p *SBOMInternalProvisioner) ConfigSpec() hcldec.ObjectSpec { return p.ConfigSpec() } | ||
func (p *SBOMInternalProvisioner) FlatConfig() interface{} { return p.FlatConfig() } | ||
func (p *SBOMInternalProvisioner) Prepare(raws ...interface{}) error { | ||
return p.Provisioner.Prepare(raws...) | ||
} | ||
|
||
func (p *SBOMInternalProvisioner) Provision( | ||
ctx context.Context, ui packersdk.Ui, comm packersdk.Communicator, | ||
generatedData map[string]interface{}, | ||
) error { | ||
// Get the current working directory | ||
cwd, err := os.Getwd() | ||
if err != nil { | ||
return fmt.Errorf("failed to get current working directory for Packer SBOM: %s", err) | ||
} | ||
|
||
// Create a temporary file in the current working directory | ||
tmpFile, err := os.CreateTemp(cwd, "packer-sbom-*.json") | ||
if err != nil { | ||
return fmt.Errorf("failed to create internal temporary file for Packer SBOM: %s", err) | ||
} | ||
defer tmpFile.Close() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the underlying provisioner writes to this file, we should not defer its closure I'd think, typically on Windows this might fail depending on the kind of Handle we've received from |
||
defer func(name string) { | ||
fileRemoveErr := os.Remove(name) | ||
if fileRemoveErr != nil { | ||
log.Printf("Error removing SBOM temporary file %s: %s", name, fileRemoveErr) | ||
} | ||
}(p.TempFileLoc) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At this point |
||
|
||
generatedData["dst"] = tmpFile.Name() | ||
p.TempFileLoc = tmpFile.Name() | ||
|
||
err = p.Provisioner.Provision(ctx, ui, comm, generatedData) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
compressedData, err := p.compressFile(p.TempFileLoc) | ||
if err != nil { | ||
return err | ||
} | ||
p.CompressedData = compressedData | ||
return nil | ||
} | ||
|
||
func (p *SBOMInternalProvisioner) compressFile(filePath string) ([]byte, error) { | ||
sourceFile, err := os.Open(filePath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer sourceFile.Close() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can use a function like |
||
|
||
data, err := io.ReadAll(sourceFile) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
encoder, err := zstd.NewWriter(nil) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would suggest maybe setting the compression level option to |
||
if err != nil { | ||
return nil, err | ||
} | ||
defer encoder.Close() | ||
|
||
compressedData := encoder.EncodeAll(data, nil) | ||
|
||
fmt.Printf(fmt.Sprintf("SBOM file compressed successfully. Size: %d bytes", len(compressedData))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should become a |
||
return compressedData, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That
if
is superfluous, ifb.Provisioners
is empty, the loop won't run at all, so you can remove it I'd think