<commit_before>package config

import (
    "fmt"
    "strings"
)

// A single Config may contain Nodes from different Sources
// TODO: tag each Config object with a Node/Source, such that
//		 sources override, and recursive updates are scoped to that source only
type Source interface {
    // uniquely identifying
    String()    string
}

/* Config objects */
type Meta struct {
	node		Node	// origin node
}


func (meta Meta) Source() string {
	if meta.node.Source != nil {
		return meta.node.Source.String()
	} else {
		// some nodes are implicitly created, and thus not directly applicable..
		return ""
	}
}

func (meta Meta) Path() string {
	return meta.node.Path
}

type ServiceFrontend struct {
	Meta			`json:"-"`

    IPv4    string  `json:"ipv4,omitempty"`
    IPv6    string  `json:"ipv6,omitempty"`
    TCP     uint16  `json:"tcp,omitempty"`
    UDP     uint16  `json:"udp,omitempty"`
}

type ServiceBackend struct {
	Meta			`json:"-"`

    IPv4    string  `json:"ipv4,omitempty"`
    IPv6    string  `json:"ipv6,omitempty"`
    TCP     uint16  `json:"tcp,omitempty"`
    UDP     uint16  `json:"udp,omitempty"`

    Weight  uint    `json:"weight,omitempty"`   // default: 10
}

const ServiceBackendWeight uint = 10

type Service struct {
	Meta			`json:"-"`

    Frontend        ServiceFrontend
    Backends        map[string]ServiceBackend
}

// Copy-on-write update for .Backends
func (service *Service) setBackend(backendName string, serviceBackend ServiceBackend, remove bool) {
    serviceBackends := make(map[string]ServiceBackend)

    for backendName, backend := range service.Backends {
        serviceBackends[backendName] = backend
    }

    if remove {
        delete(serviceBackends, backendName)
    } else {
        serviceBackends[backendName] = serviceBackend
    }

    service.Backends = serviceBackends
}

// Apply a recursive remove from source
func (service *Service) removeBackends(source Source) {
    serviceBackends := make(map[string]ServiceBackend)

    for backendName, backend := range service.Backends {
		if backend.Source() == source.String() {
			//log.Printf("service %s drop source=%s backends: %s source=%s\n", service.Path(), source.String(), backendName, backend.Source())

			// remove all from this source
			continue
		} else {
			//log.Printf("service %s keep source=%s backends: %s source=%s\n", service.Path(), source.String(), backendName, backend.Source())
		}

        serviceBackends[backendName] = backend
    }

    service.Backends = serviceBackends
}

type Route struct {
	Meta			`json:"-"`

    // IPv4 prefix to match
    // empty for default match
    Prefix4     string

    // Override backend IPv4 address for ipvs
    Gateway4    string

    // Configure IPVS fwd-method to use for destination
    //  droute tunnel masq
    // Filter out backend if set to
    //  filter
    IpvsMethod  string
}

// Top-level config object
type Config struct {
    Routes      map[string]Route
    Services    map[string]Service
}

// Copy-on-write update for .Services 
func (config *Config) setService(serviceName string, service Service, remove bool) {
    services := make(map[string]Service)

    for serviceName, service := range config.Services {
        services[serviceName] = service
    }

    if remove {
        delete(services, serviceName)
    } else {
        services[serviceName] = service
    }

    config.Services = services
}

