-
Notifications
You must be signed in to change notification settings - Fork 9
/
tenant.go
121 lines (98 loc) · 2.63 KB
/
tenant.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package main
import (
"context"
"fmt"
"io"
"log"
"os"
"time"
"gopkg.in/yaml.v3"
)
type tenantsSetter interface {
SetTenants(tenants []string)
}
type tenantsReader func() ([]string, error)
// newTenantsFileReloader reloads tenants at a given interval and sets them on the given tenantsSetter.
// It returns an error if the tenants file cannot be read 3 times in a row.
// It stops reloading when the context is cancelled.
func newTenantsFileReloader(ctx context.Context, readTenants tenantsReader, interval time.Duration, tenset tenantsSetter) error {
var tenants []string
var err error
interval = min(interval, 1*time.Minute)
ticker := time.NewTicker(interval)
defer ticker.Stop()
// Count successive errors and fail if we get 3 in a row.
var errorCount uint
for {
select {
case <-ticker.C:
tenants, err = readTenants()
if err != nil {
log.Printf("failed to read tenants file: %v", err)
errorCount++
if errorCount >= 3 {
return fmt.Errorf("failed to read tenants file 3 times in a row")
}
continue
} else {
errorCount = 0
}
tenset.SetTenants(tenants)
case <-ctx.Done():
log.Printf("tenants file reloader exiting: %v", ctx.Err())
return nil
}
}
}
// readTenantsFile reads tenants from a file.
func readTenantsFile(file string) ([]string, error) {
f, err := os.Open(file)
if err != nil {
return nil, fmt.Errorf("failed to open tenants file: %w", err)
}
defer f.Close()
fileData, err := io.ReadAll(f)
if err != nil {
return nil, fmt.Errorf("failed to read tenants file: %w", err)
}
return readTenantsConfig(fileData)
}
type TenantsConfig struct {
Tenants []TenantConfig `yaml:"tenants"`
}
type TenantConfig struct {
ID string `yaml:"id"`
}
func readTenantsConfig(f []byte) ([]string, error) {
if len(f) == 0 {
return nil, fmt.Errorf("no tenants found in file")
}
tenantsCfg := &TenantsConfig{}
err := yaml.Unmarshal(f, tenantsCfg)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal tenants file: %w", err)
}
tenants := make([]string, 0, len(tenantsCfg.Tenants))
for _, tenant := range tenantsCfg.Tenants {
tenants = append(tenants, tenant.ID)
}
if len(tenants) == 0 {
return nil, fmt.Errorf("no tenants found in file")
}
// check for duplicates
tenantsSet := make(map[string]struct{}, len(tenants))
duplicates := []string{}
for _, tenant := range tenants {
if tenant != "" {
if _, ok := tenantsSet[tenant]; ok {
duplicates = append(duplicates, tenant)
continue
}
tenantsSet[tenant] = struct{}{}
}
}
if len(duplicates) > 0 {
return nil, fmt.Errorf("found duplicate tenants in file: %v", duplicates)
}
return tenants, nil
}