mirror of
https://github.com/gitpod-io/gitpod.git
synced 2025-12-08 17:36:30 +00:00
210 lines
5.3 KiB
Go
210 lines
5.3 KiB
Go
// Copyright (c) 2020 Gitpod GmbH. All rights reserved.
|
|
// Licensed under the GNU Affero General Public License (AGPL).
|
|
// See License.AGPL.txt in the project root for license information.
|
|
|
|
package ports
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"reflect"
|
|
"regexp"
|
|
"strconv"
|
|
|
|
gitpod "github.com/gitpod-io/gitpod/gitpod-protocol"
|
|
"github.com/gitpod-io/gitpod/supervisor/pkg/config"
|
|
)
|
|
|
|
const NON_CONFIGED_BASIC_SCORE = 100000
|
|
|
|
// RangeConfig is a port range config.
|
|
type RangeConfig struct {
|
|
gitpod.PortsItems
|
|
Start uint32
|
|
End uint32
|
|
Sort uint32
|
|
}
|
|
|
|
// SortConfig is a port with a sort field
|
|
type SortConfig struct {
|
|
gitpod.PortConfig
|
|
Sort uint32
|
|
}
|
|
|
|
// Configs provides access to port configurations.
|
|
type Configs struct {
|
|
instancePortConfigs map[uint32]*SortConfig
|
|
instanceRangeConfigs []*RangeConfig
|
|
}
|
|
|
|
// ForEach iterates over all configured ports.
|
|
func (configs *Configs) ForEach(callback func(port uint32, config *SortConfig)) {
|
|
if configs == nil {
|
|
return
|
|
}
|
|
visited := make(map[uint32]struct{})
|
|
for port, config := range configs.instancePortConfigs {
|
|
_, exists := visited[port]
|
|
if exists {
|
|
continue
|
|
}
|
|
visited[port] = struct{}{}
|
|
callback(port, config)
|
|
}
|
|
}
|
|
|
|
// ConfigKind indicates a type of config.
|
|
type ConfigKind uint8
|
|
|
|
var (
|
|
// PortConfigKind is a port based config type.
|
|
PortConfigKind ConfigKind = 0
|
|
// RangeConfigKind is a range based config type.
|
|
RangeConfigKind ConfigKind = 1
|
|
)
|
|
|
|
// Get returns the config for the give port.
|
|
func (configs *Configs) Get(port uint32) (*SortConfig, ConfigKind, bool) {
|
|
if configs == nil {
|
|
return nil, PortConfigKind, false
|
|
}
|
|
config, exists := configs.instancePortConfigs[port]
|
|
if exists {
|
|
return config, PortConfigKind, true
|
|
}
|
|
for _, rangeConfig := range configs.instanceRangeConfigs {
|
|
if rangeConfig.Start <= port && port <= rangeConfig.End {
|
|
return &SortConfig{
|
|
PortConfig: gitpod.PortConfig{
|
|
Port: float64(port),
|
|
OnOpen: rangeConfig.OnOpen,
|
|
Visibility: rangeConfig.Visibility,
|
|
Description: rangeConfig.Description,
|
|
Name: rangeConfig.Name,
|
|
},
|
|
Sort: rangeConfig.Sort,
|
|
}, RangeConfigKind, true
|
|
}
|
|
}
|
|
return nil, PortConfigKind, false
|
|
}
|
|
|
|
// ConfigInterace allows to watch port configurations.
|
|
type ConfigInterace interface {
|
|
// Observe provides channels triggered whenever the port configurations are changed.
|
|
Observe(ctx context.Context) (<-chan *Configs, <-chan error)
|
|
}
|
|
|
|
// ConfigService allows to watch port configurations.
|
|
type ConfigService struct {
|
|
workspaceID string
|
|
configService config.ConfigInterface
|
|
}
|
|
|
|
// NewConfigService creates a new instance of ConfigService.
|
|
func NewConfigService(workspaceID string, configService config.ConfigInterface) *ConfigService {
|
|
return &ConfigService{
|
|
workspaceID: workspaceID,
|
|
configService: configService,
|
|
}
|
|
}
|
|
|
|
// Observe provides channels triggered whenever the port configurations are changed.
|
|
func (service *ConfigService) Observe(ctx context.Context) (<-chan *Configs, <-chan error) {
|
|
updatesChan := make(chan *Configs)
|
|
errorsChan := make(chan error, 1)
|
|
|
|
go func() {
|
|
defer close(updatesChan)
|
|
defer close(errorsChan)
|
|
|
|
configs := service.configService.Observe(ctx)
|
|
|
|
current := &Configs{}
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case config, ok := <-configs:
|
|
if !ok {
|
|
return
|
|
}
|
|
changed := service.update(config, current)
|
|
if !changed {
|
|
continue
|
|
}
|
|
updatesChan <- &Configs{
|
|
instancePortConfigs: current.instancePortConfigs,
|
|
instanceRangeConfigs: current.instanceRangeConfigs,
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
return updatesChan, errorsChan
|
|
}
|
|
|
|
func (service *ConfigService) update(config *gitpod.GitpodConfig, current *Configs) bool {
|
|
currentPortConfigs, currentRangeConfigs := current.instancePortConfigs, current.instanceRangeConfigs
|
|
var ports []*gitpod.PortsItems
|
|
if config != nil {
|
|
ports = config.Ports
|
|
}
|
|
portConfigs, rangeConfigs := parseInstanceConfigs(ports)
|
|
current.instancePortConfigs = portConfigs
|
|
current.instanceRangeConfigs = rangeConfigs
|
|
return !reflect.DeepEqual(currentPortConfigs, portConfigs) || !reflect.DeepEqual(currentRangeConfigs, rangeConfigs)
|
|
}
|
|
|
|
var portRangeRegexp = regexp.MustCompile(`^(\d+)[-:](\d+)$`)
|
|
|
|
func parseInstanceConfigs(ports []*gitpod.PortsItems) (portConfigs map[uint32]*SortConfig, rangeConfigs []*RangeConfig) {
|
|
for index, config := range ports {
|
|
if config == nil {
|
|
continue
|
|
}
|
|
|
|
rawPort := fmt.Sprintf("%v", config.Port)
|
|
Port, err := strconv.ParseUint(rawPort, 10, 16)
|
|
if err == nil {
|
|
if portConfigs == nil {
|
|
portConfigs = make(map[uint32]*SortConfig)
|
|
}
|
|
port := uint32(Port)
|
|
_, exists := portConfigs[port]
|
|
if !exists {
|
|
portConfigs[port] = &SortConfig{
|
|
PortConfig: gitpod.PortConfig{
|
|
OnOpen: config.OnOpen,
|
|
Port: float64(Port),
|
|
Visibility: config.Visibility,
|
|
Description: config.Description,
|
|
Name: config.Name,
|
|
},
|
|
Sort: uint32(index),
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
matches := portRangeRegexp.FindStringSubmatch(rawPort)
|
|
if len(matches) != 3 {
|
|
continue
|
|
}
|
|
start, err := strconv.Atoi(matches[1])
|
|
if err != nil {
|
|
continue
|
|
}
|
|
end, err := strconv.Atoi(matches[2])
|
|
if err != nil || start >= end {
|
|
continue
|
|
}
|
|
rangeConfigs = append(rangeConfigs, &RangeConfig{
|
|
PortsItems: *config,
|
|
Start: uint32(start),
|
|
End: uint32(end),
|
|
Sort: uint32(index),
|
|
})
|
|
}
|
|
return portConfigs, rangeConfigs
|
|
}
|