When installing a plugin from a remote source, we list the installed
plugins that match the constraints specified, and if the constraint is
already satisfied, we don't do anything.
However, since remote installation is only relevant for releases of a
plugin, we should only look at the installed releases of a plugin, and
not consider pre-releases for that step.
This wasn't the case before this commit, as if a prerelease version of a
commit (ex: 10.8.1-dev), and we try to invoke `packer init` with a
constraint on this version specifically, Packer would locate that
pre-release and assume it was already installed, so would silently
succeed the command and do nothing.
This isn't the expected behaviour as we should install the final release
of that plugin, regardless of any prerelease installation of the plugin.
So this commit fixes that by only listing releases, so we don't report
the plugin being already installed if a prerelease is what's installed.
When packer init is invoked with a --force argument, but no --update, we
clamp the version to install based on the last one locally installed.
Doing this may however cause the constraint to always be false if the
latest available version of a plugin is a pre-release, as none of the
upstream constraints will match that.
Therefore this commit changes how the constraint is derived from the
local list of installations, so that only the last installation that
matches the original constraint will be used, and not a pre-release.
Since we're hardening what Packer is able to load locally when it comes
to plugins, we need also to harden the installation process a bit.
While testing we noticed some remotes had published their plugins with
version mismatches between the tag and the binary.
This was not a problem in the past, as Packer did not care for this,
only the binary name was important, and the plugin could be installed
without problem.
Nowadays however, since Packer enforces the plugin version reported in
the name to be the same as the plugin self-reported version, this makes
it impossible for the installed plugin to load anymore in such an
instance.
Therefore in order to limit confusion, and so users are able to
understand the problem and report it to the plugins with that mismatch,
we instead install the plugin as a dev version, and report it to the
user so they can still use it, but are able to report it to the plugin
developers.
When listing installed plugins, we check that the plugin's reported
version through describe matches what's in the name of the file.
Doing do, we were parsing the same version string twice without
modifying it, which was not necessary, so this commit changes that.
Whenever a Github release exposes an entry for another OS/arch
combination, this gets registered as an error, which in the event no
binary is compatible with the host's OS/arch, gets reported at the end
of the getter process.
While this is sound in theory, in practice we get the list of all the
combinations that don't match the host's, which is not something a
Packer user can act on, and might therefore be more confusing than
helping to solve the issue.
Therefore we opt in this commit to stop registering those cases as real
errors, and only log them as an INFO statement.
Since we named the version from the getter `version', this means we have
a naming conflict inside the loop that attempts to install a versioned
candidate for a plugin, making it impossible to invoke something from
the go-version package.
Since we'll introduce a change that needs the latter capability, we must
either rename the local variable to something else than `version', or we
need to alias the package locally.
This commit implements the latter, opting to call the package goversion.
When installing a remote plugin, and after we've either successfully
installed a binary, or exhausted all the possible sources, we print a
final error message if nothing was reported.
However, given that errs is a pointer to a structure, and if no errors
were produced, the the error list could be nil, leading to the call to
`Len()' to crash Packer.
This is exceedingly rare as in general the code attempts to read
multiple sources from Github, and therefore we almost always get some
error reported, but while changing the function's code, I managed to
make it crash while removing/changing some error statements.
Therefore to avoid future surprises, we first check that `errs' is not
nil before invoking `Len()' on it, as no errors and no plugins installed
mean that something went wrong all the same.
Given that calling the describe command on plugins and deserialising the
output as a plugin description is something done in multiple places in
the code, we factorise this operation so we don't need to copy/paste the
code around.
The zipfile containing the binaries we attempt to install from a remote
source is placed in the temporary directory of the host.
In general it is wiped automatically by the OS, but in some cases
(windows typically), it isn't.
To avoid cluttering the temporary directory, we clean-up after
ourselves, and remove the temporary zip file that we create when
attempting to install a plugin, regardless of it succeeding or not.
The source parsing logic was heavily directed towards Github compatible
source URIs, however if we want to support more cases, we need to make
sure we are able to specify those URIs, and to load plugins installed
from those sources.
Right now, since the getters available are only github.com, we will not
support remotely instlling plugins from sources other than github.com,
with the same set of constraints as before. However, we do support now
installing from a local plugin binary to any kind of source, and we
support loading them, including if a template wants this plugin
installed locally with version constraints.
The invalid_inexplicit_source_2 subtest for parse used to fail in
previous versions of Packer, but with the latest changes to plugin
source management, it won't, at least at parsing time, instead it fails
later when the Github getter (the only available for now) cannot get the
plugin binary from the source, as the source is not a valid Github URI.
A bug in the SDK prevents us from calling SemVer on the PluginVersion
derived from the rawVersion, as when doing so, we reset the semVer
attribute to only use the core part of the version, thereby dropping any
information on pre-release/metadata.
This is not ideal, and arguably not wanted. So in order to not lose this
information, we re-compute the SemVer from rawVersion, this way we don't
overwrite it for both structures.
Since the SDK was updated and now accepts raw version strings in
addition to also supporting metadata officially, we can start using
those functions in our version package as well.
The SDK's Ui interface has had some additions recently for supporting
format-strings and their arguments as paramereters to Say, Ask and
Error.
These changes to the interface means that all the implementations of
that interface need to match the definition, so in this case the
implementations we had in packer/ui.go need to be updated to support
those functions.
Since we have both version/version.go and version/VERSION to specify
version strings, both are a bit redundant.
As version/VERSION is supposed to be the source of truth now, we are
using it to derive the version informaiton we used to rely on in Packer
and its subcommands.
Note: doing this prevents us from changing the version/prerelease
through ldflags though as we derive Version/VersionPrerelease from the
rawVersion variable.
When building the container images for Packer (light and full), we
should only publish them in the version being built is a release, and
not a pre-release.
Changing this means that both light and full latest will always be only
a final release at any time.
When introduced back in January, the version variable extracted from the
binary name had a typo in its name, and was named `protocolVerionStr'
instead of `protocolVersionStr'.
This commit was already merged into main, so it's too late to fix at
introduction site, but we can fix it today as a separate commit ontop
the stack.
If a plugin is installed manually, its version number could be valid but
non-canonical (ex: 1.2.3 vs 01.002.0003).
Since these two versions refer to the same version, but the looks are
different, this may become ambiguous which version should be loaded.
To avoid such a situation, we reject explicitely non-canonical version
numbers in plugins, but only in path, we're aware that because of
metadata, the version from `describe' may already differ from the file
name.
The former way mock plugin sets were created meant that no API version
was set, and since it's private in the SDK, it cannot be set outside of
the package itself.
Fortunately, there is a NewSet function we can call, which initialises a
set properly so we can fill-in the information later.
However, because all the set maps were created in a `var` section, we
cannot create the set with `NewSet`, and then fill the information in
(outside of if there was a fluent interface, but this isn't the case
here).
We therefore opted to keep the variables defined and accessible
globally, but gone through a `init` function to initialise their values
for tests.
As with versions, API versions are useful possibly for ordering plugin
installations in order for Packer to choose which plugin to load.
This could be unnecessary as API versions are stable, and only in dev
plugins this could be a problem normally (there shouldn't be two same
releases of a plugin), but this cements API version in ordering plugins
so we avoid surprises later down the line.
As with the version of the plugin, the API version should also match
between the path and the self-reported API version from the describe
command.
This was not checked before, so users could masquerade a plugin's use of
an API version that may be incompatible with Packer.
To avoid this problem, we make sure both versions are the same, so that
they work as expected.
When installing plugins with the `packer plugins install --path'
command, the metadata is now scrubbed from the file installed locally.
This is as a protection against collisions in the versions, as metadata
is meaningless for version comparison, so if two versions of the same
plugin are installed, the precedence order between them is undefined.
Therefore to avoid such collisions, we remove the metadata from the file
name, that way if two successive versions of a plugin include metadata
in the version, they won't coexist, and the last installed will be the
only installed version locally.
When installing a plugin from a local binary, Packer builds the name of
the plugin from the results of the `describe' command.
Depending on how the plugin is built, the version reported may or may
not contain a leading `v', which was not taken into account beforehand
and the leading `v' was always injected in the path.
This caused plugins that report a leading `v' in their version to
be installed with two v's in their path, making them impossible to load.
Therefore to fix this issue, we count on the version library to print
out a version without the leading v, and we inject that in the resulting
path.