Skip to content

Commit

Permalink
Upgrade terraform to 0.13.7
Browse files Browse the repository at this point in the history
  • Loading branch information
stoyanr committed Nov 4, 2021
1 parent 55083e5 commit acc2afa
Show file tree
Hide file tree
Showing 19 changed files with 172 additions and 40 deletions.
2 changes: 1 addition & 1 deletion build/alicloud/TF_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.12.31
0.13.7
12 changes: 9 additions & 3 deletions build/alicloud/terraform-bundle.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ terraform {
}

providers {
alicloud = ["1.124.2"]
template = ["2.1.2"]
null = ["2.1.2"]
alicloud = {
versions = ["1.124.2"]
}
template = {
versions = ["2.1.2"]
}
null = {
versions = ["2.1.2"]
}
}
2 changes: 1 addition & 1 deletion build/all/TF_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.12.31
0.13.7
38 changes: 29 additions & 9 deletions build/all/terraform-bundle.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,33 @@ terraform {
}

providers {
aws = ["3.63.0"]
azurerm = ["2.68.0"]
google = ["3.62.0"]
google-beta = ["3.62.0"]
openstack = ["1.37.0"]
alicloud = ["1.124.2"]
packet = ["2.3.0"]
template = ["2.1.2"]
null = ["2.1.2"]
aws = {
versions = ["3.63.0"]
}
azurerm = {
versions = ["2.68.0"]
}
google = {
versions = ["3.62.0"]
}
google-beta = {
versions = ["3.62.0"]
}
openstack = {
versions = ["1.37.0"]
source = "terraform-provider-openstack/openstack"
}
alicloud = {
versions = ["1.124.2"]
}
metal = {
versions = ["3.1.0"]
source = "equinix/metal"
}
template = {
versions = ["2.1.2"]
}
null = {
versions = ["2.1.2"]
}
}
2 changes: 1 addition & 1 deletion build/aws/TF_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.12.31
0.13.7
12 changes: 9 additions & 3 deletions build/aws/terraform-bundle.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ terraform {
}

providers {
aws = ["3.63.0"]
template = ["2.1.2"]
null = ["2.1.2"]
aws = {
versions = ["3.63.0"]
}
template = {
versions = ["2.1.2"]
}
null = {
versions = ["2.1.2"]
}
}
2 changes: 1 addition & 1 deletion build/azure/TF_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.12.31
0.13.7
12 changes: 9 additions & 3 deletions build/azure/terraform-bundle.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ terraform {
}

providers {
azurerm = ["2.68.0"]
template = ["2.1.2"]
null = ["2.1.2"]
azurerm = {
versions = ["2.68.0"]
}
template = {
versions = ["2.1.2"]
}
null = {
versions = ["2.1.2"]
}
}
2 changes: 1 addition & 1 deletion build/equinixmetal/terraform-bundle.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ terraform {
}

