github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/core/service/manager.go (about) 1 /* 2 * Copyright (C) 2018 The "MysteriumNetwork/node" Authors. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package service 19 20 import ( 21 "fmt" 22 "time" 23 24 "github.com/gofrs/uuid" 25 "github.com/pkg/errors" 26 "github.com/rs/zerolog/log" 27 28 "github.com/mysteriumnetwork/node/core/location/locationstate" 29 "github.com/mysteriumnetwork/node/core/policy" 30 "github.com/mysteriumnetwork/node/core/policy/localcopy" 31 "github.com/mysteriumnetwork/node/core/service/servicestate" 32 "github.com/mysteriumnetwork/node/identity" 33 "github.com/mysteriumnetwork/node/market" 34 "github.com/mysteriumnetwork/node/p2p" 35 "github.com/mysteriumnetwork/node/services/datatransfer" 36 "github.com/mysteriumnetwork/node/services/dvpn" 37 "github.com/mysteriumnetwork/node/services/scraping" 38 "github.com/mysteriumnetwork/node/services/wireguard" 39 "github.com/mysteriumnetwork/node/session/connectivity" 40 "github.com/mysteriumnetwork/node/utils/netutil" 41 "github.com/mysteriumnetwork/node/utils/reftracker" 42 ) 43 44 var ( 45 // ErrorLocation error indicates that action (i.e. disconnect) 46 ErrorLocation = errors.New("failed to detect service location") 47 // ErrUnsupportedServiceType indicates that manager tried to create an unsupported service type 48 ErrUnsupportedServiceType = errors.New("unsupported service type") 49 // ErrUnsupportedAccessPolicy indicates that manager tried to create service with unsupported access policy 50 ErrUnsupportedAccessPolicy = errors.New("unsupported access policy") 51 ) 52 53 const ( 54 channelIdleTimeout = 1 * time.Minute 55 ) 56 57 // Service interface represents pluggable Mysterium service 58 type Service interface { 59 Serve(instance *Instance) error 60 Stop() error 61 ConfigProvider 62 } 63 64 // DiscoveryFactory initiates instance which is able announce service discoverability 65 type DiscoveryFactory func() Discovery 66 67 // Discovery registers the service to the discovery api periodically 68 type Discovery interface { 69 Start(ownIdentity identity.Identity, proposal func() market.ServiceProposal) 70 Stop() 71 Wait() 72 } 73 74 // LocationResolver detects location for service proposal. 75 type locationResolver interface { 76 DetectLocation() (locationstate.Location, error) 77 } 78 79 // WaitForNATHole blocks until NAT hole is punched towards consumer through local NAT or until hole punching failed 80 type WaitForNATHole func() error 81 82 // NewManager creates new instance of pluggable instances manager 83 func NewManager( 84 serviceRegistry *Registry, 85 discoveryFactory DiscoveryFactory, 86 eventPublisher Publisher, 87 policyOracle *localcopy.Oracle, 88 policyProvider policy.Provider, 89 p2pListener p2p.Listener, 90 sessionManager func(service *Instance, channel p2p.Channel) *SessionManager, 91 statusStorage connectivity.StatusStorage, 92 location locationResolver, 93 ) *Manager { 94 return &Manager{ 95 serviceRegistry: serviceRegistry, 96 servicePool: NewPool(eventPublisher), 97 discoveryFactory: discoveryFactory, 98 eventPublisher: eventPublisher, 99 policyOracle: policyOracle, 100 policyProvider: policyProvider, 101 p2pListener: p2pListener, 102 sessionManager: sessionManager, 103 statusStorage: statusStorage, 104 location: location, 105 } 106 } 107 108 // Manager entrypoint which knows how to start pluggable Mysterium instances 109 type Manager struct { 110 serviceRegistry *Registry 111 servicePool *Pool 112 113 discoveryFactory DiscoveryFactory 114 eventPublisher Publisher 115 policyOracle *localcopy.Oracle 116 policyProvider policy.Provider 117 118 p2pListener p2p.Listener 119 sessionManager func(service *Instance, channel p2p.Channel) *SessionManager 120 statusStorage connectivity.StatusStorage 121 location locationResolver 122 } 123 124 // Start starts an instance of the given service type if knows one in service registry. 125 // It passes the options to the start method of the service. 126 // If an error occurs in the underlying service, the error is then returned. 127 func (manager *Manager) Start(providerID identity.Identity, serviceType string, policyIDs []string, options Options) (id ID, err error) { 128 log.Debug().Fields(map[string]interface{}{ 129 "providerID": providerID.Address, 130 "serviceType": serviceType, 131 "policyIDs": policyIDs, 132 "options": options, 133 }).Msg("Starting service") 134 service, err := manager.serviceRegistry.Create(serviceType, options) 135 if err != nil { 136 return id, err 137 } 138 139 accessPolicies := manager.policyOracle.Policies(policyIDs) 140 var policyProvider policy.Provider 141 if len(policyIDs) == 1 && policyIDs[0] == "mysterium" { 142 policyProvider = manager.policyProvider 143 } else { 144 policyRules := localcopy.NewRepository() 145 if len(policyIDs) > 0 { 146 if err = manager.policyOracle.SubscribePolicies(accessPolicies, policyRules); err != nil { 147 log.Warn().Err(err).Msg("Can't find given access policyOracle") 148 return id, ErrUnsupportedAccessPolicy 149 } 150 } 151 policyProvider = policyRules 152 } 153 154 location, err := manager.location.DetectLocation() 155 if err != nil { 156 return "", err 157 } 158 159 proposal := market.NewProposal(providerID.Address, serviceType, market.NewProposalOpts{ 160 Location: market.NewLocation(location), 161 AccessPolicies: accessPolicies, 162 Contacts: []market.Contact{manager.p2pListener.GetContact()}, 163 }) 164 165 discovery := manager.discoveryFactory() 166 167 id, err = generateID() 168 if err != nil { 169 return id, err 170 } 171 172 instance := &Instance{ 173 ID: id, 174 ProviderID: providerID, 175 Type: serviceType, 176 state: servicestate.Starting, 177 Options: options, 178 service: service, 179 Proposal: proposal, 180 policyProvider: policyProvider, 181 discovery: discovery, 182 eventPublisher: manager.eventPublisher, 183 location: manager.location, 184 } 185 186 discovery.Start(providerID, instance.proposalWithCurrentLocation) 187 188 channelHandlers := func(ch p2p.Channel) { 189 chID := "channel:" + ch.ID() 190 log.Info().Msgf("tracking p2p.Channel: %q", chID) 191 reftracker.Singleton().Put(chID, channelIdleTimeout, func() { 192 log.Debug().Msgf("collecting unused p2p.Channel %q", chID) 193 ch.Close() 194 }) 195 instance.addP2PChannel(ch) 196 mng := manager.sessionManager(instance, ch) 197 subscribeSessionCreate(mng, ch) 198 subscribeSessionStatus(ch, manager.statusStorage) 199 subscribeSessionAcknowledge(mng, ch) 200 subscribeSessionDestroy(mng, ch) 201 subscribeSessionPayments(mng, ch) 202 } 203 stopP2PListener, err := manager.p2pListener.Listen(providerID, serviceType, channelHandlers) 204 if err != nil { 205 return id, fmt.Errorf("could not subscribe to p2p channels: %w", err) 206 } 207 208 manager.servicePool.Add(instance) 209 210 go func() { 211 instance.setState(servicestate.Running) 212 213 serveErr := service.Serve(instance) 214 if serveErr != nil { 215 log.Error().Err(serveErr).Msg("Service serve failed") 216 } 217 218 stopP2PListener() 219 220 stopErr := manager.servicePool.Stop(id) 221 if stopErr != nil { 222 log.Error().Err(stopErr).Msg("Service stop failed") 223 } 224 225 discovery.Wait() 226 }() 227 228 netutil.LogNetworkStats() 229 230 return id, nil 231 } 232 233 func generateID() (ID, error) { 234 uid, err := uuid.NewV4() 235 if err != nil { 236 return ID(""), err 237 } 238 return ID(uid.String()), nil 239 } 240 241 // List returns array of service instances. 242 func (manager *Manager) List(includeAll bool) []*Instance { 243 runningInstances := manager.servicePool.List() 244 if !includeAll { 245 return runningInstances 246 } 247 248 added := map[string]bool{ 249 wireguard.ServiceType: false, 250 scraping.ServiceType: false, 251 datatransfer.ServiceType: false, 252 dvpn.ServiceType: false, 253 } 254 255 result := make([]*Instance, 0, len(added)) 256 for _, instance := range runningInstances { 257 result = append(result, instance) 258 added[instance.Type] = true 259 } 260 261 for serviceType, alreadyAdded := range added { 262 if alreadyAdded { 263 continue 264 } 265 266 result = append(result, &Instance{ 267 Type: serviceType, 268 state: servicestate.NotRunning, 269 }) 270 } 271 272 return result 273 } 274 275 // Kill stops all services. 276 func (manager *Manager) Kill() error { 277 return manager.servicePool.StopAll() 278 } 279 280 // Stop stops the service. 281 func (manager *Manager) Stop(id ID) error { 282 err := manager.servicePool.Stop(id) 283 if err != nil { 284 return err 285 } 286 287 return nil 288 } 289 290 // Service returns a service instance by requested id. 291 func (manager *Manager) Service(id ID) *Instance { 292 return manager.servicePool.Instance(id) 293 }