Skip to content

Commit e85a6e0

Browse files
Write deployment ID to workspace only after CreateDeployment succeeds
If CreateDeployment fails, the workspace should not contain a dangling deployment ID pointing to a non-existent server record. Co-authored-by: Isaac
1 parent 47e1a14 commit e85a6e0

1 file changed

Lines changed: 24 additions & 7 deletions

File tree

bundle/deploy/lock/deployment_metadata_service.go

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ func acquireLock(ctx context.Context, b *bundle.Bundle, svc *tmpdms.DeploymentMe
120120
if createErr != nil {
121121
return "", "", fmt.Errorf("failed to create deployment: %w", createErr)
122122
}
123+
// Write the deployment ID to workspace only after the server-side
124+
// record is created. This avoids leaving a dangling ID if creation fails.
125+
if err := writeDeploymentID(ctx, b, deploymentID); err != nil {
126+
return "", "", err
127+
}
123128
versionID = "1"
124129
} else {
125130
// Existing deployment: get the last version ID to determine the next one.
@@ -157,8 +162,10 @@ func acquireLock(ctx context.Context, b *bundle.Bundle, svc *tmpdms.DeploymentMe
157162

158163
// resolveDeploymentID reads the deployment ID from resources.json in the
159164
// workspace state directory. If the file doesn't exist or has no deployment ID,
160-
// a new UUID is generated and written. The boolean return indicates whether
161-
// this is a fresh deployment (true) or an existing one (false).
165+
// a new UUID is generated. The boolean return indicates whether this is a fresh
166+
// deployment (true) or an existing one (false). For fresh deployments, the
167+
// caller is responsible for writing the deployment ID to workspace after the
168+
// server-side deployment record is created successfully.
162169
func resolveDeploymentID(ctx context.Context, b *bundle.Bundle) (string, bool, error) {
163170
f, err := deploy.StateFiler(b)
164171
if err != nil {
@@ -184,18 +191,28 @@ func resolveDeploymentID(ctx context.Context, b *bundle.Bundle) (string, bool, e
184191
return "", false, fmt.Errorf("failed to read resources.json: %w", readErr)
185192
}
186193

187-
// Fresh deployment: generate a new ID and write resources.json.
188-
deploymentID := uuid.New().String()
194+
// Fresh deployment: generate a new ID but don't write yet.
195+
return uuid.New().String(), true, nil
196+
}
197+
198+
// writeDeploymentID writes the deployment ID to resources.json in the workspace
199+
// state directory. This should be called after the server-side deployment record
200+
// is created successfully.
201+
func writeDeploymentID(ctx context.Context, b *bundle.Bundle, deploymentID string) error {
202+
f, err := deploy.StateFiler(b)
203+
if err != nil {
204+
return fmt.Errorf("failed to create state filer: %w", err)
205+
}
189206
rj := statemgmt.ResourcesJSON{DeploymentID: deploymentID}
190207
data, err := json.Marshal(rj)
191208
if err != nil {
192-
return "", false, fmt.Errorf("failed to marshal resources.json: %w", err)
209+
return fmt.Errorf("failed to marshal resources.json: %w", err)
193210
}
194211
err = f.Write(ctx, "resources.json", bytes.NewReader(data), filer.CreateParentDirectories, filer.OverwriteIfExists)
195212
if err != nil {
196-
return "", false, fmt.Errorf("failed to write resources.json: %w", err)
213+
return fmt.Errorf("failed to write resources.json: %w", err)
197214
}
198-
return deploymentID, true, nil
215+
return nil
199216
}
200217

201218
// makeOperationReporter returns an OperationReporter that reports each resource

0 commit comments

Comments
 (0)