providers {
metal = {
metal = {
versions = ["3.1.0"]
source = "equinix/metal"
}
Expand Down
6 changes: 5 additions & 1 deletion build/fetch-providers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,9 @@ else
# Above `terraform-bundle` command already maintains such structure, hence, we keep it instead of copying
# the `terraform-provider` binaries.
find . -name "*.md" | xargs -I % rm -f %
mv ./plugins ./tfproviders/
if [ -d ./plugins ]; then
mv ./plugins ./tfproviders/
else
mkdir "tfproviders"
fi
fi
2 changes: 1 addition & 1 deletion build/gcp/TF_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.12.31
0.13.7
16 changes: 12 additions & 4 deletions build/gcp/terraform-bundle.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@ terraform {
}

providers {
google = ["3.62.0"]
google-beta = ["3.62.0"]
template = ["2.1.2"]
null = ["2.1.2"]
google = {
versions = ["3.62.0"]
}
google-beta = {
versions = ["3.62.0"]
}
template = {
versions = ["2.1.2"]
}
null = {
versions = ["2.1.2"]
}
}
2 changes: 1 addition & 1 deletion build/openstack/TF_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.12.31
0.13.7
13 changes: 10 additions & 3 deletions build/openstack/terraform-bundle.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@ terraform {
}

providers {
openstack = ["1.37.0"]
template = ["2.1.2"]
null = ["2.1.2"]
openstack = {
versions = ["1.37.0"]
source = "terraform-provider-openstack/openstack"
}
template = {
versions = ["2.1.2"]
}
null = {
versions = ["2.1.2"]
}
}
2 changes: 1 addition & 1 deletion build/slim/TF_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.12.31
0.13.7
2 changes: 1 addition & 1 deletion hack/get-version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ fi
# as a dirty work tree.
# Additionally, it filters out changes to the `VERSION` file, as this is currently the only way to inject the
# version-to-build in our pipelines (see https://github.com/gardener/cc-utils/issues/431).
TREE_STATE="$([ -z "$(git status --porcelain 2>/dev/null | grep -vf <(git ls-files --deleted --ignored --exclude-from=.dockerignore) -e 'VERSION')" ] && echo clean || echo dirty)"
TREE_STATE="$([ -z "$(git status --porcelain 2>/dev/null | grep -vf <(git ls-files -o --deleted --ignored --exclude-from=.dockerignore) -e 'VERSION')" ] && echo clean || echo dirty)"

if [ "$TREE_STATE" = dirty ] ; then
VERSION="$VERSION-dirty"
Expand Down
81 changes: 77 additions & 4 deletions pkg/terraformer/terraformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package terraformer
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"os"
Expand Down Expand Up @@ -34,6 +35,13 @@ import (
const (
// maxPatchRetries define the maximum number of attempts to patch a resource in case of conflict
maxPatchRetries = 2

// terraformVersionKey is the terraform version key in the terraform state JSON
terraformVersionKey = "terraform_version"
// previousVersionPrefix is the previous version from which upgrades are supported
previousVersionPrefix = "0.12."
// registry is the terraform registry
registry = "registry.terraform.io"
)

var (
Expand All @@ -50,6 +58,22 @@ var (
SignalNotify = signal.Notify
)

var (
// providers is a map from the short provider names (used with terraform 0.12 or lower)
// to their longer names (used with terraform 0.13 or higher)
providers = map[string]string{
"aws": "hashicorp/aws",
"azurerm": "hashicorp/azurerm",
"google": "hashicorp/google",
"google-beta": "hashicorp/google-beta",
"openstack": "terraform-provider-openstack/openstack",
"alicloud": "hashicorp/alicloud",
"packet": "equinix/metal",
"template": "hashicorp/template",
"null": "hashicorp/null",
}
)

// NewDefaultTerraformer creates a new Terraformer with the default PathSet and logger.
func NewDefaultTerraformer(config *Config) (*Terraformer, error) {
return NewTerraformer(config, runtimelog.Log, DefaultPaths().WithBaseDir(config.BaseDir), clock.RealClock{})
Expand Down Expand Up @@ -164,6 +188,20 @@ func (t *Terraformer) execute(command Command) (rErr error) {
return fmt.Errorf("error executing terraform %s: %w", Init, err)
}

// get terraform version from state and execute state replace-provider commands if needed
terraformVersion, err := t.getTerraformVersionFromState(ctx)
if err != nil {
return fmt.Errorf("error getting terraform version from state: %w", err)
}
if strings.HasPrefix(terraformVersion, previousVersionPrefix) {
for key, value := range providers {
fromProvider, toProvider := fmt.Sprintf("%s/-/%s", registry, key), fmt.Sprintf("%s/%s", registry, value)
if err := t.executeTerraform(ctx, StateReplaceProvider, fromProvider, toProvider); err != nil {
return fmt.Errorf("error executing terraform %s: %w", StateReplaceProvider, err)
}
}
}

// execute main terraform command
if err := t.executeTerraform(ctx, command); err != nil {
return fmt.Errorf("error executing terraform %s: %w", command, err)
Expand All @@ -185,7 +223,7 @@ func (t *Terraformer) execute(command Command) (rErr error) {
return nil
}

func (t *Terraformer) executeTerraform(ctx context.Context, command Command) error {
func (t *Terraformer) executeTerraform(ctx context.Context, command Command, params ...string) error {
log := t.stepLogger("executeTerraform")

// open termination log file already to ensure we can write to it. If we can't write to it, we should exit early
Expand All @@ -196,22 +234,32 @@ func (t *Terraformer) executeTerraform(ctx context.Context, command Command) err
}
defer terminationLogFile.Close()

args := []string{string(command)}
if command == StateReplaceProvider {
args = append(args, "replace-provider")
}

// disable colors, which will look weird in termination message, k8s status fields and so on
args := []string{string(command), "-no-color"}
args = append(args, "-no-color")

switch command {
case Init:
args = append(args, "-plugin-dir="+t.paths.ProvidersDir)
args = append(args, t.paths.ConfigDir)
case Plan:
args = append(args, "-var-file="+t.paths.VarsPath, "-parallelism=4", "-detailed-exitcode", "-state="+t.paths.StatePath)
args = append(args, t.paths.ConfigDir)
case Apply:
args = append(args, "-var-file="+t.paths.VarsPath, "-parallelism=4", "-auto-approve", "-state="+t.paths.StatePath)
args = append(args, t.paths.ConfigDir)
case Destroy:
args = append(args, "-var-file="+t.paths.VarsPath, "-parallelism=4", "-auto-approve", "-state="+t.paths.StatePath)
args = append(args, t.paths.ConfigDir)
case StateReplaceProvider:
args = append(args, "-auto-approve", "-state="+t.paths.StatePath)
args = append(args, params...)
}

args = append(args, t.paths.ConfigDir)

log.Info("executing terraform", "command", command, "args", strings.Join(args[1:], " "))
tfCmd := exec.Command(TerraformBinary, args...)

Expand Down Expand Up @@ -293,6 +341,31 @@ func (t *Terraformer) isStateEmpty(ctx context.Context) (bool, error) {
return !ok || len(data) == 0, nil
}

func (t *Terraformer) getTerraformVersionFromState(ctx context.Context) (string, error) {
state := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: t.config.StateConfigMapName,
Namespace: t.config.Namespace,
},
}
if err := t.client.Get(ctx, client.ObjectKeyFromObject(state), state); client.IgnoreNotFound(err) != nil {
return "", err
}
data, ok := state.Data[tfStateKey]
if !ok || len(data) == 0 {
return "", nil
}
var terraformState map[string]interface{}
if err := json.Unmarshal([]byte(data), &terraformState); err != nil {
return "", fmt.Errorf("could not unmarshal terraform state from JSON: %w", err)
}
terraformVersion, ok := terraformState[terraformVersionKey].(string)
if !ok {
return "", fmt.Errorf("terraform state key %s is not of type string", terraformVersionKey)
}
return terraformVersion, nil
}

func (t *Terraformer) terraformObjects() []client.Object {
return []client.Object{
&corev1.Secret{
Expand Down
2 changes: 2 additions & 0 deletions pkg/terraformer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ const (
Validate Command = "validate"
// Plan is the terraform `plan` command.
Plan Command = "plan"
// StateReplaceProvider is the terraform `state` command with the `replace-provider` subcommand.
StateReplaceProvider Command = "state"
// TerraformerFinalizer is the finalizer used by the terraformer on the terraform configmaps and secrets
TerraformerFinalizer = "gardener.cloud/terraformer"
)
Expand Down
2 changes: 1 addition & 1 deletion test/utils/objects.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func PrepareTestObjects(ctx context.Context, c client.Client, namespacePrefix st
o.StateConfigMap = &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Name: "tf-state", Namespace: o.Namespace},
Data: map[string]string{
StateKey: `some state`,
StateKey: `{"terraform_version":"0.13.7"}`,
},
}
err = o.client.Create(ctx, o.StateConfigMap)
Expand Down

0 comments on commit acc2afa

Please sign in to comment.