package main import ( "fmt" "strings" ) // seededNamespaces stores created namespace names for later use var seededNamespaces []string // seededProviderGroups stores created provider groups for later use var seededProviderGroups []ProviderGroupResponse // seededProviders stores created providers (API keys) per group for usage samples var seededProviders map[uint][]APIKeyResponse // seededMasters stores created masters for later use var seededMasters []MasterResponse // seededKeys stores created keys per master for usage samples var seededKeys map[uint][]KeyResponse // matchesSeederTag checks if a string contains the current seeder tag func (s *Seeder) matchesSeederTag(text string) bool { return strings.Contains(text, s.seederTag) } // matchesSeederPrefix checks if a name matches seeder naming convention func (s *Seeder) matchesSeederPrefix(name string) bool { return strings.HasPrefix(name, "demo-") || strings.HasPrefix(name, "seeder-") || strings.HasSuffix(name, "-demo") } func (s *Seeder) seedNamespaces() error { gen := NewGenerator(s.rng, s.seederTag, s.cfg.Profile) namespaces := gen.GenerateNamespaces(s.profile.Namespaces) // Get existing namespaces existing, err := s.client.ListNamespaces() if err != nil { if s.cfg.DryRun { existing = []NamespaceResponse{} fmt.Printf(" [DRY-RUN] Unable to list existing namespaces, proceeding anyway\n") } else { return fmt.Errorf("list namespaces: %w", err) } } existingMap := make(map[string]NamespaceResponse) for _, ns := range existing { existingMap[ns.Name] = ns } seededNamespaces = make([]string, 0, len(namespaces)) for _, ns := range namespaces { if existingNs, exists := existingMap[ns.Name]; exists { // Check if we should reset - only reset if tag matches if s.cfg.Reset && s.matchesSeederTag(existingNs.Description) { if err := s.client.DeleteNamespace(existingNs.ID); err != nil { return fmt.Errorf("delete namespace %s: %w", ns.Name, err) } if s.cfg.Verbose { fmt.Printf(" ✗ %s (deleted for reset)\n", ns.Name) } } else { if s.cfg.Verbose { fmt.Printf(" ○ %s (exists, skipped)\n", ns.Name) } s.summary.Namespaces.Skipped++ seededNamespaces = append(seededNamespaces, ns.Name) continue } } // Create namespace created, err := s.client.CreateNamespace(ns) if err != nil { if apiErr, ok := err.(*APIError); ok && apiErr.IsConflict() { if s.cfg.Verbose { fmt.Printf(" ○ %s (exists, skipped)\n", ns.Name) } s.summary.Namespaces.Skipped++ seededNamespaces = append(seededNamespaces, ns.Name) continue } return fmt.Errorf("create namespace %s: %w", ns.Name, err) } if s.cfg.Verbose || s.cfg.DryRun { fmt.Printf(" ✓ %s (created)\n", ns.Name) } s.summary.Namespaces.Created++ if created != nil { seededNamespaces = append(seededNamespaces, created.Name) } else { seededNamespaces = append(seededNamespaces, ns.Name) } } return nil } func (s *Seeder) seedProviderGroups() error { gen := NewGenerator(s.rng, s.seederTag, s.cfg.Profile) groups := gen.GenerateProviderGroups(s.profile.ProviderGroups) // Get existing provider groups existing, err := s.client.ListProviderGroups() if err != nil { if s.cfg.DryRun { existing = []ProviderGroupResponse{} fmt.Printf(" [DRY-RUN] Unable to list existing provider groups, proceeding anyway\n") } else { return fmt.Errorf("list provider groups: %w", err) } } existingMap := make(map[string]ProviderGroupResponse) for _, g := range existing { existingMap[g.Name] = g } seededProviderGroups = make([]ProviderGroupResponse, 0, len(groups)) for _, group := range groups { if existingGroup, exists := existingMap[group.Name]; exists { // Check if we should reset - match by seeder naming convention // Provider groups use name suffix like "openai-demo", "anthropic-demo" if s.cfg.Reset && s.matchesSeederPrefix(group.Name) { if err := s.client.DeleteProviderGroup(existingGroup.ID); err != nil { return fmt.Errorf("delete provider group %s: %w", group.Name, err) } if s.cfg.Verbose { fmt.Printf(" ✗ %s (deleted for reset)\n", group.Name) } } else { if s.cfg.Verbose { fmt.Printf(" ○ %s (exists, skipped)\n", group.Name) } s.summary.ProviderGroups.Skipped++ seededProviderGroups = append(seededProviderGroups, existingGroup) continue } } // Create provider group created, err := s.client.CreateProviderGroup(group) if err != nil { if apiErr, ok := err.(*APIError); ok && apiErr.IsConflict() { if s.cfg.Verbose { fmt.Printf(" ○ %s (exists, skipped)\n", group.Name) } s.summary.ProviderGroups.Skipped++ continue } return fmt.Errorf("create provider group %s: %w", group.Name, err) } if s.cfg.Verbose || s.cfg.DryRun { fmt.Printf(" ✓ %s (created)\n", group.Name) } s.summary.ProviderGroups.Created++ if created != nil { seededProviderGroups = append(seededProviderGroups, *created) } } return nil } func (s *Seeder) seedProviders() error { gen := NewGenerator(s.rng, s.seederTag, s.cfg.Profile) seededProviders = make(map[uint][]APIKeyResponse) for _, group := range seededProviderGroups { providers := gen.GenerateProviders(group.ID, group.Name, s.profile.ProvidersPerGroup) groupProviders := make([]APIKeyResponse, 0, len(providers)) createdCount := 0 for _, provider := range providers { created, err := s.client.CreateAPIKey(provider) if err != nil { if apiErr, ok := err.(*APIError); ok && apiErr.IsConflict() { s.summary.Providers.Skipped++ continue } return fmt.Errorf("create provider for group %s: %w", group.Name, err) } s.summary.Providers.Created++ createdCount++ if created != nil { groupProviders = append(groupProviders, *created) } } seededProviders[group.ID] = groupProviders if s.cfg.Verbose || s.cfg.DryRun { fmt.Printf(" ✓ %s: %d providers created\n", group.Name, createdCount) } } return nil } func (s *Seeder) seedModels() error { gen := NewGenerator(s.rng, s.seederTag, s.cfg.Profile) models := gen.GenerateModels(s.profile.Models) // Get existing models existing, err := s.client.ListModels() if err != nil { if s.cfg.DryRun { existing = []ModelResponse{} fmt.Printf(" [DRY-RUN] Unable to list existing models, proceeding anyway\n") } else { return fmt.Errorf("list models: %w", err) } } existingMap := make(map[string]ModelResponse) for _, m := range existing { existingMap[m.Name] = m } for _, model := range models { if existingModel, exists := existingMap[model.Name]; exists { // Models are shared resources - only reset if explicitly requested // and the model name matches seeder pattern (unlikely for real models) if s.cfg.Reset && s.matchesSeederPrefix(model.Name) { if err := s.client.DeleteModel(existingModel.ID); err != nil { return fmt.Errorf("delete model %s: %w", model.Name, err) } } else { if s.cfg.Verbose { fmt.Printf(" ○ %s (exists, skipped)\n", model.Name) } s.summary.Models.Skipped++ continue } } _, err := s.client.CreateModel(model) if err != nil { if apiErr, ok := err.(*APIError); ok && apiErr.IsConflict() { if s.cfg.Verbose { fmt.Printf(" ○ %s (exists, skipped)\n", model.Name) } s.summary.Models.Skipped++ continue } return fmt.Errorf("create model %s: %w", model.Name, err) } if s.cfg.Verbose || s.cfg.DryRun { fmt.Printf(" ✓ %s (created)\n", model.Name) } s.summary.Models.Created++ } if !s.cfg.Verbose && !s.cfg.DryRun { fmt.Printf(" ✓ %d models created\n", s.summary.Models.Created) } return nil } func (s *Seeder) seedBindings() error { gen := NewGenerator(s.rng, s.seederTag, s.cfg.Profile) bindings := gen.GenerateBindings(seededNamespaces, seededProviderGroups, s.profile.Bindings) // Get existing bindings existing, err := s.client.ListBindings() if err != nil { if s.cfg.DryRun { existing = []BindingResponse{} fmt.Printf(" [DRY-RUN] Unable to list existing bindings, proceeding anyway\n") } else { return fmt.Errorf("list bindings: %w", err) } } // Create a map of existing bindings by composite key existingMap := make(map[string]BindingResponse) for _, b := range existing { key := fmt.Sprintf("%s:%s:%d", b.Namespace, b.PublicModel, b.GroupID) existingMap[key] = b } for _, binding := range bindings { key := fmt.Sprintf("%s:%s:%d", binding.Namespace, binding.PublicModel, binding.GroupID) if existingBinding, exists := existingMap[key]; exists { // Only reset bindings in seeder namespaces if s.cfg.Reset && s.matchesSeederPrefix(binding.Namespace) { if err := s.client.DeleteBinding(existingBinding.ID); err != nil { return fmt.Errorf("delete binding: %w", err) } } else { s.summary.Bindings.Skipped++ continue } } _, err := s.client.CreateBinding(binding) if err != nil { if apiErr, ok := err.(*APIError); ok && apiErr.IsConflict() { s.summary.Bindings.Skipped++ continue } return fmt.Errorf("create binding: %w", err) } s.summary.Bindings.Created++ } if s.cfg.Verbose || s.cfg.DryRun { fmt.Printf(" ✓ %d bindings created\n", s.summary.Bindings.Created) } return nil } func (s *Seeder) seedMasters() error { gen := NewGenerator(s.rng, s.seederTag, s.cfg.Profile) masters := gen.GenerateMasters(s.profile.Masters) // Get existing masters existing, err := s.client.ListMasters() if err != nil { if s.cfg.DryRun { existing = []MasterResponse{} fmt.Printf(" [DRY-RUN] Unable to list existing masters, proceeding anyway\n") } else { return fmt.Errorf("list masters: %w", err) } } existingMap := make(map[string]MasterResponse) for _, m := range existing { existingMap[m.Name] = m } seededMasters = make([]MasterResponse, 0, len(masters)) for _, master := range masters { if existingMaster, exists := existingMap[master.Name]; exists { // Reset masters with seeder naming pattern if s.cfg.Reset && s.matchesSeederPrefix(master.Name) { if err := s.client.DeleteMaster(existingMaster.ID); err != nil { return fmt.Errorf("delete master %s: %w", master.Name, err) } if s.cfg.Verbose { fmt.Printf(" ✗ %s (deleted for reset)\n", master.Name) } } else { if s.cfg.Verbose { fmt.Printf(" ○ %s (exists, skipped)\n", master.Name) } s.summary.Masters.Skipped++ seededMasters = append(seededMasters, existingMaster) continue } } created, err := s.client.CreateMaster(master) if err != nil { if apiErr, ok := err.(*APIError); ok && apiErr.IsConflict() { if s.cfg.Verbose { fmt.Printf(" ○ %s (exists, skipped)\n", master.Name) } s.summary.Masters.Skipped++ continue } return fmt.Errorf("create master %s: %w", master.Name, err) } if s.cfg.Verbose || s.cfg.DryRun { fmt.Printf(" ✓ %s (created)\n", master.Name) } s.summary.Masters.Created++ if created != nil { seededMasters = append(seededMasters, *created) } } return nil } func (s *Seeder) seedKeys() error { gen := NewGenerator(s.rng, s.seederTag, s.cfg.Profile) seededKeys = make(map[uint][]KeyResponse) for _, master := range seededMasters { keys := gen.GenerateKeys(s.profile.KeysPerMaster) masterKeys := make([]KeyResponse, 0, len(keys)) createdCount := 0 for _, key := range keys { created, err := s.client.CreateKey(master.ID, key) if err != nil { if apiErr, ok := err.(*APIError); ok && apiErr.IsConflict() { s.summary.Keys.Skipped++ continue } return fmt.Errorf("create key for master %s: %w", master.Name, err) } s.summary.Keys.Created++ createdCount++ if created != nil { masterKeys = append(masterKeys, *created) } } seededKeys[master.ID] = masterKeys if s.cfg.Verbose || s.cfg.DryRun { fmt.Printf(" ✓ %s: %d keys created\n", master.Name, createdCount) } } return nil } func (s *Seeder) seedUsageSamples() error { gen := NewGenerator(s.rng, s.seederTag, s.cfg.Profile) ctx := UsageSampleContext{ Masters: seededMasters, Keys: seededKeys, Groups: seededProviderGroups, Providers: seededProviders, UsageDays: s.cfg.UsageDays, } logs := gen.GenerateUsageSamples(ctx) if len(logs) == 0 { fmt.Printf(" ○ No masters or groups to generate samples for\n") return nil } // Note about timestamps if s.cfg.Verbose { fmt.Printf(" ⚠ Note: Usage samples will have current timestamp (API does not support historical timestamps)\n") } // Batch insert logs batchSize := 100 for i := 0; i < len(logs); i += batchSize { end := i + batchSize if end > len(logs) { end = len(logs) } batch := logs[i:end] if err := s.client.CreateLogsBatch(batch); err != nil { return fmt.Errorf("create logs batch: %w", err) } } s.summary.UsageSamples = len(logs) if s.cfg.Verbose || s.cfg.DryRun { fmt.Printf(" ✓ %d days of data generated (%d log entries)\n", s.cfg.UsageDays, len(logs)) } return nil }