package orchestrator import ( "context" "log" "provisioning/internal/config" "provisioning/internal/infra" "provisioning/internal/model" "provisioning/internal/statemachine" "provisioning/internal/store" ) type HostOrchestrator struct { Runner *Runner Hosts *store.Hosts Ops *store.Operations Locks *store.Locks Cluster *ClusterJoiner InfraClient *infra.Client Config *config.Config ServerTypes *config.ServerTypeRegistry } func (o *HostOrchestrator) HandlePhoneHome(ctx context.Context, hostID int64, ip string, hardwareID string) { if err := o.Hosts.UpdateIP(ctx, hostID, ip, hardwareID); err != nil { log.Printf("host %d: failed to update IP: %v", hostID, err) o.Runner.FailHost(ctx, hostID, "failed to update IP: "+err.Error()) return } if _, err := o.Runner.Transition(ctx, hostID, statemachine.TriggerPhoneHome); err != nil { log.Printf("host %d: phone-home transition failed: %v", hostID, err) return } go o.postPhoneHome(hostID, ip, hardwareID) } func (o *HostOrchestrator) postPhoneHome(hostID int64, ip string, hardwareID string) { ctx := context.Background() host, err := o.Hosts.Get(ctx, hostID) if err != nil { log.Printf("host %d: failed to get host for cluster join: %v", hostID, err) o.Runner.FailHost(ctx, hostID, "get host: "+err.Error()) return } if _, err := o.Runner.Transition(ctx, hostID, statemachine.TriggerClusterJoinStart); err != nil { log.Printf("host %d: cluster join start transition failed: %v", hostID, err) return } if err := o.Cluster.Join(ctx, ip); err != nil { log.Printf("host %d: cluster join failed: %v", hostID, err) o.Runner.FailHost(ctx, hostID, "cluster join: "+err.Error()) return } if err := o.registerInfra(ctx, host, ip, hardwareID); err != nil { log.Printf("host %d: infra registration failed: %v", hostID, err) o.Runner.FailHost(ctx, hostID, "infra registration: "+err.Error()) return } if _, err := o.Runner.Transition(ctx, hostID, statemachine.TriggerJoinComplete); err != nil { log.Printf("host %d: join complete transition failed: %v", hostID, err) return } op, err := o.Ops.GetActive(ctx, hostID) if err == nil { _ = o.Ops.Complete(ctx, op.ID) } _ = o.Locks.Release(ctx, hostID) log.Printf("host %d (%s): provisioning complete", hostID, host.Hostname) } func (o *HostOrchestrator) registerInfra(ctx context.Context, host *model.Host, ip string, hardwareID string) error { if o.InfraClient == nil { return nil } st, _ := o.ServerTypes.Get(host.ServerType) serverTypeID := o.Config.Infrastructure.ServerTypeMap[host.ServerType] infraID, err := o.InfraClient.CreateHost(ctx, infra.CreateHostRequest{ HardwareID: hardwareID, Hostname: host.Hostname, AssetID: host.Hostname, RoomID: o.Config.Infrastructure.RoomID, ServerTypeID: serverTypeID, }) if err != nil { return err } if err := o.Hosts.UpdateInfraID(ctx, host.ID, infraID); err != nil { return err } _ = o.InfraClient.CreateInterface(ctx, infra.CreateInterfaceRequest{ HostID: int(infraID), Name: st.ManagementNIC, MACAddress: host.MAC, IPAddress: ip, }) return nil }