github.com/noisysockets/noisysockets@v0.21.2-0.20240515114641-7f467e651c90/config/config.go (about) 1 // SPDX-License-Identifier: MPL-2.0 2 /* 3 * Copyright (C) 2024 The Noisy Sockets Authors. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 */ 9 10 package config 11 12 import ( 13 "fmt" 14 "io" 15 16 "github.com/noisysockets/noisysockets/config/types" 17 "github.com/noisysockets/noisysockets/config/v1alpha1" 18 latestconfig "github.com/noisysockets/noisysockets/config/v1alpha2" 19 "github.com/noisysockets/noisysockets/internal/util" 20 "github.com/noisysockets/noisysockets/networkutil" 21 "gopkg.in/yaml.v3" 22 ) 23 24 // FromYAML reads the given reader and returns a config object. 25 func FromYAML(r io.Reader) (conf *latestconfig.Config, err error) { 26 confBytes, err := io.ReadAll(r) 27 if err != nil { 28 return nil, fmt.Errorf("failed to read config from reader: %w", err) 29 } 30 31 var typeMeta types.TypeMeta 32 if err := yaml.Unmarshal(confBytes, &typeMeta); err != nil { 33 return nil, fmt.Errorf("failed to unmarshal type meta from config file: %w", err) 34 } 35 36 var versionedConf types.Config 37 switch typeMeta.APIVersion { 38 case v1alpha1.APIVersion: 39 versionedConf, err = v1alpha1.GetConfigByKind(typeMeta.Kind) 40 case latestconfig.APIVersion: 41 versionedConf, err = latestconfig.GetConfigByKind(typeMeta.Kind) 42 default: 43 return nil, fmt.Errorf("unsupported api version: %s", typeMeta.APIVersion) 44 } 45 if err != nil { 46 return nil, fmt.Errorf("failed to get config by kind %q: %w", typeMeta.Kind, err) 47 } 48 49 if err := yaml.Unmarshal(confBytes, versionedConf); err != nil { 50 return nil, fmt.Errorf("failed to unmarshal config from config file: %w", err) 51 } 52 53 if versionedConf.GetAPIVersion() != latestconfig.APIVersion { 54 conf, err = migrate(versionedConf) 55 if err != nil { 56 return nil, fmt.Errorf("failed to migrate config: %w", err) 57 } 58 } else { 59 conf = versionedConf.(*latestconfig.Config) 60 } 61 62 // TODO: validate config? 63 64 return conf, nil 65 } 66 67 // ToYAML writes the given config object to the given writer. 68 func ToYAML(w io.Writer, versionedConf types.Config) error { 69 var conf *latestconfig.Config 70 if versionedConf.GetAPIVersion() != latestconfig.APIVersion { 71 var err error 72 conf, err = migrate(versionedConf) 73 if err != nil { 74 return fmt.Errorf("failed to migrate config: %w", err) 75 } 76 } else { 77 conf = versionedConf.(*latestconfig.Config) 78 } 79 80 conf.PopulateTypeMeta() 81 82 if err := yaml.NewEncoder(w).Encode(conf); err != nil { 83 return fmt.Errorf("failed to marshal config: %w", err) 84 } 85 86 return nil 87 } 88 89 func migrate(versionedConf types.Config) (*latestconfig.Config, error) { 90 switch conf := versionedConf.(type) { 91 case *v1alpha1.Config: 92 return migrateV1Alpha1ToV1Alpha2(conf) 93 default: 94 return nil, fmt.Errorf("unsupported config version: %s", versionedConf.GetAPIVersion()) 95 } 96 } 97 98 func migrateV1Alpha1ToV1Alpha2(conf *v1alpha1.Config) (*latestconfig.Config, error) { 99 interfaceAddrs, err := util.ParseAddrList(conf.IPs) 100 if err != nil { 101 return nil, fmt.Errorf("could not parse local addresses: %w", err) 102 } 103 104 migratedConf := &latestconfig.Config{} 105 migratedConf.PopulateTypeMeta() 106 107 migratedConf.Name = conf.Name 108 migratedConf.ListenPort = conf.ListenPort 109 migratedConf.PrivateKey = conf.PrivateKey 110 migratedConf.IPs = conf.IPs 111 112 migratedConf.Peers = make([]latestconfig.PeerConfig, len(conf.Peers)) 113 for i, peerConf := range conf.Peers { 114 migratedConf.Peers[i] = latestconfig.PeerConfig{ 115 Name: peerConf.Name, 116 PublicKey: peerConf.PublicKey, 117 Endpoint: peerConf.Endpoint, 118 IPs: peerConf.IPs, 119 } 120 } 121 122 if conf.DNSServers != nil { 123 migratedConf.DNS = &latestconfig.DNSConfig{ 124 Nameservers: conf.DNSServers, 125 } 126 } 127 128 for _, peerConf := range conf.Peers { 129 if peerConf.DefaultGateway { 130 if networkutil.HasIPv4(interfaceAddrs) { 131 peerConf.GatewayForCIDRs = append(peerConf.GatewayForCIDRs, "0.0.0.0/0") 132 } 133 if networkutil.HasIPv6(interfaceAddrs) { 134 peerConf.GatewayForCIDRs = append(peerConf.GatewayForCIDRs, "::/0") 135 } 136 } 137 138 for _, prefix := range peerConf.GatewayForCIDRs { 139 routeConf := latestconfig.RouteConfig{ 140 Destination: prefix, 141 Via: peerConf.PublicKey, 142 } 143 144 if peerConf.Name != "" { 145 routeConf.Via = peerConf.Name 146 } 147 148 migratedConf.Routes = append(migratedConf.Routes, routeConf) 149 } 150 } 151 152 return migratedConf, nil 153 }