remove unused code (#26503)
* remove unused code I've removed the provider-specific code under registry, and unused nil backend, and replaced a call to helper from backend/oss (the other callers of that func are provisioners scheduled to be deprecated). I also removed the Dockerfile, as our build process uses a different file. Finally I removed the examples directory, which had outdated examples and links. There are better, actively maintained examples available. * command: remove various unused bits * test wasn't running * backend: remove unused err
This commit is contained in:
parent
7a7ad23113
commit
d2e999ba1f
24
Dockerfile
24
Dockerfile
|
@ -1,24 +0,0 @@
|
||||||
# This Dockerfile builds on golang:alpine by building Terraform from source
|
|
||||||
# using the current working directory.
|
|
||||||
#
|
|
||||||
# This produces a docker image that contains a working Terraform binary along
|
|
||||||
# with all of its source code, which is what gets released on hub.docker.com
|
|
||||||
# as terraform:full. The main releases (terraform:latest, terraform:light and
|
|
||||||
# the release tags) are lighter images including only the officially-released
|
|
||||||
# binary from releases.hashicorp.com; these are built instead from
|
|
||||||
# scripts/docker-release/Dockerfile-release.
|
|
||||||
|
|
||||||
FROM golang:alpine
|
|
||||||
LABEL maintainer="HashiCorp Terraform Team <terraform@hashicorp.com>"
|
|
||||||
|
|
||||||
RUN apk add --no-cache git bash openssh
|
|
||||||
|
|
||||||
ENV TF_DEV=true
|
|
||||||
ENV TF_RELEASE=1
|
|
||||||
|
|
||||||
WORKDIR $GOPATH/src/github.com/hashicorp/terraform
|
|
||||||
COPY . .
|
|
||||||
RUN /bin/bash scripts/build.sh
|
|
||||||
|
|
||||||
WORKDIR $GOPATH
|
|
||||||
ENTRYPOINT ["terraform"]
|
|
|
@ -34,10 +34,6 @@ var (
|
||||||
ErrDefaultWorkspaceNotSupported = errors.New("default workspace not supported\n" +
|
ErrDefaultWorkspaceNotSupported = errors.New("default workspace not supported\n" +
|
||||||
"You can create a new workspace with the \"workspace new\" command.")
|
"You can create a new workspace with the \"workspace new\" command.")
|
||||||
|
|
||||||
// ErrOperationNotSupported is returned when an unsupported operation
|
|
||||||
// is detected by the configured backend.
|
|
||||||
ErrOperationNotSupported = errors.New("operation not supported")
|
|
||||||
|
|
||||||
// ErrWorkspacesNotSupported is an error returned when a caller attempts
|
// ErrWorkspacesNotSupported is an error returned when a caller attempts
|
||||||
// to perform an operation on a workspace other than "default" for a
|
// to perform an operation on a workspace other than "default" for a
|
||||||
// backend that doesn't support multiple workspaces.
|
// backend that doesn't support multiple workspaces.
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
package backend
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/hashicorp/terraform/configs/configschema"
|
|
||||||
"github.com/hashicorp/terraform/states/statemgr"
|
|
||||||
"github.com/hashicorp/terraform/tfdiags"
|
|
||||||
"github.com/zclconf/go-cty/cty"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Nil is a no-op implementation of Backend.
|
|
||||||
//
|
|
||||||
// This is useful to embed within another struct to implement all of the
|
|
||||||
// backend interface for testing.
|
|
||||||
type Nil struct{}
|
|
||||||
|
|
||||||
func (Nil) ConfigSchema() *configschema.Block {
|
|
||||||
return &configschema.Block{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Nil) PrepareConfig(v cty.Value) (cty.Value, tfdiags.Diagnostics) {
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Nil) Configure(cty.Value) tfdiags.Diagnostics {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Nil) StateMgr(string) (statemgr.Full, error) {
|
|
||||||
// We must return a non-nil manager to adhere to the interface, so
|
|
||||||
// we'll return an in-memory-only one.
|
|
||||||
return statemgr.NewFullFake(statemgr.NewTransientInMemory(nil), nil), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Nil) DeleteWorkspace(string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Nil) Workspaces() ([]string, error) {
|
|
||||||
return []string{DefaultStateName}, nil
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
package backend
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNil_impl(t *testing.T) {
|
|
||||||
var _ Backend = new(Nil)
|
|
||||||
}
|
|
|
@ -5,33 +5,30 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
|
|
||||||
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses"
|
|
||||||
"github.com/aliyun/alibaba-cloud-sdk-go/services/sts"
|
|
||||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
|
||||||
"github.com/hashicorp/terraform/backend"
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
|
||||||
"github.com/hashicorp/terraform/helper/validation"
|
|
||||||
"github.com/jmespath/go-jmespath"
|
|
||||||
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
|
"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
|
||||||
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
|
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
|
||||||
|
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
|
||||||
|
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses"
|
||||||
"github.com/aliyun/alibaba-cloud-sdk-go/services/location"
|
"github.com/aliyun/alibaba-cloud-sdk-go/services/location"
|
||||||
|
"github.com/aliyun/alibaba-cloud-sdk-go/services/sts"
|
||||||
|
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||||
"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
|
"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
|
||||||
"github.com/hashicorp/go-cleanhttp"
|
"github.com/hashicorp/go-cleanhttp"
|
||||||
|
"github.com/hashicorp/terraform/backend"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/hashicorp/terraform/version"
|
"github.com/hashicorp/terraform/version"
|
||||||
|
"github.com/jmespath/go-jmespath"
|
||||||
"github.com/mitchellh/go-homedir"
|
"github.com/mitchellh/go-homedir"
|
||||||
"net/url"
|
|
||||||
"regexp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// New creates a new backend for OSS remote state.
|
// New creates a new backend for OSS remote state.
|
||||||
|
@ -196,10 +193,23 @@ func assumeRoleSchema() *schema.Schema {
|
||||||
Description: "The permissions applied when assuming a role. You cannot use this policy to grant permissions which exceed those of the role that is being assumed.",
|
Description: "The permissions applied when assuming a role. You cannot use this policy to grant permissions which exceed those of the role that is being assumed.",
|
||||||
},
|
},
|
||||||
"session_expiration": {
|
"session_expiration": {
|
||||||
Type: schema.TypeInt,
|
Type: schema.TypeInt,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Description: "The time after which the established session for assuming role expires.",
|
Description: "The time after which the established session for assuming role expires.",
|
||||||
ValidateFunc: validation.IntBetween(900, 3600),
|
ValidateFunc: func(v interface{}, k string) ([]string, []error) {
|
||||||
|
min := 900
|
||||||
|
max := 3600
|
||||||
|
value, ok := v.(int)
|
||||||
|
if !ok {
|
||||||
|
return nil, []error{fmt.Errorf("expected type of %s to be int", k)}
|
||||||
|
}
|
||||||
|
|
||||||
|
if value < min || value > max {
|
||||||
|
return nil, []error{fmt.Errorf("expected %s to be in the range (%d - %d), got %d", k, min, max, v)}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
141
command/init.go
141
command/init.go
|
@ -427,9 +427,8 @@ func (c *InitCommand) getProviders(config *configs.Config, state *states.State,
|
||||||
if moreDiags.HasErrors() {
|
if moreDiags.HasErrors() {
|
||||||
return false, true, diags
|
return false, true, diags
|
||||||
}
|
}
|
||||||
stateReqs := make(getproviders.Requirements, 0)
|
|
||||||
if state != nil {
|
if state != nil {
|
||||||
stateReqs = state.ProviderRequirements()
|
stateReqs := state.ProviderRequirements()
|
||||||
reqs = reqs.Merge(stateReqs)
|
reqs = reqs.Merge(stateReqs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -774,16 +773,6 @@ func (c *InitCommand) getProviders(config *configs.Config, state *states.State,
|
||||||
return true, false, diags
|
return true, false, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *InitCommand) populateProviderToReqs(reqs map[addrs.Provider][]*configs.ModuleRequirements, node *configs.ModuleRequirements) {
|
|
||||||
for fqn := range node.Requirements {
|
|
||||||
reqs[fqn] = append(reqs[fqn], node)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, child := range node.Children {
|
|
||||||
c.populateProviderToReqs(reqs, child)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// backendConfigOverrideBody interprets the raw values of -backend-config
|
// backendConfigOverrideBody interprets the raw values of -backend-config
|
||||||
// arguments into a hcl Body that should override the backend settings given
|
// arguments into a hcl Body that should override the backend settings given
|
||||||
// in the configuration.
|
// in the configuration.
|
||||||
|
@ -1030,117 +1019,6 @@ changes, we recommend adding version constraints in a required_providers block
|
||||||
in your configuration, with the constraint strings suggested below.
|
in your configuration, with the constraint strings suggested below.
|
||||||
`
|
`
|
||||||
|
|
||||||
const errDiscoveryServiceUnreachable = `
|
|
||||||
[reset][bold][red]Registry service unreachable.[reset][red]
|
|
||||||
|
|
||||||
This may indicate a network issue, or an issue with the requested Terraform Registry.
|
|
||||||
`
|
|
||||||
|
|
||||||
const errProviderNotFound = `
|
|
||||||
[reset][bold][red]Provider %[1]q not available for installation.[reset][red]
|
|
||||||
|
|
||||||
A provider named %[1]q could not be found in the Terraform Registry.
|
|
||||||
|
|
||||||
This may result from mistyping the provider name, or the given provider may
|
|
||||||
be a third-party provider that cannot be installed automatically.
|
|
||||||
|
|
||||||
In the latter case, the plugin must be installed manually by locating and
|
|
||||||
downloading a suitable distribution package and placing the plugin's executable
|
|
||||||
file in the following directory:
|
|
||||||
%[2]s
|
|
||||||
|
|
||||||
Terraform detects necessary plugins by inspecting the configuration and state.
|
|
||||||
To view the provider versions requested by each module, run
|
|
||||||
"terraform providers".
|
|
||||||
`
|
|
||||||
|
|
||||||
const errProviderVersionsUnsuitable = `
|
|
||||||
[reset][bold][red]No provider %[1]q plugins meet the constraint %[2]q.[reset][red]
|
|
||||||
|
|
||||||
The version constraint is derived from the "version" argument within the
|
|
||||||
provider %[1]q block in configuration. Child modules may also apply
|
|
||||||
provider version constraints. To view the provider versions requested by each
|
|
||||||
module in the current configuration, run "terraform providers".
|
|
||||||
|
|
||||||
To proceed, the version constraints for this provider must be relaxed by
|
|
||||||
either adjusting or removing the "version" argument in the provider blocks
|
|
||||||
throughout the configuration.
|
|
||||||
`
|
|
||||||
|
|
||||||
const errProviderIncompatible = `
|
|
||||||
[reset][bold][red]No available provider %[1]q plugins are compatible with this Terraform version.[reset][red]
|
|
||||||
|
|
||||||
From time to time, new Terraform major releases can change the requirements for
|
|
||||||
plugins such that older plugins become incompatible.
|
|
||||||
|
|
||||||
Terraform checked all of the plugin versions matching the given constraint:
|
|
||||||
%[2]s
|
|
||||||
|
|
||||||
Unfortunately, none of the suitable versions are compatible with this version
|
|
||||||
of Terraform. If you have recently upgraded Terraform, it may be necessary to
|
|
||||||
move to a newer major release of this provider. Alternatively, if you are
|
|
||||||
attempting to upgrade the provider to a new major version you may need to
|
|
||||||
also upgrade Terraform to support the new version.
|
|
||||||
|
|
||||||
Consult the documentation for this provider for more information on
|
|
||||||
compatibility between provider versions and Terraform versions.
|
|
||||||
`
|
|
||||||
|
|
||||||
const errProviderInstallError = `
|
|
||||||
[reset][bold][red]Error installing provider %[1]q: %[2]s.[reset][red]
|
|
||||||
|
|
||||||
Terraform analyses the configuration and state and automatically downloads
|
|
||||||
plugins for the providers used. However, when attempting to download this
|
|
||||||
plugin an unexpected error occurred.
|
|
||||||
|
|
||||||
This may be caused if for some reason Terraform is unable to reach the
|
|
||||||
plugin repository. The repository may be unreachable if access is blocked
|
|
||||||
by a firewall.
|
|
||||||
|
|
||||||
If automatic installation is not possible or desirable in your environment,
|
|
||||||
you may alternatively manually install plugins by downloading a suitable
|
|
||||||
distribution package and placing the plugin's executable file in the
|
|
||||||
following directory:
|
|
||||||
%[3]s
|
|
||||||
`
|
|
||||||
|
|
||||||
const errMissingProvidersNoInstall = `
|
|
||||||
[reset][bold][red]Missing required providers.[reset][red]
|
|
||||||
|
|
||||||
The following provider constraints are not met by the currently-installed
|
|
||||||
provider plugins:
|
|
||||||
|
|
||||||
%[1]s
|
|
||||||
Terraform can automatically download and install plugins to meet the given
|
|
||||||
constraints, but this step was skipped due to the use of -get-plugins=false
|
|
||||||
and/or -plugin-dir on the command line.
|
|
||||||
|
|
||||||
If automatic installation is not possible or desirable in your environment,
|
|
||||||
you may manually install plugins by downloading a suitable distribution package
|
|
||||||
and placing the plugin's executable file in one of the directories given in
|
|
||||||
by -plugin-dir on the command line, or in the following directory if custom
|
|
||||||
plugin directories are not set:
|
|
||||||
%[2]s
|
|
||||||
`
|
|
||||||
|
|
||||||
const errChecksumVerification = `
|
|
||||||
[reset][bold][red]Error verifying checksum for provider %[1]q[reset][red]
|
|
||||||
The checksum for provider distribution from the Terraform Registry
|
|
||||||
did not match the source. This may mean that the distributed files
|
|
||||||
were changed after this version was released to the Registry.
|
|
||||||
`
|
|
||||||
|
|
||||||
const errSignatureVerification = `
|
|
||||||
[reset][bold][red]Error:[reset][bold] Untrusted signing key for provider %[1]q[reset]
|
|
||||||
|
|
||||||
This provider package is not signed with the HashiCorp signing key, and is
|
|
||||||
therefore incompatible with Terraform v%[2]s.
|
|
||||||
|
|
||||||
A later version of Terraform may have introduced other signing keys that would
|
|
||||||
accept this provider. Alternatively, an earlier version of this provider may
|
|
||||||
be compatible with Terraform v%[2]s.
|
|
||||||
`
|
|
||||||
|
|
||||||
// providerProtocolTooOld is a message sent to the CLI UI if the provider's
|
// providerProtocolTooOld is a message sent to the CLI UI if the provider's
|
||||||
// supported protocol versions are too old for the user's version of terraform,
|
// supported protocol versions are too old for the user's version of terraform,
|
||||||
// but a newer version of the provider is compatible.
|
// but a newer version of the provider is compatible.
|
||||||
|
@ -1171,20 +1049,3 @@ Alternatively, upgrade to the latest version of Terraform for compatibility with
|
||||||
|
|
||||||
// No version of the provider is compatible.
|
// No version of the provider is compatible.
|
||||||
const errProviderVersionIncompatible = `No compatible versions of provider %s were found.`
|
const errProviderVersionIncompatible = `No compatible versions of provider %s were found.`
|
||||||
|
|
||||||
// Logic from internal/initwd/getter.go
|
|
||||||
var localSourcePrefixes = []string{
|
|
||||||
"./",
|
|
||||||
"../",
|
|
||||||
".\\",
|
|
||||||
"..\\",
|
|
||||||
}
|
|
||||||
|
|
||||||
func isLocalSourceAddr(addr string) bool {
|
|
||||||
for _, prefix := range localSourcePrefixes {
|
|
||||||
if strings.HasPrefix(addr, prefix) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
|
@ -316,20 +316,6 @@ func (m *Meta) backendCLIOpts() (*backend.CLIOpts, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsLocalBackend returns true if the backend is a local backend. We use this
|
|
||||||
// for some checks that require a remote backend.
|
|
||||||
func (m *Meta) IsLocalBackend(b backend.Backend) bool {
|
|
||||||
// Is it a local backend?
|
|
||||||
bLocal, ok := b.(*backendLocal.Local)
|
|
||||||
|
|
||||||
// If it is, does it not have an alternate state backend?
|
|
||||||
if ok {
|
|
||||||
ok = bLocal.Backend == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Operation initializes a new backend.Operation struct.
|
// Operation initializes a new backend.Operation struct.
|
||||||
//
|
//
|
||||||
// This prepares the operation. After calling this, the caller is expected
|
// This prepares the operation. After calling this, the caller is expected
|
||||||
|
@ -728,36 +714,6 @@ func (m *Meta) backend_c_r_S(c *configs.Backend, cHash int, sMgr *clistate.Local
|
||||||
return nil, diags
|
return nil, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legacy remote state
|
|
||||||
func (m *Meta) backend_c_R_s(c *configs.Backend, sMgr *clistate.LocalState) (backend.Backend, tfdiags.Diagnostics) {
|
|
||||||
var diags tfdiags.Diagnostics
|
|
||||||
|
|
||||||
m.Ui.Error(strings.TrimSpace(errBackendLegacy) + "\n")
|
|
||||||
|
|
||||||
diags = diags.Append(fmt.Errorf("Cannot initialize legacy remote state"))
|
|
||||||
return nil, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsetting backend, saved backend, legacy remote state
|
|
||||||
func (m *Meta) backend_c_R_S(c *configs.Backend, cHash int, sMgr *clistate.LocalState) (backend.Backend, tfdiags.Diagnostics) {
|
|
||||||
var diags tfdiags.Diagnostics
|
|
||||||
|
|
||||||
m.Ui.Error(strings.TrimSpace(errBackendLegacy) + "\n")
|
|
||||||
|
|
||||||
diags = diags.Append(fmt.Errorf("Cannot initialize legacy remote state"))
|
|
||||||
return nil, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configuring a backend for the first time with legacy remote state.
|
|
||||||
func (m *Meta) backend_C_R_s(c *configs.Backend, sMgr *clistate.LocalState) (backend.Backend, tfdiags.Diagnostics) {
|
|
||||||
var diags tfdiags.Diagnostics
|
|
||||||
|
|
||||||
m.Ui.Error(strings.TrimSpace(errBackendLegacy) + "\n")
|
|
||||||
|
|
||||||
diags = diags.Append(fmt.Errorf("Cannot initialize legacy remote state"))
|
|
||||||
return nil, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configuring a backend for the first time.
|
// Configuring a backend for the first time.
|
||||||
func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.LocalState) (backend.Backend, tfdiags.Diagnostics) {
|
func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.LocalState) (backend.Backend, tfdiags.Diagnostics) {
|
||||||
// Get the backend
|
// Get the backend
|
||||||
|
@ -1025,26 +981,6 @@ func (m *Meta) backend_C_r_S_unchanged(c *configs.Backend, cHash int, sMgr *clis
|
||||||
return b, diags
|
return b, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initiailizing a changed saved backend with legacy remote state.
|
|
||||||
func (m *Meta) backend_C_R_S_changed(c *configs.Backend, sMgr *clistate.LocalState) (backend.Backend, tfdiags.Diagnostics) {
|
|
||||||
var diags tfdiags.Diagnostics
|
|
||||||
|
|
||||||
m.Ui.Error(strings.TrimSpace(errBackendLegacy) + "\n")
|
|
||||||
|
|
||||||
diags = diags.Append(fmt.Errorf("Cannot initialize legacy remote state"))
|
|
||||||
return nil, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initiailizing an unchanged saved backend with legacy remote state.
|
|
||||||
func (m *Meta) backend_C_R_S_unchanged(c *configs.Backend, sMgr *clistate.LocalState, output bool) (backend.Backend, tfdiags.Diagnostics) {
|
|
||||||
var diags tfdiags.Diagnostics
|
|
||||||
|
|
||||||
m.Ui.Error(strings.TrimSpace(errBackendLegacy) + "\n")
|
|
||||||
|
|
||||||
diags = diags.Append(fmt.Errorf("Cannot initialize legacy remote state"))
|
|
||||||
return nil, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------
|
//-------------------------------------------------------------------
|
||||||
// Reusable helper functions for backend management
|
// Reusable helper functions for backend management
|
||||||
//-------------------------------------------------------------------
|
//-------------------------------------------------------------------
|
||||||
|
@ -1150,36 +1086,6 @@ func (m *Meta) backendInitFromConfig(c *configs.Backend) (backend.Backend, cty.V
|
||||||
return b, configVal, diags
|
return b, configVal, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Meta) backendInitFromSaved(s *terraform.BackendState) (backend.Backend, tfdiags.Diagnostics) {
|
|
||||||
var diags tfdiags.Diagnostics
|
|
||||||
|
|
||||||
// Get the backend
|
|
||||||
f := backendInit.Backend(s.Type)
|
|
||||||
if f == nil {
|
|
||||||
diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), s.Type))
|
|
||||||
return nil, diags
|
|
||||||
}
|
|
||||||
b := f()
|
|
||||||
|
|
||||||
schema := b.ConfigSchema()
|
|
||||||
configVal, err := s.Config(schema)
|
|
||||||
if err != nil {
|
|
||||||
diags = diags.Append(errwrap.Wrapf("saved backend configuration is invalid: {{err}}", err))
|
|
||||||
return nil, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
newVal, validateDiags := b.PrepareConfig(configVal)
|
|
||||||
diags = diags.Append(validateDiags)
|
|
||||||
if validateDiags.HasErrors() {
|
|
||||||
return nil, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
configureDiags := b.Configure(newVal)
|
|
||||||
diags = diags.Append(configureDiags)
|
|
||||||
|
|
||||||
return b, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Meta) backendInitRequired(reason string) {
|
func (m *Meta) backendInitRequired(reason string) {
|
||||||
m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
|
m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
|
||||||
"[reset]"+strings.TrimSpace(errBackendInit)+"\n", reason)))
|
"[reset]"+strings.TrimSpace(errBackendInit)+"\n", reason)))
|
||||||
|
@ -1194,30 +1100,6 @@ func (m *Meta) backendInitRequired(reason string) {
|
||||||
var errBackendInitRequired = errors.New(
|
var errBackendInitRequired = errors.New(
|
||||||
"Initialization required. Please see the error message above.")
|
"Initialization required. Please see the error message above.")
|
||||||
|
|
||||||
const errBackendLegacyConfig = `
|
|
||||||
One or more errors occurred while configuring the legacy remote state.
|
|
||||||
If fixing these errors requires changing your remote state configuration,
|
|
||||||
you must switch your configuration to the new remote backend configuration.
|
|
||||||
You can learn more about remote backends at the URL below:
|
|
||||||
|
|
||||||
https://www.terraform.io/docs/backends/index.html
|
|
||||||
|
|
||||||
The error(s) configuring the legacy remote state:
|
|
||||||
|
|
||||||
%s
|
|
||||||
`
|
|
||||||
|
|
||||||
const errBackendLegacyUnknown = `
|
|
||||||
The legacy remote state type %q could not be found.
|
|
||||||
|
|
||||||
Terraform 0.9.0 shipped with backwards compatibility for all built-in
|
|
||||||
legacy remote state types. This error may mean that you were using a
|
|
||||||
custom Terraform build that perhaps supported a different type of
|
|
||||||
remote state.
|
|
||||||
|
|
||||||
Please check with the creator of the remote state above and try again.
|
|
||||||
`
|
|
||||||
|
|
||||||
const errBackendLocalRead = `
|
const errBackendLocalRead = `
|
||||||
Error reading local state: %s
|
Error reading local state: %s
|
||||||
|
|
||||||
|
@ -1237,29 +1119,6 @@ with a backend, we must delete the local state file. Please resolve the
|
||||||
issue above and retry the command.
|
issue above and retry the command.
|
||||||
`
|
`
|
||||||
|
|
||||||
const errBackendMigrateNew = `
|
|
||||||
Error migrating local state to backend: %s
|
|
||||||
|
|
||||||
Your local state remains intact and unmodified. Please resolve the error
|
|
||||||
above and try again.
|
|
||||||
`
|
|
||||||
|
|
||||||
const errBackendNewConfig = `
|
|
||||||
Error configuring the backend %q: %s
|
|
||||||
|
|
||||||
Please update the configuration in your Terraform files to fix this error
|
|
||||||
then run this command again.
|
|
||||||
`
|
|
||||||
|
|
||||||
const errBackendNewRead = `
|
|
||||||
Error reading newly configured backend state: %s
|
|
||||||
|
|
||||||
Terraform is trying to read the state from your newly configured backend
|
|
||||||
to determine the copy process for your existing state. Backends are expected
|
|
||||||
to not error even if there is no state yet written. Please resolve the
|
|
||||||
error above and try again.
|
|
||||||
`
|
|
||||||
|
|
||||||
const errBackendNewUnknown = `
|
const errBackendNewUnknown = `
|
||||||
The backend %q could not be found.
|
The backend %q could not be found.
|
||||||
|
|
||||||
|
@ -1280,34 +1139,6 @@ If the backend already contains existing workspaces, you may need to update
|
||||||
the backend configuration.
|
the backend configuration.
|
||||||
`
|
`
|
||||||
|
|
||||||
const errBackendRemoteRead = `
|
|
||||||
Error reading backend state: %s
|
|
||||||
|
|
||||||
Terraform is trying to read the state from your configured backend to
|
|
||||||
determine if there is any migration steps necessary. Terraform can't continue
|
|
||||||
without this check because that would risk losing state. Please resolve the
|
|
||||||
error above and try again.
|
|
||||||
`
|
|
||||||
|
|
||||||
const errBackendSavedConfig = `
|
|
||||||
Error configuring the backend %q: %s
|
|
||||||
|
|
||||||
Please update the configuration in your Terraform files to fix this error.
|
|
||||||
If you'd like to update the configuration interactively without storing
|
|
||||||
the values in your configuration, run "terraform init".
|
|
||||||
`
|
|
||||||
|
|
||||||
const errBackendSavedUnsetConfig = `
|
|
||||||
Error configuring the existing backend %q: %s
|
|
||||||
|
|
||||||
Terraform must configure the existing backend in order to copy the state
|
|
||||||
from the existing backend, as requested. Please resolve the error and try
|
|
||||||
again. If you choose to not copy the existing state, Terraform will not
|
|
||||||
configure the backend. If the configuration is invalid, please update your
|
|
||||||
Terraform configuration with proper configuration for this backend first
|
|
||||||
before unsetting the backend.
|
|
||||||
`
|
|
||||||
|
|
||||||
const errBackendSavedUnknown = `
|
const errBackendSavedUnknown = `
|
||||||
The backend %q could not be found.
|
The backend %q could not be found.
|
||||||
|
|
||||||
|
@ -1321,14 +1152,6 @@ If you'd like to force remove this backend, you must update your configuration
|
||||||
to not use the backend and run "terraform init" (or any other command) again.
|
to not use the backend and run "terraform init" (or any other command) again.
|
||||||
`
|
`
|
||||||
|
|
||||||
const errBackendClearLegacy = `
|
|
||||||
Error clearing the legacy remote state configuration: %s
|
|
||||||
|
|
||||||
Terraform completed configuring your backend. It is now safe to remove
|
|
||||||
the legacy remote state configuration, but an error occurred while trying
|
|
||||||
to do so. Please look at the error above, resolve it, and try again.
|
|
||||||
`
|
|
||||||
|
|
||||||
const errBackendClearSaved = `
|
const errBackendClearSaved = `
|
||||||
Error clearing the backend configuration: %s
|
Error clearing the backend configuration: %s
|
||||||
|
|
||||||
|
@ -1365,72 +1188,14 @@ are usually due to simple file permission errors. Please look at the error
|
||||||
above, resolve it, and try again.
|
above, resolve it, and try again.
|
||||||
`
|
`
|
||||||
|
|
||||||
const errBackendPlanBoth = `
|
|
||||||
The plan file contained both a legacy remote state and backend configuration.
|
|
||||||
This is not allowed. Please recreate the plan file with the latest version of
|
|
||||||
Terraform.
|
|
||||||
`
|
|
||||||
|
|
||||||
const errBackendPlanLineageDiff = `
|
|
||||||
The plan file contains a state with a differing lineage than the current
|
|
||||||
state. By continuing, your current state would be overwritten by the state
|
|
||||||
in the plan. Please either update the plan with the latest state or delete
|
|
||||||
your current state and try again.
|
|
||||||
|
|
||||||
"Lineage" is a unique identifier generated only once on the creation of
|
|
||||||
a new, empty state. If these values differ, it means they were created new
|
|
||||||
at different times. Therefore, Terraform must assume that they're completely
|
|
||||||
different states.
|
|
||||||
|
|
||||||
The most common cause of seeing this error is using a plan that was
|
|
||||||
created against a different state. Perhaps the plan is very old and the
|
|
||||||
state has since been recreated, or perhaps the plan was against a completely
|
|
||||||
different infrastructure.
|
|
||||||
`
|
|
||||||
|
|
||||||
const errBackendPlanStateFlag = `
|
|
||||||
The -state and -state-out flags cannot be set with a plan that has a remote
|
|
||||||
state. The plan itself contains the configuration for the remote backend to
|
|
||||||
store state. The state will be written there for consistency.
|
|
||||||
|
|
||||||
If you wish to change this behavior, please create a plan from local state.
|
|
||||||
You may use the state flags with plans from local state to affect where
|
|
||||||
the final state is written.
|
|
||||||
`
|
|
||||||
|
|
||||||
const errBackendPlanOlder = `
|
|
||||||
This plan was created against an older state than is current. Please create
|
|
||||||
a new plan file against the latest state and try again.
|
|
||||||
|
|
||||||
Terraform doesn't allow you to run plans that were created from older
|
|
||||||
states since it doesn't properly represent the latest changes Terraform
|
|
||||||
may have made, and can result in unsafe behavior.
|
|
||||||
|
|
||||||
Plan Serial: %[1]d
|
|
||||||
Current Serial: %[2]d
|
|
||||||
`
|
|
||||||
|
|
||||||
const outputBackendMigrateChange = `
|
const outputBackendMigrateChange = `
|
||||||
Terraform detected that the backend type changed from %q to %q.
|
Terraform detected that the backend type changed from %q to %q.
|
||||||
`
|
`
|
||||||
|
|
||||||
const outputBackendMigrateLegacy = `
|
|
||||||
Terraform detected legacy remote state.
|
|
||||||
`
|
|
||||||
|
|
||||||
const outputBackendMigrateLocal = `
|
const outputBackendMigrateLocal = `
|
||||||
Terraform has detected you're unconfiguring your previously set %q backend.
|
Terraform has detected you're unconfiguring your previously set %q backend.
|
||||||
`
|
`
|
||||||
|
|
||||||
const outputBackendConfigureWithLegacy = `
|
|
||||||
[reset][bold]New backend configuration detected with legacy remote state![reset]
|
|
||||||
|
|
||||||
Terraform has detected that you're attempting to configure a new backend.
|
|
||||||
At the same time, legacy remote state configuration was found. Terraform will
|
|
||||||
first configure the new backend, and then ask if you'd like to migrate
|
|
||||||
your remote state to the new backend.
|
|
||||||
`
|
|
||||||
|
|
||||||
const outputBackendReconfigure = `
|
const outputBackendReconfigure = `
|
||||||
[reset][bold]Backend configuration changed![reset]
|
[reset][bold]Backend configuration changed![reset]
|
||||||
|
|
||||||
|
@ -1438,45 +1203,6 @@ Terraform has detected that the configuration specified for the backend
|
||||||
has changed. Terraform will now check for existing state in the backends.
|
has changed. Terraform will now check for existing state in the backends.
|
||||||
`
|
`
|
||||||
|
|
||||||
const outputBackendSavedWithLegacy = `
|
|
||||||
[reset][bold]Legacy remote state was detected![reset]
|
|
||||||
|
|
||||||
Terraform has detected you still have legacy remote state enabled while
|
|
||||||
also having a backend configured. Terraform will now ask if you want to
|
|
||||||
migrate your legacy remote state data to the configured backend.
|
|
||||||
`
|
|
||||||
|
|
||||||
const outputBackendSavedWithLegacyChanged = `
|
|
||||||
[reset][bold]Legacy remote state was detected while also changing your current backend!reset]
|
|
||||||
|
|
||||||
Terraform has detected that you have legacy remote state, a configured
|
|
||||||
current backend, and you're attempting to reconfigure your backend. To handle
|
|
||||||
all of these changes, Terraform will first reconfigure your backend. After
|
|
||||||
this, Terraform will handle optionally copying your legacy remote state
|
|
||||||
into the newly configured backend.
|
|
||||||
`
|
|
||||||
|
|
||||||
const outputBackendUnsetWithLegacy = `
|
|
||||||
[reset][bold]Detected a request to unset the backend with legacy remote state present![reset]
|
|
||||||
|
|
||||||
Terraform has detected that you're attempting to unset a previously configured
|
|
||||||
backend (by not having the "backend" configuration set in your Terraform files).
|
|
||||||
At the same time, legacy remote state was detected. To handle this complex
|
|
||||||
scenario, Terraform will first unset your configured backend, and then
|
|
||||||
ask you how to handle the legacy remote state. This will be multi-step
|
|
||||||
process.
|
|
||||||
`
|
|
||||||
|
|
||||||
const successBackendLegacyUnset = `
|
|
||||||
Terraform has successfully migrated from legacy remote state to your
|
|
||||||
configured backend (%q).
|
|
||||||
`
|
|
||||||
|
|
||||||
const successBackendReconfigureWithLegacy = `
|
|
||||||
Terraform has successfully reconfigured your backend and migrate
|
|
||||||
from legacy remote state to the new backend.
|
|
||||||
`
|
|
||||||
|
|
||||||
const successBackendUnset = `
|
const successBackendUnset = `
|
||||||
Successfully unset the backend %q. Terraform will now operate locally.
|
Successfully unset the backend %q. Terraform will now operate locally.
|
||||||
`
|
`
|
||||||
|
|
|
@ -68,30 +68,6 @@ func (m *Meta) loadConfig(rootDir string) (*configs.Config, tfdiags.Diagnostics)
|
||||||
return config, diags
|
return config, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadConfigEarly is a variant of loadConfig that uses the special
|
|
||||||
// "early config" loader that is more forgiving of unexpected constructs and
|
|
||||||
// legacy syntax.
|
|
||||||
//
|
|
||||||
// Early-loaded config is not registered in the source code cache, so
|
|
||||||
// diagnostics produced from it may render without source code snippets. In
|
|
||||||
// practice this is not a big concern because the early config loader also
|
|
||||||
// cannot generate detailed source locations, so it prefers to produce
|
|
||||||
// diagnostics without explicit source location information and instead includes
|
|
||||||
// approximate locations in the message text.
|
|
||||||
//
|
|
||||||
// Most callers should use loadConfig. This method exists to support early
|
|
||||||
// initialization use-cases where the root module must be inspected in order
|
|
||||||
// to determine what else needs to be installed before the full configuration
|
|
||||||
// can be used
|
|
||||||
func (m *Meta) loadConfigEarly(rootDir string) (*earlyconfig.Config, tfdiags.Diagnostics) {
|
|
||||||
var diags tfdiags.Diagnostics
|
|
||||||
rootDir = m.normalizePath(rootDir)
|
|
||||||
|
|
||||||
config, hclDiags := initwd.LoadConfig(rootDir, m.modulesDir())
|
|
||||||
diags = diags.Append(hclDiags)
|
|
||||||
return config, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
// loadSingleModule reads configuration from the given directory and returns
|
// loadSingleModule reads configuration from the given directory and returns
|
||||||
// a description of that module only, without attempting to assemble a module
|
// a description of that module only, without attempting to assemble a module
|
||||||
// tree for referenced child modules.
|
// tree for referenced child modules.
|
||||||
|
@ -182,23 +158,6 @@ func (m *Meta) loadBackendConfig(rootDir string) (*configs.Backend, tfdiags.Diag
|
||||||
return mod.Backend, nil
|
return mod.Backend, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadValuesFile loads a file that defines a single map of key/value pairs.
|
|
||||||
// This is the format used for "tfvars" files.
|
|
||||||
func (m *Meta) loadValuesFile(filename string) (map[string]cty.Value, tfdiags.Diagnostics) {
|
|
||||||
var diags tfdiags.Diagnostics
|
|
||||||
filename = m.normalizePath(filename)
|
|
||||||
|
|
||||||
loader, err := m.initConfigLoader()
|
|
||||||
if err != nil {
|
|
||||||
diags = diags.Append(err)
|
|
||||||
return nil, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
vals, hclDiags := loader.Parser().LoadValuesFile(filename)
|
|
||||||
diags = diags.Append(hclDiags)
|
|
||||||
return vals, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
// loadHCLFile reads an arbitrary HCL file and returns the unprocessed body
|
// loadHCLFile reads an arbitrary HCL file and returns the unprocessed body
|
||||||
// representing its toplevel. Most callers should use one of the more
|
// representing its toplevel. Most callers should use one of the more
|
||||||
// specialized "load..." methods to get a higher-level representation.
|
// specialized "load..." methods to get a higher-level representation.
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
ctyjson "github.com/zclconf/go-cty/cty/json"
|
ctyjson "github.com/zclconf/go-cty/cty/json"
|
||||||
|
@ -201,119 +199,6 @@ func (c *OutputCommand) Run(args []string) int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatNestedList(indent string, outputList []interface{}) string {
|
|
||||||
outputBuf := new(bytes.Buffer)
|
|
||||||
outputBuf.WriteString(fmt.Sprintf("%s[", indent))
|
|
||||||
|
|
||||||
lastIdx := len(outputList) - 1
|
|
||||||
|
|
||||||
for i, value := range outputList {
|
|
||||||
outputBuf.WriteString(fmt.Sprintf("\n%s%s%s", indent, " ", value))
|
|
||||||
if i != lastIdx {
|
|
||||||
outputBuf.WriteString(",")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
outputBuf.WriteString(fmt.Sprintf("\n%s]", indent))
|
|
||||||
return strings.TrimPrefix(outputBuf.String(), "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatListOutput(indent, outputName string, outputList []interface{}) string {
|
|
||||||
keyIndent := ""
|
|
||||||
|
|
||||||
outputBuf := new(bytes.Buffer)
|
|
||||||
|
|
||||||
if outputName != "" {
|
|
||||||
outputBuf.WriteString(fmt.Sprintf("%s%s = [", indent, outputName))
|
|
||||||
keyIndent = " "
|
|
||||||
}
|
|
||||||
|
|
||||||
lastIdx := len(outputList) - 1
|
|
||||||
|
|
||||||
for i, value := range outputList {
|
|
||||||
switch typedValue := value.(type) {
|
|
||||||
case string:
|
|
||||||
outputBuf.WriteString(fmt.Sprintf("\n%s%s%s", indent, keyIndent, value))
|
|
||||||
case []interface{}:
|
|
||||||
outputBuf.WriteString(fmt.Sprintf("\n%s%s", indent,
|
|
||||||
formatNestedList(indent+keyIndent, typedValue)))
|
|
||||||
case map[string]interface{}:
|
|
||||||
outputBuf.WriteString(fmt.Sprintf("\n%s%s", indent,
|
|
||||||
formatNestedMap(indent+keyIndent, typedValue)))
|
|
||||||
}
|
|
||||||
|
|
||||||
if lastIdx != i {
|
|
||||||
outputBuf.WriteString(",")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if outputName != "" {
|
|
||||||
if len(outputList) > 0 {
|
|
||||||
outputBuf.WriteString(fmt.Sprintf("\n%s]", indent))
|
|
||||||
} else {
|
|
||||||
outputBuf.WriteString("]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.TrimPrefix(outputBuf.String(), "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatNestedMap(indent string, outputMap map[string]interface{}) string {
|
|
||||||
ks := make([]string, 0, len(outputMap))
|
|
||||||
for k, _ := range outputMap {
|
|
||||||
ks = append(ks, k)
|
|
||||||
}
|
|
||||||
sort.Strings(ks)
|
|
||||||
|
|
||||||
outputBuf := new(bytes.Buffer)
|
|
||||||
outputBuf.WriteString(fmt.Sprintf("%s{", indent))
|
|
||||||
|
|
||||||
lastIdx := len(outputMap) - 1
|
|
||||||
for i, k := range ks {
|
|
||||||
v := outputMap[k]
|
|
||||||
outputBuf.WriteString(fmt.Sprintf("\n%s%s = %v", indent+" ", k, v))
|
|
||||||
|
|
||||||
if lastIdx != i {
|
|
||||||
outputBuf.WriteString(",")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
outputBuf.WriteString(fmt.Sprintf("\n%s}", indent))
|
|
||||||
|
|
||||||
return strings.TrimPrefix(outputBuf.String(), "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatMapOutput(indent, outputName string, outputMap map[string]interface{}) string {
|
|
||||||
ks := make([]string, 0, len(outputMap))
|
|
||||||
for k, _ := range outputMap {
|
|
||||||
ks = append(ks, k)
|
|
||||||
}
|
|
||||||
sort.Strings(ks)
|
|
||||||
|
|
||||||
keyIndent := ""
|
|
||||||
|
|
||||||
outputBuf := new(bytes.Buffer)
|
|
||||||
if outputName != "" {
|
|
||||||
outputBuf.WriteString(fmt.Sprintf("%s%s = {", indent, outputName))
|
|
||||||
keyIndent = " "
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, k := range ks {
|
|
||||||
v := outputMap[k]
|
|
||||||
outputBuf.WriteString(fmt.Sprintf("\n%s%s%s = %v", indent, keyIndent, k, v))
|
|
||||||
}
|
|
||||||
|
|
||||||
if outputName != "" {
|
|
||||||
if len(outputMap) > 0 {
|
|
||||||
outputBuf.WriteString(fmt.Sprintf("\n%s}", indent))
|
|
||||||
} else {
|
|
||||||
outputBuf.WriteString("}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.TrimPrefix(outputBuf.String(), "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *OutputCommand) Help() string {
|
func (c *OutputCommand) Help() string {
|
||||||
helpText := `
|
helpText := `
|
||||||
Usage: terraform output [options] [NAME]
|
Usage: terraform output [options] [NAME]
|
||||||
|
|
|
@ -119,17 +119,6 @@ func (m *Meta) pluginDirs(includeAutoInstalled bool) []string {
|
||||||
return dirs
|
return dirs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Meta) pluginCache() discovery.PluginCache {
|
|
||||||
dir := m.PluginCacheDir
|
|
||||||
if dir == "" {
|
|
||||||
return nil // cache disabled
|
|
||||||
}
|
|
||||||
|
|
||||||
dir = filepath.Join(dir, pluginMachineName)
|
|
||||||
|
|
||||||
return discovery.NewLocalPluginCache(dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Meta) provisionerFactories() map[string]terraform.ProvisionerFactory {
|
func (m *Meta) provisionerFactories() map[string]terraform.ProvisionerFactory {
|
||||||
dirs := m.pluginDirs(true)
|
dirs := m.pluginDirs(true)
|
||||||
plugins := discovery.FindPlugins("provisioner", dirs)
|
plugins := discovery.FindPlugins("provisioner", dirs)
|
||||||
|
|
|
@ -143,7 +143,7 @@ func (c *ShowCommand) Run(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
if plan != nil {
|
if plan != nil {
|
||||||
if jsonOutput == true {
|
if jsonOutput {
|
||||||
config := ctx.Config()
|
config := ctx.Config()
|
||||||
jsonPlan, err := jsonplan.Marshal(config, plan, stateFile, schemas)
|
jsonPlan, err := jsonplan.Marshal(config, plan, stateFile, schemas)
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ func (c *ShowCommand) Run(args []string) int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if jsonOutput == true {
|
if jsonOutput {
|
||||||
// At this point, it is possible that there is neither state nor a plan.
|
// At this point, it is possible that there is neither state nor a plan.
|
||||||
// That's ok, we'll just return an empty object.
|
// That's ok, we'll just return an empty object.
|
||||||
jsonState, err := jsonstate.Marshal(stateFile, schemas)
|
jsonState, err := jsonstate.Marshal(stateFile, schemas)
|
||||||
|
|
|
@ -126,10 +126,6 @@ func (c *StateListCommand) Synopsis() string {
|
||||||
return "List resources in the state"
|
return "List resources in the state"
|
||||||
}
|
}
|
||||||
|
|
||||||
const errStateFilter = `Error filtering state: %[1]s
|
|
||||||
|
|
||||||
Please ensure that all your addresses are formatted properly.`
|
|
||||||
|
|
||||||
const errStateLoadingState = `Error loading the state: %[1]s
|
const errStateLoadingState = `Error loading the state: %[1]s
|
||||||
|
|
||||||
Please ensure that your Terraform state exists and that you've
|
Please ensure that your Terraform state exists and that you've
|
||||||
|
|
|
@ -148,10 +148,6 @@ func (c *StateMeta) lookupResourceInstanceAddr(state *states.State, allowMissing
|
||||||
return ret, diags
|
return ret, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *StateMeta) lookupSingleResourceInstanceAddr(state *states.State, addrStr string) (addrs.AbsResourceInstance, tfdiags.Diagnostics) {
|
|
||||||
return addrs.ParseAbsResourceInstanceStr(addrStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *StateMeta) lookupSingleStateObjectAddr(state *states.State, addrStr string) (addrs.Targetable, tfdiags.Diagnostics) {
|
func (c *StateMeta) lookupSingleStateObjectAddr(state *states.State, addrStr string) (addrs.Targetable, tfdiags.Diagnostics) {
|
||||||
target, diags := addrs.ParseTargetStr(addrStr)
|
target, diags := addrs.ParseTargetStr(addrStr)
|
||||||
if diags.HasErrors() {
|
if diags.HasErrors() {
|
||||||
|
|
|
@ -503,9 +503,3 @@ const errStateMv = `Error moving state: %s
|
||||||
|
|
||||||
Please ensure your addresses and state paths are valid. No
|
Please ensure your addresses and state paths are valid. No
|
||||||
state was persisted. Your existing states are untouched.`
|
state was persisted. Your existing states are untouched.`
|
||||||
|
|
||||||
const errStateMvPersist = `Error saving the state: %s
|
|
||||||
|
|
||||||
The state wasn't saved properly. If the error happening after a partial
|
|
||||||
write occurred, a backup file will have been created. Otherwise, the state
|
|
||||||
is in the same state it was when the operation started.`
|
|
||||||
|
|
|
@ -153,25 +153,3 @@ Options:
|
||||||
func (c *StatePushCommand) Synopsis() string {
|
func (c *StatePushCommand) Synopsis() string {
|
||||||
return "Update remote state from a local state file"
|
return "Update remote state from a local state file"
|
||||||
}
|
}
|
||||||
|
|
||||||
const errStatePushLineage = `
|
|
||||||
The lineages do not match! The state will not be pushed.
|
|
||||||
|
|
||||||
The "lineage" is a unique identifier given to a state on creation. It helps
|
|
||||||
protect Terraform from overwriting a seemingly unrelated state file since it
|
|
||||||
represents potentially losing real state.
|
|
||||||
|
|
||||||
Please verify you're pushing the correct state. If you're sure you are, you
|
|
||||||
can force the behavior with the "-force" flag.
|
|
||||||
`
|
|
||||||
|
|
||||||
const errStatePushSerialNewer = `
|
|
||||||
The destination state has a higher serial number! The state will not be pushed.
|
|
||||||
|
|
||||||
A higher serial could indicate that there is data in the destination state
|
|
||||||
that was not present when the source state was created. As a protection measure,
|
|
||||||
Terraform will not automatically overwrite this state.
|
|
||||||
|
|
||||||
Please verify you're pushing the correct state. If you're sure you are, you
|
|
||||||
can force the behavior with the "-force" flag.
|
|
||||||
`
|
|
||||||
|
|
|
@ -167,12 +167,6 @@ func (c *StateRmCommand) Synopsis() string {
|
||||||
return "Remove instances from the state"
|
return "Remove instances from the state"
|
||||||
}
|
}
|
||||||
|
|
||||||
const errStateRm = `Error removing items from the state: %s
|
|
||||||
|
|
||||||
The state was not saved. No items were removed from the persisted
|
|
||||||
state. No backup was created since no modification occurred. Please
|
|
||||||
resolve the issue above and try again.`
|
|
||||||
|
|
||||||
const errStateRmPersist = `Error saving the state: %s
|
const errStateRmPersist = `Error saving the state: %s
|
||||||
|
|
||||||
The state was not saved. No items were removed from the persisted
|
The state was not saved. No items were removed from the persisted
|
||||||
|
|
|
@ -17,8 +17,6 @@ type ValidateCommand struct {
|
||||||
Meta
|
Meta
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultPath = "."
|
|
||||||
|
|
||||||
func (c *ValidateCommand) Run(args []string) int {
|
func (c *ValidateCommand) Run(args []string) int {
|
||||||
args = c.Meta.process(args)
|
args = c.Meta.process(args)
|
||||||
// TODO: The `var` and `var-file` options are not actually used, and should
|
// TODO: The `var` and `var-file` options are not actually used, and should
|
||||||
|
|
|
@ -63,8 +63,6 @@ will be removed in a future Terraform version.
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
envNotSupported = `Backend does not support multiple workspaces`
|
|
||||||
|
|
||||||
envExists = `Workspace %q already exists`
|
envExists = `Workspace %q already exists`
|
||||||
|
|
||||||
envDoesNotExist = `
|
envDoesNotExist = `
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
# Terraform Examples
|
|
||||||
|
|
||||||
This folder contains a set of Terraform examples. These examples each
|
|
||||||
have their own README you can read for more details on what the example
|
|
||||||
does.
|
|
||||||
|
|
||||||
To try any example, clone this repository and run the following commands
|
|
||||||
from within the example's directory:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ terraform init
|
|
||||||
$ terraform apply
|
|
||||||
```
|
|
||||||
|
|
||||||
## Provider-specific Examples
|
|
||||||
|
|
||||||
Terraform providers each live in their own repository. Some of these
|
|
||||||
repositories contain documentation specific to their provider:
|
|
||||||
|
|
||||||
* [AliCloud Examples](https://github.com/terraform-providers/terraform-provider-alicloud/tree/master/examples)
|
|
||||||
* [Amazon Web Services Examples](https://github.com/terraform-providers/terraform-provider-aws/tree/master/examples)
|
|
||||||
* [Azure Examples](https://github.com/terraform-providers/terraform-provider-azurerm/tree/master/examples)
|
|
||||||
* [CenturyLink Cloud Examples](https://github.com/terraform-providers/terraform-provider-clc/tree/master/examples)
|
|
||||||
* [Consul Examples](https://github.com/terraform-providers/terraform-provider-consul/tree/master/examples)
|
|
||||||
* [DigitalOcean Examples](https://github.com/terraform-providers/terraform-provider-digitalocean/tree/master/examples)
|
|
||||||
* [Google Cloud Platform Examples](https://github.com/terraform-providers/terraform-provider-google/tree/master/examples)
|
|
||||||
* [OpenStack Examples](https://github.com/terraform-providers/terraform-provider-openstack/tree/master/examples)
|
|
|
@ -1,11 +0,0 @@
|
||||||
# Cross Provider Examples
|
|
||||||
|
|
||||||
This is a simple example of the cross-provider capabilities of
|
|
||||||
Terraform.
|
|
||||||
|
|
||||||
Very simply, this creates a Heroku application and points a DNS
|
|
||||||
CNAME record at the result via DNSimple. A `host` query to the outputted
|
|
||||||
hostname should reveal the correct DNS configuration.
|
|
||||||
|
|
||||||
As with all examples, just copy and paste the example and run
|
|
||||||
`terraform apply` to see it work.
|
|
|
@ -1,26 +0,0 @@
|
||||||
# Create our Heroku application. Heroku will
|
|
||||||
# automatically assign a name.
|
|
||||||
resource "heroku_app" "web" {}
|
|
||||||
|
|
||||||
# Create our DNSimple record to point to the
|
|
||||||
# heroku application.
|
|
||||||
resource "dnsimple_record" "web" {
|
|
||||||
domain = "${var.dnsimple_domain}"
|
|
||||||
|
|
||||||
name = "terraform"
|
|
||||||
|
|
||||||
# heroku_hostname is a computed attribute on the heroku
|
|
||||||
# application we can use to determine the hostname
|
|
||||||
value = "${heroku_app.web.heroku_hostname}"
|
|
||||||
|
|
||||||
type = "CNAME"
|
|
||||||
ttl = 3600
|
|
||||||
}
|
|
||||||
|
|
||||||
# The Heroku domain, which will be created and added
|
|
||||||
# to the heroku application after we have assigned the domain
|
|
||||||
# in DNSimple
|
|
||||||
resource "heroku_domain" "foobar" {
|
|
||||||
app = "${heroku_app.web.name}"
|
|
||||||
hostname = "${dnsimple_record.web.hostname}"
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
output "address" {
|
|
||||||
value = "${dnsimple_record.web.hostname}"
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
variable "dnsimple_domain" {
|
|
||||||
description = "The domain we are creating a record for."
|
|
||||||
}
|
|
|
@ -273,121 +273,6 @@ func (c *Client) ModuleLocation(module *regsrc.Module, version string) (string,
|
||||||
return location, nil
|
return location, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TerraformProviderVersions queries the registry for a provider, and returns the available versions.
|
|
||||||
func (c *Client) TerraformProviderVersions(provider *regsrc.TerraformProvider) (*response.TerraformProviderVersions, error) {
|
|
||||||
host, err := provider.SvcHost()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
service, err := c.Discover(host, providersServiceID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := url.Parse(path.Join(provider.TerraformProvider(), "versions"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
service = service.ResolveReference(p)
|
|
||||||
|
|
||||||
log.Printf("[DEBUG] fetching provider versions from %q", service)
|
|
||||||
|
|
||||||
req, err := retryablehttp.NewRequest("GET", service.String(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.addRequestCreds(host, req.Request)
|
|
||||||
req.Header.Set(xTerraformVersion, tfVersion)
|
|
||||||
|
|
||||||
resp, err := c.client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
switch resp.StatusCode {
|
|
||||||
case http.StatusOK:
|
|
||||||
// OK
|
|
||||||
case http.StatusNotFound:
|
|
||||||
return nil, &errProviderNotFound{addr: provider}
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("error looking up provider versions: %s", resp.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
var versions response.TerraformProviderVersions
|
|
||||||
|
|
||||||
dec := json.NewDecoder(resp.Body)
|
|
||||||
if err := dec.Decode(&versions); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &versions, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TerraformProviderLocation queries the registry for a provider download metadata
|
|
||||||
func (c *Client) TerraformProviderLocation(provider *regsrc.TerraformProvider, version string) (*response.TerraformProviderPlatformLocation, error) {
|
|
||||||
host, err := provider.SvcHost()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
service, err := c.Discover(host, providersServiceID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := url.Parse(path.Join(
|
|
||||||
provider.TerraformProvider(),
|
|
||||||
version,
|
|
||||||
"download",
|
|
||||||
provider.OS,
|
|
||||||
provider.Arch,
|
|
||||||
))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
service = service.ResolveReference(p)
|
|
||||||
|
|
||||||
log.Printf("[DEBUG] fetching provider location from %q", service)
|
|
||||||
|
|
||||||
req, err := retryablehttp.NewRequest("GET", service.String(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.addRequestCreds(host, req.Request)
|
|
||||||
req.Header.Set(xTerraformVersion, tfVersion)
|
|
||||||
|
|
||||||
resp, err := c.client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
var loc response.TerraformProviderPlatformLocation
|
|
||||||
|
|
||||||
dec := json.NewDecoder(resp.Body)
|
|
||||||
if err := dec.Decode(&loc); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch resp.StatusCode {
|
|
||||||
case http.StatusOK, http.StatusNoContent:
|
|
||||||
// OK
|
|
||||||
case http.StatusNotFound:
|
|
||||||
return nil, fmt.Errorf("provider %q version %q not found", provider.TerraformProvider(), version)
|
|
||||||
default:
|
|
||||||
// anything else is an error:
|
|
||||||
return nil, fmt.Errorf("error getting download location for %q: %s", provider.TerraformProvider(), resp.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &loc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// configureDiscoveryRetry configures the number of retries the registry client
|
// configureDiscoveryRetry configures the number of retries the registry client
|
||||||
// will attempt for requests with retryable errors, like 502 status codes
|
// will attempt for requests with retryable errors, like 502 status codes
|
||||||
func configureDiscoveryRetry() {
|
func configureDiscoveryRetry() {
|
||||||
|
|
|
@ -2,7 +2,6 @@ package registry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -368,90 +367,3 @@ func TestLookupModuleNetworkError(t *testing.T) {
|
||||||
t.Fatal("unexpected error, got:", err)
|
t.Fatal("unexpected error, got:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLookupProviderVersions(t *testing.T) {
|
|
||||||
server := test.Registry()
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
client := NewClient(test.Disco(server), nil)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
}{
|
|
||||||
{"foo"},
|
|
||||||
{"bar"},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
provider := regsrc.NewTerraformProvider(tt.name, "", "")
|
|
||||||
resp, err := client.TerraformProviderVersions(provider)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
name := fmt.Sprintf("terraform-providers/%s", tt.name)
|
|
||||||
if resp.ID != name {
|
|
||||||
t.Fatalf("expected provider name %q, got %q", name, resp.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(resp.Versions) != 2 {
|
|
||||||
t.Fatal("expected 2 versions, got", len(resp.Versions))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range resp.Versions {
|
|
||||||
_, err := version.NewVersion(v.Version)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("invalid version %#v: %v", v, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLookupProviderLocation(t *testing.T) {
|
|
||||||
server := test.Registry()
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
client := NewClient(test.Disco(server), nil)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
Name string
|
|
||||||
Version string
|
|
||||||
Err bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"foo",
|
|
||||||
"0.2.3",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bar",
|
|
||||||
"0.1.1",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"baz",
|
|
||||||
"0.0.0",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
// FIXME: the tests are set up to succeed - os/arch is not being validated at this time
|
|
||||||
p := regsrc.NewTerraformProvider(tt.Name, "linux", "amd64")
|
|
||||||
|
|
||||||
locationMetadata, err := client.TerraformProviderLocation(p, tt.Version)
|
|
||||||
if tt.Err {
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("succeeded; want error")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
} else if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadURL := fmt.Sprintf("https://releases.hashicorp.com/terraform-provider-%s/%s/terraform-provider-%s.zip", tt.Name, tt.Version, tt.Name)
|
|
||||||
|
|
||||||
if locationMetadata.DownloadURL != downloadURL {
|
|
||||||
t.Fatalf("incorrect download URL: expected %q, got %q", downloadURL, locationMetadata.DownloadURL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -23,22 +23,6 @@ func IsModuleNotFound(err error) bool {
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
type errProviderNotFound struct {
|
|
||||||
addr *regsrc.TerraformProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *errProviderNotFound) Error() string {
|
|
||||||
return fmt.Sprintf("provider %s not found", e.addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsProviderNotFound returns true only if the given error is a "provider not found"
|
|
||||||
// error. This allows callers to recognize this particular error condition
|
|
||||||
// as distinct from operational errors such as poor network connectivity.
|
|
||||||
func IsProviderNotFound(err error) bool {
|
|
||||||
_, ok := err.(*errProviderNotFound)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsServiceNotProvided returns true only if the given error is a "service not provided"
|
// IsServiceNotProvided returns true only if the given error is a "service not provided"
|
||||||
// error. This allows callers to recognize this particular error condition
|
// error. This allows callers to recognize this particular error condition
|
||||||
// as distinct from operational errors such as poor network connectivity.
|
// as distinct from operational errors such as poor network connectivity.
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
package regsrc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-svchost"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// DefaultProviderNamespace represents the namespace for canonical
|
|
||||||
// HashiCorp-controlled providers.
|
|
||||||
DefaultProviderNamespace = "-"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TerraformProvider describes a Terraform Registry Provider source.
|
|
||||||
type TerraformProvider struct {
|
|
||||||
RawHost *FriendlyHost
|
|
||||||
RawNamespace string
|
|
||||||
RawName string
|
|
||||||
OS string
|
|
||||||
Arch string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTerraformProvider constructs a new provider source.
|
|
||||||
func NewTerraformProvider(name, os, arch string) *TerraformProvider {
|
|
||||||
if os == "" {
|
|
||||||
os = runtime.GOOS
|
|
||||||
}
|
|
||||||
if arch == "" {
|
|
||||||
arch = runtime.GOARCH
|
|
||||||
}
|
|
||||||
|
|
||||||
// separate namespace if included
|
|
||||||
namespace := DefaultProviderNamespace
|
|
||||||
if names := strings.SplitN(name, "/", 2); len(names) == 2 {
|
|
||||||
namespace, name = names[0], names[1]
|
|
||||||
}
|
|
||||||
p := &TerraformProvider{
|
|
||||||
RawHost: PublicRegistryHost,
|
|
||||||
RawNamespace: namespace,
|
|
||||||
RawName: name,
|
|
||||||
OS: os,
|
|
||||||
Arch: arch,
|
|
||||||
}
|
|
||||||
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Provider returns just the registry ID of the provider
|
|
||||||
func (p *TerraformProvider) TerraformProvider() string {
|
|
||||||
return fmt.Sprintf("%s/%s", p.RawNamespace, p.RawName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SvcHost returns the svchost.Hostname for this provider. The
|
|
||||||
// default PublicRegistryHost is returned.
|
|
||||||
func (p *TerraformProvider) SvcHost() (svchost.Hostname, error) {
|
|
||||||
return svchost.ForComparison(PublicRegistryHost.Raw)
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
package regsrc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewTerraformProviderNamespace(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
provider string
|
|
||||||
expectedNamespace string
|
|
||||||
expectedName string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "default",
|
|
||||||
provider: "null",
|
|
||||||
expectedNamespace: "-",
|
|
||||||
expectedName: "null",
|
|
||||||
}, {
|
|
||||||
name: "explicit",
|
|
||||||
provider: "terraform-providers/null",
|
|
||||||
expectedNamespace: "terraform-providers",
|
|
||||||
expectedName: "null",
|
|
||||||
}, {
|
|
||||||
name: "community",
|
|
||||||
provider: "community-providers/null",
|
|
||||||
expectedNamespace: "community-providers",
|
|
||||||
expectedName: "null",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
actual := NewTerraformProvider(tt.provider, "", "")
|
|
||||||
|
|
||||||
if actual == nil {
|
|
||||||
t.Fatal("NewTerraformProvider() unexpectedly returned nil provider")
|
|
||||||
}
|
|
||||||
|
|
||||||
if v := actual.RawNamespace; v != tt.expectedNamespace {
|
|
||||||
t.Fatalf("RawNamespace = %v, wanted %v", v, tt.expectedNamespace)
|
|
||||||
}
|
|
||||||
if v := actual.RawName; v != tt.expectedName {
|
|
||||||
t.Fatalf("RawName = %v, wanted %v", v, tt.expectedName)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
package response
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Provider is the response structure with the data for a single provider
|
|
||||||
// version. This is just the metadata. A full provider response will be
|
|
||||||
// ProviderDetail.
|
|
||||||
type Provider struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// Metadata about the overall provider.
|
|
||||||
|
|
||||||
Owner string `json:"owner"`
|
|
||||||
Namespace string `json:"namespace"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Version string `json:"version"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Source string `json:"source"`
|
|
||||||
PublishedAt time.Time `json:"published_at"`
|
|
||||||
Downloads int `json:"downloads"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProviderDetail represents a Provider with full detail.
|
|
||||||
type ProviderDetail struct {
|
|
||||||
Provider
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// The fields below are only set when requesting this specific
|
|
||||||
// module. They are available to easily know all available versions
|
|
||||||
// without multiple API calls.
|
|
||||||
|
|
||||||
Versions []string `json:"versions"` // All versions
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package response
|
|
||||||
|
|
||||||
// ProviderList is the response structure for a pageable list of providers.
|
|
||||||
type ProviderList struct {
|
|
||||||
Meta PaginationMeta `json:"meta"`
|
|
||||||
Providers []*Provider `json:"providers"`
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
package response
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
version "github.com/hashicorp/go-version"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TerraformProvider is the response structure for all required information for
|
|
||||||
// Terraform to choose a download URL. It must include all versions and all
|
|
||||||
// platforms for Terraform to perform version and os/arch constraint matching
|
|
||||||
// locally.
|
|
||||||
type TerraformProvider struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
|
|
||||||
Versions []*TerraformProviderVersion `json:"versions"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TerraformProviderVersion is the Terraform-specific response structure for a
|
|
||||||
// provider version.
|
|
||||||
type TerraformProviderVersion struct {
|
|
||||||
Version string `json:"version"`
|
|
||||||
Protocols []string `json:"protocols"`
|
|
||||||
|
|
||||||
Platforms []*TerraformProviderPlatform `json:"platforms"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TerraformProviderVersions is the Terraform-specific response structure for an
|
|
||||||
// array of provider versions
|
|
||||||
type TerraformProviderVersions struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Versions []*TerraformProviderVersion `json:"versions"`
|
|
||||||
Warnings []string `json:"warnings"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TerraformProviderPlatform is the Terraform-specific response structure for a
|
|
||||||
// provider platform.
|
|
||||||
type TerraformProviderPlatform struct {
|
|
||||||
OS string `json:"os"`
|
|
||||||
Arch string `json:"arch"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TerraformProviderPlatformLocation is the Terraform-specific response
|
|
||||||
// structure for a provider platform with all details required to perform a
|
|
||||||
// download.
|
|
||||||
type TerraformProviderPlatformLocation struct {
|
|
||||||
Protocols []string `json:"protocols"`
|
|
||||||
OS string `json:"os"`
|
|
||||||
Arch string `json:"arch"`
|
|
||||||
Filename string `json:"filename"`
|
|
||||||
DownloadURL string `json:"download_url"`
|
|
||||||
ShasumsURL string `json:"shasums_url"`
|
|
||||||
ShasumsSignatureURL string `json:"shasums_signature_url"`
|
|
||||||
Shasum string `json:"shasum"`
|
|
||||||
|
|
||||||
SigningKeys SigningKeyList `json:"signing_keys"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// SigningKeyList is the response structure for a list of signing keys.
|
|
||||||
type SigningKeyList struct {
|
|
||||||
GPGKeys []*GPGKey `json:"gpg_public_keys"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GPGKey is the response structure for a GPG key.
|
|
||||||
type GPGKey struct {
|
|
||||||
ASCIIArmor string `json:"ascii_armor"`
|
|
||||||
Source string `json:"source"`
|
|
||||||
SourceURL *string `json:"source_url"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collection type for TerraformProviderVersion
|
|
||||||
type ProviderVersionCollection []*TerraformProviderVersion
|
|
||||||
|
|
||||||
// GPGASCIIArmor returns an ASCII-armor-formatted string for all of the gpg
|
|
||||||
// keys in the response.
|
|
||||||
func (signingKeys *SigningKeyList) GPGASCIIArmor() string {
|
|
||||||
keys := []string{}
|
|
||||||
|
|
||||||
for _, gpgKey := range signingKeys.GPGKeys {
|
|
||||||
keys = append(keys, gpgKey.ASCIIArmor)
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(keys, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort sorts versions from newest to oldest.
|
|
||||||
func (v ProviderVersionCollection) Sort() {
|
|
||||||
sort.Slice(v, func(i, j int) bool {
|
|
||||||
versionA, _ := version.NewVersion(v[i].Version)
|
|
||||||
versionB, _ := version.NewVersion(v[j].Version)
|
|
||||||
|
|
||||||
return versionA.GreaterThan(versionB)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
package response
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
testGPGKeyOne = &GPGKey{
|
|
||||||
ASCIIArmor: "---\none\n---",
|
|
||||||
}
|
|
||||||
testGPGKeyTwo = &GPGKey{
|
|
||||||
ASCIIArmor: "---\ntwo\n---",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSigningKeyList_GPGASCIIArmor(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
name string
|
|
||||||
gpgKeys []*GPGKey
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "no keys",
|
|
||||||
gpgKeys: []*GPGKey{},
|
|
||||||
expected: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "one key",
|
|
||||||
gpgKeys: []*GPGKey{testGPGKeyOne},
|
|
||||||
expected: testGPGKeyOne.ASCIIArmor,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "two keys",
|
|
||||||
gpgKeys: []*GPGKey{testGPGKeyOne, testGPGKeyTwo},
|
|
||||||
expected: fmt.Sprintf("%s\n%s",
|
|
||||||
testGPGKeyOne.ASCIIArmor, testGPGKeyTwo.ASCIIArmor),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
signingKeys := &SigningKeyList{
|
|
||||||
GPGKeys: tt.gpgKeys,
|
|
||||||
}
|
|
||||||
actual := signingKeys.GPGASCIIArmor()
|
|
||||||
|
|
||||||
if actual != tt.expected {
|
|
||||||
t.Errorf("expected %s, got %s", tt.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -256,102 +256,6 @@ func mockRegHandler() http.Handler {
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
|
|
||||||
providerDownload := func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
p := strings.TrimLeft(r.URL.Path, "/")
|
|
||||||
v := strings.Split(string(p), "/")
|
|
||||||
|
|
||||||
if len(v) != 6 {
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
name := fmt.Sprintf("%s/%s", v[0], v[1])
|
|
||||||
|
|
||||||
providers, ok := testProviders[name]
|
|
||||||
if !ok {
|
|
||||||
http.NotFound(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// for this test / moment we will only return the one provider
|
|
||||||
loc := response.TerraformProviderPlatformLocation{
|
|
||||||
DownloadURL: providers[0].url,
|
|
||||||
}
|
|
||||||
|
|
||||||
js, err := json.Marshal(loc)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.Write(js)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
providerVersions := func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
p := strings.TrimLeft(r.URL.Path, "/")
|
|
||||||
re := regexp.MustCompile(`^([-a-z]+/\w+)/versions$`)
|
|
||||||
matches := re.FindStringSubmatch(p)
|
|
||||||
|
|
||||||
if len(matches) != 2 {
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for auth
|
|
||||||
if strings.Contains(matches[1], "private/") {
|
|
||||||
if !strings.Contains(r.Header.Get("Authorization"), testCred) {
|
|
||||||
http.Error(w, "", http.StatusForbidden)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
name := providerAlias(fmt.Sprintf("%s", matches[1]))
|
|
||||||
versions, ok := testProviders[name]
|
|
||||||
if !ok {
|
|
||||||
http.NotFound(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// only adding the single requested provider for now
|
|
||||||
// this is the minimal that any registry is expected to support
|
|
||||||
pvs := &response.TerraformProviderVersions{
|
|
||||||
ID: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range versions {
|
|
||||||
pv := &response.TerraformProviderVersion{
|
|
||||||
Version: v.version,
|
|
||||||
}
|
|
||||||
pvs.Versions = append(pvs.Versions, pv)
|
|
||||||
}
|
|
||||||
|
|
||||||
js, err := json.Marshal(pvs)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.Write(js)
|
|
||||||
}
|
|
||||||
|
|
||||||
mux.Handle("/v1/providers/",
|
|
||||||
http.StripPrefix("/v1/providers/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if strings.Contains(r.URL.Path, "/download") {
|
|
||||||
providerDownload(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasSuffix(r.URL.Path, "/versions") {
|
|
||||||
providerVersions(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
http.NotFound(w, r)
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
|
|
||||||
mux.HandleFunc("/.well-known/terraform.json", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/.well-known/terraform.json", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
io.WriteString(w, `{"modules.v1":"http://localhost/v1/modules/", "providers.v1":"http://localhost/v1/providers/"}`)
|
io.WriteString(w, `{"modules.v1":"http://localhost/v1/modules/", "providers.v1":"http://localhost/v1/providers/"}`)
|
||||||
|
|
|
@ -9,7 +9,7 @@ func TestPrefixUIInput_impl(t *testing.T) {
|
||||||
var _ UIInput = new(PrefixUIInput)
|
var _ UIInput = new(PrefixUIInput)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPrefixUIInput(t *testing.T) {
|
func TestPrefixUIInput(t *testing.T) {
|
||||||
input := new(MockUIInput)
|
input := new(MockUIInput)
|
||||||
prefix := &PrefixUIInput{
|
prefix := &PrefixUIInput{
|
||||||
IdPrefix: "foo",
|
IdPrefix: "foo",
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
package terraform
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/hashicorp/terraform/httpclient"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Generate a UserAgent string
|
|
||||||
//
|
|
||||||
// Deprecated: Use httpclient.UserAgent(version) instead
|
|
||||||
func UserAgentString() string {
|
|
||||||
return httpclient.UserAgentString()
|
|
||||||
}
|
|
Loading…
Reference in New Issue