Path: blob/main/components/supervisor/pkg/ports/ports-config.go
2500 views
// Copyright (c) 2020 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package ports56import (7"context"8"fmt"9"reflect"10"regexp"11"strconv"1213gitpod "github.com/gitpod-io/gitpod/gitpod-protocol"14"github.com/gitpod-io/gitpod/supervisor/pkg/config"15)1617const NON_CONFIGED_BASIC_SCORE = 1000001819// RangeConfig is a port range config.20type RangeConfig struct {21gitpod.PortsItems22Start uint3223End uint3224Sort uint3225}2627// SortConfig is a port with a sort field28type SortConfig struct {29gitpod.PortConfig30Sort uint3231}3233// Configs provides access to port configurations.34type Configs struct {35instancePortConfigs map[uint32]*SortConfig36instanceRangeConfigs []*RangeConfig37}3839// ForEach iterates over all configured ports.40func (configs *Configs) ForEach(callback func(port uint32, config *SortConfig)) {41if configs == nil {42return43}44visited := make(map[uint32]struct{})45for port, config := range configs.instancePortConfigs {46_, exists := visited[port]47if exists {48continue49}50visited[port] = struct{}{}51callback(port, config)52}53}5455// ConfigKind indicates a type of config.56type ConfigKind uint85758var (59// PortConfigKind is a port based config type.60PortConfigKind ConfigKind = 061// RangeConfigKind is a range based config type.62RangeConfigKind ConfigKind = 163)6465// Get returns the config for the give port.66func (configs *Configs) Get(port uint32) (*SortConfig, ConfigKind, bool) {67if configs == nil {68return nil, PortConfigKind, false69}70config, exists := configs.instancePortConfigs[port]71if exists {72return config, PortConfigKind, true73}74for _, rangeConfig := range configs.instanceRangeConfigs {75if rangeConfig.Start <= port && port <= rangeConfig.End {76return &SortConfig{77PortConfig: gitpod.PortConfig{78Port: float64(port),79OnOpen: rangeConfig.OnOpen,80Visibility: rangeConfig.Visibility,81Description: rangeConfig.Description,82Protocol: rangeConfig.Protocol,83Name: rangeConfig.Name,84},85Sort: rangeConfig.Sort,86}, RangeConfigKind, true87}88}89return nil, PortConfigKind, false90}9192// ConfigInterace allows to watch port configurations.93type ConfigInterace interface {94// Observe provides channels triggered whenever the port configurations are changed.95Observe(ctx context.Context) (<-chan *Configs, <-chan error)96}9798// ConfigService allows to watch port configurations.99type ConfigService struct {100workspaceID string101configService config.ConfigInterface102}103104// NewConfigService creates a new instance of ConfigService.105func NewConfigService(workspaceID string, configService config.ConfigInterface) *ConfigService {106return &ConfigService{107workspaceID: workspaceID,108configService: configService,109}110}111112// Observe provides channels triggered whenever the port configurations are changed.113func (service *ConfigService) Observe(ctx context.Context) (<-chan *Configs, <-chan error) {114updatesChan := make(chan *Configs)115errorsChan := make(chan error, 1)116117go func() {118defer close(updatesChan)119defer close(errorsChan)120121configs := service.configService.Observe(ctx)122123current := &Configs{}124125for {126select {127case <-ctx.Done():128return129case config, ok := <-configs:130if !ok {131return132}133changed := service.update(config, current)134if !changed {135continue136}137updatesChan <- &Configs{138instancePortConfigs: current.instancePortConfigs,139instanceRangeConfigs: current.instanceRangeConfigs,140}141}142}143}()144return updatesChan, errorsChan145}146147func (service *ConfigService) update(config *gitpod.GitpodConfig, current *Configs) bool {148currentPortConfigs, currentRangeConfigs := current.instancePortConfigs, current.instanceRangeConfigs149var ports []*gitpod.PortsItems150if config != nil {151ports = config.Ports152}153portConfigs, rangeConfigs := parseInstanceConfigs(ports)154current.instancePortConfigs = portConfigs155current.instanceRangeConfigs = rangeConfigs156return !reflect.DeepEqual(currentPortConfigs, portConfigs) || !reflect.DeepEqual(currentRangeConfigs, rangeConfigs)157}158159var portRangeRegexp = regexp.MustCompile(`^(\d+)[-:](\d+)$`)160161func parseInstanceConfigs(ports []*gitpod.PortsItems) (portConfigs map[uint32]*SortConfig, rangeConfigs []*RangeConfig) {162for index, config := range ports {163if config == nil {164continue165}166167rawPort := fmt.Sprintf("%v", config.Port)168Port, err := strconv.ParseUint(rawPort, 10, 16)169if err == nil {170if portConfigs == nil {171portConfigs = make(map[uint32]*SortConfig)172}173port := uint32(Port)174_, exists := portConfigs[port]175if !exists {176portConfigs[port] = &SortConfig{177PortConfig: gitpod.PortConfig{178OnOpen: config.OnOpen,179Port: float64(Port),180Visibility: config.Visibility,181Description: config.Description,182Protocol: config.Protocol,183Name: config.Name,184},185Sort: uint32(index),186}187}188continue189}190matches := portRangeRegexp.FindStringSubmatch(rawPort)191if len(matches) != 3 {192continue193}194start, err := strconv.Atoi(matches[1])195if err != nil {196continue197}198end, err := strconv.Atoi(matches[2])199if err != nil || start >= end {200continue201}202rangeConfigs = append(rangeConfigs, &RangeConfig{203PortsItems: *config,204Start: uint32(start),205End: uint32(end),206Sort: uint32(index),207})208}209return portConfigs, rangeConfigs210}211212213