(
sess *provisionersdk.Session, request *proto.ApplyRequest, canceledOrComplete <-chan struct{},
)
| 282 | } |
| 283 | |
| 284 | func (s *server) Apply( |
| 285 | sess *provisionersdk.Session, request *proto.ApplyRequest, canceledOrComplete <-chan struct{}, |
| 286 | ) *proto.ApplyComplete { |
| 287 | ctx, span := s.startTrace(sess.Context(), tracing.FuncName()) |
| 288 | defer span.End() |
| 289 | ctx, cancel, killCtx, kill := s.setupContexts(ctx, canceledOrComplete) |
| 290 | defer cancel() |
| 291 | defer kill() |
| 292 | |
| 293 | e := s.executor(sess.Files, database.ProvisionerJobTimingStageApply) |
| 294 | if err := e.checkMinVersion(ctx); err != nil { |
| 295 | return provisionersdk.ApplyErrorf("%s", err.Error()) |
| 296 | } |
| 297 | logTerraformEnvVars(sess) |
| 298 | |
| 299 | // Earlier in the session, Plan() will have written the state file and the plan file. |
| 300 | statefilePath := sess.Files.StateFilePath() |
| 301 | |
| 302 | // Exit early if there is no state file. This is necessary to |
| 303 | // avoid any cases where a workspace is "locked out" of terraform due to |
| 304 | // e.g. bad template param values and cannot be deleted. This is just for |
| 305 | // contingency, in the future we will try harder to prevent workspaces being |
| 306 | // broken this hard. |
| 307 | if request.Metadata.GetWorkspaceTransition() == proto.WorkspaceTransition_DESTROY { |
| 308 | if _, err := os.Stat(statefilePath); errors.Is(err, os.ErrNotExist) { |
| 309 | sess.ProvisionLog(proto.LogLevel_INFO, "The terraform state does not exist, there is nothing to do") |
| 310 | return &proto.ApplyComplete{} |
| 311 | } |
| 312 | } |
| 313 | |
| 314 | env, err := provisionEnv(sess.Config, request.Metadata, nil, nil, nil) |
| 315 | if err != nil { |
| 316 | return provisionersdk.ApplyErrorf("provision env: %s", err) |
| 317 | } |
| 318 | env = otelEnvInject(ctx, env) |
| 319 | endStage := e.timings.startStage(database.ProvisionerJobTimingStageApply) |
| 320 | resp, err := e.apply( |
| 321 | ctx, killCtx, env, sess, |
| 322 | ) |
| 323 | endStage(err) |
| 324 | if err != nil { |
| 325 | errorMessage := err.Error() |
| 326 | // Terraform can fail and apply and still need to store it's state. |
| 327 | // In this case, we return Complete with an explicit error message. |
| 328 | stateData, _ := os.ReadFile(statefilePath) |
| 329 | return &proto.ApplyComplete{ |
| 330 | State: stateData, |
| 331 | Error: errorMessage, |
| 332 | Timings: e.timings.aggregate(), |
| 333 | } |
| 334 | } |
| 335 | resp.Timings = e.timings.aggregate() |
| 336 | return resp |
| 337 | } |
| 338 | |
| 339 | func planVars(plan *proto.PlanRequest) ([]string, error) { |
| 340 | vars := []string{} |
nothing calls this directly
no test coverage detected