package conf import ( "encoding/json" "fmt" "io/fs" "os" "path/filepath" "strconv" "strings" "time" "shared-sqs/app/models" "shared-sqs/app/utils" log "github.com/sirupsen/logrus" "github.com/ghodss/yaml" ) var envs map[string]models.Environment func LoadYamlConfig(filename string, env string) []string { ports := []string{"4100"} if filename == "" { root, _ := filepath.Abs(".") err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { if "goaws.yaml" == d.Name() { filename = path } return nil }) if err != nil || filename == "" { log.Warn("Failure to find default config file") return ports } } filename, _ = filepath.Abs(filename) if _, err := os.Stat(filename); err != nil { log.Warnf("Failure to find config file: %s", filename) return ports } log.Infof("Loading config file: %s", filename) yamlFile, err := os.ReadFile(filename) if err != nil { return ports } err = yaml.Unmarshal(yamlFile, &envs) if err != nil { log.Errorf("err: %v\n", err) return ports } if env == "" { env = "Local" } if envs[env].Region == "" { models.CurrentEnvironment.Region = "local" } models.CurrentEnvironment = envs[env] if envs[env].Port != "" { ports = []string{envs[env].Port} } models.LogMessages = false models.LogFile = "./goaws_messages.log" if envs[env].LogToFile == true { models.LogMessages = true if envs[env].LogFile != "" { models.LogFile = envs[env].LogFile } } if models.CurrentEnvironment.QueueAttributeDefaults.VisibilityTimeout <= 0 { models.CurrentEnvironment.QueueAttributeDefaults.VisibilityTimeout = 30 } if models.CurrentEnvironment.QueueAttributeDefaults.MaximumMessageSize <= 0 { models.CurrentEnvironment.QueueAttributeDefaults.MaximumMessageSize = 262144 // 256K } if models.CurrentEnvironment.QueueAttributeDefaults.MessageRetentionPeriod <= 0 { models.CurrentEnvironment.QueueAttributeDefaults.MessageRetentionPeriod = 345600 // 4 days } if models.CurrentEnvironment.QueueAttributeDefaults.ReceiveMessageWaitTimeSeconds <= 0 { models.CurrentEnvironment.QueueAttributeDefaults.ReceiveMessageWaitTimeSeconds = 0 } if models.CurrentEnvironment.AccountID == "" { models.CurrentEnvironment.AccountID = "queue" } if models.CurrentEnvironment.Host == "" { models.CurrentEnvironment.Host = "localhost" models.CurrentEnvironment.Port = "4100" } models.SyncQueues.Lock() for _, queue := range envs[env].Queues { queueUrl := "http://" + models.CurrentEnvironment.Host + ":" + models.CurrentEnvironment.Port + "/" + models.CurrentEnvironment.AccountID + "/" + queue.Name if models.CurrentEnvironment.Region != "" { queueUrl = "http://" + models.CurrentEnvironment.Region + "." + models.CurrentEnvironment.Host + ":" + models.CurrentEnvironment.Port + "/" + models.CurrentEnvironment.AccountID + "/" + queue.Name } queueArn := "arn:aws:sqs:" + models.CurrentEnvironment.Region + ":" + models.CurrentEnvironment.AccountID + ":" + queue.Name if queue.ReceiveMessageWaitTimeSeconds == 0 { queue.ReceiveMessageWaitTimeSeconds = models.CurrentEnvironment.QueueAttributeDefaults.ReceiveMessageWaitTimeSeconds } if queue.MaximumMessageSize == 0 { queue.MaximumMessageSize = models.CurrentEnvironment.QueueAttributeDefaults.MaximumMessageSize } if queue.VisibilityTimeout == 0 { queue.VisibilityTimeout = models.CurrentEnvironment.QueueAttributeDefaults.VisibilityTimeout } if queue.MessageRetentionPeriod == 0 { queue.MessageRetentionPeriod = models.CurrentEnvironment.QueueAttributeDefaults.MessageRetentionPeriod } models.SyncQueues.Queues[queue.Name] = &models.Queue{ Name: queue.Name, VisibilityTimeout: queue.VisibilityTimeout, Arn: queueArn, URL: queueUrl, ReceiveMessageWaitTimeSeconds: queue.ReceiveMessageWaitTimeSeconds, MaximumMessageSize: queue.MaximumMessageSize, MessageRetentionPeriod: queue.MessageRetentionPeriod, IsFIFO: utils.HasFIFOQueueName(queue.Name), EnableDuplicates: models.CurrentEnvironment.EnableDuplicates, Duplicates: make(map[string]time.Time), } } // Второй проход — устанавливаем RedrivePolicy, чтобы DLQ были доступны независимо от порядка for _, queue := range envs[env].Queues { q := models.SyncQueues.Queues[queue.Name] if queue.RedrivePolicy != "" { err := setQueueRedrivePolicy(models.SyncQueues.Queues, q, queue.RedrivePolicy) if err != nil { log.Errorf("err: %s", err) return ports } } } models.SyncQueues.Unlock() return ports } func setQueueRedrivePolicy(queues map[string]*models.Queue, q *models.Queue, strRedrivePolicy string) error { // Поддерживаем maxReceiveCount как int и как string (AWS SDK использует string) redrivePolicy1 := struct { MaxReceiveCount int `json:"maxReceiveCount"` DeadLetterTargetArn string `json:"deadLetterTargetArn"` }{} redrivePolicy2 := struct { MaxReceiveCount string `json:"maxReceiveCount"` DeadLetterTargetArn string `json:"deadLetterTargetArn"` }{} err1 := json.Unmarshal([]byte(strRedrivePolicy), &redrivePolicy1) err2 := json.Unmarshal([]byte(strRedrivePolicy), &redrivePolicy2) maxReceiveCount := redrivePolicy1.MaxReceiveCount deadLetterQueueArn := redrivePolicy1.DeadLetterTargetArn if err1 != nil && err2 != nil { return fmt.Errorf("invalid json for queue redrive policy ") } else if err1 != nil { maxReceiveCount, _ = strconv.Atoi(redrivePolicy2.MaxReceiveCount) deadLetterQueueArn = redrivePolicy2.DeadLetterTargetArn } if (deadLetterQueueArn != "" && maxReceiveCount == 0) || (deadLetterQueueArn == "" && maxReceiveCount != 0) { return fmt.Errorf("invalid redrive policy values") } dlt := strings.Split(deadLetterQueueArn, ":") deadLetterQueueName := dlt[len(dlt)-1] deadLetterQueue, ok := queues[deadLetterQueueName] if !ok { return fmt.Errorf("deadletter queue not found") } q.DeadLetterQueue = deadLetterQueue q.MaxReceiveCount = maxReceiveCount return nil }