github.com/opentofu/opentofu@v1.7.1/internal/tofumigrate/tofumigrate.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package tofumigrate
     7  
     8  import (
     9  	"os"
    10  
    11  	"github.com/hashicorp/hcl/v2"
    12  	tfaddr "github.com/opentofu/registry-address"
    13  
    14  	"github.com/opentofu/opentofu/internal/configs"
    15  	"github.com/opentofu/opentofu/internal/getproviders"
    16  	"github.com/opentofu/opentofu/internal/states"
    17  	"github.com/opentofu/opentofu/internal/tfdiags"
    18  )
    19  
    20  // MigrateStateProviderAddresses can be used to update the in-memory view of the state to use registry.opentofu.org
    21  // provider addresses. This only applies for providers which are *not* explicitly referenced in the configuration in full form.
    22  // For example, if the configuration contains a provider block like this:
    23  //
    24  //	terraform {
    25  //	 required_providers {
    26  //	   random = {}
    27  //	 }
    28  //	}
    29  //
    30  // we will migrate the in-memory view of the statefile to use registry.opentofu.org/hashicorp/random.
    31  // However, if the configuration contains a provider block like this:
    32  //
    33  //	terraform {
    34  //	 required_providers {
    35  //	   random = {
    36  //	     source = "registry.terraform.io/hashicorp/random"
    37  //	   }
    38  //	 }
    39  //	}
    40  //
    41  // then we keep the old address.
    42  func MigrateStateProviderAddresses(config *configs.Config, state *states.State) (*states.State, tfdiags.Diagnostics) {
    43  	if os.Getenv("OPENTOFU_STATEFILE_PROVIDER_ADDRESS_TRANSLATION") == "0" {
    44  		return state, nil
    45  	}
    46  
    47  	if state == nil {
    48  		return nil, nil
    49  	}
    50  
    51  	var diags tfdiags.Diagnostics
    52  
    53  	stateCopy := state.DeepCopy()
    54  
    55  	providers := getproviders.Requirements{}
    56  	// config could be nil when we're e.g. showing a statefile without the configuration present
    57  	if config != nil {
    58  		var hclDiags hcl.Diagnostics
    59  		providers, hclDiags = config.ProviderRequirements()
    60  		diags = diags.Append(hclDiags)
    61  		if hclDiags.HasErrors() {
    62  			return nil, diags
    63  		}
    64  	}
    65  
    66  	for _, module := range stateCopy.Modules {
    67  		for _, resource := range module.Resources {
    68  			_, referencedInConfig := providers[resource.ProviderConfig.Provider]
    69  			if resource.ProviderConfig.Provider.Hostname == "registry.terraform.io" && !referencedInConfig {
    70  				resource.ProviderConfig.Provider.Hostname = tfaddr.DefaultProviderRegistryHost
    71  			}
    72  		}
    73  	}
    74  
    75  	return stateCopy, diags
    76  }