@@ -4,13 +4,15 @@ import (
44 "context"
55 "errors"
66 "fmt"
7+ "maps"
78 "reflect"
89 "slices"
910 "strings"
1011
1112 "github.com/databricks/cli/bundle/config"
1213 "github.com/databricks/cli/bundle/deployplan"
1314 "github.com/databricks/cli/bundle/direct/dresources"
15+ "github.com/databricks/cli/bundle/direct/dstate"
1416 "github.com/databricks/cli/libs/dyn"
1517 "github.com/databricks/cli/libs/dyn/dynvar"
1618 "github.com/databricks/cli/libs/log"
@@ -21,7 +23,6 @@ import (
2123 "github.com/databricks/cli/libs/structs/structvar"
2224 "github.com/databricks/cli/libs/utils"
2325 "github.com/databricks/databricks-sdk-go"
24- "github.com/databricks/databricks-sdk-go/apierr"
2526)
2627
2728var errDelayed = errors .New ("must be resolved after apply" )
@@ -43,14 +44,14 @@ func (b *DeploymentBundle) Init(client *databricks.WorkspaceClient) error {
4344 return err
4445}
4546
46- func (b * DeploymentBundle ) CalculatePlanForDeploy (ctx context.Context , client * databricks.WorkspaceClient , configRoot * config.Root ) (* deployplan.Plan , error ) {
47+ func (b * DeploymentBundle ) CalculatePlan (ctx context.Context , client * databricks.WorkspaceClient , configRoot * config.Root ) (* deployplan.Plan , error ) {
4748 b .StateDB .AssertOpened ()
4849 err := b .Init (client )
4950 if err != nil {
5051 return nil , err
5152 }
5253
53- plan , err := b .makePlan (ctx , configRoot )
54+ plan , err := b .makePlan (ctx , configRoot , & b . StateDB . Data )
5455 if err != nil {
5556 return nil , fmt .Errorf ("reading config: %w" , err )
5657 }
@@ -67,7 +68,7 @@ func (b *DeploymentBundle) CalculatePlanForDeploy(ctx context.Context, client *d
6768 return nil , err
6869 }
6970
70- // We're processing resources in DAG order because we're resolving refernces (that can be resolved at plan stage).
71+ // We're processing resources in DAG order because we're resolving references (that can be resolved at plan stage).
7172 g .Run (defaultParallelism , func (resourceKey string , failedDependency * string ) bool {
7273 errorPrefix := "cannot plan " + resourceKey
7374
@@ -90,6 +91,27 @@ func (b *DeploymentBundle) CalculatePlanForDeploy(ctx context.Context, client *d
9091 return false
9192 }
9293
94+ if entry .Action == deployplan .ActionTypeDelete .String () {
95+ dbentry , hasEntry := b .StateDB .GetResourceEntry (resourceKey )
96+ if ! hasEntry {
97+ logdiag .LogError (ctx , fmt .Errorf ("%s: internal error, missing in state" , errorPrefix ))
98+ return false
99+ }
100+
101+ _ , err := adapter .DoRefresh (ctx , dbentry .ID )
102+ if err != nil {
103+ if isResourceGone (err ) {
104+ // no such resource
105+ plan .RemoveEntry (resourceKey )
106+ } else {
107+ log .Warnf (ctx , "cannot read %s id=%q: %s" , resourceKey , dbentry .ID , err )
108+ return false
109+ }
110+ }
111+
112+ return true
113+ }
114+
93115 // Process all references in the resource using Refs map
94116 // Refs maps path inside resource to references e.g. "${resources.jobs.foo.id} ${resources.jobs.foo.name}"
95117 if ! b .resolveReferences (ctx , entry , errorPrefix , true ) {
@@ -139,7 +161,7 @@ func (b *DeploymentBundle) CalculatePlanForDeploy(ctx context.Context, client *d
139161
140162 remoteState , err := adapter .DoRefresh (ctx , dbentry .ID )
141163 if err != nil {
142- if errors . Is (err , apierr . ErrResourceDoesNotExist ) || errors . Is ( err , apierr . ErrNotFound ) {
164+ if isResourceGone (err ) {
143165 remoteState = nil
144166 } else {
145167 logdiag .LogError (ctx , fmt .Errorf ("%s: failed to read id=%q: %w (localAction=%q)" , errorPrefix , dbentry .ID , err , localAction .String ()))
@@ -188,28 +210,6 @@ func (b *DeploymentBundle) CalculatePlanForDeploy(ctx context.Context, client *d
188210 return nil , errors .New ("planning failed" )
189211 }
190212
191- // Note, we cannot simply remove 'skip' entries here as then we'd need to ensure there are no edges to them
192-
193- state := b .StateDB .ExportState (ctx )
194-
195- // Remained in state are resources that no longer present in the config
196- for _ , group := range utils .SortedKeys (state ) {
197- _ , ok := b .Adapters [group ]
198-
199- if ! ok {
200- log .Warnf (ctx , "%s: resource type not supported on direct backend" , group )
201- continue
202- }
203- for _ , key := range utils .SortedKeys (state [group ]) {
204- n := "resources." + group + "." + key
205- _ , exists := plan .Plan [n ]
206- if exists {
207- continue
208- }
209- plan .Plan [n ] = & deployplan.PlanEntry {Action : deployplan .ActionTypeDelete .String ()}
210- }
211- }
212-
213213 for _ , entry := range plan .Plan {
214214 if entry .Action == deployplan .ActionTypeSkipString {
215215 entry .NewState = nil
@@ -271,32 +271,6 @@ func interpretOldStateVsRemoteState(ctx context.Context, adapter *dresources.Ada
271271 return action , m
272272}
273273
274- func (b * DeploymentBundle ) CalculatePlanForDestroy (ctx context.Context , client * databricks.WorkspaceClient ) (* deployplan.Plan , error ) {
275- b .StateDB .AssertOpened ()
276-
277- err := b .Init (client )
278- if err != nil {
279- return nil , err
280- }
281-
282- plan := deployplan .NewPlan ()
283-
284- state := b .StateDB .ExportState (ctx )
285- for group , groupData := range state {
286- _ , ok := b .Adapters [group ]
287- if ! ok {
288- logdiag .LogError (ctx , fmt .Errorf ("cannot destroy %s: resource type not supported on direct backend" , group ))
289- continue
290- }
291- for key := range groupData {
292- n := "resources." + group + "." + key
293- plan .Plan [n ] = & deployplan.PlanEntry {Action : deployplan .ActionTypeDelete .String ()}
294- }
295- }
296-
297- return plan , nil
298- }
299-
300274func (b * DeploymentBundle ) LookupReferenceLocal (ctx context.Context , path * structpath.PathNode ) (any , error ) {
301275 targetResourceKey := path .Prefix (3 ).String ()
302276 fieldPath := path .SkipPrefix (3 )
@@ -432,35 +406,41 @@ func (b *DeploymentBundle) resolveReferences(ctx context.Context, entry *deployp
432406 return true
433407}
434408
435- func (b * DeploymentBundle ) makePlan (ctx context.Context , configRoot * config.Root ) (* deployplan.Plan , error ) {
409+ func (b * DeploymentBundle ) makePlan (ctx context.Context , configRoot * config.Root , db * dstate. Database ) (* deployplan.Plan , error ) {
436410 p := deployplan .NewPlan ()
437411
438412 // Collect and sort nodes first, because MapByPattern gives them in randomized order
439413 var nodes []string
440414
441- // Walk?
442- _ , err := dyn .MapByPattern (
443- configRoot .Value (),
444- dyn .NewPattern (dyn .Key ("resources" ), dyn .AnyKey (), dyn .AnyKey ()),
445- func (p dyn.Path , v dyn.Value ) (dyn.Value , error ) {
446- group := p [1 ].Key ()
415+ existingKeys := maps .Clone (db .State )
447416
448- _ , ok := dresources .SupportedResources [group ]
449- if ! ok {
450- return v , fmt .Errorf ("unsupported resource: %s" , group )
451- }
417+ // Walk?
418+ if configRoot != nil {
419+ _ , err := dyn .MapByPattern (
420+ configRoot .Value (),
421+ dyn .NewPattern (dyn .Key ("resources" ), dyn .AnyKey (), dyn .AnyKey ()),
422+ func (p dyn.Path , v dyn.Value ) (dyn.Value , error ) {
423+ group := p [1 ].Key ()
424+
425+ _ , ok := dresources .SupportedResources [group ]
426+ if ! ok {
427+ return v , fmt .Errorf ("unsupported resource: %s" , group )
428+ }
452429
453- nodes = append (nodes , p .String ())
454- return dyn .InvalidValue , nil
455- },
456- )
457- if err != nil {
458- return nil , fmt .Errorf ("reading config: %w" , err )
430+ nodes = append (nodes , p .String ())
431+ return dyn .InvalidValue , nil
432+ },
433+ )
434+ if err != nil {
435+ return nil , fmt .Errorf ("reading config: %w" , err )
436+ }
459437 }
460438
461439 slices .Sort (nodes )
462440
463441 for _ , node := range nodes {
442+ delete (existingKeys , node )
443+
464444 prefix := "cannot plan " + node
465445 inputConfig , ok := configRoot .GetResourceConfig (node )
466446 if ! ok {
@@ -541,6 +521,15 @@ func (b *DeploymentBundle) makePlan(ctx context.Context, configRoot *config.Root
541521 p .Plan [node ] = & e
542522 }
543523
524+ for n := range existingKeys {
525+ if p .Plan [n ] != nil {
526+ panic ("unexpected node " + n )
527+ }
528+ p .Plan [n ] = & deployplan.PlanEntry {
529+ Action : deployplan .ActionTypeDelete .String (),
530+ }
531+ }
532+
544533 return p , nil
545534}
546535
0 commit comments