4
4
5
5
package io.gitpod.jetbrains.remote.latest
6
6
7
- import com.intellij.openapi.Disposable
8
7
import com.intellij.openapi.client.ClientProjectSession
9
8
import com.intellij.openapi.diagnostic.thisLogger
9
+ import com.intellij.openapi.rd.createLifetime
10
10
import com.intellij.remoteDev.util.onTerminationOrNow
11
11
import com.intellij.util.application
12
- import com.jetbrains.rd.util.lifetime.Lifetime
12
+ import com.jetbrains.rdserver.portForwarding.PortForwardingDiscovery
13
+ import com.jetbrains.rdserver.portForwarding.PortForwardingManager
14
+ import com.jetbrains.rdserver.portForwarding.remoteDev.PortEventsProcessor
13
15
import com.jetbrains.rdserver.terminal.BackendTerminalManager
14
16
import io.gitpod.jetbrains.remote.GitpodManager
15
17
import io.gitpod.supervisor.api.Status
@@ -25,43 +27,33 @@ import java.util.concurrent.CompletableFuture
25
27
import java.util.concurrent.TimeUnit
26
28
27
29
@Suppress(" UnstableApiUsage" )
28
- class GitpodTerminalService (session : ClientProjectSession ) : Disposable {
30
+ class GitpodTerminalService (private val session : ClientProjectSession ) {
29
31
private companion object {
30
- /* * Indicates if this service is already running, because we shouldn't run it more than once. */
31
- var isRunning = false
32
+ var hasStarted = false
33
+ val forwardedPortsList : MutableSet < Int > = mutableSetOf ()
32
34
}
33
35
34
- private val lifetime = Lifetime .Eternal .createNested()
35
36
private val terminalView = TerminalView .getInstance(session.project)
36
37
private val backendTerminalManager = BackendTerminalManager .getInstance(session.project)
38
+ private val portForwardingManager = PortForwardingManager .getInstance(session.project)
37
39
private val terminalServiceFutureStub = TerminalServiceGrpc .newFutureStub(GitpodManager .supervisorChannel)
38
40
private val statusServiceStub = StatusServiceGrpc .newStub(GitpodManager .supervisorChannel)
39
41
40
- override fun dispose () {
41
- lifetime.terminate()
42
- }
43
-
44
- init {
45
- run ()
46
- }
42
+ init { start() }
47
43
48
- private fun run () {
49
- if (application.isHeadlessEnvironment || isRunning ) return
44
+ private fun start () {
45
+ if (application.isHeadlessEnvironment || hasStarted ) return
50
46
51
- isRunning = true
47
+ hasStarted = true
52
48
53
- val task = application.executeOnPooledThread {
49
+ application.executeOnPooledThread {
54
50
val terminals = getSupervisorTerminalsList()
55
51
val tasks = getSupervisorTasksList()
56
52
57
53
application.invokeLater {
58
54
createTerminalsAttachedToTasks(terminals, tasks)
59
55
}
60
56
}
61
-
62
- lifetime.onTerminationOrNow {
63
- task.cancel(true )
64
- }
65
57
}
66
58
67
59
private fun createSharedTerminalAndExecuteCommand (title : String , command : String ) {
@@ -70,12 +62,11 @@ class GitpodTerminalService(session: ClientProjectSession) : Disposable {
70
62
backendTerminalManager.createNewSharedTerminal(UUID .randomUUID().toString(), title)
71
63
72
64
for (widget in terminalView.widgets) {
73
- if (! registeredTerminals.contains(widget)) {
74
- widget.terminalTitle.change {
75
- applicationTitle = title
76
- }
77
- (widget as ShellTerminalWidget ).executeCommand(command)
78
- }
65
+ if (registeredTerminals.contains(widget)) continue
66
+
67
+ widget.terminalTitle.change { applicationTitle = title }
68
+
69
+ (widget as ShellTerminalWidget ).executeCommand(command)
79
70
}
80
71
}
81
72
@@ -94,11 +85,10 @@ class GitpodTerminalService(session: ClientProjectSession) : Disposable {
94
85
95
86
for (task in tasks) {
96
87
val terminalAlias = task.terminal
97
- val terminal = aliasToTerminalMap[terminalAlias]
88
+ val terminal = aliasToTerminalMap[terminalAlias] ? : continue
98
89
99
- if (terminal != null ) {
100
- createAttachedSharedTerminal(terminal)
101
- }
90
+ createAttachedSharedTerminal(terminal)
91
+ autoForwardAllPortsFromTerminal(terminal)
102
92
}
103
93
}
104
94
@@ -112,22 +102,17 @@ class GitpodTerminalService(session: ClientProjectSession) : Disposable {
112
102
113
103
val taskStatusResponseObserver = object :
114
104
ClientResponseObserver <Status .TasksStatusRequest , Status .TasksStatusResponse > {
115
- override fun beforeStart (request : ClientCallStreamObserver <Status .TasksStatusRequest >) {
116
- lifetime.onTerminationOrNow {
117
- request.cancel(null , null )
118
- }
119
- }
105
+ override fun beforeStart (request : ClientCallStreamObserver <Status .TasksStatusRequest >) = Unit
120
106
121
107
override fun onNext (response : Status .TasksStatusResponse ) {
122
108
for (task in response.tasksList) {
123
- if (task.state == = Status .TaskState .opening) {
124
- return
125
- }
109
+ if (task.state == = Status .TaskState .opening) return
126
110
}
111
+
127
112
completableFuture.complete(response.tasksList)
128
113
}
129
114
130
- override fun onCompleted () {}
115
+ override fun onCompleted () = Unit
131
116
132
117
override fun onError (throwable : Throwable ) {
133
118
completableFuture.completeExceptionally(throwable)
@@ -143,7 +128,7 @@ class GitpodTerminalService(session: ClientProjectSession) : Disposable {
143
128
}
144
129
145
130
thisLogger().error(
146
- " Got an error while trying to get tasks list from Supervisor. Trying again in on second." ,
131
+ " gitpod: Got an error while trying to get tasks list from Supervisor. Trying again in on second." ,
147
132
throwable
148
133
)
149
134
}
@@ -164,10 +149,6 @@ class GitpodTerminalService(session: ClientProjectSession) : Disposable {
164
149
165
150
val listTerminalsResponseFuture = terminalServiceFutureStub.list(listTerminalsRequest)
166
151
167
- lifetime.onTerminationOrNow {
168
- listTerminalsResponseFuture.cancel(true )
169
- }
170
-
171
152
val listTerminalsResponse = listTerminalsResponseFuture.get()
172
153
173
154
terminalsList = listTerminalsResponse.terminalsList
@@ -177,7 +158,7 @@ class GitpodTerminalService(session: ClientProjectSession) : Disposable {
177
158
}
178
159
179
160
thisLogger().error(
180
- " Got an error while trying to get terminals list from Supervisor. Trying again in on second." ,
161
+ " gitpod: Got an error while trying to get terminals list from Supervisor. Trying again in on second." ,
181
162
throwable
182
163
)
183
164
}
@@ -196,4 +177,40 @@ class GitpodTerminalService(session: ClientProjectSession) : Disposable {
196
177
" gp tasks attach ${supervisorTerminal.alias} "
197
178
)
198
179
}
180
+
181
+ private fun autoForwardAllPortsFromTerminal (supervisorTerminal : TerminalOuterClass .Terminal ) {
182
+ val projectLifetime = session.project.createLifetime()
183
+
184
+ val discoveryCallback = object : PortForwardingDiscovery {
185
+ /* *
186
+ * @return Whether port should be forwarded or not.
187
+ * We shouldn't try to forward ports that are already forwarded.
188
+ */
189
+ override fun onPortDiscovered (hostPort : Int ): Boolean = ! forwardedPortsList.contains(hostPort)
190
+
191
+ override fun getEventsProcessor (hostPort : Int ) = object : PortEventsProcessor {
192
+ override fun onPortForwarded (hostPort : Int , clientPort : Int ) {
193
+ forwardedPortsList.add(hostPort)
194
+ thisLogger().info(" gitpod: Forwarded port $hostPort from Supervisor's Terminal " +
195
+ " ${supervisorTerminal.pid} to client's port $clientPort ." )
196
+
197
+ projectLifetime.onTerminationOrNow {
198
+ if (forwardedPortsList.contains(hostPort)) {
199
+ forwardedPortsList.remove(hostPort)
200
+ portForwardingManager.removePort(hostPort)
201
+ thisLogger().info(" gitpod: Removing forwarded port $hostPort from Supervisor's Terminal " +
202
+ " ${supervisorTerminal.pid} " )
203
+ }
204
+ }
205
+ }
206
+
207
+ override fun onPortForwardingFailed (hostPort : Int , reason : String ) {
208
+ thisLogger().error(" gitpod: Failed to forward port $hostPort from Supervisor's Terminal " +
209
+ " ${supervisorTerminal.pid} : $reason " )
210
+ }
211
+ }
212
+ }
213
+
214
+ portForwardingManager.forwardPortsOfPid(projectLifetime, supervisorTerminal.pid, discoveryCallback, true )
215
+ }
199
216
}
0 commit comments