|
5 | 5 | "fmt" |
6 | 6 | "strings" |
7 | 7 | "text/template" |
| 8 | + "time" |
8 | 9 |
|
9 | 10 | "github.com/CodeMonkeyCybersecurity/eos/pkg/authentik" |
10 | 11 | "github.com/CodeMonkeyCybersecurity/eos/pkg/eos_io" |
@@ -199,41 +200,13 @@ func EnableDefaultFlows(rc *eos_io.RuntimeContext, cfg *DefaultFlowsConfig) erro |
199 | 200 | zap.Error(err)) |
200 | 201 | } else { |
201 | 202 | // CRITICAL: Brand API requires flow UUIDs, not slugs |
202 | | - // Lookup each flow to get its PK (UUID) |
203 | | - authFlow, err := client.GetFlow(rc.Ctx, fmt.Sprintf("%s-authentication", appSlug)) |
204 | | - if err != nil { |
205 | | - logger.Warn("Failed to lookup authentication flow UUID", |
206 | | - zap.String("slug", fmt.Sprintf("%s-authentication", appSlug)), |
207 | | - zap.Error(err)) |
208 | | - } |
209 | | - |
210 | | - enrollmentFlow, err := client.GetFlow(rc.Ctx, fmt.Sprintf("%s-enrollment", appSlug)) |
211 | | - if err != nil { |
212 | | - logger.Warn("Failed to lookup enrollment flow UUID", |
213 | | - zap.String("slug", fmt.Sprintf("%s-enrollment", appSlug)), |
214 | | - zap.Error(err)) |
215 | | - } |
216 | | - |
217 | | - invalidationGlobalFlow, err := client.GetFlow(rc.Ctx, fmt.Sprintf("%s-invalidation-global", appSlug)) |
218 | | - if err != nil { |
219 | | - logger.Warn("Failed to lookup invalidation (global) flow UUID", |
220 | | - zap.String("slug", fmt.Sprintf("%s-invalidation-global", appSlug)), |
221 | | - zap.Error(err)) |
222 | | - } |
223 | | - |
224 | | - recoveryFlow, err := client.GetFlow(rc.Ctx, fmt.Sprintf("%s-recovery", appSlug)) |
225 | | - if err != nil { |
226 | | - logger.Warn("Failed to lookup recovery flow UUID", |
227 | | - zap.String("slug", fmt.Sprintf("%s-recovery", appSlug)), |
228 | | - zap.Error(err)) |
229 | | - } |
230 | | - |
231 | | - unenrollmentFlow, err := client.GetFlow(rc.Ctx, fmt.Sprintf("%s-unenrollment", appSlug)) |
232 | | - if err != nil { |
233 | | - logger.Warn("Failed to lookup unenrollment flow UUID", |
234 | | - zap.String("slug", fmt.Sprintf("%s-unenrollment", appSlug)), |
235 | | - zap.Error(err)) |
236 | | - } |
| 203 | + // Lookup each flow to get its PK (UUID) with retry logic for eventual consistency |
| 204 | + // RATIONALE: Freshly imported flows may not be immediately queryable due to Authentik indexing |
| 205 | + authFlow := getFlowWithRetry(rc, client, logger, fmt.Sprintf("%s-authentication", appSlug), 3, 2*time.Second) |
| 206 | + enrollmentFlow := getFlowWithRetry(rc, client, logger, fmt.Sprintf("%s-enrollment", appSlug), 3, 2*time.Second) |
| 207 | + invalidationGlobalFlow := getFlowWithRetry(rc, client, logger, fmt.Sprintf("%s-invalidation-global", appSlug), 3, 2*time.Second) |
| 208 | + recoveryFlow := getFlowWithRetry(rc, client, logger, fmt.Sprintf("%s-recovery", appSlug), 3, 2*time.Second) |
| 209 | + unenrollmentFlow := getFlowWithRetry(rc, client, logger, fmt.Sprintf("%s-unenrollment", appSlug), 3, 2*time.Second) |
237 | 210 |
|
238 | 211 | // Only update brand if we successfully looked up all flow UUIDs |
239 | 212 | if authFlow != nil && enrollmentFlow != nil && invalidationGlobalFlow != nil && recoveryFlow != nil && unenrollmentFlow != nil { |
@@ -989,3 +962,42 @@ entries: |
989 | 962 | enabled: true |
990 | 963 | timeout: 30 |
991 | 964 | ` |
| 965 | + |
| 966 | +// getFlowWithRetry attempts to retrieve a flow with retry logic for eventual consistency |
| 967 | +// RATIONALE: Freshly imported flows may not be immediately queryable in Authentik |
| 968 | +// This is a timing issue - the flow exists but the index hasn't updated yet |
| 969 | +func getFlowWithRetry(rc *eos_io.RuntimeContext, client *authentik.APIClient, logger otelzap.LoggerWithCtx, slug string, maxRetries int, retryDelay time.Duration) *authentik.FlowResponse { |
| 970 | + for attempt := 1; attempt <= maxRetries; attempt++ { |
| 971 | + flow, err := client.GetFlow(rc.Ctx, slug) |
| 972 | + if err != nil { |
| 973 | + logger.Warn("Failed to lookup flow UUID", |
| 974 | + zap.String("slug", slug), |
| 975 | + zap.Int("attempt", attempt), |
| 976 | + zap.Int("max_retries", maxRetries), |
| 977 | + zap.Error(err)) |
| 978 | + } else if flow != nil { |
| 979 | + // Success - flow found |
| 980 | + logger.Debug("Found flow UUID", |
| 981 | + zap.String("slug", slug), |
| 982 | + zap.String("uuid", flow.PK), |
| 983 | + zap.Int("attempt", attempt)) |
| 984 | + return flow |
| 985 | + } |
| 986 | + |
| 987 | + // Flow not found yet (flow == nil, err == nil means "not found" per GetFlow contract) |
| 988 | + if attempt < maxRetries { |
| 989 | + logger.Debug("Flow not found yet, waiting before retry", |
| 990 | + zap.String("slug", slug), |
| 991 | + zap.Int("attempt", attempt), |
| 992 | + zap.Int("max_retries", maxRetries), |
| 993 | + zap.Duration("retry_delay", retryDelay)) |
| 994 | + time.Sleep(retryDelay) |
| 995 | + } |
| 996 | + } |
| 997 | + |
| 998 | + // All retries exhausted |
| 999 | + logger.Warn("Flow not found after all retries", |
| 1000 | + zap.String("slug", slug), |
| 1001 | + zap.Int("max_retries", maxRetries)) |
| 1002 | + return nil |
| 1003 | +} |
0 commit comments