@@ -8,8 +8,11 @@ import (
88
99 "github.com/google/uuid"
1010
11+ "github.com/pgEdge/control-plane/server/internal/config"
1112 "github.com/pgEdge/control-plane/server/internal/host"
13+ "github.com/pgEdge/control-plane/server/internal/ports"
1214 "github.com/pgEdge/control-plane/server/internal/storage"
15+ "github.com/pgEdge/control-plane/server/internal/utils"
1316)
1417
1518var (
@@ -24,16 +27,26 @@ var (
2427)
2528
2629type Service struct {
30+ cfg config.Config
2731 orchestrator Orchestrator
2832 store * Store
2933 hostSvc * host.Service
34+ portsSvc * ports.Service
3035}
3136
32- func NewService (orchestrator Orchestrator , store * Store , hostSvc * host.Service ) * Service {
37+ func NewService (
38+ cfg config.Config ,
39+ orchestrator Orchestrator ,
40+ store * Store ,
41+ hostSvc * host.Service ,
42+ portsSvc * ports.Service ,
43+ ) * Service {
3344 return & Service {
45+ cfg : cfg ,
3446 orchestrator : orchestrator ,
3547 store : store ,
3648 hostSvc : hostSvc ,
49+ portsSvc : portsSvc ,
3750 }
3851}
3952
@@ -119,6 +132,18 @@ func (s *Service) UpdateDatabase(ctx context.Context, state DatabaseState, spec
119132}
120133
121134func (s * Service ) DeleteDatabase (ctx context.Context , databaseID string ) error {
135+ specs , err := s .store .InstanceSpec .
136+ GetByDatabaseID (databaseID ).
137+ Exec (ctx )
138+ if err != nil {
139+ return fmt .Errorf ("failed to get instance specs: %w" , err )
140+ }
141+ for _ , spec := range specs {
142+ if err := s .releaseInstancePorts (ctx , spec .Spec ); err != nil {
143+ return err
144+ }
145+ }
146+
122147 var ops []storage.TxnOperation
123148
124149 spec , err := s .store .Spec .GetByKey (databaseID ).Exec (ctx )
@@ -141,6 +166,7 @@ func (s *Service) DeleteDatabase(ctx context.Context, databaseID string) error {
141166
142167 ops = append (ops ,
143168 s .store .Instance .DeleteByDatabaseID (databaseID ),
169+ s .store .InstanceSpec .DeleteByDatabaseID (databaseID ),
144170 s .store .InstanceStatus .DeleteByDatabaseID (databaseID ),
145171 )
146172
@@ -279,7 +305,7 @@ func (s *Service) DeleteInstance(ctx context.Context, databaseID, instanceID str
279305 return fmt .Errorf ("failed to delete stored instance status: %w" , err )
280306 }
281307
282- return nil
308+ return s . DeleteInstanceSpec ( ctx , databaseID , instanceID )
283309}
284310
285311func (s * Service ) UpdateInstanceStatus (
@@ -490,6 +516,112 @@ func (s *Service) PopulateSpecDefaults(ctx context.Context, spec *Spec) error {
490516 return nil
491517}
492518
519+ func (s * Service ) ReconcileInstanceSpec (ctx context.Context , spec * InstanceSpec ) (* InstanceSpec , error ) {
520+ if s .cfg .HostID != spec .HostID {
521+ return nil , fmt .Errorf ("this instance belongs to another host - this host='%s', instance host='%s'" , s .cfg .HostID , spec .HostID )
522+ }
523+ current , err := s .store .InstanceSpec .
524+ GetByKey (spec .DatabaseID , spec .InstanceID ).
525+ Exec (ctx )
526+ switch {
527+ case errors .Is (err , storage .ErrNotFound ):
528+ // Nothing to copy from, so we'll just generate any newly-needed random
529+ // ports.
530+ case err != nil :
531+ return nil , fmt .Errorf ("failed to get current spec for instance '%s': %w" , spec .InstanceID , err )
532+ default :
533+ spec .CopySettingsFrom (current .Spec )
534+ }
535+
536+ if spec .Port != nil && * spec .Port == 0 {
537+ port , err := s .portsSvc .AllocatePort (ctx , spec .HostID )
538+ if err != nil {
539+ return nil , fmt .Errorf ("failed to allocate port: %w" , err )
540+ }
541+ spec .Port = utils .PointerTo (port )
542+ }
543+ if spec .PatroniPort != nil && * spec .PatroniPort == 0 {
544+ port , err := s .portsSvc .AllocatePort (ctx , spec .HostID )
545+ if err != nil {
546+ return nil , fmt .Errorf ("failed to allocate patroni port: %w" , err )
547+ }
548+ spec .PatroniPort = utils .PointerTo (port )
549+ }
550+
551+ err = s .store .InstanceSpec .
552+ Put (& StoredInstanceSpec {Spec : spec }).
553+ Exec (ctx )
554+ if err != nil {
555+ return nil , fmt .Errorf ("failed to persist updated instance spec: %w" , err )
556+ }
557+
558+ if current != nil {
559+ if portShouldBeReleased (current .Spec .Port , spec .Port ) {
560+ err := s .portsSvc .ReleasePortIfDefined (ctx , spec .HostID , current .Spec .Port )
561+ if err != nil {
562+ return nil , fmt .Errorf ("failed to release previous port: %w" , err )
563+ }
564+ }
565+ if portShouldBeReleased (current .Spec .PatroniPort , spec .PatroniPort ) {
566+ err := s .portsSvc .ReleasePortIfDefined (ctx , spec .HostID , current .Spec .PatroniPort )
567+ if err != nil {
568+ return nil , fmt .Errorf ("failed to release previous patroni port: %w" , err )
569+ }
570+ }
571+ }
572+
573+ return spec , nil
574+ }
575+
576+ func (s * Service ) DeleteInstanceSpec (ctx context.Context , databaseID , instanceID string ) error {
577+ spec , err := s .store .InstanceSpec .
578+ GetByKey (databaseID , instanceID ).
579+ Exec (ctx )
580+ if errors .Is (err , storage .ErrNotFound ) {
581+ // Spec has already been deleted
582+ return nil
583+ } else if err != nil {
584+ return fmt .Errorf ("failed to check if instance spec exists: %w" , err )
585+ }
586+
587+ if err := s .releaseInstancePorts (ctx , spec .Spec ); err != nil {
588+ return err
589+ }
590+
591+ _ , err = s .store .InstanceSpec .
592+ DeleteByKey (databaseID , instanceID ).
593+ Exec (ctx )
594+ if err != nil {
595+ return fmt .Errorf ("failed to delete instance spec: %w" , err )
596+ }
597+
598+ return nil
599+ }
600+
601+ func (s * Service ) releaseInstancePorts (ctx context.Context , spec * InstanceSpec ) error {
602+ err := s .portsSvc .ReleasePortIfDefined (ctx , spec .HostID , spec .Port , spec .PatroniPort )
603+ if err != nil {
604+ return fmt .Errorf ("failed to release ports for instance '%s': %w" , spec .InstanceID , err )
605+ }
606+
607+ return nil
608+ }
609+
610+ func portShouldBeReleased (current * int , new * int ) bool {
611+ if current == nil || * current == 0 {
612+ // we didn't previously have an assigned port
613+ return false
614+ }
615+ if new == nil || * current != * new {
616+ // we had a previously assigned port and now the port is either nil or
617+ // different
618+ return true
619+ }
620+
621+ // the current and new ports are equal, so it should not be released.
622+ return false
623+ }
624+
493625func ValidateChangedSpec (current , updated * Spec ) error {
494626 var errs []error
495627
0 commit comments