]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/lib/friends.js
Try to make a better communication (between pods) module
[github/Chocobozzz/PeerTube.git] / server / lib / friends.js
CommitLineData
9f10b292
C
1'use strict'
2
f0f5567b
C
3const async = require('async')
4const config = require('config')
5const fs = require('fs')
6const request = require('request')
7
8const constants = require('../initializers/constants')
9const logger = require('../helpers/logger')
10const peertubeCrypto = require('../helpers/peertubeCrypto')
11const Pods = require('../models/pods')
e3647ae2 12const requestsScheduler = require('../lib/requestsScheduler')
f0f5567b 13const requests = require('../helpers/requests')
cbe2f7c3 14const videos = require('../lib/videos')
f0f5567b
C
15const Videos = require('../models/videos')
16
17const http = config.get('webserver.https') ? 'https' : 'http'
18const host = config.get('webserver.host')
19const port = config.get('webserver.port')
20
21const pods = {
9f10b292
C
22 addVideoToFriends: addVideoToFriends,
23 hasFriends: hasFriends,
cbe2f7c3 24 getMyCertificate: getMyCertificate,
9f10b292
C
25 makeFriends: makeFriends,
26 quitFriends: quitFriends,
528a9efa
C
27 removeVideoToFriends: removeVideoToFriends,
28 sendOwnedVideosToPod: sendOwnedVideosToPod
9f10b292
C
29}
30
31function addVideoToFriends (video) {
9f10b292
C
32 // ensure namePath is null
33 video.namePath = null
528a9efa
C
34
35 requestsScheduler.addRequest('add', video)
9f10b292
C
36}
37
38function hasFriends (callback) {
39 Pods.count(function (err, count) {
40 if (err) return callback(err)
41
bc503c2a
C
42 const hasFriends = (count !== 0)
43 callback(null, hasFriends)
9f10b292
C
44 })
45}
46
cbe2f7c3
C
47function getMyCertificate (callback) {
48 fs.readFile(peertubeCrypto.getCertDir() + 'peertube.pub', 'utf8', callback)
49}
50
9f10b292 51function makeFriends (callback) {
bc503c2a 52 const podsScore = {}
9f10b292
C
53
54 logger.info('Make friends!')
cbe2f7c3 55 getMyCertificate(function (err, cert) {
9f10b292
C
56 if (err) {
57 logger.error('Cannot read public cert.')
58 return callback(err)
59 }
c173e565 60
f0f5567b 61 const urls = config.get('network.friends')
c173e565 62
528a9efa 63 async.eachSeries(urls, function (url, callbackEach) {
bc503c2a 64 computeForeignPodsList(url, podsScore, callbackEach)
89d1d8ba 65 }, function (err) {
c173e565
C
66 if (err) return callback(err)
67
bc503c2a
C
68 logger.debug('Pods scores computed.', { podsScore: podsScore })
69 const podsList = computeWinningPods(urls, podsScore)
70 logger.debug('Pods that we keep.', { podsToKeep: podsList })
9f10b292 71
bc503c2a 72 makeRequestsToWinningPods(cert, podsList, callback)
c173e565 73 })
9f10b292 74 })
9f10b292
C
75}
76
77function quitFriends (callback) {
78 // Stop pool requests
e3647ae2 79 requestsScheduler.deactivate()
9f10b292 80 // Flush pool requests
528a9efa 81 requestsScheduler.flush()
9f10b292 82
e7ea2817
C
83 async.waterfall([
84 function getPodsList (callbackAsync) {
85 return Pods.list(callbackAsync)
86 },
87
88 function announceIQuitMyFriends (pods, callbackAsync) {
528a9efa 89 const requestParams = {
e7ea2817
C
90 method: 'POST',
91 path: '/api/' + constants.API_VERSION + '/pods/remove',
528a9efa 92 sign: true
c173e565
C
93 }
94
e7ea2817 95 // Announce we quit them
528a9efa
C
96 // We don't care if the request fails
97 // The other pod will exclude us automatically after a while
98 async.eachLimit(pods, constants.REQUESTS_IN_PARALLEL, function (pod, callbackEach) {
99 requestParams.toPod = pod
100 requests.makeSecureRequest(requestParams, callbackEach)
101 }, function (err) {
102 if (err) {
103 logger.error('Some errors while quitting friends.', { err: err })
104 // Don't stop the process
105 }
106
107 return callbackAsync()
e7ea2817
C
108 })
109 },
c173e565 110
e7ea2817
C
111 function removePodsFromDB (callbackAsync) {
112 Pods.removeAll(function (err) {
113 return callbackAsync(err)
114 })
115 },
c173e565 116
e7ea2817
C
117 function listRemoteVideos (callbackAsync) {
118 logger.info('Broke friends, so sad :(')
c173e565 119
e7ea2817
C
120 Videos.listFromRemotes(callbackAsync)
121 },
c173e565 122
e7ea2817
C
123 function removeTheRemoteVideos (videosList, callbackAsync) {
124 videos.removeRemoteVideos(videosList, function (err) {
125 if (err) {
126 logger.error('Cannot remove remote videos.', { error: err })
127 return callbackAsync(err)
128 }
cbe2f7c3 129
e7ea2817 130 return callbackAsync(null)
c173e565 131 })
e7ea2817
C
132 }
133 ], function (err) {
134 // Don't forget to re activate the scheduler, even if there was an error
135 requestsScheduler.activate()
136
137 if (err) return callback(err)
138
139 logger.info('Removed all remote videos.')
140 return callback(null)
9f10b292
C
141 })
142}
c173e565 143
9f10b292 144function removeVideoToFriends (video) {
528a9efa
C
145 requestsScheduler.addRequest('remove', video)
146}
147
148function sendOwnedVideosToPod (podId) {
149 Videos.listOwned(function (err, videosList) {
150 if (err) {
151 logger.error('Cannot get the list of videos we own.')
152 return
153 }
154
155 videosList.forEach(function (video) {
156 videos.convertVideoToRemote(video, function (err, remoteVideo) {
157 if (err) {
158 logger.error('Cannot convert video to remote.', { error: err })
159 // Don't break the process
160 return
161 }
162
163 requestsScheduler.addRequestTo([ podId ], 'add', remoteVideo)
164 })
165 })
166 })
9f10b292 167}
c173e565 168
9f10b292 169// ---------------------------------------------------------------------------
c173e565 170
9f10b292 171module.exports = pods
c173e565 172
9f10b292 173// ---------------------------------------------------------------------------
c173e565 174
bc503c2a 175function computeForeignPodsList (url, podsScore, callback) {
bc503c2a 176 getForeignPodsList(url, function (err, foreignPodsList) {
89d1d8ba 177 if (err) return callback(err)
528a9efa
C
178
179 if (!foreignPodsList) foreignPodsList = []
180
181 // Let's give 1 point to the pod we ask the friends list
182 foreignPodsList.push({ url: url })
89d1d8ba 183
bc503c2a 184 foreignPodsList.forEach(function (foreignPod) {
528a9efa 185 const foreignPodUrl = foreignPod.url
89d1d8ba 186
528a9efa
C
187 if (podsScore[foreignPodUrl]) podsScore[foreignPodUrl]++
188 else podsScore[foreignPodUrl] = 1
89d1d8ba 189 })
cbe2f7c3
C
190
191 callback()
89d1d8ba
C
192 })
193}
194
bc503c2a 195function computeWinningPods (urls, podsScore) {
89d1d8ba
C
196 // Build the list of pods to add
197 // Only add a pod if it exists in more than a half base pods
bc503c2a
C
198 const podsList = []
199 const baseScore = urls.length / 2
d6ea0175 200 Object.keys(podsScore).forEach(function (pod) {
bc503c2a 201 if (podsScore[pod] > baseScore) podsList.push({ url: pod })
89d1d8ba 202 })
e7ea2817 203
bc503c2a 204 return podsList
89d1d8ba
C
205}
206
9f10b292 207function getForeignPodsList (url, callback) {
f0f5567b 208 const path = '/api/' + constants.API_VERSION + '/pods'
c173e565 209
9f10b292
C
210 request.get(url + path, function (err, response, body) {
211 if (err) return callback(err)
8425cb89 212
9f10b292
C
213 callback(null, JSON.parse(body))
214 })
215}
89d1d8ba 216
bc503c2a 217function makeRequestsToWinningPods (cert, podsList, callback) {
89d1d8ba 218 // Stop pool requests
e3647ae2 219 requestsScheduler.deactivate()
89d1d8ba 220 // Flush pool requests
e3647ae2 221 requestsScheduler.forceSend()
89d1d8ba 222
528a9efa
C
223 async.eachLimit(podsList, constants.REQUESTS_IN_PARALLEL, function (pod, callbackEach) {
224 const params = {
225 url: pod.url + '/api/' + constants.API_VERSION + '/pods/',
226 method: 'POST',
227 json: {
228 url: http + '://' + host + ':' + port,
229 publicKey: cert
230 }
89d1d8ba
C
231 }
232
528a9efa
C
233 requests.makeRetryRequest(params, function (err, res, body) {
234 if (err) {
235 logger.error('Error with adding %s pod.', pod.url, { error: err })
236 // Don't break the process
237 return callbackEach()
238 }
89d1d8ba 239
528a9efa
C
240 if (res.statusCode === 200) {
241 Pods.add({ url: pod.url, publicKey: body.cert, score: constants.FRIEND_BASE_SCORE }, function (err, podCreated) {
242 if (err) logger.error('Cannot add friend %s pod.', pod.url)
89d1d8ba 243
528a9efa
C
244 // Add our videos to the request scheduler
245 sendOwnedVideosToPod(podCreated._id)
89d1d8ba 246
528a9efa
C
247 return callbackEach()
248 })
249 } else {
250 logger.error('Status not 200 for %s pod.', pod.url)
251 return callbackEach()
89d1d8ba 252 }
528a9efa
C
253 })
254 }, function endRequests () {
255 // Final callback, we've ended all the requests
256 // Now we made new friends, we can re activate the pool of requests
257 requestsScheduler.activate()
258
259 logger.debug('makeRequestsToWinningPods finished.')
260 return callback()
89d1d8ba
C
261 })
262}