github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/command/views/json/change.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package json
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/terramate-io/tf/plans"
    10  )
    11  
    12  func NewResourceInstanceChange(change *plans.ResourceInstanceChangeSrc) *ResourceInstanceChange {
    13  	c := &ResourceInstanceChange{
    14  		Resource:        newResourceAddr(change.Addr),
    15  		Action:          changeAction(change.Action),
    16  		Reason:          changeReason(change.ActionReason),
    17  		GeneratedConfig: change.GeneratedConfig,
    18  	}
    19  
    20  	// The order here matters, we want the moved action to take precedence over
    21  	// the import action. We're basically taking "the most recent action" as the
    22  	// primary action in the streamed logs. That is to say, that if a resource
    23  	// is imported and then moved in a single operation then the change for that
    24  	// resource will be reported as ActionMove while the Importing flag will
    25  	// still be set to true.
    26  	//
    27  	// Since both the moved and imported actions only overwrite a NoOp this
    28  	// behaviour is consistent across the other actions as well. Something that
    29  	// is imported and then updated, or moved and then updated, will have the
    30  	// ActionUpdate as the recognised action for the change.
    31  
    32  	if !change.Addr.Equal(change.PrevRunAddr) {
    33  		if c.Action == ActionNoOp {
    34  			c.Action = ActionMove
    35  		}
    36  		pr := newResourceAddr(change.PrevRunAddr)
    37  		c.PreviousResource = &pr
    38  	}
    39  	if change.Importing != nil {
    40  		if c.Action == ActionNoOp {
    41  			c.Action = ActionImport
    42  		}
    43  		c.Importing = &Importing{ID: change.Importing.ID}
    44  	}
    45  
    46  	return c
    47  }
    48  
    49  type ResourceInstanceChange struct {
    50  	Resource         ResourceAddr  `json:"resource"`
    51  	PreviousResource *ResourceAddr `json:"previous_resource,omitempty"`
    52  	Action           ChangeAction  `json:"action"`
    53  	Reason           ChangeReason  `json:"reason,omitempty"`
    54  	Importing        *Importing    `json:"importing,omitempty"`
    55  	GeneratedConfig  string        `json:"generated_config,omitempty"`
    56  }
    57  
    58  func (c *ResourceInstanceChange) String() string {
    59  	return fmt.Sprintf("%s: Plan to %s", c.Resource.Addr, c.Action)
    60  }
    61  
    62  type ChangeAction string
    63  
    64  const (
    65  	ActionNoOp    ChangeAction = "noop"
    66  	ActionMove    ChangeAction = "move"
    67  	ActionCreate  ChangeAction = "create"
    68  	ActionRead    ChangeAction = "read"
    69  	ActionUpdate  ChangeAction = "update"
    70  	ActionReplace ChangeAction = "replace"
    71  	ActionDelete  ChangeAction = "delete"
    72  	ActionImport  ChangeAction = "import"
    73  )
    74  
    75  func changeAction(action plans.Action) ChangeAction {
    76  	switch action {
    77  	case plans.NoOp:
    78  		return ActionNoOp
    79  	case plans.Create:
    80  		return ActionCreate
    81  	case plans.Read:
    82  		return ActionRead
    83  	case plans.Update:
    84  		return ActionUpdate
    85  	case plans.DeleteThenCreate, plans.CreateThenDelete:
    86  		return ActionReplace
    87  	case plans.Delete:
    88  		return ActionDelete
    89  	default:
    90  		return ActionNoOp
    91  	}
    92  }
    93  
    94  type ChangeReason string
    95  
    96  const (
    97  	ReasonNone               ChangeReason = ""
    98  	ReasonTainted            ChangeReason = "tainted"
    99  	ReasonRequested          ChangeReason = "requested"
   100  	ReasonReplaceTriggeredBy ChangeReason = "replace_triggered_by"
   101  	ReasonCannotUpdate       ChangeReason = "cannot_update"
   102  	ReasonUnknown            ChangeReason = "unknown"
   103  
   104  	ReasonDeleteBecauseNoResourceConfig ChangeReason = "delete_because_no_resource_config"
   105  	ReasonDeleteBecauseWrongRepetition  ChangeReason = "delete_because_wrong_repetition"
   106  	ReasonDeleteBecauseCountIndex       ChangeReason = "delete_because_count_index"
   107  	ReasonDeleteBecauseEachKey          ChangeReason = "delete_because_each_key"
   108  	ReasonDeleteBecauseNoModule         ChangeReason = "delete_because_no_module"
   109  	ReasonDeleteBecauseNoMoveTarget     ChangeReason = "delete_because_no_move_target"
   110  	ReasonReadBecauseConfigUnknown      ChangeReason = "read_because_config_unknown"
   111  	ReasonReadBecauseDependencyPending  ChangeReason = "read_because_dependency_pending"
   112  	ReasonReadBecauseCheckNested        ChangeReason = "read_because_check_nested"
   113  )
   114  
   115  func changeReason(reason plans.ResourceInstanceChangeActionReason) ChangeReason {
   116  	switch reason {
   117  	case plans.ResourceInstanceChangeNoReason:
   118  		return ReasonNone
   119  	case plans.ResourceInstanceReplaceBecauseTainted:
   120  		return ReasonTainted
   121  	case plans.ResourceInstanceReplaceByRequest:
   122  		return ReasonRequested
   123  	case plans.ResourceInstanceReplaceBecauseCannotUpdate:
   124  		return ReasonCannotUpdate
   125  	case plans.ResourceInstanceReplaceByTriggers:
   126  		return ReasonReplaceTriggeredBy
   127  	case plans.ResourceInstanceDeleteBecauseNoResourceConfig:
   128  		return ReasonDeleteBecauseNoResourceConfig
   129  	case plans.ResourceInstanceDeleteBecauseWrongRepetition:
   130  		return ReasonDeleteBecauseWrongRepetition
   131  	case plans.ResourceInstanceDeleteBecauseCountIndex:
   132  		return ReasonDeleteBecauseCountIndex
   133  	case plans.ResourceInstanceDeleteBecauseEachKey:
   134  		return ReasonDeleteBecauseEachKey
   135  	case plans.ResourceInstanceDeleteBecauseNoModule:
   136  		return ReasonDeleteBecauseNoModule
   137  	case plans.ResourceInstanceReadBecauseConfigUnknown:
   138  		return ReasonReadBecauseConfigUnknown
   139  	case plans.ResourceInstanceDeleteBecauseNoMoveTarget:
   140  		return ReasonDeleteBecauseNoMoveTarget
   141  	case plans.ResourceInstanceReadBecauseDependencyPending:
   142  		return ReasonReadBecauseDependencyPending
   143  	case plans.ResourceInstanceReadBecauseCheckNested:
   144  		return ReasonReadBecauseCheckNested
   145  	default:
   146  		// This should never happen, but there's no good way to guarantee
   147  		// exhaustive handling of the enum, so a generic fall back is better
   148  		// than a misleading result or a panic
   149  		return ReasonUnknown
   150  	}
   151  }