Update plugin-getter version matching check

This change is an attempt to remove the need for additional temporary files, along with
calls to stat the temp files, to reduce the number of files being created, opened, and closed.
In addition to this change, the logic for falling back to a previous version if the highest matched version
is a pre-release has been removed. Instead we will assume that any prior versions will exhibit the same issue and
return immediately. A user can install the version manually if they will or they can modify their version constraint
to a properly released version.
pull/12964/head
Wilken Rivera 2 years ago committed by Lucas Bajolet
parent 35bd020a3e
commit eafdda8a94

@ -24,7 +24,6 @@ import (
"github.com/hashicorp/go-multierror"
goversion "github.com/hashicorp/go-version"
pluginsdk "github.com/hashicorp/packer-plugin-sdk/plugin"
"github.com/hashicorp/packer-plugin-sdk/random"
"github.com/hashicorp/packer-plugin-sdk/tmp"
"github.com/hashicorp/packer/hcl2template/addrs"
"golang.org/x/mod/semver"
@ -741,11 +740,8 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
}
expectedZipFilename := checksum.Filename
expectedBinaryFilename := strings.TrimSuffix(expectedZipFilename, filepath.Ext(expectedZipFilename)) + opts.BinaryInstallationOptions.Ext
outputFileName := filepath.Join(outputFolder, expectedBinaryFilename)
outputFileName := filepath.Join(
outputFolder,
expectedBinaryFilename,
)
for _, potentialChecksumer := range opts.Checksummers {
// First check if a local checksum file is already here in the expected
// download folder. Here we want to download a binary so we only check
@ -758,7 +754,7 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
Checksummer: potentialChecksumer,
}
log.Printf("[TRACE] found a pre-exising %q checksum file", potentialChecksumer.Type)
log.Printf("[TRACE] found a pre-existing %q checksum file", potentialChecksumer.Type)
// if outputFile is there and matches the checksum: do nothing more.
if err := localChecksum.ChecksumFile(localChecksum.Expected, outputFileName); err == nil && !opts.Force {
log.Printf("[INFO] %s v%s plugin is already correctly installed in %q", pr.Identifier, version, outputFileName)
@ -767,9 +763,6 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
}
}
// The last folder from the installation list is where we will install.
outputFileName = filepath.Join(outputFolder, expectedBinaryFilename)
// create directories if need be
if err := os.MkdirAll(outputFolder, 0755); err != nil {
err := fmt.Errorf("could not create plugin folder %q: %w", outputFolder, err)
@ -777,12 +770,24 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
log.Printf("[TRACE] %s", err.Error())
return nil, errs
}
for _, getter := range getters {
// start fetching binary
remoteZipFile, err := getter.Get("zip", GetOptions{
PluginRequirement: pr,
BinaryInstallationOptions: opts.BinaryInstallationOptions,
version: version,
expectedZipFilename: expectedZipFilename,
})
if err != nil {
errs = multierror.Append(errs,
fmt.Errorf("could not get binary for %s version %s. Is the file present on the release and correctly named ? %s",
pr.Identifier, version, err))
continue
}
// create temporary file that will receive a temporary binary.zip
tmpFile, err := tmp.File("packer-plugin-*.zip")
if err != nil {
err = fmt.Errorf("could not create temporary file to dowload plugin: %w", err)
err = fmt.Errorf("could not create temporary file to download plugin: %w", err)
errs = multierror.Append(errs, err)
return nil, errs
}
@ -791,38 +796,20 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
tmpFile.Close()
os.Remove(tmpFilePath)
}()
// start fetching binary
remoteZipFile, err := getter.Get("zip", GetOptions{
PluginRequirement: pr,
BinaryInstallationOptions: opts.BinaryInstallationOptions,
version: version,
expectedZipFilename: expectedZipFilename,
})
if err != nil {
err := fmt.Errorf("could not get binary for %s version %s. Is the file present on the release and correctly named ? %s", pr.Identifier, version, err)
errs = multierror.Append(errs, err)
log.Printf("[TRACE] %v", err)
continue
}
// write binary to tmp file
_, err = io.Copy(tmpFile, remoteZipFile)
_ = remoteZipFile.Close()
if err != nil {
err := fmt.Errorf("Error getting plugin, trying another getter: %w", err)
errs = multierror.Append(errs, err)
log.Printf("[TRACE] %s", err)
continue
}
if _, err := tmpFile.Seek(0, 0); err != nil {
err := fmt.Errorf("Error seeking begining of temporary file for checksumming, continuing: %w", err)
err := fmt.Errorf("Error seeking beginning of temporary file for checksumming, continuing: %w", err)
errs = multierror.Append(errs, err)
log.Printf("[TRACE] %s", err)
continue
}
// verify that the checksum for the zip is what we expect.
if err := checksum.Checksummer.Checksum(checksum.Expected, tmpFile); err != nil {
err := fmt.Errorf("%w. Is the checksum file correct ? Is the binary file correct ?", err)
@ -830,17 +817,9 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
continue
}
tmpFileStat, err := tmpFile.Stat()
zr, err := zip.OpenReader(tmpFile.Name())
if err != nil {
err := fmt.Errorf("failed to stat: %w", err)
errs = multierror.Append(errs, err)
return nil, errs
}
zr, err := zip.NewReader(tmpFile, tmpFileStat.Size())
if err != nil {
err := fmt.Errorf("zip : %v", err)
errs = multierror.Append(errs, err)
errs = multierror.Append(errs, fmt.Errorf("zip : %v", err))
return nil, errs
}
@ -851,8 +830,7 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
}
copyFrom, err = f.Open()
if err != nil {
err := fmt.Errorf("failed to open temp file: %w", err)
errs = multierror.Append(errs, err)
multierror.Append(errs, fmt.Errorf("failed to open temp file: %w", err))
return nil, errs
}
break
@ -863,50 +841,61 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
return nil, errs
}
tempPluginPath := filepath.Join(os.TempDir(), fmt.Sprintf(
"packer-plugin-temp-%s%s",
random.Numbers(8),
opts.BinaryInstallationOptions.Ext))
// Save binary to temp so we can ensure it is really the version advertised
tempOutput, err := os.OpenFile(tempPluginPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
tmpBinFileName := filepath.Join(os.TempDir(), expectedBinaryFilename)
if err != nil {
log.Printf("[ERROR] failed to create temp plugin executable: %s", err)
return nil, multierror.Append(errs, err)
err = fmt.Errorf("could not create temporary file to download plugin: %w", err)
errs = multierror.Append(errs, err)
return nil, errs
}
defer os.Remove(tempPluginPath)
defer func() {
tmpBinPath := tmpBinFileName
os.Remove(tmpBinPath)
}()
_, err = io.Copy(tempOutput, copyFrom)
tmpOutputFile, err := os.OpenFile(tmpBinFileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
if err != nil {
log.Printf("[ERROR] failed to copy uncompressed binary to %q: %s", tempPluginPath, err)
return nil, multierror.Append(errs, err)
err := fmt.Errorf("failed to create %s: %w", tmpBinFileName, err)
errs = multierror.Append(errs, err)
return nil, errs
}
if _, err := io.Copy(tmpOutputFile, copyFrom); err != nil {
err := fmt.Errorf("extract file: %w", err)
errs = multierror.Append(errs, err)
return nil, errs
}
// Not a problem on most platforms, but unsure Windows will let us execute an already
// open file, so we close it temporarily to avoid problems
_ = tempOutput.Close()
if _, err := tmpOutputFile.Seek(0, 0); err != nil {
err := fmt.Errorf("Error seeking beginning of binary file for checksumming: %w", err)
errs = multierror.Append(errs, err)
log.Printf("[WARNING] %v, ignoring", err)
}
desc, err := GetPluginDescription(tempPluginPath)
outputFileCS, err := checksum.Checksummer.Sum(tmpOutputFile)
if err != nil {
err := fmt.Errorf("failed to describe plugin binary %q: %s", tempPluginPath, err)
err := fmt.Errorf("failed to checksum binary file: %s", err)
errs = multierror.Append(errs, err)
continue
log.Printf("[WARNING] %v, ignoring", err)
}
tmpOutputFile.Close()
desc, err := GetPluginDescription(tmpBinFileName)
if err != nil {
err := fmt.Errorf("failed to describe plugin binary %q: %s", tmpBinFileName, err)
errs = multierror.Append(errs, err)
continue
}
descVersion, err := goversion.NewSemver(desc.Version)
if err != nil {
err := fmt.Errorf("invalid self-reported version %q: %s", desc.Version, err)
errs = multierror.Append(errs, err)
continue
}
if descVersion.Core().Compare(version.Core()) != 0 {
log.Printf("[ERROR] binary reported version (%q) is different from the expected %q, skipping", desc.Version, version.String())
err := fmt.Errorf("binary reported version (%q) is different from the expected %q, skipping", desc.Version, version.String())
errs = multierror.Append(errs, err)
continue
}
localOutputFileName := outputFileName
// Since releases only can be installed remotely, a non-empty prerelease version
// means something's not right on the release, as it should report a final version instead.
//
@ -914,50 +903,26 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
// cannot be loaded), we error here, and advise users to manually install the plugin if they
// need it.
if descVersion.Prerelease() != "" {
fmt.Printf("Plugin %q release v%s binary reports version %q. This is likely an upstream issue.\n"+
"Try opening an issue on the plugin repository asking them to update the plugin's version information.\n",
pr.Identifier.String(), version, desc.Version)
fmt.Printf("If you need this exact version of the plugin, you can download the binary from the source and install "+
"it locally with `packer plugins install --path '<plugin_binary>' %q`.\n"+
"This will install the plugin in version %q (required_plugins constraints may need to be updated).\n",
pr.Identifier.String(), desc.Version)
continue
}
err := fmt.Errorf(`binary for %[1]q release v%[2]s reported version %[3]q.
copyFrom, err = os.OpenFile(tempPluginPath, os.O_RDONLY, 0755)
if err != nil {
log.Printf("[ERROR] failed to re-open temporary plugin file %q: %s", tempPluginPath, err)
return nil, multierror.Append(errs, err)
}
Remote installation of pre-release binaries is unsupported an likely an upstream plugin issue.
We encourage you to open an issue on the plugin repository asking to update the version information.
If you require this specific version of the plugin, you can manually download the binary from the source and install
the versioned binary as %[3]q using the following command:
outputFile, err := os.OpenFile(localOutputFileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
if err != nil {
err := fmt.Errorf("failed to create %s: %w", localOutputFileName, err)
errs = multierror.Append(errs, err)
return nil, errs
}
defer outputFile.Close()
packer plugins install --path '<plugin_binary>' %[1]q`,
pr.Identifier.String(), version, desc.Version)
if _, err := io.Copy(outputFile, copyFrom); err != nil {
err := fmt.Errorf("extract file: %w", err)
errs = multierror.Append(errs, err)
return nil, errs
}
if _, err := outputFile.Seek(0, 0); err != nil {
err := fmt.Errorf("Error seeking begining of binary file for checksumming: %w", err)
if err := os.Rename(tmpBinFileName, outputFileName); err != nil {
err := fmt.Errorf("failed to write local binary file to plugin path: %s", err)
errs = multierror.Append(errs, err)
log.Printf("[WARNING] %v, ignoring", err)
}
cs, err := checksum.Checksummer.Sum(outputFile)
if err != nil {
err := fmt.Errorf("failed to checksum binary file: %s", err)
errs = multierror.Append(errs, err)
log.Printf("[WARNING] %v, ignoring", err)
return nil, errs
}
if err := os.WriteFile(localOutputFileName+checksum.Checksummer.FileExt(), []byte(hex.EncodeToString(cs)), 0644); err != nil {
if err := os.WriteFile(outputFileName+checksum.Checksummer.FileExt(), []byte(hex.EncodeToString(outputFileCS)), 0644); err != nil {
err := fmt.Errorf("failed to write local binary checksum file: %s", err)
errs = multierror.Append(errs, err)
log.Printf("[WARNING] %v, ignoring", err)
@ -965,7 +930,7 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
// Success !!
return &Installation{
BinaryPath: strings.ReplaceAll(localOutputFileName, "\\", "/"),
BinaryPath: strings.ReplaceAll(outputFileName, "\\", "/"),
Version: "v" + version.String(),
}, nil
}
@ -976,13 +941,11 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
}
}
if errs == nil || errs.Len() == 0 {
if errs.ErrorOrNil() == nil {
err := fmt.Errorf("could not find a local nor a remote checksum for plugin %q %q", pr.Identifier, pr.VersionConstraints)
errs = multierror.Append(errs, err)
}
errs = multierror.Append(errs, fmt.Errorf("could not install any compatible version of plugin %q", pr.Identifier))
return nil, errs
}

Loading…
Cancel
Save