command: Include provider versions in "terraform version"
We encourage users to share the "terraform version" output as part of filing an issue, but previously it only printed the core Terraform version and this left provider maintainers with no information about which _provider_ version an issue relates to. Here we make a best effort to show versions for providers, though we will omit some or all of them if either "terraform init" hasn't been run (and so no providers were selected yet) or if there are other inconsistencies that would cause Terraform to object on startup and require a re-run of "terraform init".
This commit is contained in:
parent
34cecfa839
commit
5347f82f9a
|
@ -33,8 +33,64 @@ func TestVersion(t *testing.T) {
|
|||
|
||||
wantVersion := fmt.Sprintf("Terraform v%s", tfcore.VersionString())
|
||||
if !strings.Contains(stdout, wantVersion) {
|
||||
wantVersion := fmt.Sprintf("Terraform %s", tfcore.VersionString())
|
||||
if strings.Contains(stdout, wantVersion) {
|
||||
t.Errorf("output does not contain our current version %q:\n%s", wantVersion, stdout)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionWithProvider(t *testing.T) {
|
||||
// This is a more elaborate use of "version" that shows the selected
|
||||
// versions of plugins too.
|
||||
t.Parallel()
|
||||
|
||||
// This test reaches out to releases.hashicorp.com to download the
|
||||
// template and null providers, so it can only run if network access is
|
||||
// allowed.
|
||||
skipIfCannotAccessNetwork(t)
|
||||
|
||||
fixturePath := filepath.Join("test-fixtures", "template-provider")
|
||||
tf := e2e.NewBinary(terraformBin, fixturePath)
|
||||
defer tf.Close()
|
||||
|
||||
// Initial run (before "init") should work without error but will not
|
||||
// include the provider version, since we've not "locked" one yet.
|
||||
{
|
||||
stdout, stderr, err := tf.Run("version")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if stderr != "" {
|
||||
t.Errorf("unexpected stderr output:\n%s", stderr)
|
||||
}
|
||||
|
||||
wantVersion := fmt.Sprintf("Terraform v%s", tfcore.VersionString())
|
||||
if !strings.Contains(stdout, wantVersion) {
|
||||
t.Errorf("output does not contain our current version %q:\n%s", wantVersion, stdout)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
_, _, err := tf.Run("init")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// After running init, we additionally include information about the
|
||||
// selected version of the "template" provider.
|
||||
{
|
||||
stdout, stderr, err := tf.Run("version")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if stderr != "" {
|
||||
t.Errorf("unexpected stderr output:\n%s", stderr)
|
||||
}
|
||||
|
||||
wantMsg := "+ provider.template v" // we don't know which version we'll get here
|
||||
if !strings.Contains(stdout, wantMsg) {
|
||||
t.Errorf("output does not contain provider information %q:\n%s", wantMsg, stdout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package command
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// VersionCommand is a Command implementation prints the version.
|
||||
|
@ -50,6 +51,50 @@ func (c *VersionCommand) Run(args []string) int {
|
|||
|
||||
c.Ui.Output(versionString.String())
|
||||
|
||||
// We'll also attempt to print out the selected plugin versions. We can
|
||||
// do this only if "terraform init" was already run and thus we've committed
|
||||
// to a specific set of plugins. If not, the plugins lock will be empty
|
||||
// and so we'll show _no_ providers.
|
||||
//
|
||||
// Generally-speaking this is a best-effort thing that will give us a good
|
||||
// result in the usual case where the user successfully ran "terraform init"
|
||||
// and then hit a problem running _another_ command.
|
||||
providerPlugins := c.providerPluginSet()
|
||||
pluginsLockFile := c.providerPluginsLock()
|
||||
pluginsLock := pluginsLockFile.Read()
|
||||
var pluginVersions []string
|
||||
for meta := range providerPlugins {
|
||||
name := meta.Name
|
||||
wantHash, wanted := pluginsLock[name]
|
||||
if !wanted {
|
||||
// Ignore providers that aren't used by the current config at all
|
||||
continue
|
||||
}
|
||||
gotHash, err := meta.SHA256()
|
||||
if err != nil {
|
||||
// if we can't read the file to hash it, ignore it.
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(gotHash, wantHash) {
|
||||
// Not the plugin we've locked, so ignore it.
|
||||
continue
|
||||
}
|
||||
|
||||
// If we get here then we've found a selected plugin, so we'll print
|
||||
// out its details.
|
||||
if meta.Version == "0.0.0" {
|
||||
pluginVersions = append(pluginVersions, fmt.Sprintf("+ provider.%s (unversioned)", name))
|
||||
} else {
|
||||
pluginVersions = append(pluginVersions, fmt.Sprintf("+ provider.%s v%s", name, meta.Version))
|
||||
}
|
||||
}
|
||||
if len(pluginVersions) != 0 {
|
||||
sort.Strings(pluginVersions)
|
||||
for _, str := range pluginVersions {
|
||||
c.Ui.Output(str)
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a version check function, then let's check for
|
||||
// the latest version as well.
|
||||
if c.CheckFunc != nil {
|
||||
|
|
Loading…
Reference in New Issue