func (config *Config) updateServices(node Node) error {
	if node.Remove {
		services := make(map[string]Service)

		for serviceName, service := range config.Services {
			if service.Source() == node.Source.String() {
				// remove service from this source
				continue
			} else {
				// remove any backends from this source
				service.removeBackends(node.Source)
			}

			services[serviceName] = service
		}
<commit_msg>config: fix Config.update() with nil Node.Source<commit_after>package config

import (
    "fmt"
    "strings"
)

// A single Config may contain Nodes from different Sources
// TODO: tag each Config object with a Node/Source, such that
//		 sources override, and recursive updates are scoped to that source only
type Source interface {
    // uniquely identifying
    String()    string
}

/* Config objects */
type Meta struct {
	node		Node	// origin node
}


func (meta Meta) Source() string {
	if meta.node.Source != nil {
		return meta.node.Source.String()
	} else {
		// some nodes are implicitly created, and thus not directly applicable..
		return ""
	}
}

func (meta Meta) Path() string {
	return meta.node.Path
}

type ServiceFrontend struct {
	Meta			`json:"-"`

    IPv4    string  `json:"ipv4,omitempty"`
    IPv6    string  `json:"ipv6,omitempty"`
    TCP     uint16  `json:"tcp,omitempty"`
    UDP     uint16  `json:"udp,omitempty"`
}

type ServiceBackend struct {
	Meta			`json:"-"`

    IPv4    string  `json:"ipv4,omitempty"`
    IPv6    string  `json:"ipv6,omitempty"`
    TCP     uint16  `json:"tcp,omitempty"`
    UDP     uint16  `json:"udp,omitempty"`

    Weight  uint    `json:"weight,omitempty"`   // default: 10
}

const ServiceBackendWeight uint = 10

type Service struct {
	Meta			`json:"-"`

    Frontend        ServiceFrontend
    Backends        map[string]ServiceBackend
}

// Copy-on-write update for .Backends
func (service *Service) setBackend(backendName string, serviceBackend ServiceBackend, remove bool) {
    serviceBackends := make(map[string]ServiceBackend)

    for backendName, backend := range service.Backends {
        serviceBackends[backendName] = backend
    }

    if remove {
        delete(serviceBackends, backendName)
    } else {
        serviceBackends[backendName] = serviceBackend
    }

    service.Backends = serviceBackends
}

// Apply a recursive remove from source
func (service *Service) removeBackends(source Source) {
    serviceBackends := make(map[string]ServiceBackend)

    for backendName, backend := range service.Backends {
		if source == nil || backend.Source() == source.String() {
			//log.Printf("service %s drop source=%s backends: %s source=%s\n", service.Path(), source.String(), backendName, backend.Source())

			// remove all from this source
			continue
		} else {
			//log.Printf("service %s keep source=%s backends: %s source=%s\n", service.Path(), source.String(), backendName, backend.Source())
		}

        serviceBackends[backendName] = backend
    }

    service.Backends = serviceBackends
}

type Route struct {
	Meta			`json:"-"`

    // IPv4 prefix to match
    // empty for default match
    Prefix4     string

    // Override backend IPv4 address for ipvs
    Gateway4    string

    // Configure IPVS fwd-method to use for destination
    //  droute tunnel masq
    // Filter out backend if set to
    //  filter
    IpvsMethod  string
}

// Top-level config object
type Config struct {
    Routes      map[string]Route
    Services    map[string]Service
}

// Copy-on-write update for .Services 
func (config *Config) setService(serviceName string, service Service, remove bool) {
    services := make(map[string]Service)

    for serviceName, service := range config.Services {
        services[serviceName] = service
    }

    if remove {
        delete(services, serviceName)
    } else {
        services[serviceName] = service
    }

    config.Services = services
}

func (config *Config) updateServices(node Node) error {
	if node.Remove {
		services := make(map[string]Service)

		for serviceName, service := range config.Services {
			if node.Source == nil || service.Source() == node.Source.String() {
				// remove service from this source
				continue
			} else {
				// remove any backends from this source
				service.removeBackends(node.Source)
			}

			services[serviceName] = service
		}

{'fraction_non_alphanumeric': 0.10651499482936919, 'fraction_numerical': 0.0062047569803516025, 'mean_word_length': 3.0025866528711846, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4962695', 'n_tokens_mistral': 2390, 'n_tokens_neox': 2261, 'n_words': 677}