mustard f4ea3a56a1 Revert "Revert "[supervisor] use public api in supervisor""
This reverts commit 44dfc5e19be3b89d96ab25f2f83476d57aa7b8ef.
2022-12-12 09:05:22 -03:00

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
}