From 91bbb6d2036881af046d2c1121a0e982acdd2b30 Mon Sep 17 00:00:00 2001 From: pawelharacz Date: Wed, 5 Jun 2024 23:21:32 +0200 Subject: [PATCH] Day 97/100 commits - use http backend for plan and apply. Run apply based on the plan and deploy resources. log outputs when error --- README.md | 5 +- cmd/handlers/main.go | 2 +- cmd/monolith/main.go | 2 +- config.go | 17 +++--- docs/docs.go | 29 +++++++++ docs/swagger.json | 29 +++++++++ docs/swagger.yaml | 19 ++++++ internal/handlers/event_handler_factory.go | 24 ++++---- .../handlers/event_handler_factory_test.go | 2 +- internal/handlers/helpers.go | 27 +++++++++ internal/handlers/plan_worker_handler.go | 14 +++-- internal/handlers/plan_worker_handler_test.go | 2 +- .../handlers/scheduled_iac_apply_handler.go | 21 ++++--- internal/managers/delay_task.go | 2 +- internal/routers/api/terraform_controller.go | 2 + internal/services/iac/assembler.go | 6 ++ internal/services/iac/tofuIacService.go | 59 +++++++++++-------- 17 files changed, 196 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index f3486f9..f92a93d 100644 --- a/README.md +++ b/README.md @@ -57,12 +57,13 @@ Upps here should be demo video - no worry, I will be soon! - Now check the API o - [X] Add every method ctx to enrich logger - [X] Integrate handlers to use logger - [X] Handle scheduled plan in TerraformPlanner -- [ ] Run plan using http backend +- [X] Run plan using http backend - [ ] Access Token for Backend http - [ ] Clean solution to be more DDD - [ ] Create Plan changes during run, what was happened during the time - [ ] Apply Mechanism to handle the state - - [ ] Apply based on the Plan + - [X] Apply based on the Plan + - [ ] Save outputs as deployment, handle errors - [ ] Backup before apply using ApplyOptions.Backup - [ ] Integrate with the Git - [ ] Handle other version than version 4.0 of tf diff --git a/cmd/handlers/main.go b/cmd/handlers/main.go index 6e623f4..58eec55 100644 --- a/cmd/handlers/main.go +++ b/cmd/handlers/main.go @@ -80,7 +80,7 @@ func main() { log.Panic().Err(err) } - handlerFactory := handlers.NewEventHandlerFactory(eventBus, eventBus, uow) + handlerFactory := handlers.NewEventHandlerFactory(eventBus, eventBus, uow, fmt.Sprintf("%s:%d", cfg.ServiceDiscovery, cfg.HttpPort)) allHandlers, err := handlerFactory.RegisterAllHandlers() if err != nil { diff --git a/cmd/monolith/main.go b/cmd/monolith/main.go index e7a939b..7074df0 100644 --- a/cmd/monolith/main.go +++ b/cmd/monolith/main.go @@ -95,7 +95,7 @@ func main() { if err != nil { log.Panic().Err(err) } - handlerFactory := handlers.NewEventHandlerFactory(eventBus, eventBus, uow) + handlerFactory := handlers.NewEventHandlerFactory(eventBus, eventBus, uow, fmt.Sprintf("%s:%d", cfg.ServiceDiscovery, cfg.HttpPort)) allHandlers, err := handlerFactory.RegisterAllHandlers() if err != nil { diff --git a/config.go b/config.go index 55e5764..0f01e7c 100644 --- a/config.go +++ b/config.go @@ -1,12 +1,13 @@ package labraboard type Config struct { - ConnectionString string `yaml:"connectionString" env:"CONNECTION_STRING" env-description:"Connection string to the database" env-required:"true"` - HttpPort int `yaml:"httpPort" env:"HTTP_PORT" env-description:"HTTP port to serve the application" env-default:"8080"` - RedisHost string `yaml:"redisHost" env:"REDIS_HOST" env-description:"Redis host" env-default:"localhost"` - RedisPort int `yaml:"redisPort" env:"REDIS_PORT" env-description:"Redis port" env-default:"6379"` - RedisPassword string `yaml:"redisPassword" env:"REDIS_PASSWORD" env-description:"Redis password" env-default:"eYVX7EwVmmxKPCDmwMtyKVge8oLd2t81"` - RedisDB int `yaml:"redisDB" env:"REDIS_DB" env-description:"Redis database" env-default:"0"` - LogLevel int8 `yaml:"logLevel" env:"LOG_LEVEL" env-description:"Redis database" env-default:"1"` - UsePrettyLogs bool `yaml:"usePrettyLogs" env:"USE_PRETTY_LOGS" env-description:"use pretty logs instead of json. Logs are pushed to stdout" env-default:"false"` + ConnectionString string `yaml:"connectionString" env:"CONNECTION_STRING" env-description:"Connection string to the database" env-required:"true" json:"connectionString,omitempty"` + HttpPort int `yaml:"httpPort" env:"HTTP_PORT" env-description:"HTTP port to serve the application" env-default:"8080" json:"httpPort,omitempty"` + RedisHost string `yaml:"redisHost" env:"REDIS_HOST" env-description:"Redis host" env-default:"localhost" json:"redisHost,omitempty"` + RedisPort int `yaml:"redisPort" env:"REDIS_PORT" env-description:"Redis port" env-default:"6379" json:"redisPort,omitempty"` + RedisPassword string `yaml:"redisPassword" env:"REDIS_PASSWORD" env-description:"Redis password" env-default:"eYVX7EwVmmxKPCDmwMtyKVge8oLd2t81" json:"redisPassword,omitempty"` + RedisDB int `yaml:"redisDB" env:"REDIS_DB" env-description:"Redis database" env-default:"0" json:"redisDB,omitempty"` + LogLevel int8 `yaml:"logLevel" env:"LOG_LEVEL" env-description:"Redis database" env-default:"1" json:"logLevel,omitempty"` + UsePrettyLogs bool `yaml:"usePrettyLogs" env:"USE_PRETTY_LOGS" env-description:"use pretty logs instead of json. Logs are pushed to stdout" env-default:"false" json:"usePrettyLogs,omitempty"` + ServiceDiscovery string `yaml:"serviceDiscovery" env:"SERVICE_DISCOVERY" env-default:"http://localhost" json:"serviceDiscovery,omitempty"` } diff --git a/docs/docs.go b/docs/docs.go index 045c0b4..69b30e6 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -386,6 +386,15 @@ const docTemplate = `{ "name": "projectId", "in": "path", "required": true + }, + { + "description": "Plan to override", + "name": "plan", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dtos.CreatePlan" + } } ], "responses": { @@ -599,6 +608,26 @@ const docTemplate = `{ } } }, + "dtos.CreatePlan": { + "type": "object", + "properties": { + "repoCommit": { + "type": "string" + }, + "repoCommitType": { + "type": "string" + }, + "repoPath": { + "type": "string" + }, + "variables": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, "dtos.CreateProjectDto": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 5e81e0e..f7b1622 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -375,6 +375,15 @@ "name": "projectId", "in": "path", "required": true + }, + { + "description": "Plan to override", + "name": "plan", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dtos.CreatePlan" + } } ], "responses": { @@ -588,6 +597,26 @@ } } }, + "dtos.CreatePlan": { + "type": "object", + "properties": { + "repoCommit": { + "type": "string" + }, + "repoCommitType": { + "type": "string" + }, + "repoPath": { + "type": "string" + }, + "variables": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, "dtos.CreateProjectDto": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 7750712..ba27f45 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -15,6 +15,19 @@ definitions: value: type: string type: object + dtos.CreatePlan: + properties: + repoCommit: + type: string + repoCommitType: + type: string + repoPath: + type: string + variables: + additionalProperties: + type: string + type: object + type: object dtos.CreateProjectDto: properties: 'repositoryBranch:': @@ -324,6 +337,12 @@ paths: name: projectId required: true type: string + - description: Plan to override + in: body + name: plan + required: true + schema: + $ref: '#/definitions/dtos.CreatePlan' produces: - application/json responses: diff --git a/internal/handlers/event_handler_factory.go b/internal/handlers/event_handler_factory.go index 6374cbc..18ddb0a 100644 --- a/internal/handlers/event_handler_factory.go +++ b/internal/handlers/event_handler_factory.go @@ -14,18 +14,20 @@ var ( ) type EventHandlerFactory struct { - eventSubscriber eb.EventSubscriber - unitOfWork *repositories.UnitOfWork - allowedEvents []events.EventName - eventPublisher eb.EventPublisher + eventSubscriber eb.EventSubscriber + unitOfWork *repositories.UnitOfWork + allowedEvents []events.EventName + eventPublisher eb.EventPublisher + serviceDiscovery string } -func NewEventHandlerFactory(eventSubscriber eb.EventSubscriber, eventPublisher eb.EventPublisher, unitOfWork *repositories.UnitOfWork) *EventHandlerFactory { +func NewEventHandlerFactory(eventSubscriber eb.EventSubscriber, eventPublisher eb.EventPublisher, unitOfWork *repositories.UnitOfWork, serviceDiscovery string) *EventHandlerFactory { return &EventHandlerFactory{ - eventSubscriber: eventSubscriber, - unitOfWork: unitOfWork, - eventPublisher: eventPublisher, - allowedEvents: []events.EventName{events.LEASE_LOCK, events.TRIGGERED_PLAN, events.SCHEDULED_PLAN, events.IAC_APPLY_SCHEDULED}, + eventSubscriber: eventSubscriber, + unitOfWork: unitOfWork, + eventPublisher: eventPublisher, + serviceDiscovery: serviceDiscovery, + allowedEvents: []events.EventName{events.LEASE_LOCK, events.TRIGGERED_PLAN, events.SCHEDULED_PLAN, events.IAC_APPLY_SCHEDULED}, } } @@ -39,13 +41,13 @@ func (factory *EventHandlerFactory) RegisterHandler(event events.EventName) (Eve return newTerraformStateLeaseLockHandler(factory.eventSubscriber, factory.unitOfWork) case events.TRIGGERED_PLAN: factory.allowedEvents = helpers.Remove(factory.allowedEvents, event) - return newTriggeredPlanHandler(factory.eventSubscriber, factory.unitOfWork) + return newTriggeredPlanHandler(factory.eventSubscriber, factory.unitOfWork, factory.serviceDiscovery) case events.SCHEDULED_PLAN: factory.allowedEvents = helpers.Remove(factory.allowedEvents, event) return newScheduledPlanHandler(factory.eventSubscriber, factory.unitOfWork, factory.eventPublisher) case events.IAC_APPLY_SCHEDULED: factory.allowedEvents = helpers.Remove(factory.allowedEvents, event) - return newScheduledIaCApplyHandler(factory.eventSubscriber, factory.unitOfWork) + return newScheduledIaCApplyHandler(factory.eventSubscriber, factory.unitOfWork, factory.serviceDiscovery) } return nil, MissingHandlerImplementedFactory } diff --git a/internal/handlers/event_handler_factory_test.go b/internal/handlers/event_handler_factory_test.go index 8d54901..3205cc8 100644 --- a/internal/handlers/event_handler_factory_test.go +++ b/internal/handlers/event_handler_factory_test.go @@ -7,7 +7,7 @@ import ( ) func TestEventHandlerFactory(t *testing.T) { - var factory = NewEventHandlerFactory(nil, nil, nil) + var factory = NewEventHandlerFactory(nil, nil, nil, "") t.Run("Register Event that doesn't exist, should return null", func(t *testing.T) { var Z events.EventName = "Z" act, err := factory.RegisterHandler(Z) diff --git a/internal/handlers/helpers.go b/internal/handlers/helpers.go index 6100e5b..2736310 100644 --- a/internal/handlers/helpers.go +++ b/internal/handlers/helpers.go @@ -5,6 +5,33 @@ import ( "os" ) +func createLabraboardBackendFile(path string, endpoint string, projectId string) error { + content := `terraform { + backend "http" { + address = "%[1]s/api/v1/state/terraform/%[2]s" + lock_address = "%[1]s/api/v1/state/terraform/%[2]s/lock" + unlock_address = "%[1]s/api/v1/state/terraform/%[2]s/lock" + } +}` + file, err := os.Create(fmt.Sprintf("%s/backend_override.tf", path)) + if err != nil { + return err + } + defer func(file *os.File) { + err := file.Close() + if err != nil { + + } + }(file) + + _, err = fmt.Fprintf(file, content, endpoint, projectId) + if err != nil { + return err + } + + return nil +} + func createBackendFile(path string, statePath string) error { content := `terraform { backend "local" { diff --git a/internal/handlers/plan_worker_handler.go b/internal/handlers/plan_worker_handler.go index 45eba62..f696133 100644 --- a/internal/handlers/plan_worker_handler.go +++ b/internal/handlers/plan_worker_handler.go @@ -15,18 +15,20 @@ import ( "labraboard/internal/valueobjects/iacPlans" ) -// /todo redesing how to treat plan aggregate to keep whole runs history +// todo redesing how to treat plan aggregate to keep whole runs history type triggeredPlanHandler struct { - eventSubscriber eb.EventSubscriber - unitOfWork *repositories.UnitOfWork - assembler *iacSvc.Assembler + eventSubscriber eb.EventSubscriber + unitOfWork *repositories.UnitOfWork + assembler *iacSvc.Assembler + serviceDiscovery string } -func newTriggeredPlanHandler(eventSubscriber eb.EventSubscriber, unitOfWork *repositories.UnitOfWork) (*triggeredPlanHandler, error) { +func newTriggeredPlanHandler(eventSubscriber eb.EventSubscriber, unitOfWork *repositories.UnitOfWork, discovery string) (*triggeredPlanHandler, error) { return &triggeredPlanHandler{ eventSubscriber, unitOfWork, iacSvc.NewAssembler(unitOfWork), + discovery, }, nil } @@ -85,7 +87,7 @@ func (handler *triggeredPlanHandler) handlePlanTriggered(obj events.PlanTriggere } }(git) - if err = createBackendFile(tofuFolderPath, "./.local-state"); err != nil { + if err = createLabraboardBackendFile(tofuFolderPath, handler.serviceDiscovery, assembly.ProjectId.String()); err != nil { log.Error().Err(err) return errors.Wrap(err, "Cannot create backend") } diff --git a/internal/handlers/plan_worker_handler_test.go b/internal/handlers/plan_worker_handler_test.go index 4e3f085..4e6e377 100644 --- a/internal/handlers/plan_worker_handler_test.go +++ b/internal/handlers/plan_worker_handler_test.go @@ -50,7 +50,7 @@ func TestPlanTriggerHandler(t *testing.T) { Name: "2f5e1489476513212ae2f08c9a93beed7de47313", }, } - handler, _ := newTriggeredPlanHandler(nil, uow) + handler, _ := newTriggeredPlanHandler(nil, uow, "") handler.handlePlanTriggered(*obj, context.Background()) aggregate, _ = uow.IacRepository.Get(aggregate.GetID(), context.Background()) plan, err := aggregate.GetPlan(planId) diff --git a/internal/handlers/scheduled_iac_apply_handler.go b/internal/handlers/scheduled_iac_apply_handler.go index 8ee9e17..532a659 100644 --- a/internal/handlers/scheduled_iac_apply_handler.go +++ b/internal/handlers/scheduled_iac_apply_handler.go @@ -15,14 +15,16 @@ import ( ) type scheduledIaCApplyHandler struct { - eventSubscriber eb.EventSubscriber - unitOfWork *repositories.UnitOfWork + eventSubscriber eb.EventSubscriber + unitOfWork *repositories.UnitOfWork + serviceDiscovery string } -func newScheduledIaCApplyHandler(eventSubscriber eb.EventSubscriber, unitOfWork *repositories.UnitOfWork) (*scheduledIaCApplyHandler, error) { +func newScheduledIaCApplyHandler(eventSubscriber eb.EventSubscriber, unitOfWork *repositories.UnitOfWork, discovery string) (*scheduledIaCApplyHandler, error) { return &scheduledIaCApplyHandler{ eventSubscriber, unitOfWork, + discovery, }, nil } @@ -36,12 +38,15 @@ func (handler *scheduledIaCApplyHandler) Handle(ctx context.Context) { log.Error().Err(fmt.Errorf("cannot handle message type %T", event)) } log.Info().Msgf("Received message: %s", msg) - go handler.handle(event, log.WithContext(ctx)) + err = handler.handle(event, log.WithContext(ctx)) + if err != nil { + log.Error().Err(err).Msg("error to handle") + return + } } } func (handler *scheduledIaCApplyHandler) handle(event events.IacApplyScheduled, ctx context.Context) error { - const tfPlanPath = "plan.tfplan" log := logger.GetWitContext(ctx). With(). Str("changeId", event.ChangeId.String()). @@ -79,7 +84,7 @@ func (handler *scheduledIaCApplyHandler) handle(event events.IacApplyScheduled, folderPath := fmt.Sprintf("/tmp/%s/apply", output.PlanId) tofuFolderPath := fmt.Sprintf("%s/%s", folderPath, output.RepoPath) - planPath := fmt.Sprintf("%s/%s", tofuFolderPath, tfPlanPath) + planPath := fmt.Sprintf("%s/%s", tofuFolderPath, iac.PlanPath) git, err := iac.GitClone(output.RepoUrl, folderPath, output.CommitName, output.CommitType) if err != nil { log.Error().Err(err).Msg(err.Error()) @@ -94,7 +99,7 @@ func (handler *scheduledIaCApplyHandler) handle(event events.IacApplyScheduled, } }(git) - if err = createBackendFile(tofuFolderPath, "./.local-state"); err != nil { + if err = createLabraboardBackendFile(tofuFolderPath, handler.serviceDiscovery, output.ProjectId.String()); err != nil { log.Error().Err(err) return nil } @@ -109,7 +114,7 @@ func (handler *scheduledIaCApplyHandler) handle(event events.IacApplyScheduled, log.Error().Err(err) return nil } - _, err = tofu.Apply(output.PlanId, output.InlineEnvVariable(), output.InlineVariable(), planPath, ctx) + _, err = tofu.Apply(output.PlanId, output.InlineEnvVariable(), ctx) //todo handle output return nil } diff --git a/internal/managers/delay_task.go b/internal/managers/delay_task.go index 5fde1a9..a9979a2 100644 --- a/internal/managers/delay_task.go +++ b/internal/managers/delay_task.go @@ -98,7 +98,7 @@ func (dt *delayTask) Listen(ctx context.Context) { log.Trace().Msg("nothing to publish") return } - + ///todo fix it: {"EventName":"lease_lock","Content":{"Id":"dfd5677a-7b62-4444-8f81-5dfad0ce1627","Type":"terraform","LeaseTime":"2024-06-05T19:59:38.160871Z"},"WaitTime":3600000000000} for i, t := range resultSet { err = json.Unmarshal([]byte(t), &tasks[i]) if err != nil { diff --git a/internal/routers/api/terraform_controller.go b/internal/routers/api/terraform_controller.go index cd363d3..f08bd0d 100644 --- a/internal/routers/api/terraform_controller.go +++ b/internal/routers/api/terraform_controller.go @@ -24,6 +24,7 @@ func NewTerraformPlanController(iac *services.IacService) (*TerraformPlanControl // @Summary Method to run Terraform Plan for a given project and return the plan id // @Schemes http // @Param projectId path string true "project id" +// @Param plan body dtos.CreatePlan true "Plan to override" // @Description // @Tags terraform // @Accept json @@ -48,6 +49,7 @@ func (c *TerraformPlanController) CreateTerraformPlan(g *gin.Context) { if err = g.BindJSON(&dto); err != nil { l.Warn().Err(err) g.JSON(http.StatusBadRequest, gin.H{"message": "invalid payload"}) + return } var planRunner = services.TerraformPlanRunner{ diff --git a/internal/services/iac/assembler.go b/internal/services/iac/assembler.go index 9c685bc..08623e3 100644 --- a/internal/services/iac/assembler.go +++ b/internal/services/iac/assembler.go @@ -162,6 +162,9 @@ func (assembler *Assembler) Assemble(input Input, ctx context.Context) (Output, func (output Output) InlineVariable() []string { var variables []string for key, value := range output.Variables { + if key == "" || value == "" { + continue + } variables = append(variables, fmt.Sprintf("%s=%s", key, value)) } return variables @@ -171,6 +174,9 @@ func (output Output) InlineVariable() []string { func (output Output) InlineEnvVariable() map[string]string { var envVariables = map[string]string{} for _, env := range output.EnvVariables { + if env.Name == "" || env.Value == "" { + continue + } envVariables[env.Name] = env.Value } return envVariables diff --git a/internal/services/iac/tofuIacService.go b/internal/services/iac/tofuIacService.go index 1c871de..66a03c7 100644 --- a/internal/services/iac/tofuIacService.go +++ b/internal/services/iac/tofuIacService.go @@ -17,9 +17,10 @@ import ( "labraboard/internal/logger" "labraboard/internal/models" "os" - "path/filepath" ) +const PlanPath = "plan.tfplan" + type TofuIacService struct { iacFolderPath string tf *tfexec.Terraform @@ -68,13 +69,13 @@ func NewTofuIacService(iacFolderPath string, ctx context.Context) (*TofuIacServi func (svc *TofuIacService) Plan(envs map[string]string, variables []string, ctx context.Context) (*models.IacTerraformPlanJson, error) { log := logger.GetWitContext(ctx) var b bytes.Buffer - var planPath = "plan.tfplan" + jsonWriter := bufio.NewWriter(&b) planConfig := []tfexec.PlanOption{ tfexec.Lock(true), tfexec.Destroy(false), tfexec.Refresh(false), - tfexec.Out(planPath), + tfexec.Out(PlanPath), } if len(variables) > 0 { @@ -99,7 +100,7 @@ func (svc *TofuIacService) Plan(envs map[string]string, variables []string, ctx return nil, errors.New("plan is not finish well") } - planJson, err := svc.tf.ShowPlanFile(context.Background(), planPath) + planJson, err := svc.tf.ShowPlanFile(context.Background(), PlanPath) if err != nil { return nil, fmt.Errorf("%s: %v", "error running ShowPlanFile", err) } @@ -115,7 +116,7 @@ func (svc *TofuIacService) Plan(envs map[string]string, variables []string, ctx return nil, errors.New("Cannot reade plan") } - planContent, err := os.ReadFile(fmt.Sprintf("%s/%s", svc.iacFolderPath, planPath)) //read the content of file + planContent, err := os.ReadFile(fmt.Sprintf("%s/%s", svc.iacFolderPath, PlanPath)) //read the content of file if err != nil { log.Error().Err(err).Msg(err.Error()) return nil, err @@ -126,15 +127,15 @@ func (svc *TofuIacService) Plan(envs map[string]string, variables []string, ctx return planChanges, nil } -func (svc *TofuIacService) Apply(planId uuid.UUID, envs map[string]string, variables []string, planPath string, ctx context.Context) (interface{}, error) { - log := logger.GetWitContext(ctx) +func (svc *TofuIacService) Apply(planId uuid.UUID, envs map[string]string, ctx context.Context) (interface{}, error) { - tfLogPath := filepath.Join("/tmp/13e92193-7c36-4d01-9589-16d8e3c96ff5/apply", "test.log") - if planPath == "" { - var err = errors.New("plan path is empty") - log.Error().Err(err) - return nil, err - } + log := logger.GetWitContext(ctx) + //tfLogPath := filepath.Join("/tmp/13e92193-7c36-4d01-9589-16d8e3c96ff5/apply", "test.log") + //if planPath == "" { + // err := errors.New("plan path is empty") + // log.Error().Err(err) + // return nil, err + //} var b bytes.Buffer jsonWriter := bufio.NewWriter(&b) @@ -143,14 +144,14 @@ func (svc *TofuIacService) Apply(planId uuid.UUID, envs map[string]string, varia tfexec.Lock(true), tfexec.Destroy(false), tfexec.Refresh(true), - tfexec.DirOrPlan(planPath), + tfexec.DirOrPlan(fmt.Sprintf("%s/%s", svc.tf.WorkingDir(), PlanPath)), } - if len(variables) > 0 { - for _, v := range variables { - applyConfig = append(applyConfig, tfexec.Var(v)) - } - } + //if len(variables) > 0 { + // for _, v := range variables { + // applyConfig = append(applyConfig, tfexec.Var(v)) + // } + //} if len(envs) > 0 { err := svc.tf.SetEnv(envs) if err != nil { @@ -160,17 +161,23 @@ func (svc *TofuIacService) Apply(planId uuid.UUID, envs map[string]string, varia log.Info().Msgf("Apply plan %s", planId.String()) //svc.tf.SetLog("") - err := svc.tf.SetLogPath(tfLogPath) - if err != nil { - log.Error().Err(err) - return nil, err - } + //err := svc.tf.SetLogPath(tfLogPath) + //if err != nil { + // log.Error().Err(err) + // return nil, err + //} //svc.tf.SetStderr(lpError) //svc.tf.SetStdout(lpDebug) - err = svc.tf.ApplyJSON(ctx, jsonWriter, applyConfig...) + //todo handle outputs: {"@level":"error","@message":"Error: Error creating Resource Group \"rg-starterterraform-staging-main\": resources.GroupsClient#CreateOrUpdate: Failure responding to request: StatusCode=400 -- Original Error: autorest/azure: Service returned an error. Status=400 Code=\"LocationNotAvailableForResourceGroup\" Message=\"The provided location 'polandcenter' is not available for resource group. List of available regions is 'eastasia,southeastasia,australiaeast,australiasoutheast,brazilsouth,canadacentral,canadaeast,switzerlandnorth,germanywestcentral,eastus2,eastus,centralus,northcentralus,francecentral,uksouth,ukwest,centralindia,southindia,jioindiawest,italynorth,japaneast,japanwest,koreacentral,koreasouth,mexicocentral,northeurope,norwayeast,polandcentral,qatarcentral,spaincentral,swedencentral,uaenorth,westcentralus,westeurope,westus2,westus,southcentralus,westus3,southafricanorth,australiacentral,australiacentral2,israelcentral,westindia'.\"","@module":"terraform.ui","@timestamp":"2024-06-05T23:04:28.550164+02:00","diagnostic":{"severity":"error","summary":"Error creating Resource Group \"rg-starterterraform-staging-main\": resources.GroupsClient#CreateOrUpdate: Failure responding to request: StatusCode=400 -- Original Error: autorest/azure: Service returned an error. Status=400 Code=\"LocationNotAvailableForResourceGroup\" Message=\"The provided location 'polandcenter' is not available for resource group. List of available regions is 'eastasia,southeastasia,australiaeast,australiasoutheast,brazilsouth,canadacentral,canadaeast,switzerlandnorth,germanywestcentral,eastus2,eastus,centralus,northcentralus,francecentral,uksouth,ukwest,centralindia,southindia,jioindiawest,italynorth,japaneast,japanwest,koreacentral,koreasouth,mexicocentral,northeurope,norwayeast,polandcentral,qatarcentral,spaincentral,swedencentral,uaenorth,westcentralus,westeurope,westus2,westus,southcentralus,westus3,southafricanorth,australiacentral,australiacentral2,israelcentral,westindia'.\"","detail":"","address":"azurerm_resource_group.main","range":{"filename":"main.tf","start":{"line":8,"column":42,"byte":277},"end":{"line":8,"column":43,"byte":278}},"snippet":{"context":"resource \"azurerm_resource_group\" \"main\"","code":"resource \"azurerm_resource_group\" \"main\" {","start_line":8,"highlight_start_offset":41,"highlight_end_offset":42,"values":[]}},"type":"diagnostic"} + err := svc.tf.ApplyJSON(ctx, jsonWriter, applyConfig...) if err != nil { - log.Error().Err(err).Msg("") + if err = jsonWriter.Flush(); err != nil { + return nil, errors.New("error running Flush") + } + //r := bytes.NewReader(b.Bytes()) + //var iacDeserialized, err = svc.serializer.DeserializeJsonl(r) + log.Error().Err(err).Msg(string(b.Bytes())) return nil, err } if err = jsonWriter.Flush(); err != nil {