Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/supervisor/pkg/ports/ports-config.go
2500 views
1
// Copyright (c) 2020 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package ports
6
7
import (
8
"context"
9
"fmt"
10
"reflect"
11
"regexp"
12
"strconv"
13
14
gitpod "github.com/gitpod-io/gitpod/gitpod-protocol"
15
"github.com/gitpod-io/gitpod/supervisor/pkg/config"
16
)
17
18
const NON_CONFIGED_BASIC_SCORE = 100000
19
20
// RangeConfig is a port range config.
21
type RangeConfig struct {
22
gitpod.PortsItems
23
Start uint32
24
End uint32
25
Sort uint32
26
}
27
28
// SortConfig is a port with a sort field
29
type SortConfig struct {
30
gitpod.PortConfig
31
Sort uint32
32
}
33
34
// Configs provides access to port configurations.
35
type Configs struct {
36
instancePortConfigs map[uint32]*SortConfig
37
instanceRangeConfigs []*RangeConfig
38
}
39
40
// ForEach iterates over all configured ports.
41
func (configs *Configs) ForEach(callback func(port uint32, config *SortConfig)) {
42
if configs == nil {
43
return
44
}
45
visited := make(map[uint32]struct{})
46
for port, config := range configs.instancePortConfigs {
47
_, exists := visited[port]
48
if exists {
49
continue
50
}
51
visited[port] = struct{}{}
52
callback(port, config)
53
}
54
}
55
56
// ConfigKind indicates a type of config.
57
type ConfigKind uint8
58
59
var (
60
// PortConfigKind is a port based config type.
61
PortConfigKind ConfigKind = 0
62
// RangeConfigKind is a range based config type.
63
RangeConfigKind ConfigKind = 1
64
)
65
66
// Get returns the config for the give port.
67
func (configs *Configs) Get(port uint32) (*SortConfig, ConfigKind, bool) {
68
if configs == nil {
69
return nil, PortConfigKind, false
70
}
71
config, exists := configs.instancePortConfigs[port]
72
if exists {
73
return config, PortConfigKind, true
74
}
75
for _, rangeConfig := range configs.instanceRangeConfigs {
76
if rangeConfig.Start <= port && port <= rangeConfig.End {
77
return &SortConfig{
78
PortConfig: gitpod.PortConfig{
79
Port: float64(port),
80
OnOpen: rangeConfig.OnOpen,
81
Visibility: rangeConfig.Visibility,
82
Description: rangeConfig.Description,
83
Protocol: rangeConfig.Protocol,
84
Name: rangeConfig.Name,
85
},
86
Sort: rangeConfig.Sort,
87
}, RangeConfigKind, true
88
}
89
}
90
return nil, PortConfigKind, false
91
}
92
93
// ConfigInterace allows to watch port configurations.
94
type ConfigInterace interface {
95
// Observe provides channels triggered whenever the port configurations are changed.
96
Observe(ctx context.Context) (<-chan *Configs, <-chan error)
97
}
98
99
// ConfigService allows to watch port configurations.
100
type ConfigService struct {
101
workspaceID string
102
configService config.ConfigInterface
103
}
104
105
// NewConfigService creates a new instance of ConfigService.
106
func NewConfigService(workspaceID string, configService config.ConfigInterface) *ConfigService {
107
return &ConfigService{
108
workspaceID: workspaceID,
109
configService: configService,
110
}
111
}
112
113
// Observe provides channels triggered whenever the port configurations are changed.
114
func (service *ConfigService) Observe(ctx context.Context) (<-chan *Configs, <-chan error) {
115
updatesChan := make(chan *Configs)
116
errorsChan := make(chan error, 1)
117
118
go func() {
119
defer close(updatesChan)
120
defer close(errorsChan)
121
122
configs := service.configService.Observe(ctx)
123
124
current := &Configs{}
125
126
for {
127
select {
128
case <-ctx.Done():
129
return
130
case config, ok := <-configs:
131
if !ok {
132
return
133
}
134
changed := service.update(config, current)
135
if !changed {
136
continue
137
}
138
updatesChan <- &Configs{
139
instancePortConfigs: current.instancePortConfigs,
140
instanceRangeConfigs: current.instanceRangeConfigs,
141
}
142
}
143
}
144
}()
145
return updatesChan, errorsChan
146
}
147
148
func (service *ConfigService) update(config *gitpod.GitpodConfig, current *Configs) bool {
149
currentPortConfigs, currentRangeConfigs := current.instancePortConfigs, current.instanceRangeConfigs
150
var ports []*gitpod.PortsItems
151
if config != nil {
152
ports = config.Ports
153
}
154
portConfigs, rangeConfigs := parseInstanceConfigs(ports)
155
current.instancePortConfigs = portConfigs
156
current.instanceRangeConfigs = rangeConfigs
157
return !reflect.DeepEqual(currentPortConfigs, portConfigs) || !reflect.DeepEqual(currentRangeConfigs, rangeConfigs)
158
}
159
160
var portRangeRegexp = regexp.MustCompile(`^(\d+)[-:](\d+)$`)
161
162
func parseInstanceConfigs(ports []*gitpod.PortsItems) (portConfigs map[uint32]*SortConfig, rangeConfigs []*RangeConfig) {
163
for index, config := range ports {
164
if config == nil {
165
continue
166
}
167
168
rawPort := fmt.Sprintf("%v", config.Port)
169
Port, err := strconv.ParseUint(rawPort, 10, 16)
170
if err == nil {
171
if portConfigs == nil {
172
portConfigs = make(map[uint32]*SortConfig)
173
}
174
port := uint32(Port)
175
_, exists := portConfigs[port]
176
if !exists {
177
portConfigs[port] = &SortConfig{
178
PortConfig: gitpod.PortConfig{
179
OnOpen: config.OnOpen,
180
Port: float64(Port),
181
Visibility: config.Visibility,
182
Description: config.Description,
183
Protocol: config.Protocol,
184
Name: config.Name,
185
},
186
Sort: uint32(index),
187
}
188
}
189
continue
190
}
191
matches := portRangeRegexp.FindStringSubmatch(rawPort)
192
if len(matches) != 3 {
193
continue
194
}
195
start, err := strconv.Atoi(matches[1])
196
if err != nil {
197
continue
198
}
199
end, err := strconv.Atoi(matches[2])
200
if err != nil || start >= end {
201
continue
202
}
203
rangeConfigs = append(rangeConfigs, &RangeConfig{
204
PortsItems: *config,
205
Start: uint32(start),
206
End: uint32(end),
207
Sort: uint32(index),
208
})
209
}
210
return portConfigs, rangeConfigs
211
}
212
213