You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
terraform/internal/command/workdir/backend_config_state.go

104 lines
3.2 KiB

// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: BUSL-1.1
package workdir
import (
"encoding/json"
"errors"
"fmt"
"github.com/zclconf/go-cty/cty"
ctyjson "github.com/zclconf/go-cty/cty/json"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/plans"
)
var _ ConfigState = &BackendConfigState{}
var _ DeepCopier[BackendConfigState] = &BackendConfigState{}
var _ PlanDataProvider[plans.Backend] = &BackendConfigState{}
// BackendConfigState describes the physical storage format for the backend state
// in a working directory, and provides the lowest-level API for decoding it.
type BackendConfigState struct {
Type string `json:"type"` // Backend type
ConfigRaw json.RawMessage `json:"config"` // Backend raw config
Hash uint64 `json:"hash"` // Hash of portion of configuration from config files
}
// Empty returns true if there is no active backend.
//
// In practice this typically means that the working directory is using the
// implied local backend, but that decision is made by the caller.
func (s *BackendConfigState) Empty() bool {
return s == nil || s.Type == ""
}
// Config decodes the type-specific configuration object using the provided
// schema and returns the result as a cty.Value.
//
// An error is returned if the stored configuration does not conform to the
// given schema, or is otherwise invalid.
func (s *BackendConfigState) Config(schema *configschema.Block) (cty.Value, error) {
ty := schema.ImpliedType()
if s == nil {
return cty.NullVal(ty), nil
}
return ctyjson.Unmarshal(s.ConfigRaw, ty)
}
// SetConfig replaces (in-place) the type-specific configuration object using
// the provided value and associated schema.
//
// An error is returned if the given value does not conform to the implied
// type of the schema.
func (s *BackendConfigState) SetConfig(val cty.Value, schema *configschema.Block) error {
if s == nil {
return errors.New("SetConfig called on nil BackendConfigState receiver")
}
ty := schema.ImpliedType()
buf, err := ctyjson.Marshal(val, ty)
if err != nil {
return err
}
s.ConfigRaw = buf
return nil
}
// PlanData produces an alternative representation of the receiver that is
// suitable for storing in a plan. The current workspace must additionally
// be provided, to be stored alongside the backend configuration.
//
// The backend configuration schema is required in order to properly
// encode the backend-specific configuration settings.
//
// As backends are not implemented by providers, the provider schema argument should always be nil
func (s *BackendConfigState) PlanData(schema *configschema.Block, _ *configschema.Block, workspaceName string) (*plans.Backend, error) {
if s == nil {
return nil, nil
}
configVal, err := s.Config(schema)
if err != nil {
return nil, fmt.Errorf("failed to decode backend config: %w", err)
}
return plans.NewBackend(s.Type, configVal, schema, workspaceName)
}
func (s *BackendConfigState) DeepCopy() *BackendConfigState {
if s == nil {
return nil
}
ret := &BackendConfigState{
Type: s.Type,
Hash: s.Hash,
}
if s.ConfigRaw != nil {
ret.ConfigRaw = make([]byte, len(s.ConfigRaw))
copy(ret.ConfigRaw, s.ConfigRaw)
}
return ret
}