Skip to content

Commit 4665a2a

Browse files
committed
Update main.go
1 parent 60aaf9c commit 4665a2a

File tree

1 file changed

+62
-73
lines changed

1 file changed

+62
-73
lines changed

main.go

Lines changed: 62 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/cloudflare/cloudflare-go"
1212
"github.com/go-resty/resty/v2"
1313
"github.com/traefik/traefik/v3/pkg/muxer/http"
14-
1514
log "github.com/sirupsen/logrus"
1615
)
1716

@@ -20,11 +19,9 @@ func init() {
2019
DisableColors: true,
2120
FullTimestamp: true,
2221
})
23-
// Set log level based on environment variable or default to info
24-
logLevel := os.Getenv("LOG_LEVEL")
25-
if logLevel != "" {
26-
level, err := log.ParseLevel(logLevel)
27-
if err == nil {
22+
// Set log level from environment variable, default to info
23+
if logLevel := os.Getenv("LOG_LEVEL"); logLevel != "" {
24+
if level, err := log.ParseLevel(logLevel); err == nil {
2825
log.SetLevel(level)
2926
}
3027
}
@@ -40,20 +37,21 @@ func main() {
4037
"TRAEFIK_SERVICE_ENDPOINT",
4138
"TRAEFIK_API_ENDPOINT",
4239
}
43-
4440
for _, envVar := range requiredEnvVars {
4541
if os.Getenv(envVar) == "" {
4642
log.Fatalf("Required environment variable %s is not set", envVar)
4743
}
4844
}
4945

46+
// Initialize Cloudflare client
5047
cf, err := cloudflare.NewWithAPIToken(os.Getenv("CLOUDFLARE_API_TOKEN"))
5148
if err != nil {
52-
log.Fatal(err)
49+
log.Fatalf("Failed to initialize Cloudflare client: %v", err)
5350
}
5451

5552
ctx := context.Background()
5653

54+
// Initialize HTTP client for Traefik API
5755
client := resty.New().
5856
SetBaseURL(os.Getenv("TRAEFIK_API_ENDPOINT")).
5957
SetTimeout(10 * time.Second).
@@ -63,80 +61,78 @@ func main() {
6361
var cache []Router
6462
for poll := range pollCh {
6563
if poll.Err != nil {
66-
log.Fatal(poll.Err)
64+
log.Errorf("Polling error: %v", poll.Err)
65+
continue // Continue instead of fatal to keep the loop running
6766
}
6867

69-
// skip if no changes to traefik routers
68+
// Skip if no changes detected
7069
if reflect.DeepEqual(cache, poll.Routers) {
7170
continue
7271
}
7372

74-
log.Info("changes detected")
75-
76-
// update the cache
73+
log.Info("Traefik router changes detected")
7774
cache = poll.Routers
7875

79-
// Group routes by domain to avoid duplicates
76+
// Group routes by domain, avoiding duplicates
8077
domainRoutes := make(map[string]bool)
81-
8278
for _, r := range poll.Routers {
83-
// Only enabled routes
84-
if r.Status != "enabled" {
85-
log.Debugf("Skipping disabled route: %s", r.Rule)
79+
// Skip disabled or internal routers
80+
if r.Status != "enabled" || r.Provider == "internal" {
81+
log.Debugf("Skipping route: %s (status: %s, provider: %s)", r.Rule, r.Status, r.Provider)
8682
continue
8783
}
8884

89-
// Handle both HTTP and HTTPS routes
90-
// We want to include all routes, including those with TLS configured
91-
if os.Getenv("TRAEFIK_ENTRYPOINT") != "" {
92-
// If TRAEFIK_ENTRYPOINT is specified, only use routes with that entrypoint
93-
if !contains(r.EntryPoints, os.Getenv("TRAEFIK_ENTRYPOINT")) {
94-
log.Debugf("Skipping route with different entrypoint: %s", r.Rule)
85+
// Filter by entrypoint if specified
86+
if entrypoint := os.Getenv("TRAEFIK_ENTRYPOINT"); entrypoint != "" {
87+
if !contains(r.EntryPoints, entrypoint) {
88+
log.Debugf("Skipping route with mismatched entrypoint: %s", r.Rule)
9589
continue
9690
}
9791
}
9892

93+
// Parse domains from the rule
9994
domains, err := http.ParseDomains(r.Rule)
10095
if err != nil {
10196
log.Errorf("Failed to parse domains from rule %s: %v", r.Rule, err)
10297
continue
10398
}
10499

105-
// Add each domain only once
100+
// Log and add unique domains
106101
for _, domain := range domains {
107-
if _, exists := domainRoutes[domain]; !exists {
102+
if !domainRoutes[domain] {
108103
domainRoutes[domain] = true
104+
log.Debugf("Processing domain: %s from rule: %s", domain, r.Rule)
109105
}
110106
}
111107
}
112108

113-
// Create tunnel configuration with unique domains
109+
// Build Cloudflare tunnel ingress rules
114110
ingress := []cloudflare.UnvalidatedIngressRule{}
115111
for domain := range domainRoutes {
116112
log.WithFields(log.Fields{
117113
"domain": domain,
118114
"service": os.Getenv("TRAEFIK_SERVICE_ENDPOINT"),
119-
}).Info("upserting tunnel")
115+
}).Info("Adding tunnel ingress rule")
120116

121-
// Create a single ingress rule for the domain that handles all routes
122117
ingress = append(ingress, cloudflare.UnvalidatedIngressRule{
123118
Hostname: domain,
124119
Service: os.Getenv("TRAEFIK_SERVICE_ENDPOINT"),
125120
OriginRequest: &cloudflare.OriginRequestConfig{
126121
HTTPHostHeader: &domain,
127-
NoTLSVerify: boolPtr(true), // Skip TLS verification for internal traffic
122+
NoTLSVerify: boolPtr(true), // Skip TLS for internal traffic
128123
},
129124
})
130125
}
131126

132-
// add catch-all rule
127+
// Add catch-all rule
133128
ingress = append(ingress, cloudflare.UnvalidatedIngressRule{
134129
Service: "http_status:404",
135130
})
136131

137-
err = updateTunnels(ctx, cf, ingress)
138-
if err != nil {
139-
log.Fatal(err)
132+
// Update tunnel configuration
133+
if err := updateTunnels(ctx, cf, ingress); err != nil {
134+
log.Errorf("Failed to update tunnels: %v", err)
135+
continue
140136
}
141137
}
142138
}
@@ -146,18 +142,16 @@ func boolPtr(b bool) *bool {
146142
return &b
147143
}
148144

149-
func pollTraefikRouters(client *resty.Client) (ch chan PollResponse) {
150-
ch = make(chan PollResponse)
145+
// Poll Traefik routers periodically
146+
func pollTraefikRouters(client *resty.Client) chan PollResponse {
147+
ch := make(chan PollResponse)
151148
go func() {
152-
defer func() {
153-
close(ch)
154-
}()
149+
defer close(ch)
155150
r := rand.New(rand.NewSource(time.Now().UnixNano()))
156-
c := time.Tick(10 * time.Second)
151+
ticker := time.Tick(10 * time.Second)
157152

158-
for range c {
153+
for range ticker {
159154
var pollRes PollResponse
160-
161155
resp, err := client.R().
162156
EnableTrace().
163157
SetResult(&pollRes.Routers).
@@ -167,70 +161,69 @@ func pollTraefikRouters(client *resty.Client) (ch chan PollResponse) {
167161
if err != nil {
168162
log.Errorf("Error polling Traefik API: %v", err)
169163
ch <- pollRes
170-
time.Sleep(5 * time.Second) // Wait before retrying
164+
time.Sleep(5 * time.Second)
171165
continue
172166
}
173167

174168
if resp.StatusCode() != 200 {
175169
log.Errorf("Unexpected status code from Traefik API: %d", resp.StatusCode())
176170
pollRes.Err = fmt.Errorf("unexpected status code: %d", resp.StatusCode())
177171
ch <- pollRes
178-
time.Sleep(5 * time.Second) // Wait before retrying
172+
time.Sleep(5 * time.Second)
179173
continue
180174
}
181175

182176
ch <- pollRes
183-
184177
jitter := time.Duration(r.Int31n(5000)) * time.Millisecond
185178
time.Sleep(jitter)
186179
}
187180
}()
188-
return
181+
return ch
189182
}
190183

184+
// Update Cloudflare tunnel configuration and DNS records
191185
func updateTunnels(ctx context.Context, cf *cloudflare.API, ingress []cloudflare.UnvalidatedIngressRule) error {
192-
// Get Current tunnel config
193186
aid := cloudflare.AccountIdentifier(os.Getenv("CLOUDFLARE_ACCOUNT_ID"))
187+
188+
// Fetch current tunnel config
194189
cfg, err := cf.GetTunnelConfiguration(ctx, aid, os.Getenv("CLOUDFLARE_TUNNEL_ID"))
195190
if err != nil {
196-
return fmt.Errorf("unable to pull current tunnel config, %s", err.Error())
191+
return fmt.Errorf("failed to fetch tunnel config: %v", err)
197192
}
198193

199-
// Update config with new ingress rules
194+
// Update with new ingress rules
200195
cfg.Config.Ingress = ingress
201-
cfg, err = cf.UpdateTunnelConfiguration(ctx, aid, cloudflare.TunnelConfigurationParams{
196+
_, err = cf.UpdateTunnelConfiguration(ctx, aid, cloudflare.TunnelConfigurationParams{
202197
TunnelID: os.Getenv("CLOUDFLARE_TUNNEL_ID"),
203198
Config: cfg.Config,
204199
})
205200
if err != nil {
206-
return fmt.Errorf("unable to update tunnel config, %s", err.Error())
201+
return fmt.Errorf("failed to update tunnel config: %v", err)
207202
}
203+
log.Info("Tunnel configuration updated successfully")
208204

209-
log.Info("tunnel config updated")
210-
211-
// Update DNS to point to new tunnel
212-
for _, i := range ingress {
213-
if i.Hostname == "" {
205+
// Update DNS records
206+
for _, rule := range ingress {
207+
if rule.Hostname == "" {
214208
continue
215209
}
216210

217-
var proxied bool = true
218-
211+
proxied := true
219212
record := cloudflare.DNSRecord{
220213
Type: "CNAME",
221-
Name: i.Hostname,
214+
Name: rule.Hostname,
222215
Content: fmt.Sprintf("%s.cfargotunnel.com", os.Getenv("CLOUDFLARE_TUNNEL_ID")),
223216
TTL: 1,
224217
Proxied: &proxied,
225218
}
226219

227220
zid := cloudflare.ZoneIdentifier(os.Getenv("CLOUDFLARE_ZONE_ID"))
228-
r, _, err := cf.ListDNSRecords(ctx, zid, cloudflare.ListDNSRecordsParams{Name: i.Hostname})
221+
records, _, err := cf.ListDNSRecords(ctx, zid, cloudflare.ListDNSRecordsParams{Name: rule.Hostname})
229222
if err != nil {
230-
return fmt.Errorf("err checking DNS records, %s", err.Error())
223+
return fmt.Errorf("failed to list DNS records for %s: %v", rule.Hostname, err)
231224
}
232225

233-
if len(r) == 0 {
226+
if len(records) == 0 {
234227
_, err := cf.CreateDNSRecord(ctx, zid, cloudflare.CreateDNSRecordParams{
235228
Name: record.Name,
236229
Type: record.Type,
@@ -239,35 +232,31 @@ func updateTunnels(ctx context.Context, cf *cloudflare.API, ingress []cloudflare
239232
Proxied: record.Proxied,
240233
})
241234
if err != nil {
242-
return fmt.Errorf("unable to create DNS record, %s", err.Error())
235+
return fmt.Errorf("failed to create DNS record for %s: %v", rule.Hostname, err)
243236
}
244-
log.WithFields(log.Fields{
245-
"domain": record.Name,
246-
}).Info("DNS created")
237+
log.WithFields(log.Fields{"domain": record.Name}).Info("DNS record created")
247238
continue
248239
}
249240

250-
if r[0].Content != record.Content {
241+
if records[0].Content != record.Content {
251242
_, err = cf.UpdateDNSRecord(ctx, zid, cloudflare.UpdateDNSRecordParams{
252-
ID: r[0].ID, // Use the actual ID from the retrieved record
243+
ID: records[0].ID,
253244
Name: record.Name,
254245
Type: record.Type,
255246
Content: record.Content,
256247
TTL: record.TTL,
257248
Proxied: record.Proxied,
258249
})
259250
if err != nil {
260-
return fmt.Errorf("could not update record for %s, %s", i.Hostname, err)
251+
return fmt.Errorf("failed to update DNS record for %s: %v", rule.Hostname, err)
261252
}
262-
log.WithFields(log.Fields{
263-
"domain": record.Name,
264-
}).Info("DNS updated")
253+
log.WithFields(log.Fields{"domain": record.Name}).Info("DNS record updated")
265254
}
266255
}
267-
268256
return nil
269257
}
270258

259+
// Check if a slice contains a string
271260
func contains(s []string, e string) bool {
272261
for _, a := range s {
273262
if a == e {

0 commit comments

Comments
 (0)