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" +
|
||||
"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
|
||||
// to perform an operation on a workspace other than "default" for a
|
||||
// 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"
|
||||
"fmt"
|
||||
"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"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/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/sts"
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/version"
|
||||
"github.com/jmespath/go-jmespath"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"net/url"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// New creates a new backend for OSS remote state.
|
||||
|
@ -199,7 +196,20 @@ func assumeRoleSchema() *schema.Schema {
|
|||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
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() {
|
||||
return false, true, diags
|
||||
}
|
||||
stateReqs := make(getproviders.Requirements, 0)
|
||||
if state != nil {
|
||||
stateReqs = state.ProviderRequirements()
|
||||
stateReqs := state.ProviderRequirements()
|
||||
reqs = reqs.Merge(stateReqs)
|
||||
}
|
||||
|
||||
|
@ -774,16 +773,6 @@ func (c *InitCommand) getProviders(config *configs.Config, state *states.State,
|
|||
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
|
||||
// arguments into a hcl Body that should override the backend settings given
|
||||
// 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.
|
||||
`
|
||||
|
||||
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
|
||||
// supported protocol versions are too old for the user's version of terraform,
|
||||
// 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.
|
||||
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
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.LocalState) (backend.Backend, tfdiags.Diagnostics) {
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
//-------------------------------------------------------------------
|
||||
|
@ -1150,36 +1086,6 @@ func (m *Meta) backendInitFromConfig(c *configs.Backend) (backend.Backend, cty.V
|
|||
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) {
|
||||
m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
|
||||
"[reset]"+strings.TrimSpace(errBackendInit)+"\n", reason)))
|
||||
|
@ -1194,30 +1100,6 @@ func (m *Meta) backendInitRequired(reason string) {
|
|||
var errBackendInitRequired = errors.New(
|
||||
"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 = `
|
||||
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.
|
||||
`
|
||||
|
||||
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 = `
|
||||
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.
|
||||
`
|
||||
|
||||
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 = `
|
||||
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.
|
||||
`
|
||||
|
||||
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 = `
|
||||
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.
|
||||
`
|
||||
|
||||
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 = `
|
||||
Terraform detected that the backend type changed from %q to %q.
|
||||
`
|
||||
|
||||
const outputBackendMigrateLegacy = `
|
||||
Terraform detected legacy remote state.
|
||||
`
|
||||
|
||||
const outputBackendMigrateLocal = `
|
||||
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 = `
|
||||
[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.
|
||||
`
|
||||
|
||||
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 = `
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// a description of that module only, without attempting to assemble a module
|
||||
// tree for referenced child modules.
|
||||
|
@ -182,23 +158,6 @@ func (m *Meta) loadBackendConfig(rootDir string) (*configs.Backend, tfdiags.Diag
|
|||
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
|
||||
// representing its toplevel. Most callers should use one of the more
|
||||
// specialized "load..." methods to get a higher-level representation.
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
ctyjson "github.com/zclconf/go-cty/cty/json"
|
||||
|
@ -201,119 +199,6 @@ func (c *OutputCommand) Run(args []string) int {
|
|||
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 {
|
||||
helpText := `
|
||||
Usage: terraform output [options] [NAME]
|
||||
|
|
|
@ -119,17 +119,6 @@ func (m *Meta) pluginDirs(includeAutoInstalled bool) []string {
|
|||
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 {
|
||||
dirs := m.pluginDirs(true)
|
||||
plugins := discovery.FindPlugins("provisioner", dirs)
|
||||
|
|
|
@ -143,7 +143,7 @@ func (c *ShowCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
if plan != nil {
|
||||
if jsonOutput == true {
|
||||
if jsonOutput {
|
||||
config := ctx.Config()
|
||||
jsonPlan, err := jsonplan.Marshal(config, plan, stateFile, schemas)
|
||||
|
||||
|
@ -170,7 +170,7 @@ func (c *ShowCommand) Run(args []string) int {
|
|||
return 0
|
||||
}
|
||||
|
||||
if jsonOutput == true {
|
||||
if jsonOutput {
|
||||
// At this point, it is possible that there is neither state nor a plan.
|
||||
// That's ok, we'll just return an empty object.
|
||||
jsonState, err := jsonstate.Marshal(stateFile, schemas)
|
||||
|
|
|
@ -126,10 +126,6 @@ func (c *StateListCommand) Synopsis() string {
|
|||
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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
target, diags := addrs.ParseTargetStr(addrStr)
|
||||
if diags.HasErrors() {
|
||||
|
|
|
@ -503,9 +503,3 @@ const errStateMv = `Error moving state: %s
|
|||
|
||||
Please ensure your addresses and state paths are valid. No
|
||||
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 {
|
||||
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"
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
The state was not saved. No items were removed from the persisted
|
||||
|
|
|
@ -17,8 +17,6 @@ type ValidateCommand struct {
|
|||
Meta
|
||||
}
|
||||
|
||||
const defaultPath = "."
|
||||
|
||||
func (c *ValidateCommand) Run(args []string) int {
|
||||
args = c.Meta.process(args)
|
||||
// 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 (
|
||||
envNotSupported = `Backend does not support multiple workspaces`
|
||||
|
||||
envExists = `Workspace %q already exists`
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// will attempt for requests with retryable errors, like 502 status codes
|
||||
func configureDiscoveryRetry() {
|
||||
|
|
|
@ -2,7 +2,6 @@ package registry
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
@ -368,90 +367,3 @@ func TestLookupModuleNetworkError(t *testing.T) {
|
|||
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
|
||||
}
|
||||
|
||||
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"
|
||||
// error. This allows callers to recognize this particular error condition
|
||||
// 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) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
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)
|
||||
}
|
||||
|
||||
func testPrefixUIInput(t *testing.T) {
|
||||
func TestPrefixUIInput(t *testing.T) {
|
||||
input := new(MockUIInput)
|
||||
prefix := &PrefixUIInput{
|
||||
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