mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Add count of forgotten resources to plan and apply outputs. (#2010)
Signed-off-by: Jarrett Duskey <jarrett@duskey.io> Signed-off-by: Christian Mesh <christianmesh1@gmail.com> Co-authored-by: Christian Mesh <christianmesh1@gmail.com>
This commit is contained in:
parent
23a26b0e58
commit
ecd4dc5c61
@ -8,13 +8,14 @@ UPGRADE NOTES:
|
|||||||
NEW FEATURES:
|
NEW FEATURES:
|
||||||
|
|
||||||
- New builtin provider functions added ([#2306](https://github.com/opentofu/opentofu/pull/2306)) :
|
- New builtin provider functions added ([#2306](https://github.com/opentofu/opentofu/pull/2306)) :
|
||||||
- `provider::terraform::decode_tfvars` - Decode a TFVars file content into an object.
|
- `provider::terraform::decode_tfvars` - Decode a TFVars file content into an object.
|
||||||
- `provider::terraform::encode_tfvars` - Encode an object into a string with the same format as a TFVars file.
|
- `provider::terraform::encode_tfvars` - Encode an object into a string with the same format as a TFVars file.
|
||||||
- `provider::terraform::encode_expr` - Encode an arbitrary expression into a string with valid OpenTofu syntax.
|
- `provider::terraform::encode_expr` - Encode an arbitrary expression into a string with valid OpenTofu syntax.
|
||||||
|
|
||||||
ENHANCEMENTS:
|
ENHANCEMENTS:
|
||||||
* OpenTofu will now recommend using `-exclude` instead of `-target`, when possible, in the error messages about unknown values in `count` and `for_each` arguments, thereby providing a more definitive workaround. ([#2154](https://github.com/opentofu/opentofu/pull/2154))
|
* OpenTofu will now recommend using `-exclude` instead of `-target`, when possible, in the error messages about unknown values in `count` and `for_each` arguments, thereby providing a more definitive workaround. ([#2154](https://github.com/opentofu/opentofu/pull/2154))
|
||||||
* State encryption now supports using external programs as key providers. Additionally, the PBKDF2 key provider now supports chaining via the `chain` parameter. ([#2023](https://github.com/opentofu/opentofu/pull/2023))
|
* State encryption now supports using external programs as key providers. Additionally, the PBKDF2 key provider now supports chaining via the `chain` parameter. ([#2023](https://github.com/opentofu/opentofu/pull/2023))
|
||||||
|
* Added count of forgotten resources to plan and apply outputs. ([#1956](https://github.com/opentofu/opentofu/issues/1956))
|
||||||
* The `element` function now accepts negative indices, which extends the existing "wrapping" model into the negative direction. In particular, choosing element `-1` selects the final element in the sequence. ([#2371](https://github.com/opentofu/opentofu/pull/2371))
|
* The `element` function now accepts negative indices, which extends the existing "wrapping" model into the negative direction. In particular, choosing element `-1` selects the final element in the sequence. ([#2371](https://github.com/opentofu/opentofu/pull/2371))
|
||||||
* `moved` now supports moving between different types ([#2370](https://github.com/opentofu/opentofu/pull/2370))
|
* `moved` now supports moving between different types ([#2370](https://github.com/opentofu/opentofu/pull/2370))
|
||||||
* `moved` block can now be used to migrate from the `null_resource` to the `terraform_data` resource. ([#2481](https://github.com/opentofu/opentofu/pull/2481))
|
* `moved` block can now be used to migrate from the `null_resource` to the `terraform_data` resource. ([#2481](https://github.com/opentofu/opentofu/pull/2481))
|
||||||
|
@ -64,6 +64,7 @@ func (plan Plan) renderHuman(renderer Renderer, mode plans.Mode, opts ...plans.Q
|
|||||||
willPrintResourceChanges := false
|
willPrintResourceChanges := false
|
||||||
counts := make(map[plans.Action]int)
|
counts := make(map[plans.Action]int)
|
||||||
importingCount := 0
|
importingCount := 0
|
||||||
|
forgettingCount := 0
|
||||||
var changes []diff
|
var changes []diff
|
||||||
for _, diff := range diffs.changes {
|
for _, diff := range diffs.changes {
|
||||||
action := jsonplan.UnmarshalActions(diff.change.Change.Actions)
|
action := jsonplan.UnmarshalActions(diff.change.Change.Actions)
|
||||||
@ -82,6 +83,10 @@ func (plan Plan) renderHuman(renderer Renderer, mode plans.Mode, opts ...plans.Q
|
|||||||
importingCount++
|
importingCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if action == plans.Forget {
|
||||||
|
forgettingCount++
|
||||||
|
}
|
||||||
|
|
||||||
// Don't count move-only changes
|
// Don't count move-only changes
|
||||||
if action != plans.NoOp {
|
if action != plans.NoOp {
|
||||||
willPrintResourceChanges = true
|
willPrintResourceChanges = true
|
||||||
@ -224,12 +229,29 @@ func (plan Plan) renderHuman(renderer Renderer, mode plans.Mode, opts ...plans.Q
|
|||||||
}
|
}
|
||||||
|
|
||||||
if importingCount > 0 {
|
if importingCount > 0 {
|
||||||
|
if forgettingCount > 0 {
|
||||||
|
renderer.Streams.Printf(
|
||||||
|
renderer.Colorize.Color("\n[bold]Plan:[reset] %d to import, %d to add, %d to change, %d to destroy, %d to forget.\n"),
|
||||||
|
importingCount,
|
||||||
|
counts[plans.Create]+counts[plans.DeleteThenCreate]+counts[plans.CreateThenDelete],
|
||||||
|
counts[plans.Update],
|
||||||
|
counts[plans.Delete]+counts[plans.DeleteThenCreate]+counts[plans.CreateThenDelete],
|
||||||
|
forgettingCount)
|
||||||
|
} else {
|
||||||
|
renderer.Streams.Printf(
|
||||||
|
renderer.Colorize.Color("\n[bold]Plan:[reset] %d to import, %d to add, %d to change, %d to destroy.\n"),
|
||||||
|
importingCount,
|
||||||
|
counts[plans.Create]+counts[plans.DeleteThenCreate]+counts[plans.CreateThenDelete],
|
||||||
|
counts[plans.Update],
|
||||||
|
counts[plans.Delete]+counts[plans.DeleteThenCreate]+counts[plans.CreateThenDelete])
|
||||||
|
}
|
||||||
|
} else if forgettingCount > 0 {
|
||||||
renderer.Streams.Printf(
|
renderer.Streams.Printf(
|
||||||
renderer.Colorize.Color("\n[bold]Plan:[reset] %d to import, %d to add, %d to change, %d to destroy.\n"),
|
renderer.Colorize.Color("\n[bold]Plan:[reset] %d to add, %d to change, %d to destroy, %d to forget.\n"),
|
||||||
importingCount,
|
|
||||||
counts[plans.Create]+counts[plans.DeleteThenCreate]+counts[plans.CreateThenDelete],
|
counts[plans.Create]+counts[plans.DeleteThenCreate]+counts[plans.CreateThenDelete],
|
||||||
counts[plans.Update],
|
counts[plans.Update],
|
||||||
counts[plans.Delete]+counts[plans.DeleteThenCreate]+counts[plans.CreateThenDelete])
|
counts[plans.Delete]+counts[plans.DeleteThenCreate]+counts[plans.CreateThenDelete],
|
||||||
|
forgettingCount)
|
||||||
} else {
|
} else {
|
||||||
renderer.Streams.Printf(
|
renderer.Streams.Printf(
|
||||||
renderer.Colorize.Color("\n[bold]Plan:[reset] %d to add, %d to change, %d to destroy.\n"),
|
renderer.Colorize.Color("\n[bold]Plan:[reset] %d to add, %d to change, %d to destroy.\n"),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{"@level":"info","@message":"Terraform 0.15.0-dev","@module":"tofu.ui","terraform":"0.15.0-dev","type":"version","ui":"0.1.0"}
|
{"@level":"info","@message":"Terraform 0.15.0-dev","@module":"tofu.ui","terraform":"0.15.0-dev","type":"version","ui":"0.1.0"}
|
||||||
{"@level":"info","@message":"test_instance.foo: Plan to create","@module":"tofu.ui","change":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create"},"type":"planned_change"}
|
{"@level":"info","@message":"test_instance.foo: Plan to create","@module":"tofu.ui","change":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create"},"type":"planned_change"}
|
||||||
{"@level":"info","@message":"Plan: 1 to add, 0 to change, 0 to destroy.","@module":"tofu.ui","changes":{"add":1,"import":0,"change":0,"remove":0,"operation":"plan"},"type":"change_summary"}
|
{"@level":"info","@message":"Plan: 1 to add, 0 to change, 0 to destroy.","@module":"tofu.ui","changes":{"add":1,"import":0,"change":0,"forget":0,"remove":0,"operation":"plan"},"type":"change_summary"}
|
||||||
{"@level":"info","@message":"test_instance.foo: Creating...","@module":"tofu.ui","hook":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create"},"type":"apply_start"}
|
{"@level":"info","@message":"test_instance.foo: Creating...","@module":"tofu.ui","hook":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create"},"type":"apply_start"}
|
||||||
{"@level":"info","@message":"test_instance.foo: Creation complete after 0s","@module":"tofu.ui","hook":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create","elapsed_seconds":0},"type":"apply_complete"}
|
{"@level":"info","@message":"test_instance.foo: Creation complete after 0s","@module":"tofu.ui","hook":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create","elapsed_seconds":0},"type":"apply_complete"}
|
||||||
{"@level":"info","@message":"Apply complete! Resources: 1 added, 0 changed, 0 destroyed.","@module":"tofu.ui","changes":{"add":1,"import":0,"change":0,"remove":0,"operation":"apply"},"type":"change_summary"}
|
{"@level":"info","@message":"Apply complete! Resources: 1 added, 0 changed, 0 destroyed.","@module":"tofu.ui","changes":{"add":1,"import":0,"change":0,"forget":0,"remove":0,"operation":"apply"},"type":"change_summary"}
|
||||||
{"@level":"info","@message":"Outputs: 0","@module":"tofu.ui","outputs":{},"type":"outputs"}
|
{"@level":"info","@message":"Outputs: 0","@module":"tofu.ui","outputs":{},"type":"outputs"}
|
||||||
|
@ -2,4 +2,4 @@
|
|||||||
{"@level":"info","@message":"data.test_data_source.a: Refreshing...","@module":"tofu.ui","hook":{"resource":{"addr":"data.test_data_source.a","module":"","resource":"data.test_data_source.a","implied_provider":"test","resource_type":"test_data_source","resource_name":"a","resource_key":null},"action":"read"},"type":"apply_start"}
|
{"@level":"info","@message":"data.test_data_source.a: Refreshing...","@module":"tofu.ui","hook":{"resource":{"addr":"data.test_data_source.a","module":"","resource":"data.test_data_source.a","implied_provider":"test","resource_type":"test_data_source","resource_name":"a","resource_key":null},"action":"read"},"type":"apply_start"}
|
||||||
{"@level":"info","@message":"data.test_data_source.a: Refresh complete after 0s [id=zzzzz]","@module":"tofu.ui","hook":{"resource":{"addr":"data.test_data_source.a","module":"","resource":"data.test_data_source.a","implied_provider":"test","resource_type":"test_data_source","resource_name":"a","resource_key":null},"action":"read","id_key":"id","id_value":"zzzzz","elapsed_seconds":0},"type":"apply_complete"}
|
{"@level":"info","@message":"data.test_data_source.a: Refresh complete after 0s [id=zzzzz]","@module":"tofu.ui","hook":{"resource":{"addr":"data.test_data_source.a","module":"","resource":"data.test_data_source.a","implied_provider":"test","resource_type":"test_data_source","resource_name":"a","resource_key":null},"action":"read","id_key":"id","id_value":"zzzzz","elapsed_seconds":0},"type":"apply_complete"}
|
||||||
{"@level":"info","@message":"test_instance.foo: Plan to create","@module":"tofu.ui","change":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create"},"type":"planned_change"}
|
{"@level":"info","@message":"test_instance.foo: Plan to create","@module":"tofu.ui","change":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create"},"type":"planned_change"}
|
||||||
{"@level":"info","@message":"Plan: 1 to add, 0 to change, 0 to destroy.","@module":"tofu.ui","changes":{"add":1,"import":0,"change":0,"remove":0,"operation":"plan"},"type":"change_summary"}
|
{"@level":"info","@message":"Plan: 1 to add, 0 to change, 0 to destroy.","@module":"tofu.ui","changes":{"add":1,"import":0,"change":0,"forget":0,"remove":0,"operation":"plan"},"type":"change_summary"}
|
||||||
|
@ -69,12 +69,31 @@ func (v *ApplyHuman) ResourceCount(stateOutPath string) {
|
|||||||
v.countHook.Removed,
|
v.countHook.Removed,
|
||||||
)
|
)
|
||||||
} else if v.countHook.Imported > 0 {
|
} else if v.countHook.Imported > 0 {
|
||||||
|
if v.countHook.Forgotten > 0 {
|
||||||
|
v.view.streams.Printf(
|
||||||
|
v.view.colorize.Color("[reset][bold][green]\nApply complete! Resources: %d imported, %d added, %d changed, %d destroyed, %d forgotten.\n"),
|
||||||
|
v.countHook.Imported,
|
||||||
|
v.countHook.Added,
|
||||||
|
v.countHook.Changed,
|
||||||
|
v.countHook.Removed,
|
||||||
|
v.countHook.Forgotten,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
v.view.streams.Printf(
|
||||||
|
v.view.colorize.Color("[reset][bold][green]\nApply complete! Resources: %d imported, %d added, %d changed, %d destroyed.\n"),
|
||||||
|
v.countHook.Imported,
|
||||||
|
v.countHook.Added,
|
||||||
|
v.countHook.Changed,
|
||||||
|
v.countHook.Removed,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if v.countHook.Forgotten > 0 {
|
||||||
v.view.streams.Printf(
|
v.view.streams.Printf(
|
||||||
v.view.colorize.Color("[reset][bold][green]\nApply complete! Resources: %d imported, %d added, %d changed, %d destroyed.\n"),
|
v.view.colorize.Color("[reset][bold][green]\nApply complete! Resources: %d added, %d changed, %d destroyed, %d forgotten.\n"),
|
||||||
v.countHook.Imported,
|
|
||||||
v.countHook.Added,
|
v.countHook.Added,
|
||||||
v.countHook.Changed,
|
v.countHook.Changed,
|
||||||
v.countHook.Removed,
|
v.countHook.Removed,
|
||||||
|
v.countHook.Forgotten,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
v.view.streams.Printf(
|
v.view.streams.Printf(
|
||||||
@ -144,6 +163,7 @@ func (v *ApplyJSON) ResourceCount(stateOutPath string) {
|
|||||||
Change: v.countHook.Changed,
|
Change: v.countHook.Changed,
|
||||||
Remove: v.countHook.Removed,
|
Remove: v.countHook.Removed,
|
||||||
Import: v.countHook.Imported,
|
Import: v.countHook.Imported,
|
||||||
|
Forget: v.countHook.Forgotten,
|
||||||
Operation: operation,
|
Operation: operation,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -105,24 +105,34 @@ func TestApplyHuman_help(t *testing.T) {
|
|||||||
// Hooks and ResourceCount are tangled up and easiest to test together.
|
// Hooks and ResourceCount are tangled up and easiest to test together.
|
||||||
func TestApply_resourceCount(t *testing.T) {
|
func TestApply_resourceCount(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
destroy bool
|
destroy bool
|
||||||
want string
|
want string
|
||||||
importing bool
|
importing bool
|
||||||
|
forgetting bool
|
||||||
}{
|
}{
|
||||||
"apply": {
|
"apply": {
|
||||||
false,
|
false,
|
||||||
"Apply complete! Resources: 1 added, 2 changed, 3 destroyed.",
|
"Apply complete! Resources: 1 added, 2 changed, 3 destroyed.",
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
"destroy": {
|
"destroy": {
|
||||||
true,
|
true,
|
||||||
"Destroy complete! Resources: 3 destroyed.",
|
"Destroy complete! Resources: 3 destroyed.",
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
"import": {
|
"import": {
|
||||||
false,
|
false,
|
||||||
"Apply complete! Resources: 1 imported, 1 added, 2 changed, 3 destroyed.",
|
"Apply complete! Resources: 1 imported, 1 added, 2 changed, 3 destroyed.",
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
"forget": {
|
||||||
|
false,
|
||||||
|
"Apply complete! Resources: 1 added, 2 changed, 3 destroyed, 1 forgotten.",
|
||||||
|
false,
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,6 +165,10 @@ func TestApply_resourceCount(t *testing.T) {
|
|||||||
count.Imported = 1
|
count.Imported = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tc.forgetting {
|
||||||
|
count.Forgotten = 1
|
||||||
|
}
|
||||||
|
|
||||||
v.ResourceCount("")
|
v.ResourceCount("")
|
||||||
|
|
||||||
got := done(t).Stdout()
|
got := done(t).Stdout()
|
||||||
|
@ -19,10 +19,11 @@ import (
|
|||||||
// countHook is a hook that counts the number of resources
|
// countHook is a hook that counts the number of resources
|
||||||
// added, removed, changed during the course of an apply.
|
// added, removed, changed during the course of an apply.
|
||||||
type countHook struct {
|
type countHook struct {
|
||||||
Added int
|
Added int
|
||||||
Changed int
|
Changed int
|
||||||
Removed int
|
Removed int
|
||||||
Imported int
|
Imported int
|
||||||
|
Forgotten int
|
||||||
|
|
||||||
ToAdd int
|
ToAdd int
|
||||||
ToChange int
|
ToChange int
|
||||||
@ -46,6 +47,7 @@ func (h *countHook) Reset() {
|
|||||||
h.Changed = 0
|
h.Changed = 0
|
||||||
h.Removed = 0
|
h.Removed = 0
|
||||||
h.Imported = 0
|
h.Imported = 0
|
||||||
|
h.Forgotten = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *countHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (tofu.HookAction, error) {
|
func (h *countHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (tofu.HookAction, error) {
|
||||||
@ -81,6 +83,7 @@ func (h *countHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generat
|
|||||||
h.Removed++
|
h.Removed++
|
||||||
case plans.Update:
|
case plans.Update:
|
||||||
h.Changed++
|
h.Changed++
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,3 +122,11 @@ func (h *countHook) PostApplyImport(addr addrs.AbsResourceInstance, importing pl
|
|||||||
h.Imported++
|
h.Imported++
|
||||||
return tofu.HookActionContinue, nil
|
return tofu.HookActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *countHook) PostApplyForget(_ addrs.AbsResourceInstance) (tofu.HookAction, error) {
|
||||||
|
h.Lock()
|
||||||
|
defer h.Unlock()
|
||||||
|
|
||||||
|
h.Forgotten++
|
||||||
|
return tofu.HookActionContinue, nil
|
||||||
|
}
|
||||||
|
@ -5,7 +5,10 @@
|
|||||||
|
|
||||||
package json
|
package json
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
type Operation string
|
type Operation string
|
||||||
|
|
||||||
@ -20,6 +23,7 @@ type ChangeSummary struct {
|
|||||||
Change int `json:"change"`
|
Change int `json:"change"`
|
||||||
Import int `json:"import"`
|
Import int `json:"import"`
|
||||||
Remove int `json:"remove"`
|
Remove int `json:"remove"`
|
||||||
|
Forget int `json:"forget"`
|
||||||
Operation Operation `json:"operation"`
|
Operation Operation `json:"operation"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,19 +31,34 @@ type ChangeSummary struct {
|
|||||||
// used by Terraform Cloud and Terraform Enterprise, so the exact formats of
|
// used by Terraform Cloud and Terraform Enterprise, so the exact formats of
|
||||||
// these strings are important.
|
// these strings are important.
|
||||||
func (cs *ChangeSummary) String() string {
|
func (cs *ChangeSummary) String() string {
|
||||||
|
var builder strings.Builder
|
||||||
switch cs.Operation {
|
switch cs.Operation {
|
||||||
case OperationApplied:
|
case OperationApplied:
|
||||||
|
builder.WriteString("Apply complete! Resources: ")
|
||||||
if cs.Import > 0 {
|
if cs.Import > 0 {
|
||||||
return fmt.Sprintf("Apply complete! Resources: %d imported, %d added, %d changed, %d destroyed.", cs.Import, cs.Add, cs.Change, cs.Remove)
|
builder.WriteString(fmt.Sprintf("%d imported, ", cs.Import))
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("Apply complete! Resources: %d added, %d changed, %d destroyed.", cs.Add, cs.Change, cs.Remove)
|
builder.WriteString(fmt.Sprintf("%d added, %d changed, %d destroyed", cs.Add, cs.Change, cs.Remove))
|
||||||
|
if cs.Forget > 0 {
|
||||||
|
builder.WriteString(fmt.Sprintf(", %d forgotten.", cs.Forget))
|
||||||
|
} else {
|
||||||
|
builder.WriteString(".")
|
||||||
|
}
|
||||||
|
return builder.String()
|
||||||
case OperationDestroyed:
|
case OperationDestroyed:
|
||||||
return fmt.Sprintf("Destroy complete! Resources: %d destroyed.", cs.Remove)
|
return fmt.Sprintf("Destroy complete! Resources: %d destroyed.", cs.Remove)
|
||||||
case OperationPlanned:
|
case OperationPlanned:
|
||||||
|
builder.WriteString("Plan: ")
|
||||||
if cs.Import > 0 {
|
if cs.Import > 0 {
|
||||||
return fmt.Sprintf("Plan: %d to import, %d to add, %d to change, %d to destroy.", cs.Import, cs.Add, cs.Change, cs.Remove)
|
builder.WriteString(fmt.Sprintf("%d to import, ", cs.Import))
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("Plan: %d to add, %d to change, %d to destroy.", cs.Add, cs.Change, cs.Remove)
|
builder.WriteString(fmt.Sprintf("%d to add, %d to change, %d to destroy", cs.Add, cs.Change, cs.Remove))
|
||||||
|
if cs.Forget > 0 {
|
||||||
|
builder.WriteString(fmt.Sprintf(", %d to forget.", cs.Forget))
|
||||||
|
} else {
|
||||||
|
builder.WriteString(".")
|
||||||
|
}
|
||||||
|
return builder.String()
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("%s: %d add, %d change, %d destroy", cs.Operation, cs.Add, cs.Change, cs.Remove)
|
return fmt.Sprintf("%s: %d add, %d change, %d destroy", cs.Operation, cs.Add, cs.Change, cs.Remove)
|
||||||
}
|
}
|
||||||
|
@ -282,6 +282,7 @@ func TestJSONView_ChangeSummary(t *testing.T) {
|
|||||||
"import": float64(0),
|
"import": float64(0),
|
||||||
"change": float64(2),
|
"change": float64(2),
|
||||||
"remove": float64(3),
|
"remove": float64(3),
|
||||||
|
"forget": float64(0),
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -312,6 +313,38 @@ func TestJSONView_ChangeSummaryWithImport(t *testing.T) {
|
|||||||
"change": float64(2),
|
"change": float64(2),
|
||||||
"remove": float64(3),
|
"remove": float64(3),
|
||||||
"import": float64(1),
|
"import": float64(1),
|
||||||
|
"forget": float64(0),
|
||||||
|
"operation": "apply",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testJSONViewOutputEquals(t, done(t).Stdout(), want)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONView_ChangeSummaryWithForget(t *testing.T) {
|
||||||
|
streams, done := terminal.StreamsForTesting(t)
|
||||||
|
jv := NewJSONView(NewView(streams))
|
||||||
|
|
||||||
|
jv.ChangeSummary(&viewsjson.ChangeSummary{
|
||||||
|
Add: 1,
|
||||||
|
Change: 2,
|
||||||
|
Remove: 3,
|
||||||
|
Forget: 1,
|
||||||
|
Operation: viewsjson.OperationApplied,
|
||||||
|
})
|
||||||
|
|
||||||
|
want := []map[string]interface{}{
|
||||||
|
{
|
||||||
|
"@level": "info",
|
||||||
|
"@message": "Apply complete! Resources: 1 added, 2 changed, 3 destroyed, 1 forgotten.",
|
||||||
|
"@module": "tofu.ui",
|
||||||
|
"type": "change_summary",
|
||||||
|
"changes": map[string]interface{}{
|
||||||
|
"add": float64(1),
|
||||||
|
"change": float64(2),
|
||||||
|
"remove": float64(3),
|
||||||
|
"import": float64(0),
|
||||||
|
"forget": float64(1),
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -247,6 +247,8 @@ func (v *OperationJSON) Plan(plan *plans.Plan, schemas *tofu.Schemas) {
|
|||||||
case plans.CreateThenDelete, plans.DeleteThenCreate:
|
case plans.CreateThenDelete, plans.DeleteThenCreate:
|
||||||
cs.Add++
|
cs.Add++
|
||||||
cs.Remove++
|
cs.Remove++
|
||||||
|
case plans.Forget:
|
||||||
|
cs.Forget++
|
||||||
}
|
}
|
||||||
|
|
||||||
if change.Action != plans.NoOp || !change.Addr.Equal(change.PrevRunAddr) || change.Importing != nil {
|
if change.Action != plans.NoOp || !change.Addr.Equal(change.PrevRunAddr) || change.Importing != nil {
|
||||||
|
@ -582,6 +582,7 @@ func TestOperationJSON_planNoChanges(t *testing.T) {
|
|||||||
"add": float64(0),
|
"add": float64(0),
|
||||||
"import": float64(0),
|
"import": float64(0),
|
||||||
"change": float64(0),
|
"change": float64(0),
|
||||||
|
"forget": float64(0),
|
||||||
"remove": float64(0),
|
"remove": float64(0),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -750,6 +751,7 @@ func TestOperationJSON_plan(t *testing.T) {
|
|||||||
"add": float64(3),
|
"add": float64(3),
|
||||||
"import": float64(0),
|
"import": float64(0),
|
||||||
"change": float64(1),
|
"change": float64(1),
|
||||||
|
"forget": float64(0),
|
||||||
"remove": float64(3),
|
"remove": float64(3),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -897,6 +899,7 @@ func TestOperationJSON_planWithImport(t *testing.T) {
|
|||||||
"add": float64(1),
|
"add": float64(1),
|
||||||
"import": float64(4),
|
"import": float64(4),
|
||||||
"change": float64(1),
|
"change": float64(1),
|
||||||
|
"forget": float64(0),
|
||||||
"remove": float64(2),
|
"remove": float64(2),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1034,6 +1037,7 @@ func TestOperationJSON_planDriftWithMove(t *testing.T) {
|
|||||||
"add": float64(0),
|
"add": float64(0),
|
||||||
"import": float64(0),
|
"import": float64(0),
|
||||||
"change": float64(0),
|
"change": float64(0),
|
||||||
|
"forget": float64(0),
|
||||||
"remove": float64(0),
|
"remove": float64(0),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1165,6 +1169,7 @@ func TestOperationJSON_planDriftWithMoveRefreshOnly(t *testing.T) {
|
|||||||
"add": float64(0),
|
"add": float64(0),
|
||||||
"import": float64(0),
|
"import": float64(0),
|
||||||
"change": float64(0),
|
"change": float64(0),
|
||||||
|
"forget": float64(0),
|
||||||
"remove": float64(0),
|
"remove": float64(0),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1226,6 +1231,7 @@ func TestOperationJSON_planOutputChanges(t *testing.T) {
|
|||||||
"import": float64(0),
|
"import": float64(0),
|
||||||
"change": float64(0),
|
"change": float64(0),
|
||||||
"remove": float64(0),
|
"remove": float64(0),
|
||||||
|
"forget": float64(0),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Output changes
|
// Output changes
|
||||||
|
@ -32,8 +32,9 @@ func (c *Context) Apply(ctx context.Context, plan *plans.Plan, config *configs.C
|
|||||||
|
|
||||||
log.Printf("[DEBUG] Building and walking apply graph for %s plan", plan.UIMode)
|
log.Printf("[DEBUG] Building and walking apply graph for %s plan", plan.UIMode)
|
||||||
|
|
||||||
|
var diags tfdiags.Diagnostics
|
||||||
|
|
||||||
if plan.Errored {
|
if plan.Errored {
|
||||||
var diags tfdiags.Diagnostics
|
|
||||||
diags = diags.Append(tfdiags.Sourceless(
|
diags = diags.Append(tfdiags.Sourceless(
|
||||||
tfdiags.Error,
|
tfdiags.Error,
|
||||||
"Cannot apply failed plan",
|
"Cannot apply failed plan",
|
||||||
@ -47,10 +48,30 @@ func (c *Context) Apply(ctx context.Context, plan *plans.Plan, config *configs.C
|
|||||||
// like to show some helpful output that mirrors the way we show other changes.
|
// like to show some helpful output that mirrors the way we show other changes.
|
||||||
if rc.Importing != nil {
|
if rc.Importing != nil {
|
||||||
for _, h := range c.hooks {
|
for _, h := range c.hooks {
|
||||||
// In future, we may need to call PostApplyImport separately elsewhere in the apply
|
// In the future, we may need to call PostApplyImport separately elsewhere in the apply
|
||||||
// operation. For now, though, we'll call Pre and Post hooks together.
|
// operation. For now, though, we'll call Pre and Post hooks together.
|
||||||
h.PreApplyImport(rc.Addr, *rc.Importing)
|
_, err := h.PreApplyImport(rc.Addr, *rc.Importing)
|
||||||
h.PostApplyImport(rc.Addr, *rc.Importing)
|
if err != nil {
|
||||||
|
return nil, diags.Append(err)
|
||||||
|
}
|
||||||
|
_, err = h.PostApplyImport(rc.Addr, *rc.Importing)
|
||||||
|
if err != nil {
|
||||||
|
return nil, diags.Append(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Following the same logic, we want to show helpful output for forget operations as well.
|
||||||
|
if rc.Action == plans.Forget {
|
||||||
|
for _, h := range c.hooks {
|
||||||
|
_, err := h.PreApplyForget(rc.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, diags.Append(err)
|
||||||
|
}
|
||||||
|
_, err = h.PostApplyForget(rc.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, diags.Append(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,10 @@ type Hook interface {
|
|||||||
PreApplyImport(addr addrs.AbsResourceInstance, importing plans.ImportingSrc) (HookAction, error)
|
PreApplyImport(addr addrs.AbsResourceInstance, importing plans.ImportingSrc) (HookAction, error)
|
||||||
PostApplyImport(addr addrs.AbsResourceInstance, importing plans.ImportingSrc) (HookAction, error)
|
PostApplyImport(addr addrs.AbsResourceInstance, importing plans.ImportingSrc) (HookAction, error)
|
||||||
|
|
||||||
|
// PreApplyForget and PostApplyForget are called during an apply for each forgotten resource.
|
||||||
|
PreApplyForget(addr addrs.AbsResourceInstance) (HookAction, error)
|
||||||
|
PostApplyForget(addr addrs.AbsResourceInstance) (HookAction, error)
|
||||||
|
|
||||||
// Stopping is called if an external signal requests that OpenTofu
|
// Stopping is called if an external signal requests that OpenTofu
|
||||||
// gracefully abort an operation in progress.
|
// gracefully abort an operation in progress.
|
||||||
//
|
//
|
||||||
@ -188,6 +192,14 @@ func (h *NilHook) PostApplyImport(addr addrs.AbsResourceInstance, importing plan
|
|||||||
return HookActionContinue, nil
|
return HookActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *NilHook) PreApplyForget(_ addrs.AbsResourceInstance) (HookAction, error) {
|
||||||
|
return HookActionContinue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *NilHook) PostApplyForget(_ addrs.AbsResourceInstance) (HookAction, error) {
|
||||||
|
return HookActionContinue, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (*NilHook) Stopping() {
|
func (*NilHook) Stopping() {
|
||||||
// Does nothing at all by default
|
// Does nothing at all by default
|
||||||
}
|
}
|
||||||
|
@ -133,6 +133,14 @@ type MockHook struct {
|
|||||||
PostApplyImportReturn HookAction
|
PostApplyImportReturn HookAction
|
||||||
PostApplyImportError error
|
PostApplyImportError error
|
||||||
|
|
||||||
|
PreApplyForgetCalled bool
|
||||||
|
PreApplyForgetReturn HookAction
|
||||||
|
PreApplyForgetError error
|
||||||
|
|
||||||
|
PostApplyForgetCalled bool
|
||||||
|
PostApplyForgetReturn HookAction
|
||||||
|
PostApplyForgetError error
|
||||||
|
|
||||||
StoppingCalled bool
|
StoppingCalled bool
|
||||||
|
|
||||||
PostStateUpdateCalled bool
|
PostStateUpdateCalled bool
|
||||||
@ -327,6 +335,22 @@ func (h *MockHook) PostApplyImport(addr addrs.AbsResourceInstance, importing pla
|
|||||||
return h.PostApplyImportReturn, h.PostApplyImportError
|
return h.PostApplyImportReturn, h.PostApplyImportError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *MockHook) PreApplyForget(_ addrs.AbsResourceInstance) (HookAction, error) {
|
||||||
|
h.Lock()
|
||||||
|
defer h.Unlock()
|
||||||
|
|
||||||
|
h.PreApplyForgetCalled = true
|
||||||
|
return h.PreApplyForgetReturn, h.PreApplyForgetError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MockHook) PostApplyForget(_ addrs.AbsResourceInstance) (HookAction, error) {
|
||||||
|
h.Lock()
|
||||||
|
defer h.Unlock()
|
||||||
|
|
||||||
|
h.PostApplyForgetCalled = true
|
||||||
|
return h.PostApplyForgetReturn, h.PostApplyForgetError
|
||||||
|
}
|
||||||
|
|
||||||
func (h *MockHook) Stopping() {
|
func (h *MockHook) Stopping() {
|
||||||
h.Lock()
|
h.Lock()
|
||||||
defer h.Unlock()
|
defer h.Unlock()
|
||||||
|
@ -92,6 +92,14 @@ func (h *stopHook) PostApplyImport(addr addrs.AbsResourceInstance, importing pla
|
|||||||
return h.hook()
|
return h.hook()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *stopHook) PreApplyForget(_ addrs.AbsResourceInstance) (HookAction, error) {
|
||||||
|
return h.hook()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *stopHook) PostApplyForget(_ addrs.AbsResourceInstance) (HookAction, error) {
|
||||||
|
return h.hook()
|
||||||
|
}
|
||||||
|
|
||||||
func (h *stopHook) Stopping() {}
|
func (h *stopHook) Stopping() {}
|
||||||
|
|
||||||
func (h *stopHook) PostStateUpdate(new *states.State) (HookAction, error) {
|
func (h *stopHook) PostStateUpdate(new *states.State) (HookAction, error) {
|
||||||
|
@ -157,6 +157,20 @@ func (h *testHook) PostApplyImport(addr addrs.AbsResourceInstance, importing pla
|
|||||||
return HookActionContinue, nil
|
return HookActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *testHook) PreApplyForget(addr addrs.AbsResourceInstance) (HookAction, error) {
|
||||||
|
h.mu.Lock()
|
||||||
|
defer h.mu.Unlock()
|
||||||
|
h.Calls = append(h.Calls, &testHookCall{"PreApplyForget", addr.String()})
|
||||||
|
return HookActionContinue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *testHook) PostApplyForget(addr addrs.AbsResourceInstance) (HookAction, error) {
|
||||||
|
h.mu.Lock()
|
||||||
|
defer h.mu.Unlock()
|
||||||
|
h.Calls = append(h.Calls, &testHookCall{"PostApplyForget", addr.String()})
|
||||||
|
return HookActionContinue, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (h *testHook) Stopping() {
|
func (h *testHook) Stopping() {
|
||||||
h.mu.Lock()
|
h.mu.Lock()
|
||||||
defer h.mu.Unlock()
|
defer h.mu.Unlock()
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 1,
|
"add": 1,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 1,
|
"add": 1,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 1,
|
"add": 1,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -117,6 +117,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 2,
|
"change": 2,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 1,
|
"add": 1,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -58,6 +58,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "plan",
|
"operation": "plan",
|
||||||
"remove": 1
|
"remove": 1
|
||||||
@ -107,6 +108,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "destroy",
|
"operation": "destroy",
|
||||||
"remove": 1
|
"remove": 1
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 1,
|
"add": 1,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -60,6 +60,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 1
|
"remove": 1
|
||||||
|
@ -97,6 +97,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 1,
|
"add": 1,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 1
|
"remove": 1
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -126,6 +126,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 2,
|
"change": 2,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -70,6 +70,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 1,
|
"add": 1,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 1,
|
"add": 1,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 1,
|
"add": 1,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 1,
|
"add": 1,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 1,
|
"add": 1,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -60,6 +60,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 1
|
"remove": 1
|
||||||
|
@ -97,6 +97,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 1,
|
"add": 1,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 1
|
"remove": 1
|
||||||
|
@ -97,6 +97,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 1,
|
"add": 1,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 1
|
"remove": 1
|
||||||
|
@ -97,6 +97,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 1,
|
"add": 1,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 1
|
"remove": 1
|
||||||
|
@ -97,6 +97,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 1,
|
"add": 1,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 1
|
"remove": 1
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 1,
|
"add": 1,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -97,6 +97,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 1,
|
"add": 1,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 1
|
"remove": 1
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 1,
|
"change": 1,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"changes": {
|
"changes": {
|
||||||
"add": 0,
|
"add": 0,
|
||||||
"change": 0,
|
"change": 0,
|
||||||
|
"forget": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"operation": "apply",
|
"operation": "apply",
|
||||||
"remove": 0
|
"remove": 0
|
||||||
|
Loading…
Reference in New Issue
Block a user