@@ -8,10 +8,12 @@ import (
88 "context"
99 "net/url"
1010 "sort"
11+ "strconv"
1112
1213 "golang.org/x/xerrors"
1314 "k8s.io/apimachinery/pkg/api/errors"
1415 "k8s.io/apimachinery/pkg/runtime"
16+ "k8s.io/apimachinery/pkg/util/uuid"
1517 "k8s.io/client-go/tools/cache"
1618 ctrl "sigs.k8s.io/controller-runtime"
1719 "sigs.k8s.io/controller-runtime/pkg/client"
@@ -20,6 +22,7 @@ import (
2022
2123 wsk8s "github.com/gitpod-io/gitpod/common-go/kubernetes"
2224 "github.com/gitpod-io/gitpod/common-go/log"
25+ "github.com/gitpod-io/gitpod/ws-manager/api"
2326 wsapi "github.com/gitpod-io/gitpod/ws-manager/api"
2427 workspacev1 "github.com/gitpod-io/gitpod/ws-manager/api/crd/v1"
2528 "github.com/gitpod-io/gitpod/ws-proxy/pkg/common"
@@ -48,11 +51,19 @@ func getPortStr(urlStr string) string {
4851 return portURL .Port ()
4952}
5053
54+ type ConnectionContext struct {
55+ WorkspaceID string
56+ Port string
57+ UUID string
58+ CancelFunc context.CancelCauseFunc
59+ }
60+
5161type CRDWorkspaceInfoProvider struct {
5262 client.Client
5363 Scheme * runtime.Scheme
5464
55- store cache.ThreadSafeStore
65+ store cache.ThreadSafeStore
66+ contextStore cache.ThreadSafeStore
5667}
5768
5869// NewCRDWorkspaceInfoProvider creates a fresh WorkspaceInfoProvider.
@@ -67,12 +78,21 @@ func NewCRDWorkspaceInfoProvider(client client.Client, scheme *runtime.Scheme) (
6778 return nil , xerrors .Errorf ("object is not a WorkspaceInfo" )
6879 },
6980 }
81+ contextIndexers := cache.Indexers {
82+ workspaceIndex : func (obj interface {}) ([]string , error ) {
83+ if connCtx , ok := obj .(* ConnectionContext ); ok {
84+ return []string {connCtx .WorkspaceID }, nil
85+ }
86+ return nil , xerrors .Errorf ("object is not a ConnectionContext" )
87+ },
88+ }
7089
7190 return & CRDWorkspaceInfoProvider {
7291 Client : client ,
7392 Scheme : scheme ,
7493
75- store : cache .NewThreadSafeStore (indexers , cache.Indices {}),
94+ store : cache .NewThreadSafeStore (indexers , cache.Indices {}),
95+ contextStore : cache .NewThreadSafeStore (contextIndexers , cache.Indices {}),
7696 }, nil
7797}
7898
@@ -101,6 +121,28 @@ func (r *CRDWorkspaceInfoProvider) WorkspaceInfo(workspaceID string) *common.Wor
101121 return nil
102122}
103123
124+ func (r * CRDWorkspaceInfoProvider ) AcquireContext (ctx context.Context , workspaceID string , port string ) (context.Context , string , error ) {
125+ ws := r .WorkspaceInfo (workspaceID )
126+ if ws == nil {
127+ return ctx , "" , xerrors .Errorf ("workspace %s not found" , workspaceID )
128+ }
129+ id := string (uuid .NewUUID ())
130+ ctx , cancel := context .WithCancelCause (ctx )
131+ connCtx := & ConnectionContext {
132+ WorkspaceID : workspaceID ,
133+ Port : port ,
134+ CancelFunc : cancel ,
135+ UUID : id ,
136+ }
137+
138+ r .contextStore .Add (id , connCtx )
139+ return ctx , id , nil
140+ }
141+
142+ func (r * CRDWorkspaceInfoProvider ) ReleaseContext (id string ) {
143+ r .contextStore .Delete (id )
144+ }
145+
104146func (r * CRDWorkspaceInfoProvider ) Reconcile (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
105147 var ws workspacev1.Workspace
106148 err := r .Client .Get (context .Background (), req .NamespacedName , & ws )
@@ -162,11 +204,44 @@ func (r *CRDWorkspaceInfoProvider) Reconcile(ctx context.Context, req ctrl.Reque
162204 }
163205
164206 r .store .Update (req .Name , wsinfo )
207+ r .invalidConnectionContext (wsinfo )
165208 log .WithField ("workspace" , req .Name ).WithField ("details" , wsinfo ).Debug ("adding/updating workspace details" )
166209
167210 return ctrl.Result {}, nil
168211}
169212
213+ func (r * CRDWorkspaceInfoProvider ) invalidConnectionContext (ws * common.WorkspaceInfo ) {
214+ connCtxs , err := r .contextStore .ByIndex (workspaceIndex , ws .WorkspaceID )
215+ if err != nil {
216+ return
217+ }
218+ if len (connCtxs ) == 0 {
219+ return
220+ }
221+
222+ if ws .Auth != nil && ws .Auth .Admission == wsapi .AdmissionLevel_ADMIT_EVERYONE {
223+ return
224+ }
225+ publicPorts := make (map [string ]struct {})
226+ for _ , p := range ws .Ports {
227+ if p .Visibility == api .PortVisibility_PORT_VISIBILITY_PUBLIC {
228+ publicPorts [strconv .FormatUint (uint64 (p .Port ), 10 )] = struct {}{}
229+ }
230+ }
231+
232+ for _ , _connCtx := range connCtxs {
233+ connCtx , ok := _connCtx .(* ConnectionContext )
234+ if ! ok {
235+ continue
236+ }
237+ if _ , ok := publicPorts [connCtx .Port ]; ok {
238+ continue
239+ }
240+ connCtx .CancelFunc (xerrors .Errorf ("workspace %s is no longer public" , ws .WorkspaceID ))
241+ r .contextStore .Delete (connCtx .UUID )
242+ }
243+ }
244+
170245// SetupWithManager sets up the controller with the Manager.
171246func (r * CRDWorkspaceInfoProvider ) SetupWithManager (mgr ctrl.Manager ) error {
172247 return ctrl .NewControllerManagedBy (mgr ).
@@ -177,28 +252,3 @@ func (r *CRDWorkspaceInfoProvider) SetupWithManager(mgr ctrl.Manager) error {
177252 ).
178253 Complete (r )
179254}
180-
181- // CompositeInfoProvider checks each of its info providers and returns the first info found.
182- type CompositeInfoProvider []common.WorkspaceInfoProvider
183-
184- func (c CompositeInfoProvider ) WorkspaceInfo (workspaceID string ) * common.WorkspaceInfo {
185- for _ , ip := range c {
186- res := ip .WorkspaceInfo (workspaceID )
187- if res != nil {
188- return res
189- }
190- }
191- return nil
192- }
193-
194- type fixedInfoProvider struct {
195- Infos map [string ]* common.WorkspaceInfo
196- }
197-
198- // WorkspaceInfo returns the workspace information of a workspace using it's workspace ID.
199- func (fp * fixedInfoProvider ) WorkspaceInfo (workspaceID string ) * common.WorkspaceInfo {
200- if fp .Infos == nil {
201- return nil
202- }
203- return fp .Infos [workspaceID ]
204- }
0 commit comments