feat: replace networkd with new network implementation

This removes networkd, updates network ready condition, enables all the
controllers which were previously disabled.

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
This commit is contained in:
Andrey Smirnov 2021-06-09 01:05:41 +03:00 committed by talos-bot
parent caec3063c8
commit f2ae9cd0c1
80 changed files with 965 additions and 4588 deletions

View File

@ -180,6 +180,7 @@ COPY --from=generate-build /api/cluster/*.pb.go /pkg/machinery/api/cluster/
COPY --from=generate-build /api/storage/*.pb.go /pkg/machinery/api/storage/
COPY --from=generate-build /api/resource/*.pb.go /pkg/machinery/api/resource/
COPY --from=generate-build /api/inspect/*.pb.go /pkg/machinery/api/inspect/
COPY --from=go-generate /src/pkg/resources/network/ /pkg/resources/network/
COPY --from=go-generate /src/pkg/machinery/config/types/v1alpha1/ /pkg/machinery/config/types/v1alpha1/
COPY --from=go-generate /src/pkg/machinery/nethelpers/ /pkg/machinery/nethelpers/

20
go.mod
View File

@ -44,27 +44,39 @@ require (
github.com/gizak/termui/v3 v3.1.0
github.com/golang/protobuf v1.5.2
github.com/google/go-cmp v0.5.6
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.2.0
github.com/googleapis/gnostic v0.5.5 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/hashicorp/go-getter v1.5.3
github.com/hashicorp/go-multierror v1.1.1
github.com/imdario/mergo v0.3.12 // indirect
github.com/insomniacslk/dhcp v0.0.0-20210528123148-fb4eaaa00ad2
github.com/jsimonetti/rtnetlink v0.0.0-20210531051304-b34cb89a106b
github.com/magiconair/properties v1.8.5 // indirect
github.com/mattn/go-isatty v0.0.13
github.com/mdlayher/arp v0.0.0-20191213142603-f72070a231fc
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43
github.com/mdlayher/genetlink v1.0.0
github.com/mdlayher/netlink v1.4.1
github.com/mdlayher/raw v0.0.0-20210412142147-51b895745faf // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d
github.com/pelletier/go-toml v1.9.0 // indirect
github.com/pin/tftp v2.1.0+incompatible
github.com/plunder-app/kube-vip v0.3.5
github.com/prometheus/client_golang v1.10.0 // indirect
github.com/prometheus/common v0.23.0 // indirect
github.com/prometheus/procfs v0.6.0
github.com/rivo/tview v0.0.0-20210531104647-807e706f86d1
github.com/rs/xid v1.3.0
github.com/ryanuber/columnize v2.1.2+incompatible
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/smira/go-xz v0.0.0-20201019130106-9921ed7a9935
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/cobra v1.1.3
github.com/spf13/viper v1.7.1 // indirect
github.com/stretchr/testify v1.7.0
github.com/talos-systems/crypto v0.2.1-0.20210601174604-cd18ef62eb9f
github.com/talos-systems/go-blockdevice v0.2.1-0.20210526155905-30c2bc3cb62a
@ -79,6 +91,7 @@ require (
github.com/talos-systems/net v0.2.1-0.20210212213224-05190541b0fa
github.com/talos-systems/talos/pkg/machinery v0.0.0-00010101000000-000000000000
github.com/u-root/u-root v7.0.0+incompatible
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
github.com/vmware-tanzu/sonobuoy v0.50.0
github.com/vmware/govmomi v0.26.0
github.com/vmware/vmw-guestinfo v0.0.0-20200218095840-687661b8bd8e
@ -88,14 +101,17 @@ require (
go.etcd.io/etcd/etcdutl/v3 v3.5.0-rc.0
go.uber.org/zap v1.17.0
golang.org/x/net v0.0.0-20210525063256-abc453219eb5
golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20210531080801-fdfd190a6549
golang.org/x/term v0.0.0-20210503060354-a79de5458b56
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210506160403-92e472f520a5
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/grpc v1.38.0
google.golang.org/protobuf v1.26.0
gopkg.in/freddierice/go-losetup.v1 v1.0.0-20170407175016-fc9adea44124
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
inet.af/netaddr v0.0.0-20210430201628-1d252cf8125e
k8s.io/api v0.21.1
@ -105,4 +121,6 @@ require (
k8s.io/cri-api v0.21.1
k8s.io/kubectl v0.21.1
k8s.io/kubelet v0.21.1
k8s.io/utils v0.0.0-20210305010621-2afb4311ab10 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.1.1 // indirect
)

56
go.sum
View File

@ -73,8 +73,6 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU=
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
@ -119,7 +117,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alessio/shellescape v1.2.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
@ -130,8 +127,6 @@ github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 h1:7Ip0wMmLHLRJdrloD
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=
github.com/armon/go-metrics v0.3.8/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
@ -197,8 +192,6 @@ github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJ
github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/cilium/ebpf v0.6.0 h1:hOQqNhQdMIi0zmjii4jKUnI0i+NB7ApvTXs2MstI5S0=
github.com/cilium/ebpf v0.6.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
@ -351,7 +344,6 @@ github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yezR0y7jJ5pceLHthLaYf4bA5T14B6q39S4q2Q=
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A=
@ -406,13 +398,11 @@ github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch/v5 v5.1.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/firecracker-microvm/firecracker-go-sdk v0.22.0 h1:hk28AO5ArAX9iHomi6axNLK+6+8gz1wi3ooNsUTlSFQ=
@ -640,8 +630,6 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs=
@ -700,27 +688,21 @@ github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-getter v1.5.3 h1:NF5+zOlQegim+w/EUhSLh6QhXHmZMEeHLQzllkQ3ROU=
github.com/hashicorp/go-getter v1.5.3/go.mod h1:BrrV/1clo8cCYu6mxvboYg+KutTiFnXjMEgDD8+i7ZI=
github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v0.16.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.0 h1:8exGP7ego3OmkfksihtSouGMZ+hQrhxx+FVELeXpVPE=
github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-memdb v1.3.2 h1:RBKHOsnSszpU6vxq80LzC2BaQjuuvoyaQbkLTf7V7g8=
github.com/hashicorp/go-memdb v1.3.2/go.mod h1:Mluclgwib3R93Hk5fxEfiRhB+6Dar64wWh71LpNSe3g=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-msgpack v1.1.5/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YNK9NfGLXJ69/4=
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
@ -742,7 +724,6 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/raft v1.3.1/go.mod h1:4Ak7FSPnuvmb0GV6vgIAJ4vYT4bek9bb6Q+7HVbyzqM=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
@ -762,7 +743,6 @@ github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod
github.com/insomniacslk/dhcp v0.0.0-20210528123148-fb4eaaa00ad2 h1:WDOgJoE6rb7G6A7i1/Yyh5FJeydXeUrXHMRYJo7iFak=
github.com/insomniacslk/dhcp v0.0.0-20210528123148-fb4eaaa00ad2/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
@ -773,7 +753,6 @@ github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9q
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA=
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
@ -800,8 +779,6 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/k-sone/critbitgo v1.4.0/go.mod h1:7E6pyoyADnFxlUBEKcnfS49b7SUAQGMK+OAp/UQvo0s=
github.com/kamhlos/upnp v0.0.0-20210324072331-5661950dff08/go.mod h1:0L/S1RSG4wA4M2Vhau3z7VsYMLxFnsX0bzzgwYRIdYU=
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
@ -856,13 +833,11 @@ github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kN
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
@ -885,8 +860,6 @@ github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43 h1:WgyLFv10Ov49JA
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0=
github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=
github.com/mdlayher/ndp v0.0.0-20200602162440-17ab9e3e5567 h1:x+xs91ZJ+lr0C6sedWeREvck4uGCt+AA1kKXwsHB6jI=
github.com/mdlayher/ndp v0.0.0-20200602162440-17ab9e3e5567/go.mod h1:32w/5dDZWVSEOxyniAgKK4d7dHTuO6TCxWmUznQe3f8=
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
@ -1026,11 +999,8 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/osrg/gobgp v2.0.0+incompatible/go.mod h1:vGVJPLW6JFDD7WA1vJsjB8OKmbbC2TKwHtr90CZS/u4=
github.com/packethost/packngo v0.13.0/go.mod h1:YrtUNN9IRjjqN6zK+cy2IYoi3EjHfoWTWxJkI1I1Vk0=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
@ -1051,21 +1021,17 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/plunder-app/kube-vip v0.3.5 h1:+i4TLCWbFNNj8RHfF6BHAReOI0NCxxUAGhX4/ZdlA34=
github.com/plunder-app/kube-vip v0.3.5/go.mod h1:lN0PiOK6uwByLTLLSM45En/VOycZz3/8wQ/UQl7Qu3E=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.10.0 h1:/o0BDeWzLWXNZ+4q5gXltUvaMpJqckTa+jTNoB+z4cg=
@ -1080,7 +1046,6 @@ github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
@ -1093,7 +1058,6 @@ github.com/prometheus/common v0.23.0 h1:GXWvPYuTUenIa+BhOq/x+L/QZzCqASkVRny5KTlP
github.com/prometheus/common v0.23.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMDk7UAI2qm7Q=
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@ -1122,8 +1086,6 @@ github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rtr7/dhcp4 v0.0.0-20181120124042-778e8c2e24a5 h1:/kzTBQ20DbbhSNaBXiFEk2gPrGhY26kajwC1ro/Vlh8=
github.com/rtr7/dhcp4 v0.0.0-20181120124042-778e8c2e24a5/go.mod h1:FwstIpm6vX98QgtR8KEwZcVjiRn2WP76LjXAHj84fK0=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
@ -1256,7 +1218,6 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/u-root/u-root v7.0.0+incompatible h1:u+KSS04pSxJGI5E7WE4Bs9+Zd75QjFv+REkjy/aoAc8=
github.com/u-root/u-root v7.0.0+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY=
github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA=
@ -1276,7 +1237,6 @@ github.com/viniciuschiele/tarx v0.0.0-20151205142357-6e3da540444d h1:Z8Bp/K+wf1+
github.com/viniciuschiele/tarx v0.0.0-20151205142357-6e3da540444d/go.mod h1:8uo3DXfN526YN7JjAp4JkOMm4foTW4vPzPHaAzb4xiY=
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.1.1-0.20200221165523-c79a4b7b4066/go.mod h1:FSQhuTO7eHT34mPzX+B04SUAjiqLxtXs1et0S6l9k4k=
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852 h1:cPXZWzzG0NllBLdjWoD1nDfaqu98YMv+OneaKc8sPOA=
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
@ -1312,8 +1272,6 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f h1:Wku8eEdeJqIOFHtrfkYUByc4bCaTeA6fL0UJgfEiFMI=
gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f/go.mod h1:Tiuhl+njh/JIg0uS/sOJVYi0x2HEa5rc1OAaVsb5tAs=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
@ -1414,7 +1372,6 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -1518,7 +1475,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210505214959-0714010a04ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -1596,7 +1552,6 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200121082415-34d275377bf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1610,7 +1565,6 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200602100848-8d3cce7afc34/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1622,7 +1576,6 @@ golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200928205150-006507a75852/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1694,7 +1647,6 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190424220101-1e8e1cfdf96b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@ -1821,7 +1773,6 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210505142820-a42aa055cf76/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210524171403-669157292da3/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
@ -1925,15 +1876,12 @@ k8s.io/api v0.18.5/go.mod h1:tN+e/2nbdGKOAH55NMV8oGrMG+3uRlA9GaRfvnCCSNk=
k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ=
k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8=
k8s.io/api v0.21.0/go.mod h1:+YbrhBBGgsxbF6o6Kj4KJPJnBmAKuXDeS3E18bgHNVU=
k8s.io/api v0.21.1 h1:94bbZ5NTjdINJEdzOkpS4vdPhkb1VFpTYC9zh43f75c=
k8s.io/api v0.21.1/go.mod h1:FstGROTmsSHBarKc8bylzXih8BLNYTiS3TZcsoEDg2s=
k8s.io/apimachinery v0.18.5/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc=
k8s.io/apimachinery v0.21.0/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY=
k8s.io/apimachinery v0.21.1 h1:Q6XuHGlj2xc+hlMCvqyYfbv3H7SRGn2c8NycxJquDVs=
k8s.io/apimachinery v0.21.1/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY=
k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
@ -1947,7 +1895,6 @@ k8s.io/client-go v0.18.5/go.mod h1:EsiD+7Fx+bRckKWZXnAXRKKetm1WuzPagH4iOSC8x58=
k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k=
k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0=
k8s.io/client-go v0.21.0/go.mod h1:nNBytTF9qPFDEhoqgEPaarobC8QPae13bElIVHzIglA=
k8s.io/client-go v0.21.1 h1:bhblWYLZKUu+pm50plvQF8WpY6TXdRRtcS/K9WauOj4=
k8s.io/client-go v0.21.1/go.mod h1:/kEw4RgW+3xnBGzvp9IWxKSNA+lXn3A7AuH3gdOAzLs=
k8s.io/code-generator v0.21.1/go.mod h1:hUlps5+9QaTrKx+jiM4rmq7YmH8wPOIko64uZCHDh6Q=
@ -1976,7 +1923,6 @@ k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts=
k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 h1:vEx13qjvaZ4yfObSSXW7BrMc/KQBBT/Jyee8XtLf4x0=
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE=
@ -1995,7 +1941,6 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
sigs.k8s.io/kind v0.10.0/go.mod h1:fb32zUw7ewC47bPwLnwhf47wd/vADtv3c38KP7sjIlo=
sigs.k8s.io/kustomize/api v0.8.8 h1:G2z6JPSSjtWWgMeWSoHdXqyftJNmMmyxXpwENGoOtGE=
sigs.k8s.io/kustomize/api v0.8.8/go.mod h1:He1zoK0nk43Pc6NlV085xDXDXTNprtcyKZVm3swsdNY=
sigs.k8s.io/kustomize/cmd/config v0.9.10/go.mod h1:Mrby0WnRH7hA6OwOYnYpfpiY0WJIMgYrEDfwOeFdMK0=
@ -2004,7 +1949,6 @@ sigs.k8s.io/kustomize/kyaml v0.10.17 h1:4zrV0ym5AYa0e512q7K3Wp1u7mzoWW0xR3UHJcGW
sigs.k8s.io/kustomize/kyaml v0.10.17/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4cPcfUVt5Hg=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=

View File

@ -46,7 +46,7 @@ Added the flag `cluster.coreDNS.disabled` to coreDNS deployment during the clust
title = "Default to Bootstrap workflow"
description = """\
The `init.yaml` is no longer an output of `talosctl gen config`.
We now encourage using the bootstrap API, instead it `init` node types, as we
We now encourage using the bootstrap API, instead of `init` node types, as we
intend on deprecating this machine type in the future.
The `init.yaml` and `controlplane.yaml` machine configs are identical with the
exception of the machine type.

View File

@ -137,6 +137,17 @@ func (suite *EtcFileSuite) TestFiles() {
}
}
func (suite *EtcFileSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
suite.Assert().NoError(suite.state.Create(context.Background(), files.NewEtcFileSpec(files.NamespaceName, "bar")))
}
func TestEtcFileSuite(t *testing.T) {
suite.Run(t, new(EtcFileSuite))
}

View File

@ -22,7 +22,7 @@ import (
"github.com/talos-systems/talos/pkg/resources/config"
"github.com/talos-systems/talos/pkg/resources/k8s"
"github.com/talos-systems/talos/pkg/resources/v1alpha1"
"github.com/talos-systems/talos/pkg/resources/network"
)
// ExtraManifestController renders manifests based on templates and config/secrets.
@ -43,9 +43,9 @@ func (ctrl *ExtraManifestController) Inputs() []controller.Input {
Kind: controller.InputWeak,
},
{
Namespace: v1alpha1.NamespaceName,
Type: v1alpha1.ServiceType,
ID: pointer.ToString("networkd"),
Namespace: network.NamespaceName,
Type: network.StatusType,
ID: pointer.ToString(network.StatusID),
Kind: controller.InputWeak,
},
}
@ -72,8 +72,8 @@ func (ctrl *ExtraManifestController) Run(ctx context.Context, r controller.Runti
case <-r.EventCh():
}
// wait for networkd to be healthy as networking is required to download extra manifests
networkdResource, err := r.Get(ctx, resource.NewMetadata(v1alpha1.NamespaceName, v1alpha1.ServiceType, "networkd", resource.VersionUndefined))
// wait for network to be ready as networking is required to download extra manifests
networkResource, err := r.Get(ctx, resource.NewMetadata(network.NamespaceName, network.StatusType, network.StatusID, resource.VersionUndefined))
if err != nil {
if state.IsNotFoundError(err) {
continue
@ -82,7 +82,9 @@ func (ctrl *ExtraManifestController) Run(ctx context.Context, r controller.Runti
return err
}
if !networkdResource.(*v1alpha1.Service).Healthy() {
networkStatus := networkResource.(*network.Status).TypedSpec()
if !(networkStatus.AddressReady && networkStatus.ConnectivityReady) {
continue
}

View File

@ -26,6 +26,7 @@ import (
"github.com/talos-systems/talos/pkg/logging"
"github.com/talos-systems/talos/pkg/resources/config"
"github.com/talos-systems/talos/pkg/resources/k8s"
"github.com/talos-systems/talos/pkg/resources/network"
"github.com/talos-systems/talos/pkg/resources/v1alpha1"
)
@ -108,12 +109,12 @@ metadata:
},
})
serviceNetworkd := v1alpha1.NewService("networkd")
serviceNetworkd.SetRunning(true)
serviceNetworkd.SetHealthy(true)
statusNetwork := network.NewStatus(network.NamespaceName, network.StatusID)
statusNetwork.TypedSpec().AddressReady = true
statusNetwork.TypedSpec().ConnectivityReady = true
suite.Require().NoError(suite.state.Create(suite.ctx, configExtraManifests))
suite.Require().NoError(suite.state.Create(suite.ctx, serviceNetworkd))
suite.Require().NoError(suite.state.Create(suite.ctx, statusNetwork))
suite.Assert().NoError(retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {

View File

@ -16,6 +16,7 @@ import (
"go.uber.org/zap"
"inet.af/netaddr"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
talosconfig "github.com/talos-systems/talos/pkg/machinery/config"
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
"github.com/talos-systems/talos/pkg/resources/config"
@ -24,7 +25,8 @@ import (
// AddressConfigController manages network.AddressSpec based on machine configuration, kernel cmdline and some built-in defaults.
type AddressConfigController struct {
Cmdline *procfs.Cmdline
Cmdline *procfs.Cmdline
V1Alpha1Mode runtime.Mode
}
// Name implements controller.Controller interface.
@ -179,6 +181,11 @@ func (ctrl *AddressConfigController) apply(ctx context.Context, r controller.Run
}
func (ctrl *AddressConfigController) loopbackDefaults() []network.AddressSpecSpec {
if ctrl.V1Alpha1Mode == runtime.ModeContainer {
// skip configuring lo addresses in container mode
return nil
}
return []network.AddressSpecSpec{
{
Address: netaddr.IPPrefix{

View File

@ -235,6 +235,25 @@ func (suite *AddressConfigSuite) TestMachineConfiguration() {
}))
}
func (suite *AddressConfigSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
err := suite.state.Create(context.Background(), config.NewMachineConfig(&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{},
}))
if state.IsConflictError(err) {
err = suite.state.Destroy(context.Background(), config.NewMachineConfig(nil).Metadata())
}
suite.Require().NoError(err)
}
func TestAddressConfigSuite(t *testing.T) {
suite.Run(t, new(AddressConfigSuite))
}

View File

@ -3,6 +3,8 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Package network provides controllers which manage network resources.
//
//nolint:dupl
package network
import (

View File

@ -190,6 +190,17 @@ func (suite *AddressMergeSuite) TestMerge() {
}))
}
func (suite *AddressMergeSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewAddressSpec(network.ConfigNamespaceName, "bar")))
}
func TestAddressMergeSuite(t *testing.T) {
suite.Run(t, new(AddressMergeSuite))
}

View File

@ -6,8 +6,10 @@ package network
import (
"context"
"errors"
"fmt"
"net"
"os"
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
@ -48,7 +50,7 @@ func (ctrl *AddressSpecController) Outputs() []controller.Output {
// Run implements controller.Controller interface.
//
//nolint:gocyclo,dupl
//nolint:gocyclo
func (ctrl *AddressSpecController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
// watch link changes as some address might need to be re-applied if the link appears
watcher, err := watch.NewRtNetlink(r, unix.RTMGRP_LINK)
@ -219,7 +221,10 @@ func (ctrl *AddressSpecController) syncAddress(ctx context.Context, r controller
Flags: uint32(address.TypedSpec().Flags),
},
}); err != nil {
return fmt.Errorf("error adding address %s to %q: %w", address.TypedSpec().Address, address.TypedSpec().LinkName, err)
// ignore EEXIST error
if !errors.Is(err, os.ErrExist) {
return fmt.Errorf("error adding address %s to %q: %w", address.TypedSpec().Address, address.TypedSpec().LinkName, err)
}
}
logger.Info("assigned address", zap.Stringer("address", address.TypedSpec().Address), zap.String("link", address.TypedSpec().LinkName))

View File

@ -9,6 +9,7 @@ import (
"context"
"fmt"
"log"
"math/rand"
"net"
"sync"
"testing"
@ -58,6 +59,10 @@ func (suite *AddressSpecSuite) SetupTest() {
suite.startRuntime()
}
func (suite *AddressSpecSuite) uniqueDummyInterface() string {
return fmt.Sprintf("dummy%02x%02x%02x", rand.Int31()&0xff, rand.Int31()&0xff, rand.Int31()&0xff)
}
func (suite *AddressSpecSuite) startRuntime() {
suite.wg.Add(1)
@ -163,7 +168,7 @@ func (suite *AddressSpecSuite) TestLoopback() {
}
func (suite *AddressSpecSuite) TestDummy() {
const dummyInterface = "dummy9"
dummyInterface := suite.uniqueDummyInterface()
conn, err := rtnetlink.Dial(nil)
suite.Require().NoError(err)
@ -223,6 +228,17 @@ func (suite *AddressSpecSuite) TestDummy() {
}
}
func (suite *AddressSpecSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewAddressSpec(network.NamespaceName, "bar")))
}
func TestAddressSpecSuite(t *testing.T) {
suite.Run(t, new(AddressSpecSuite))
}

View File

@ -104,6 +104,14 @@ func (suite *AddressStatusSuite) TestLoopback() {
}))
}
func (suite *AddressStatusSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
}
func TestAddressStatusSuite(t *testing.T) {
suite.Run(t, new(AddressStatusSuite))
}

View File

@ -240,6 +240,29 @@ func (suite *EtcFileConfigSuite) TestOnlyHostname() {
)
}
func (suite *EtcFileConfigSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
err := suite.state.Create(context.Background(), config.NewMachineConfig(&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{},
}))
if state.IsConflictError(err) {
err = suite.state.Destroy(context.Background(), config.NewMachineConfig(nil).Metadata())
}
suite.Require().NoError(err)
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewHostnameStatus(network.NamespaceName, "bar")))
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewResolverStatus(network.NamespaceName, "bar")))
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewNodeAddress(network.NamespaceName, "bar")))
}
func TestEtcFileConfigSuite(t *testing.T) {
suite.Run(t, new(EtcFileConfigSuite))
}

View File

@ -209,6 +209,27 @@ func (suite *HostnameConfigSuite) TestMachineConfiguration() {
}))
}
func (suite *HostnameConfigSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
err := suite.state.Create(context.Background(), config.NewMachineConfig(&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{},
}))
if state.IsConflictError(err) {
err = suite.state.Destroy(context.Background(), config.NewMachineConfig(nil).Metadata())
}
suite.Require().NoError(err)
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewNodeAddress(network.NamespaceName, "bar")))
}
func TestHostnameConfigSuite(t *testing.T) {
suite.Run(t, new(HostnameConfigSuite))
}

View File

@ -132,6 +132,8 @@ func (suite *HostnameMergeSuite) TestMerge() {
"hostname",
}, func(r *network.HostnameSpec) error {
suite.Assert().Equal("bar.com", r.TypedSpec().FQDN())
suite.Assert().Equal("bar", r.TypedSpec().Hostname)
suite.Assert().Equal("com", r.TypedSpec().Domainname)
return nil
})
@ -153,6 +155,17 @@ func (suite *HostnameMergeSuite) TestMerge() {
}))
}
func (suite *HostnameMergeSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewHostnameSpec(network.ConfigNamespaceName, "bar")))
}
func TestHostnameMergeSuite(t *testing.T) {
suite.Run(t, new(HostnameMergeSuite))
}

View File

@ -104,6 +104,8 @@ func (ctrl *HostnameSpecController) Run(ctx context.Context, r controller.Runtim
// apply hostname unless running in container mode
if ctrl.V1Alpha1Mode != v1alpha1runtime.ModeContainer {
logger.Info("setting hostname", zap.String("hostname", spec.TypedSpec().Hostname), zap.String("domainname", spec.TypedSpec().Domainname))
if err = unix.Sethostname([]byte(spec.TypedSpec().Hostname)); err != nil {
return fmt.Errorf("error setting hostname: %w", err)
}

View File

@ -101,6 +101,17 @@ func (suite *HostnameSpecSuite) TestSpec() {
}))
}
func (suite *HostnameSpecSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewHostnameSpec(network.NamespaceName, "bar")))
}
func TestHostnameSpecSuite(t *testing.T) {
suite.Run(t, new(HostnameSpecSuite))
}

View File

@ -382,6 +382,27 @@ func (suite *LinkConfigSuite) TestDefaultUp() {
}))
}
func (suite *LinkConfigSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
err := suite.state.Create(context.Background(), config.NewMachineConfig(&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{},
}))
if state.IsConflictError(err) {
err = suite.state.Destroy(context.Background(), config.NewMachineConfig(nil).Metadata())
}
suite.Require().NoError(err)
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewLinkStatus(network.NamespaceName, "bar")))
}
func TestLinkConfigSuite(t *testing.T) {
suite.Run(t, new(LinkConfigSuite))
}

View File

@ -185,6 +185,17 @@ func (suite *LinkMergeSuite) TestMerge() {
}))
}
func (suite *LinkMergeSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewLinkSpec(network.ConfigNamespaceName, "bar")))
}
func TestLinkMergeSuite(t *testing.T) {
suite.Run(t, new(LinkMergeSuite))
}

View File

@ -554,6 +554,17 @@ func (suite *LinkSpecSuite) TestWireguard() {
}))
}
func (suite *LinkSpecSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewLinkSpec(network.NamespaceName, "bar")))
}
func TestLinkSpecSuite(t *testing.T) {
suite.Run(t, new(LinkSpecSuite))
}

View File

@ -9,6 +9,7 @@ import (
"context"
"fmt"
"log"
"math/rand"
"net"
"sync"
"testing"
@ -67,6 +68,10 @@ func (suite *LinkStatusSuite) startRuntime() {
}()
}
func (suite *LinkStatusSuite) uniqueDummyInterface() string {
return fmt.Sprintf("dummy%02x%02x%02x", rand.Int31()&0xff, rand.Int31()&0xff, rand.Int31()&0xff)
}
func (suite *LinkStatusSuite) assertInterfaces(requiredIDs []string, check func(*network.LinkStatus) error) error {
missingIDs := make(map[string]struct{}, len(requiredIDs))
@ -127,7 +132,7 @@ func (suite *LinkStatusSuite) TestLoopbackInterface() {
}
func (suite *LinkStatusSuite) TestDummyInterface() {
const dummyInterface = "dummy9"
dummyInterface := suite.uniqueDummyInterface()
conn, err := rtnetlink.Dial(nil)
suite.Require().NoError(err)
@ -187,6 +192,17 @@ func (suite *LinkStatusSuite) TestDummyInterface() {
}))
}
func (suite *LinkStatusSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewLinkRefresh(network.NamespaceName, "bar")))
}
func TestLinkStatusSuite(t *testing.T) {
suite.Run(t, new(LinkStatusSuite))
}

View File

@ -125,6 +125,18 @@ func (suite *NodeAddressSuite) TestDefaults() {
}))
}
func (suite *NodeAddressSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewAddressStatus(network.NamespaceName, "bar")))
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewLinkStatus(network.NamespaceName, "bar")))
}
func TestNodeAddressSuite(t *testing.T) {
suite.Run(t, new(NodeAddressSuite))
}

View File

@ -148,6 +148,7 @@ func (d *DHCP4) TimeServerSpecs() []network.TimeServerSpecSpec {
return d.timeservers
}
//nolint:gocyclo
func (d *DHCP4) parseAck(ack *dhcpv4.DHCPv4) {
d.mu.Lock()
defer d.mu.Unlock()
@ -194,6 +195,7 @@ func (d *DHCP4) parseAck(ack *dhcpv4.DHCPv4) {
d.routes = append(d.routes, network.RouteSpecSpec{
Family: nethelpers.FamilyInet4,
Destination: dst,
Source: addr,
Gateway: gw,
OutLinkName: d.linkName,
Table: nethelpers.TableMain,
@ -222,6 +224,10 @@ func (d *DHCP4) parseAck(ack *dhcpv4.DHCPv4) {
}
}
for i := range d.routes {
d.routes[i].Normalize()
}
if len(ack.DNS()) > 0 {
dns := make([]netaddr.IP, len(ack.DNS()))
@ -240,12 +246,20 @@ func (d *DHCP4) parseAck(ack *dhcpv4.DHCPv4) {
}
if ack.HostName() != "" {
d.hostname = []network.HostnameSpecSpec{
{
Hostname: ack.HostName(),
Domainname: ack.DomainName(),
ConfigLayer: network.ConfigOperator,
},
spec := network.HostnameSpecSpec{
ConfigLayer: network.ConfigOperator,
}
if err = spec.ParseFQDN(ack.HostName()); err == nil {
if ack.DomainName() != "" {
spec.Domainname = ack.DomainName()
}
d.hostname = []network.HostnameSpecSpec{
spec,
}
} else {
d.hostname = nil
}
} else {
d.hostname = nil

View File

@ -456,6 +456,27 @@ func (suite *OperatorConfigSuite) TestMachineConfigurationVIP() {
}))
}
func (suite *OperatorConfigSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
err := suite.state.Create(context.Background(), config.NewMachineConfig(&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{},
}))
if state.IsConflictError(err) {
err = suite.state.Destroy(context.Background(), config.NewMachineConfig(nil).Metadata())
}
suite.Require().NoError(err)
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewLinkStatus(network.NamespaceName, "bar")))
}
func TestOperatorConfigSuite(t *testing.T) {
suite.Run(t, new(OperatorConfigSuite))
}

View File

@ -0,0 +1,203 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//nolint:dupl
package network_test
import (
"context"
"fmt"
"log"
"net"
"sync"
"testing"
"time"
"github.com/cosi-project/runtime/pkg/controller/runtime"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/state"
"github.com/cosi-project/runtime/pkg/state/impl/inmem"
"github.com/cosi-project/runtime/pkg/state/impl/namespaced"
"github.com/stretchr/testify/suite"
"github.com/talos-systems/go-procfs/procfs"
"github.com/talos-systems/go-retry/retry"
netctrl "github.com/talos-systems/talos/internal/app/machined/pkg/controllers/network"
v1alpha1runtime "github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
"github.com/talos-systems/talos/pkg/logging"
"github.com/talos-systems/talos/pkg/resources/network"
)
type PlatformConfigSuite struct {
suite.Suite
state state.State
runtime *runtime.Runtime
wg sync.WaitGroup
ctx context.Context
ctxCancel context.CancelFunc
}
func (suite *PlatformConfigSuite) SetupTest() {
suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute)
suite.state = state.WrapCore(namespaced.NewState(inmem.Build))
var err error
suite.runtime, err = runtime.NewRuntime(suite.state, logging.Wrap(log.Writer()))
suite.Require().NoError(err)
}
func (suite *PlatformConfigSuite) startRuntime() {
suite.wg.Add(1)
go func() {
defer suite.wg.Done()
suite.Assert().NoError(suite.runtime.Run(suite.ctx))
}()
}
func (suite *PlatformConfigSuite) assertHostnames(requiredIDs []string, check func(*network.HostnameSpec) error) error {
missingIDs := make(map[string]struct{}, len(requiredIDs))
for _, id := range requiredIDs {
missingIDs[id] = struct{}{}
}
resources, err := suite.state.List(suite.ctx, resource.NewMetadata(network.ConfigNamespaceName, network.HostnameSpecType, "", resource.VersionUndefined))
if err != nil {
return err
}
for _, res := range resources.Items {
_, required := missingIDs[res.Metadata().ID()]
if !required {
continue
}
delete(missingIDs, res.Metadata().ID())
if err = check(res.(*network.HostnameSpec)); err != nil {
return retry.ExpectedError(err)
}
}
if len(missingIDs) > 0 {
return retry.ExpectedError(fmt.Errorf("some resources are missing: %q", missingIDs))
}
return nil
}
func (suite *PlatformConfigSuite) assertNoHostname(id string) error {
resources, err := suite.state.List(suite.ctx, resource.NewMetadata(network.ConfigNamespaceName, network.HostnameSpecType, "", resource.VersionUndefined))
if err != nil {
return err
}
for _, res := range resources.Items {
if res.Metadata().ID() == id {
return retry.ExpectedError(fmt.Errorf("spec %q is still there", id))
}
}
return nil
}
func (suite *PlatformConfigSuite) TestNoPlatform() {
suite.Require().NoError(suite.runtime.RegisterController(&netctrl.PlatformConfigController{}))
suite.startRuntime()
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {
return suite.assertNoHostname("platform/hostname")
}))
}
func (suite *PlatformConfigSuite) TestPlatformMock() {
suite.Require().NoError(suite.runtime.RegisterController(&netctrl.PlatformConfigController{
V1alpha1Platform: &platformMock{hostname: []byte("talos-e2e-897b4e49-gcp-controlplane-jvcnl.c.talos-testbed.internal")},
}))
suite.startRuntime()
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {
return suite.assertHostnames([]string{
"platform/hostname",
}, func(r *network.HostnameSpec) error {
suite.Assert().Equal("talos-e2e-897b4e49-gcp-controlplane-jvcnl", r.TypedSpec().Hostname)
suite.Assert().Equal("c.talos-testbed.internal", r.TypedSpec().Domainname)
suite.Assert().Equal(network.ConfigPlatform, r.TypedSpec().ConfigLayer)
return nil
})
}))
}
func (suite *PlatformConfigSuite) TestPlatformMockNoDomain() {
suite.Require().NoError(suite.runtime.RegisterController(&netctrl.PlatformConfigController{
V1alpha1Platform: &platformMock{hostname: []byte("talos-e2e-897b4e49-gcp-controlplane-jvcnl")},
}))
suite.startRuntime()
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {
return suite.assertHostnames([]string{
"platform/hostname",
}, func(r *network.HostnameSpec) error {
suite.Assert().Equal("talos-e2e-897b4e49-gcp-controlplane-jvcnl", r.TypedSpec().Hostname)
suite.Assert().Equal("", r.TypedSpec().Domainname)
suite.Assert().Equal(network.ConfigPlatform, r.TypedSpec().ConfigLayer)
return nil
})
}))
}
func (suite *PlatformConfigSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
}
func TestPlatformConfigSuite(t *testing.T) {
suite.Run(t, new(PlatformConfigSuite))
}
type platformMock struct {
hostname []byte
}
func (mock *platformMock) Name() string {
return "mock"
}
func (mock *platformMock) Configuration(context.Context) ([]byte, error) {
return nil, nil
}
func (mock *platformMock) Hostname(context.Context) ([]byte, error) {
return mock.hostname, nil
}
func (mock *platformMock) Mode() v1alpha1runtime.Mode {
return v1alpha1runtime.ModeCloud
}
func (mock *platformMock) ExternalIPs(context.Context) ([]net.IP, error) {
return nil, nil
}
func (mock *platformMock) KernelArgs() procfs.Parameters {
return nil
}

View File

@ -199,6 +199,25 @@ func (suite *ResolverConfigSuite) TestMachineConfiguration() {
}))
}
func (suite *ResolverConfigSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
err := suite.state.Create(context.Background(), config.NewMachineConfig(&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{},
}))
if state.IsConflictError(err) {
err = suite.state.Destroy(context.Background(), config.NewMachineConfig(nil).Metadata())
}
suite.Require().NoError(err)
}
func TestResolverConfigSuite(t *testing.T) {
suite.Run(t, new(ResolverConfigSuite))
}

View File

@ -154,6 +154,17 @@ func (suite *ResolverMergeSuite) TestMerge() {
}))
}
func (suite *ResolverMergeSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewResolverSpec(network.ConfigNamespaceName, "bar")))
}
func TestResolverMergeSuite(t *testing.T) {
suite.Run(t, new(ResolverMergeSuite))
}

View File

@ -101,6 +101,17 @@ func (suite *ResolverSpecSuite) TestSpec() {
}))
}
func (suite *ResolverSpecSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewResolverSpec(network.NamespaceName, "bar")))
}
func TestResolverSpecSuite(t *testing.T) {
suite.Run(t, new(ResolverSpecSuite))
}

View File

@ -200,15 +200,12 @@ func (ctrl *RouteConfigController) parseCmdline(logger *zap.Logger) (route netwo
route.OutLinkName = settings.LinkName
route.ConfigLayer = network.ConfigCmdline
route.Normalize()
return route
}
var (
zero16 = netaddr.MustParseIP("::")
zero4 = netaddr.MustParseIP("0.0.0.0")
)
//nolint:gocyclo,cyclop
//nolint:gocyclo
func (ctrl *RouteConfigController) parseMachineConfiguration(logger *zap.Logger, cfgProvider talosconfig.Provider) (routes []network.RouteSpecSpec) {
convert := func(linkName string, in talosconfig.Route) (route network.RouteSpecSpec, err error) {
if in.Network() != "" {
@ -216,11 +213,6 @@ func (ctrl *RouteConfigController) parseMachineConfiguration(logger *zap.Logger,
if err != nil {
return route, fmt.Errorf("error parsing route network: %w", err)
}
if route.Destination.Bits == 0 && (route.Destination.IP.Compare(zero4) == 0 || route.Destination.IP.Compare(zero16) == 0) {
// clear destination to be zero value to support "0.0.0.0/0" routes
route.Destination = netaddr.IPPrefix{}
}
}
route.Gateway, err = netaddr.ParseIP(in.Gateway())
@ -228,6 +220,8 @@ func (ctrl *RouteConfigController) parseMachineConfiguration(logger *zap.Logger,
return route, fmt.Errorf("error parsing route gateway: %w", err)
}
route.Normalize()
route.Priority = in.Metric()
if route.Priority == 0 {
route.Priority = DefaultRouteMetric
@ -244,15 +238,6 @@ func (ctrl *RouteConfigController) parseMachineConfiguration(logger *zap.Logger,
route.OutLinkName = linkName
route.ConfigLayer = network.ConfigMachineConfiguration
switch {
case route.Destination.IP.IsLinkLocalUnicast() || route.Destination.IP.IsLinkLocalMulticast():
route.Scope = nethelpers.ScopeLink
case route.Destination.IP.IsLoopback():
route.Scope = nethelpers.ScopeHost
default:
route.Scope = nethelpers.ScopeGlobal
}
route.Type = nethelpers.TypeUnicast
if route.Destination.IP.IsMulticast() {

View File

@ -106,7 +106,7 @@ func (suite *RouteConfigSuite) TestCmdline() {
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {
return suite.assertRoutes([]string{
"cmdline//172.20.0.1",
"cmdline/172.20.0.1/",
}, func(r *network.RouteSpec) error {
suite.Assert().Equal("eth1", r.TypedSpec().OutLinkName)
suite.Assert().Equal(network.ConfigCmdline, r.TypedSpec().ConfigLayer)
@ -195,20 +195,20 @@ func (suite *RouteConfigSuite) TestMachineConfiguration() {
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {
return suite.assertRoutes([]string{
"configuration//2001:470:6d:30e:8ed2:b60c:9d2f:803b",
"configuration/10.0.3.0/24/10.0.3.1",
"configuration/192.168.0.0/18/192.168.0.25",
"configuration/2001:470:6d:30e:8ed2:b60c:9d2f:803b/",
"configuration/10.0.3.1/10.0.3.0/24",
"configuration/192.168.0.25/192.168.0.0/18",
}, func(r *network.RouteSpec) error {
switch r.Metadata().ID() {
case "configuration//2001:470:6d:30e:8ed2:b60c:9d2f:803b":
case "configuration/2001:470:6d:30e:8ed2:b60c:9d2f:803b/":
suite.Assert().Equal("eth2", r.TypedSpec().OutLinkName)
suite.Assert().Equal(nethelpers.FamilyInet6, r.TypedSpec().Family)
suite.Assert().EqualValues(netctrl.DefaultRouteMetric, r.TypedSpec().Priority)
case "configuration/10.0.3.0/24/10.0.3.1":
case "configuration/10.0.3.1/10.0.3.0/24":
suite.Assert().Equal("eth0.24", r.TypedSpec().OutLinkName)
suite.Assert().Equal(nethelpers.FamilyInet4, r.TypedSpec().Family)
suite.Assert().EqualValues(netctrl.DefaultRouteMetric, r.TypedSpec().Priority)
case "configuration/192.168.0.0/18/192.168.0.25":
case "configuration/192.168.0.25/192.168.0.0/18":
suite.Assert().Equal("eth3", r.TypedSpec().OutLinkName)
suite.Assert().Equal(nethelpers.FamilyInet4, r.TypedSpec().Family)
suite.Assert().EqualValues(25, r.TypedSpec().Priority)
@ -221,6 +221,25 @@ func (suite *RouteConfigSuite) TestMachineConfiguration() {
}))
}
func (suite *RouteConfigSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
err := suite.state.Create(context.Background(), config.NewMachineConfig(&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{},
}))
if state.IsConflictError(err) {
err = suite.state.Destroy(context.Background(), config.NewMachineConfig(nil).Metadata())
}
suite.Require().NoError(err)
}
func TestRouteConfigSuite(t *testing.T) {
suite.Run(t, new(RouteConfigSuite))
}

View File

@ -3,6 +3,8 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Package network provides controllers which manage network resources.
//
//nolint:dupl
package network
import (

View File

@ -154,13 +154,13 @@ func (suite *RouteMergeSuite) TestMerge() {
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {
return suite.assertRoutes([]string{
"/10.5.0.3",
"10.0.0.35/32/10.0.0.34",
"10.5.0.3/",
"10.0.0.34/10.0.0.35/32",
}, func(r *network.RouteSpec) error {
switch r.Metadata().ID() {
case "/10.5.0.3":
case "10.5.0.3/":
suite.Assert().Equal(*dhcp.TypedSpec(), *r.TypedSpec())
case "10.0.0.35/32/10.0.0.34":
case "10.0.0.34/10.0.0.35/32":
suite.Assert().Equal(*static.TypedSpec(), *r.TypedSpec())
}
@ -173,16 +173,16 @@ func (suite *RouteMergeSuite) TestMerge() {
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {
return suite.assertRoutes([]string{
"/10.5.0.3",
"10.0.0.35/32/10.0.0.34",
"10.5.0.3/",
"10.0.0.34/10.0.0.35/32",
}, func(r *network.RouteSpec) error {
switch r.Metadata().ID() {
case "/10.5.0.3":
case "10.5.0.3/":
if *cmdline.TypedSpec() != *r.TypedSpec() {
// using retry here, as it might not be reconciled immediately
return retry.ExpectedError(fmt.Errorf("not equal yet"))
}
case "10.0.0.35/32/10.0.0.34":
case "10.0.0.34/10.0.0.35/32":
suite.Assert().Equal(*static.TypedSpec(), *r.TypedSpec())
}
@ -194,10 +194,21 @@ func (suite *RouteMergeSuite) TestMerge() {
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {
return suite.assertNoRoute("10.0.0.35/32/10.0.0.34")
return suite.assertNoRoute("10.0.0.34/10.0.0.35/32")
}))
}
func (suite *RouteMergeSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewRouteSpec(network.ConfigNamespaceName, "bar")))
}
func TestRouteMergeSuite(t *testing.T) {
suite.Run(t, new(RouteMergeSuite))
}

View File

@ -10,12 +10,14 @@ import (
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/hashicorp/go-multierror"
"github.com/jsimonetti/rtnetlink"
"go.uber.org/zap"
"golang.org/x/sys/unix"
"inet.af/netaddr"
"github.com/talos-systems/talos/internal/app/machined/pkg/controllers/network/watch"
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
"github.com/talos-systems/talos/pkg/resources/network"
)
@ -45,10 +47,10 @@ func (ctrl *RouteSpecController) Outputs() []controller.Output {
// Run implements controller.Controller interface.
//
//nolint:gocyclo,dupl
//nolint:gocyclo
func (ctrl *RouteSpecController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
// watch link changes as some routes might need to be re-applied if the link appears
watcher, err := watch.NewRtNetlink(r, unix.RTMGRP_LINK)
watcher, err := watch.NewRtNetlink(r, unix.RTMGRP_LINK|unix.RTMGRP_IPV4_ROUTE)
if err != nil {
return err
}
@ -98,18 +100,24 @@ func (ctrl *RouteSpecController) Run(ctx context.Context, r controller.Runtime,
return fmt.Errorf("error listing addresses: %w", err)
}
// loop over route and make reconcile decision
var multiErr *multierror.Error
// loop over routes and make reconcile decision
for _, res := range list.Items {
route := res.(*network.RouteSpec) //nolint:forcetypeassert,errcheck
if err = ctrl.syncRoute(ctx, r, logger, conn, links, routes, route); err != nil {
return err
multiErr = multierror.Append(multiErr, err)
}
}
if err = multiErr.ErrorOrNil(); err != nil {
return err
}
}
}
func findRoutes(routes []rtnetlink.RouteMessage, destination netaddr.IPPrefix, gateway netaddr.IP) []*rtnetlink.RouteMessage {
func findRoutes(routes []rtnetlink.RouteMessage, destination netaddr.IPPrefix, gateway netaddr.IP, table nethelpers.RoutingTable) []*rtnetlink.RouteMessage {
var result []*rtnetlink.RouteMessage //nolint:prealloc
for i, route := range routes {
@ -125,13 +133,17 @@ func findRoutes(routes []rtnetlink.RouteMessage, destination netaddr.IPPrefix, g
continue
}
if nethelpers.RoutingTable(route.Table) != table {
continue
}
result = append(result, &routes[i])
}
return result
}
//nolint:gocyclo
//nolint:gocyclo,cyclop
func (ctrl *RouteSpecController) syncRoute(ctx context.Context, r controller.Runtime, logger *zap.Logger, conn *rtnetlink.Conn,
links []rtnetlink.LinkMessage, routes []rtnetlink.RouteMessage, route *network.RouteSpec) error {
linkIndex := resolveLinkName(links, route.TypedSpec().OutLinkName)
@ -144,7 +156,7 @@ func (ctrl *RouteSpecController) syncRoute(ctx context.Context, r controller.Run
switch route.Metadata().Phase() {
case resource.PhaseTearingDown:
for _, existing := range findRoutes(routes, route.TypedSpec().Destination, route.TypedSpec().Gateway) {
for _, existing := range findRoutes(routes, route.TypedSpec().Destination, route.TypedSpec().Gateway, route.TypedSpec().Table) {
// delete route
if err := conn.Route.Delete(existing); err != nil {
return fmt.Errorf("error removing route: %w", err)
@ -165,15 +177,17 @@ func (ctrl *RouteSpecController) syncRoute(ctx context.Context, r controller.Run
matchFound := false
for _, existing := range findRoutes(routes, route.TypedSpec().Destination, route.TypedSpec().Gateway) {
for _, existing := range findRoutes(routes, route.TypedSpec().Destination, route.TypedSpec().Gateway, route.TypedSpec().Table) {
// check if existing matches the spec: if it does, skip update
if existing.Scope == uint8(route.TypedSpec().Scope) && existing.Flags == uint32(route.TypedSpec().Flags) &&
existing.Protocol == uint8(route.TypedSpec().Protocol) && existing.Flags == uint32(route.TypedSpec().Flags) &&
existing.Attributes.OutIface == linkIndex && existing.Attributes.Priority == route.TypedSpec().Priority &&
existing.Attributes.Table == uint32(route.TypedSpec().Table) {
existing.Attributes.Table == uint32(route.TypedSpec().Table) &&
(route.TypedSpec().Source.IsZero() ||
existing.Attributes.Src.Equal(route.TypedSpec().Source.IP.IPAddr().IP)) {
matchFound = true
break
continue
}
// delete route, it doesn't match the spec
@ -192,12 +206,14 @@ func (ctrl *RouteSpecController) syncRoute(ctx context.Context, r controller.Run
msg := &rtnetlink.RouteMessage{
Family: uint8(route.TypedSpec().Family),
DstLength: route.TypedSpec().Destination.Bits,
SrcLength: route.TypedSpec().Source.Bits,
Protocol: uint8(route.TypedSpec().Protocol),
Scope: uint8(route.TypedSpec().Scope),
Type: uint8(route.TypedSpec().Type),
Flags: uint32(route.TypedSpec().Flags),
Attributes: rtnetlink.RouteAttributes{
Dst: route.TypedSpec().Destination.IP.IPAddr().IP,
Src: route.TypedSpec().Source.IP.IPAddr().IP,
Gateway: route.TypedSpec().Gateway.IPAddr().IP,
OutIface: linkIndex,
Priority: route.TypedSpec().Priority,

View File

@ -9,6 +9,8 @@ import (
"context"
"fmt"
"log"
"math/rand"
"net"
"sync"
"testing"
"time"
@ -21,6 +23,7 @@ import (
"github.com/jsimonetti/rtnetlink"
"github.com/stretchr/testify/suite"
"github.com/talos-systems/go-retry/retry"
"golang.org/x/sys/unix"
"inet.af/netaddr"
netctrl "github.com/talos-systems/talos/internal/app/machined/pkg/controllers/network"
@ -56,6 +59,10 @@ func (suite *RouteSpecSuite) SetupTest() {
suite.startRuntime()
}
func (suite *RouteSpecSuite) uniqueDummyInterface() string {
return fmt.Sprintf("dummy%02x%02x%02x", rand.Int31()&0xff, rand.Int31()&0xff, rand.Int31()&0xff)
}
func (suite *RouteSpecSuite) startRuntime() {
suite.wg.Add(1)
@ -241,6 +248,139 @@ func (suite *RouteSpecSuite) TestDefaultRoute() {
suite.Require().NoError(suite.state.Destroy(suite.ctx, def.Metadata()))
}
func (suite *RouteSpecSuite) TestDefaultAndInterfaceRoutes() {
dummyInterface := suite.uniqueDummyInterface()
conn, err := rtnetlink.Dial(nil)
suite.Require().NoError(err)
defer conn.Close() //nolint:errcheck
suite.Require().NoError(conn.Link.New(&rtnetlink.LinkMessage{
Type: unix.ARPHRD_ETHER,
Flags: unix.IFF_UP,
Change: unix.IFF_UP,
Attributes: &rtnetlink.LinkAttributes{
Name: dummyInterface,
MTU: 1400,
Info: &rtnetlink.LinkInfo{
Kind: "dummy",
},
},
}))
iface, err := net.InterfaceByName(dummyInterface)
suite.Require().NoError(err)
defer conn.Link.Delete(uint32(iface.Index)) //nolint:errcheck
localIP := net.ParseIP("10.28.0.27").To4()
suite.Require().NoError(conn.Address.New(&rtnetlink.AddressMessage{
Family: unix.AF_INET,
PrefixLength: 32,
Scope: unix.RT_SCOPE_UNIVERSE,
Index: uint32(iface.Index),
Attributes: rtnetlink.AddressAttributes{
Address: localIP,
Local: localIP,
},
}))
def := network.NewRouteSpec(network.NamespaceName, "default")
*def.TypedSpec() = network.RouteSpecSpec{
Family: nethelpers.FamilyInet4,
Destination: netaddr.IPPrefix{},
Gateway: netaddr.MustParseIP("10.28.0.1"),
Source: netaddr.MustParseIPPrefix("10.28.0.27/32"),
Table: nethelpers.TableMain,
OutLinkName: dummyInterface,
Protocol: nethelpers.ProtocolStatic,
Type: nethelpers.TypeUnicast,
Priority: 1048576,
ConfigLayer: network.ConfigMachineConfiguration,
}
def.TypedSpec().Normalize()
host := network.NewRouteSpec(network.NamespaceName, "aninterface")
*host.TypedSpec() = network.RouteSpecSpec{
Family: nethelpers.FamilyInet4,
Destination: netaddr.MustParseIPPrefix("10.28.0.1/32"),
Gateway: netaddr.MustParseIP("0.0.0.0"),
Source: netaddr.MustParseIPPrefix("10.28.0.27/32"),
Table: nethelpers.TableMain,
OutLinkName: dummyInterface,
Protocol: nethelpers.ProtocolStatic,
Type: nethelpers.TypeUnicast,
Priority: 1048576,
ConfigLayer: network.ConfigMachineConfiguration,
}
host.TypedSpec().Normalize()
for _, res := range []resource.Resource{def, host} {
suite.Require().NoError(suite.state.Create(suite.ctx, res), "%v", res.Spec())
}
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {
if err := suite.assertRoute(netaddr.IPPrefix{}, netaddr.MustParseIP("10.28.0.1"), func(route rtnetlink.RouteMessage) error {
suite.Assert().Nil(route.Attributes.Dst)
suite.Assert().EqualValues(1048576, route.Attributes.Priority)
return nil
}); err != nil {
return err
}
return suite.assertRoute(netaddr.MustParseIPPrefix("10.28.0.1/32"), netaddr.IP{}, func(route rtnetlink.RouteMessage) error {
suite.Assert().Nil(route.Attributes.Gateway)
suite.Assert().EqualValues(1048576, route.Attributes.Priority)
return nil
})
}))
// teardown the routes
for {
ready, err := suite.state.Teardown(suite.ctx, def.Metadata())
suite.Require().NoError(err)
if ready {
break
}
time.Sleep(100 * time.Millisecond)
}
for {
ready, err := suite.state.Teardown(suite.ctx, host.Metadata())
suite.Require().NoError(err)
if ready {
break
}
time.Sleep(100 * time.Millisecond)
}
// torn down route should be removed immediately
suite.Assert().NoError(suite.assertNoRoute(netaddr.IPPrefix{}, netaddr.MustParseIP("10.28.0.1")))
suite.Assert().NoError(suite.assertNoRoute(netaddr.MustParseIPPrefix("10.28.0.1/32"), netaddr.IP{}))
suite.Require().NoError(suite.state.Destroy(suite.ctx, def.Metadata()))
}
func (suite *RouteSpecSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewRouteSpec(network.NamespaceName, "bar")))
}
func TestRouteSpecSuite(t *testing.T) {
suite.Run(t, new(RouteSpecSuite))
}

View File

@ -99,7 +99,7 @@ func (suite *RouteStatusSuite) assertRoutes(requiredIDs []string, check func(*ne
func (suite *RouteStatusSuite) TestRoutes() {
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {
return suite.assertRoutes([]string{"127.0.0.0/8/"}, func(r *network.RouteStatus) error {
return suite.assertRoutes([]string{"/127.0.0.0/8"}, func(r *network.RouteStatus) error {
suite.Assert().True(r.TypedSpec().Source.IP.IsLoopback())
suite.Assert().Equal("lo", r.TypedSpec().OutLinkName)
suite.Assert().Equal(nethelpers.TableLocal, r.TypedSpec().Table)
@ -112,6 +112,14 @@ func (suite *RouteStatusSuite) TestRoutes() {
}))
}
func (suite *RouteStatusSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
}
func TestRouteStatusSuite(t *testing.T) {
suite.Run(t, new(RouteStatusSuite))
}

View File

@ -137,6 +137,20 @@ func (suite *StatusSuite) TestEtcFiles() {
}))
}
func (suite *StatusSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewNodeAddress(network.NamespaceName, "bar")))
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewResolverStatus(network.NamespaceName, "bar")))
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewHostnameStatus(network.NamespaceName, "bar")))
suite.Assert().NoError(suite.state.Create(context.Background(), files.NewEtcFileStatus(files.NamespaceName, "bar")))
}
func TestStatusSuite(t *testing.T) {
suite.Run(t, new(StatusSuite))
}

View File

@ -198,6 +198,25 @@ func (suite *TimeServerConfigSuite) TestMachineConfiguration() {
}))
}
func (suite *TimeServerConfigSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
err := suite.state.Create(context.Background(), config.NewMachineConfig(&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{},
}))
if state.IsConflictError(err) {
err = suite.state.Destroy(context.Background(), config.NewMachineConfig(nil).Metadata())
}
suite.Require().NoError(err)
}
func TestTimeServerConfigSuite(t *testing.T) {
suite.Run(t, new(TimeServerConfigSuite))
}

View File

@ -153,6 +153,17 @@ func (suite *TimeServerMergeSuite) TestMerge() {
}))
}
func (suite *TimeServerMergeSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewTimeServerSpec(network.ConfigNamespaceName, "bar")))
}
func TestTimeServerMergeSuite(t *testing.T) {
suite.Run(t, new(TimeServerMergeSuite))
}

View File

@ -100,6 +100,17 @@ func (suite *TimeServerSpecSuite) TestSpec() {
}))
}
func (suite *TimeServerSpecSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
suite.Assert().NoError(suite.state.Create(context.Background(), network.NewTimeServerSpec(network.NamespaceName, "bar")))
}
func TestTimeServerSpecSuite(t *testing.T) {
suite.Run(t, new(TimeServerSpecSuite))
}

View File

@ -15,6 +15,7 @@ import (
"go.uber.org/zap"
"github.com/talos-systems/talos/internal/pkg/etcd"
"github.com/talos-systems/talos/pkg/resources/network"
"github.com/talos-systems/talos/pkg/resources/secrets"
"github.com/talos-systems/talos/pkg/resources/time"
"github.com/talos-systems/talos/pkg/resources/v1alpha1"
@ -38,9 +39,9 @@ func (ctrl *EtcdController) Inputs() []controller.Input {
Kind: controller.InputWeak,
},
{
Namespace: v1alpha1.NamespaceName,
Type: v1alpha1.ServiceType,
ID: pointer.ToString("networkd"),
Namespace: network.NamespaceName,
Type: network.StatusType,
ID: pointer.ToString(network.StatusID),
Kind: controller.InputWeak,
},
{
@ -88,8 +89,8 @@ func (ctrl *EtcdController) Run(ctx context.Context, r controller.Runtime, logge
etcdRoot := etcdRootRes.(*secrets.Root).EtcdSpec()
// wait for networkd to be healthy as it might change IPs/hostname
networkdResource, err := r.Get(ctx, resource.NewMetadata(v1alpha1.NamespaceName, v1alpha1.ServiceType, "networkd", resource.VersionUndefined))
// wait for network to be ready as it might change IPs/hostname
networkResource, err := r.Get(ctx, resource.NewMetadata(network.NamespaceName, network.StatusType, network.StatusID, resource.VersionUndefined))
if err != nil {
if state.IsNotFoundError(err) {
continue
@ -98,7 +99,9 @@ func (ctrl *EtcdController) Run(ctx context.Context, r controller.Runtime, logge
return err
}
if !networkdResource.(*v1alpha1.Service).Healthy() {
networkStatus := networkResource.(*network.Status).TypedSpec()
if !(networkStatus.AddressReady && networkStatus.HostnameReady) {
continue
}

View File

@ -22,6 +22,7 @@ import (
"github.com/talos-systems/talos/internal/pkg/kubeconfig"
"github.com/talos-systems/talos/pkg/machinery/config"
"github.com/talos-systems/talos/pkg/machinery/constants"
"github.com/talos-systems/talos/pkg/resources/network"
"github.com/talos-systems/talos/pkg/resources/secrets"
timeresource "github.com/talos-systems/talos/pkg/resources/time"
"github.com/talos-systems/talos/pkg/resources/v1alpha1"
@ -44,21 +45,9 @@ func (ctrl *KubernetesController) Name() string {
func (ctrl *KubernetesController) Inputs() []controller.Input {
return []controller.Input{
{
Namespace: secrets.NamespaceName,
Type: secrets.RootType,
ID: pointer.ToString(secrets.RootKubernetesID),
Kind: controller.InputWeak,
},
{
Namespace: v1alpha1.NamespaceName,
Type: v1alpha1.ServiceType,
ID: pointer.ToString("networkd"),
Kind: controller.InputWeak,
},
{
Namespace: v1alpha1.NamespaceName,
Type: timeresource.StatusType,
ID: pointer.ToString(timeresource.StatusID),
Namespace: network.NamespaceName,
Type: network.StatusType,
ID: pointer.ToString(network.StatusID),
Kind: controller.InputWeak,
},
}
@ -78,6 +67,50 @@ func (ctrl *KubernetesController) Outputs() []controller.Output {
//
//nolint:gocyclo
func (ctrl *KubernetesController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
// wait for the network to be ready first, then switch to regular inputs
for {
select {
case <-ctx.Done():
return nil
case <-r.EventCh():
}
// wait for network to be ready as it might change IPs/hostname
networkResource, err := r.Get(ctx, resource.NewMetadata(network.NamespaceName, network.StatusType, network.StatusID, resource.VersionUndefined))
if err != nil {
if state.IsNotFoundError(err) {
continue
}
return err
}
networkStatus := networkResource.(*network.Status).TypedSpec()
if networkStatus.AddressReady && networkStatus.HostnameReady {
break
}
}
// switch to regular inputs once the network is ready
if err := r.UpdateInputs([]controller.Input{
{
Namespace: secrets.NamespaceName,
Type: secrets.RootType,
ID: pointer.ToString(secrets.RootKubernetesID),
Kind: controller.InputWeak,
},
{
Namespace: v1alpha1.NamespaceName,
Type: timeresource.StatusType,
ID: pointer.ToString(timeresource.StatusID),
Kind: controller.InputWeak,
},
}); err != nil {
return fmt.Errorf("error updating inputs: %w", err)
}
r.QueueReconcile()
refreshTicker := time.NewTicker(KubernetesCertificateValidityDuration / 2)
defer refreshTicker.Stop()
@ -104,20 +137,6 @@ func (ctrl *KubernetesController) Run(ctx context.Context, r controller.Runtime,
k8sRoot := k8sRootRes.(*secrets.Root).KubernetesSpec()
// wait for networkd to be healthy as it might change IPs/hostname
networkdResource, err := r.Get(ctx, resource.NewMetadata(v1alpha1.NamespaceName, v1alpha1.ServiceType, "networkd", resource.VersionUndefined))
if err != nil {
if state.IsNotFoundError(err) {
continue
}
return err
}
if !networkdResource.(*v1alpha1.Service).Healthy() {
continue
}
// wait for time sync as certs depend on current time
timeSyncResource, err := r.Get(ctx, resource.NewMetadata(v1alpha1.NamespaceName, timeresource.StatusType, timeresource.StatusID, resource.VersionUndefined))
if err != nil {

View File

@ -15,10 +15,10 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors"
"github.com/talos-systems/talos/internal/app/networkd/pkg/nic"
"github.com/talos-systems/talos/pkg/download"
"github.com/talos-systems/talos/pkg/machinery/config/configloader"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1"
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
)
// Metadata holds packet metadata info.
@ -113,7 +113,7 @@ func (p *Packet) Configuration(ctx context.Context) ([]byte, error) {
}
// translate the int returned from bond mode metadata to the type needed by networkd
bondMode := nic.BondMode(uint8(unmarshalledMetadataConfig.Network.Bonding.Mode))
bondMode := nethelpers.BondMode(uint8(unmarshalledMetadataConfig.Network.Bonding.Mode))
// determine bond name and build list of interfaces enslaved by the bond
devicesInBond := []string{}

View File

@ -64,7 +64,6 @@ func (*Sequencer) ApplyConfiguration(r runtime.Runtime, req *machineapi.ApplyCon
).Append(
"cleanup",
StopAllPods,
StopNetworkd,
).AppendList(
stopAllPhaselist(r),
).Append(
@ -114,11 +113,7 @@ func (*Sequencer) Initialize(r runtime.Runtime) []runtime.Phase {
WriteIMAPolicy,
).Append(
"etc",
CreateEtcNetworkFiles,
CreateOSReleaseFile,
).Append(
"discoverNetwork",
SetupDiscoveryNetwork,
).AppendWhen(
r.State().Machine().Installed(),
"mountSystem",
@ -130,12 +125,6 @@ func (*Sequencer) Initialize(r runtime.Runtime) []runtime.Phase {
r.State().Machine().Installed(),
"unmountSystem",
UnmountStatePartition,
).Append(
"resetNetwork",
ResetNetwork,
).Append(
"setupNetwork",
SetupDiscoveryNetwork,
)
}
@ -287,7 +276,6 @@ func (*Sequencer) Reboot(r runtime.Runtime) []runtime.Phase {
phases := PhaseList{}.Append(
"cleanup",
StopAllPods,
StopNetworkd,
).
AppendList(stopAllPhaselist(r)).
Append("reboot", Reboot)
@ -315,12 +303,10 @@ func (*Sequencer) Reset(r runtime.Runtime, in runtime.ResetOptions) []runtime.Ph
in.GetGraceful(),
"cleanup",
RemoveAllPods,
StopNetworkd,
).AppendWhen(
!in.GetGraceful(),
"cleanup",
StopAllPods,
StopNetworkd,
).AppendWhen(
in.GetGraceful() && (r.Config().Machine().Type() != machine.TypeJoin),
"leave",
@ -355,7 +341,6 @@ func (*Sequencer) Shutdown(r runtime.Runtime) []runtime.Phase {
Append(
"cleanup",
StopAllPods,
StopNetworkd,
).
AppendList(stopAllPhaselist(r)).
Append("shutdown", Shutdown)
@ -374,7 +359,6 @@ func (*Sequencer) StageUpgrade(r runtime.Runtime, in *machineapi.UpgradeRequest)
phases = phases.Append(
"cleanup",
StopAllPods,
StopNetworkd,
).AppendWhen(
!in.GetPreserve() && (r.Config().Machine().Type() != machine.TypeJoin),
"leave",
@ -405,12 +389,10 @@ func (*Sequencer) Upgrade(r runtime.Runtime, in *machineapi.UpgradeRequest) []ru
!in.GetPreserve(),
"cleanup",
RemoveAllPods,
StopNetworkd,
).AppendWhen(
in.GetPreserve(),
"cleanup",
StopAllPods,
StopNetworkd,
).AppendWhen(
!in.GetPreserve() && (r.Config().Machine().Type() != machine.TypeJoin),
"leave",

View File

@ -48,7 +48,6 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/system/events"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/services"
"github.com/talos-systems/talos/internal/app/maintenance"
"github.com/talos-systems/talos/internal/app/networkd/pkg/networkd"
"github.com/talos-systems/talos/internal/pkg/containers/cri/containerd"
"github.com/talos-systems/talos/internal/pkg/cri"
"github.com/talos-systems/talos/internal/pkg/etcd"
@ -307,16 +306,6 @@ HOME_URL="https://docs.talos-systems.com/"
BUG_REPORT_URL="https://github.com/talos-systems/talos/issues"
`
// Hosts creates a persistent and writable /etc/hosts file.
func Hosts() (err error) {
return createBindMount(filepath.Join(constants.SystemEtcPath, "hosts"), "/etc/hosts")
}
// ResolvConf creates a persistent and writable /etc/resolv.conf file.
func ResolvConf() (err error) {
return createBindMount(filepath.Join(constants.SystemEtcPath, "resolv.conf"), "/etc/resolv.conf")
}
// OSRelease renders a valid /etc/os-release file and writes it to disk. The
// node's OS Image field is reported by the node from /etc/os-release.
func OSRelease() (err error) {
@ -384,19 +373,6 @@ func createBindMount(src, dst string) (err error) {
return nil
}
// CreateEtcNetworkFiles represents the CreateEtcNetworkFiles task.
func CreateEtcNetworkFiles(seq runtime.Sequence, data interface{}) (runtime.TaskExecutionFunc, string) {
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) {
// Create /etc/resolv.conf.
if err = ResolvConf(); err != nil {
return err
}
// Create /etc/hosts
return Hosts()
}, "createEtcNetworkFiles"
}
// CreateOSReleaseFile represents the CreateOSReleaseFile task.
func CreateOSReleaseFile(seq runtime.Sequence, data interface{}) (runtime.TaskExecutionFunc, string) {
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) {
@ -405,21 +381,6 @@ func CreateOSReleaseFile(seq runtime.Sequence, data interface{}) (runtime.TaskEx
}, "createOSReleaseFile"
}
// SetupDiscoveryNetwork represents the task for setting up the initial network.
func SetupDiscoveryNetwork(seq runtime.Sequence, data interface{}) (runtime.TaskExecutionFunc, string) {
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) {
nwd, err := networkd.New(logger, r.Config())
if err != nil {
return err
}
ctx, cancel := context.WithCancel(ctx)
defer cancel()
return nwd.Configure(ctx)
}, "setupDiscoveryNetwork"
}
// LoadConfig represents the LoadConfig task.
func LoadConfig(seq runtime.Sequence, data interface{}) (runtime.TaskExecutionFunc, string) {
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) {
@ -568,20 +529,6 @@ func ValidateConfig(seq runtime.Sequence, data interface{}) (runtime.TaskExecuti
}, "validateConfig"
}
// ResetNetwork resets the network.
func ResetNetwork(seq runtime.Sequence, data interface{}) (runtime.TaskExecutionFunc, string) {
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) {
nwd, err := networkd.New(logger, r.Config())
if err != nil {
return err
}
nwd.Reset()
return nil
}, "resetNetwork"
}
// SetUserEnvVars represents the SetUserEnvVars task.
func SetUserEnvVars(seq runtime.Sequence, data interface{}) (runtime.TaskExecutionFunc, string) {
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) {
@ -630,7 +577,6 @@ func StartAllServices(seq runtime.Sequence, data interface{}) (runtime.TaskExecu
svcs.Load(
&services.APID{},
&services.Networkd{},
&services.CRI{},
&services.Kubelet{},
)
@ -669,14 +615,6 @@ func StartAllServices(seq runtime.Sequence, data interface{}) (runtime.TaskExecu
}, "startAllServices"
}
// StopNetworkd represents the StopNetworkd task.
func StopNetworkd(seq runtime.Sequence, data interface{}) (runtime.TaskExecutionFunc, string) {
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) {
// stop networkd so that it gives up on VIP lease
return system.Services(nil).Stop(ctx, "networkd")
}, "stopNetworkd"
}
// StopServicesForUpgrade represents the StopServicesForUpgrade task.
func StopServicesForUpgrade(seq runtime.Sequence, data interface{}) (runtime.TaskExecutionFunc, string) {
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) {

View File

@ -77,21 +77,20 @@ func (ctrl *Controller) Run(ctx context.Context) error {
&k8s.ManifestApplyController{},
&k8s.RenderSecretsStaticPodController{},
&network.AddressConfigController{
Cmdline: procfs.ProcCmdline(),
Cmdline: procfs.ProcCmdline(),
V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(),
},
&network.AddressMergeController{},
&network.AddressSpecController{},
&network.AddressStatusController{},
// TODO: disabled to avoid conflict with networkd
// &network.EtcFileController{},
&network.EtcFileController{},
&network.HostnameConfigController{
Cmdline: procfs.ProcCmdline(),
},
&network.HostnameMergeController{},
// TODO: disabled to avoid conflict with networkd
// &network.HostnameSpecController{
// V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(),
// },
&network.HostnameSpecController{
V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(),
},
&network.LinkConfigController{
Cmdline: procfs.ProcCmdline(),
},
@ -102,6 +101,13 @@ func (ctrl *Controller) Run(ctx context.Context) error {
&network.OperatorConfigController{
Cmdline: procfs.ProcCmdline(),
},
&network.OperatorSpecController{
V1alpha1Platform: ctrl.v1alpha1Runtime.State().Platform(),
State: ctrl.v1alpha1Runtime.State().V1Alpha2().Resources(),
},
&network.PlatformConfigController{
V1alpha1Platform: ctrl.v1alpha1Runtime.State().Platform(),
},
&network.ResolverConfigController{
Cmdline: procfs.ProcCmdline(),
},

View File

@ -31,6 +31,7 @@ import (
"github.com/talos-systems/talos/pkg/copy"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1/machine"
"github.com/talos-systems/talos/pkg/machinery/constants"
"github.com/talos-systems/talos/pkg/resources/network"
"github.com/talos-systems/talos/pkg/resources/time"
)
@ -71,6 +72,7 @@ func (o *APID) PostFunc(r runtime.Runtime, state events.ServiceState) (err error
func (o *APID) Condition(r runtime.Runtime) conditions.Condition {
conds := []conditions.Condition{
time.NewSyncCondition(r.State().V1Alpha2().Resources()),
network.NewReadyCondition(r.State().V1Alpha2().Resources(), network.AddressReady, network.HostnameReady),
}
if r.Config().Machine().Type() == machine.TypeJoin {
@ -82,7 +84,7 @@ func (o *APID) Condition(r runtime.Runtime) conditions.Condition {
// DependsOn implements the Service interface.
func (o *APID) DependsOn(r runtime.Runtime) []string {
return []string{"containerd", "networkd"}
return []string{"containerd"}
}
// Runner implements the Service interface.

View File

@ -21,6 +21,7 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/system/runner/restart"
"github.com/talos-systems/talos/pkg/conditions"
"github.com/talos-systems/talos/pkg/machinery/constants"
"github.com/talos-systems/talos/pkg/resources/network"
)
// CRI implements the Service interface. It serves as the concrete type with
@ -44,12 +45,12 @@ func (c *CRI) PostFunc(r runtime.Runtime, state events.ServiceState) (err error)
// Condition implements the Service interface.
func (c *CRI) Condition(r runtime.Runtime) conditions.Condition {
return nil
return network.NewReadyCondition(r.State().V1Alpha2().Resources(), network.AddressReady, network.HostnameReady, network.EtcFilesReady)
}
// DependsOn implements the Service interface.
func (c *CRI) DependsOn(r runtime.Runtime) []string {
return []string{"networkd"}
return nil
}
// Runner implements the Service interface.

View File

@ -40,6 +40,7 @@ import (
"github.com/talos-systems/talos/pkg/conditions"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1/machine"
"github.com/talos-systems/talos/pkg/machinery/constants"
"github.com/talos-systems/talos/pkg/resources/network"
timeresource "github.com/talos-systems/talos/pkg/resources/time"
)
@ -113,12 +114,15 @@ func (e *Etcd) PostFunc(r runtime.Runtime, state events.ServiceState) (err error
// Condition implements the Service interface.
func (e *Etcd) Condition(r runtime.Runtime) conditions.Condition {
return timeresource.NewSyncCondition(r.State().V1Alpha2().Resources())
return conditions.WaitForAll(
timeresource.NewSyncCondition(r.State().V1Alpha2().Resources()),
network.NewReadyCondition(r.State().V1Alpha2().Resources(), network.AddressReady, network.HostnameReady, network.EtcFilesReady),
)
}
// DependsOn implements the Service interface.
func (e *Etcd) DependsOn(r runtime.Runtime) []string {
return []string{"cri", "networkd"}
return []string{"cri"}
}
// Runner implements the Service interface.

View File

@ -35,6 +35,7 @@ import (
"github.com/talos-systems/talos/pkg/argsbuilder"
"github.com/talos-systems/talos/pkg/conditions"
"github.com/talos-systems/talos/pkg/machinery/constants"
"github.com/talos-systems/talos/pkg/resources/network"
timeresource "github.com/talos-systems/talos/pkg/resources/time"
)
@ -127,12 +128,15 @@ func (k *Kubelet) PostFunc(r runtime.Runtime, state events.ServiceState) (err er
// Condition implements the Service interface.
func (k *Kubelet) Condition(r runtime.Runtime) conditions.Condition {
return timeresource.NewSyncCondition(r.State().V1Alpha2().Resources())
return conditions.WaitForAll(
timeresource.NewSyncCondition(r.State().V1Alpha2().Resources()),
network.NewReadyCondition(r.State().V1Alpha2().Resources(), network.AddressReady, network.HostnameReady, network.EtcFilesReady),
)
}
// DependsOn implements the Service interface.
func (k *Kubelet) DependsOn(r runtime.Runtime) []string {
return []string{"cri", "networkd"}
return []string{"cri"}
}
// Runner implements the Service interface.

View File

@ -1,123 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//nolint:golint
package services
import (
"context"
"errors"
"fmt"
"log"
"os"
"path/filepath"
"github.com/golang/protobuf/ptypes/empty"
"google.golang.org/grpc"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/events"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/health"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/runner"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/runner/goroutine"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/runner/restart"
"github.com/talos-systems/talos/internal/app/networkd"
"github.com/talos-systems/talos/pkg/conditions"
"github.com/talos-systems/talos/pkg/grpc/dialer"
healthapi "github.com/talos-systems/talos/pkg/machinery/api/health"
"github.com/talos-systems/talos/pkg/machinery/constants"
)
// Networkd implements the Service interface. It serves as the concrete type with
// the required methods.
type Networkd struct{}
// ID implements the Service interface.
func (n *Networkd) ID(r runtime.Runtime) string {
return "networkd"
}
// PreFunc implements the Service interface.
func (n *Networkd) PreFunc(ctx context.Context, r runtime.Runtime) error {
return os.MkdirAll(filepath.Dir(constants.NetworkSocketPath), 0o750)
}
// PostFunc implements the Service interface.
func (n *Networkd) PostFunc(r runtime.Runtime, state events.ServiceState) (err error) {
return nil
}
// Condition implements the Service interface.
func (n *Networkd) Condition(r runtime.Runtime) conditions.Condition {
return nil
}
// DependsOn implements the Service interface.
func (n *Networkd) DependsOn(r runtime.Runtime) []string {
return nil
}
func (n *Networkd) Runner(r runtime.Runtime) (runner.Runner, error) {
return restart.New(goroutine.NewRunner(
r,
"networkd",
networkd.Main,
runner.WithLoggingManager(r.Logging()),
),
restart.WithType(restart.Forever),
), nil
}
// HealthFunc implements the HealthcheckedService interface.
func (n *Networkd) HealthFunc(r runtime.Runtime) health.Check {
return func(ctx context.Context) error {
var (
conn *grpc.ClientConn
err error
hcResp *healthapi.HealthCheckResponse
readyResp *healthapi.ReadyCheckResponse
)
conn, err = grpc.DialContext(
ctx,
fmt.Sprintf("%s://%s", "unix", constants.NetworkSocketPath),
grpc.WithInsecure(),
grpc.WithContextDialer(dialer.DialUnix()),
)
if err != nil {
return err
}
defer conn.Close() //nolint:errcheck
nClient := healthapi.NewHealthClient(conn)
if readyResp, err = nClient.Ready(ctx, &empty.Empty{}); err != nil {
return err
}
if readyResp.Messages[0].Status != healthapi.ReadyCheck_READY {
return errors.New("networkd is not ready")
}
if hcResp, err = nClient.Check(ctx, &empty.Empty{}); err != nil {
return err
}
if hcResp.Messages[0].Status == healthapi.HealthCheck_SERVING {
return nil
}
msg := fmt.Sprintf("networkd is unhealthy: %s", hcResp.Messages[0].Status.String())
if r.Config().Debug() {
log.Printf("DEBUG: %s", msg)
}
return errors.New(msg)
}
}
// HealthSettings implements the HealthcheckedService interface.
func (n *Networkd) HealthSettings(runtime.Runtime) *health.Settings {
return &health.DefaultSettings
}

View File

@ -1,18 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package services_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/talos-systems/talos/internal/app/machined/pkg/system"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/services"
)
func TestNetworkdInterfaces(t *testing.T) {
assert.Implements(t, (*system.HealthcheckedService)(nil), new(services.Networkd))
}

View File

@ -24,6 +24,7 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/system/runner/restart"
"github.com/talos-systems/talos/pkg/conditions"
"github.com/talos-systems/talos/pkg/machinery/constants"
"github.com/talos-systems/talos/pkg/resources/network"
timeresource "github.com/talos-systems/talos/pkg/resources/time"
)
@ -48,12 +49,15 @@ func (t *Trustd) PostFunc(r runtime.Runtime, state events.ServiceState) (err err
// Condition implements the Service interface.
func (t *Trustd) Condition(r runtime.Runtime) conditions.Condition {
return timeresource.NewSyncCondition(r.State().V1Alpha2().Resources())
return conditions.WaitForAll(
timeresource.NewSyncCondition(r.State().V1Alpha2().Resources()),
network.NewReadyCondition(r.State().V1Alpha2().Resources(), network.AddressReady, network.HostnameReady),
)
}
// DependsOn implements the Service interface.
func (t *Trustd) DependsOn(r runtime.Runtime) []string {
return []string{"containerd", "networkd"}
return []string{"containerd"}
}
func (t *Trustd) Runner(r runtime.Runtime) (runner.Runner, error) {

View File

@ -1,79 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package networkd
import (
"context"
"io"
"log"
"golang.org/x/sync/errgroup"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
"github.com/talos-systems/talos/internal/app/networkd/pkg/networkd"
"github.com/talos-systems/talos/internal/app/networkd/pkg/reg"
"github.com/talos-systems/talos/pkg/grpc/factory"
"github.com/talos-systems/talos/pkg/machinery/constants"
)
// Main is the entrypoint into networkd.
func Main(ctx context.Context, r runtime.Runtime, logOutput io.Writer) error {
logger := log.New(logOutput, "", log.Lshortfile|log.Ldate|log.Lmicroseconds|log.Ltime)
defer logger.Println("networkd stopped")
return run(ctx, r, logger)
}
func run(ctx context.Context, r runtime.Runtime, logger *log.Logger) error {
var eg errgroup.Group
logger.Println("starting initial network configuration")
nwd, err := networkd.New(logger, r.Config())
if err != nil {
return err
}
if err = nwd.Configure(ctx); err != nil {
return err
}
registrator, err := reg.NewRegistrator(nwd)
if err != nil {
return err
}
if err = nwd.RunControllers(ctx, &eg); err != nil {
return err
}
logger.Println("completed initial network configuration")
nwd.Renew(ctx)
server := factory.NewServer(
registrator,
factory.WithLog("", logger.Writer()),
)
listener, err := factory.NewListener(
factory.Network("unix"),
factory.SocketPath(constants.NetworkSocketPath),
)
if err != nil {
return err
}
eg.Go(func() error {
return server.Serve(listener)
})
<-ctx.Done()
server.GracefulStop()
return eg.Wait()
}

View File

@ -1,49 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package address
import (
"context"
"log"
"net"
"time"
)
// Addressing provides an interface for abstracting the underlying network
// addressing configuration. Currently dhcp(v4) and static methods are
// supported.
type Addressing interface {
Address() *net.IPNet
Discover(context.Context, *log.Logger, *net.Interface) error
Family() int
Hostname() string
Link() *net.Interface
MTU() uint32
Mask() net.IPMask
Name() string
Resolvers() []net.IP
Routes() []*Route
Scope() uint8
TTL() time.Duration
Valid() bool
}
// Route is a representation of a network route.
type Route struct {
// Destination is the destination network this route provides.
Destination *net.IPNet
// Gateway is the router through which the destination may be reached.
// This option is exclusive of Interface
Gateway net.IP
// Interface indicates the route is an interface route, and traffic destinted for the Gateway should be sent through the given network interface.
// This option is exclusive of Gateway.
Interface string
// Metric indicates the "distance" to the destination through this route.
// This is an integer which allows the control of priority in the case of multiple routes to the same destination.
Metric uint32
}

View File

@ -1,273 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package address
import (
"context"
"fmt"
"log"
"net"
"strings"
"time"
"github.com/insomniacslk/dhcp/dhcpv4"
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
"github.com/talos-systems/go-procfs/procfs"
"golang.org/x/sys/unix"
"github.com/talos-systems/talos/pkg/machinery/config"
"github.com/talos-systems/talos/pkg/machinery/constants"
)
const dhcpReceivedRouteMetric uint32 = 1024
// DHCP4 implements the Addressing interface.
type DHCP4 struct {
Offer *dhcpv4.DHCPv4
Ack *dhcpv4.DHCPv4
NetIf *net.Interface
DHCPOptions config.DHCPOptions
Mtu int
RouteList []config.Route
}
// Name returns back the name of the address method.
func (d *DHCP4) Name() string {
return "dhcp4"
}
// Link returns the underlying net.Interface that this address
// method is configured for.
func (d *DHCP4) Link() *net.Interface {
return d.NetIf
}
// Discover handles the DHCP client exchange stores the DHCP Ack.
func (d *DHCP4) Discover(ctx context.Context, logger *log.Logger, link *net.Interface) error {
d.NetIf = link
err := d.discover(ctx, logger)
return err
}
// Address returns back the IP address from the received DHCP offer.
func (d *DHCP4) Address() *net.IPNet {
return &net.IPNet{
IP: d.Ack.YourIPAddr,
Mask: d.Mask(),
}
}
// Mask returns the netmask from the DHCP offer.
func (d *DHCP4) Mask() net.IPMask {
return d.Ack.SubnetMask()
}
// MTU returs the MTU size from the DHCP offer.
func (d *DHCP4) MTU() uint32 {
mtuReturn := uint32(d.NetIf.MTU)
if d.Ack != nil {
// TODO do we need to implement dhcpv4.GetUint32 upstream?
mtu, err := dhcpv4.GetUint16(dhcpv4.OptionInterfaceMTU, d.Ack.Options)
if err == nil {
mtuReturn = uint32(mtu)
}
}
// override with any non-zero Mtu value passed into the dhcp object
if uint32(d.Mtu) > 0 {
mtuReturn = uint32(d.Mtu)
}
return mtuReturn
}
// TTL denotes how long a DHCP offer is valid for.
func (d *DHCP4) TTL() time.Duration {
if d.Ack == nil {
return 0
}
return d.Ack.IPAddressLeaseTime(time.Minute * 30)
}
// Family qualifies the address as ipv4 or ipv6.
func (d *DHCP4) Family() int {
return unix.AF_INET
}
// Scope sets the address scope.
func (d *DHCP4) Scope() uint8 {
return unix.RT_SCOPE_UNIVERSE
}
// Valid denotes if this address method should be used.
func (d *DHCP4) Valid() bool {
return d.Ack != nil
}
// Routes aggregates all Routers and ClasslessStaticRoutes retrieved from
// the DHCP offer.
// rfc3442:
// If the DHCP server returns both a Classless Static Routes option and
// a Router option, the DHCP client MUST ignore the Router option.
func (d *DHCP4) Routes() (routes []*Route) {
metric := dhcpReceivedRouteMetric
if d.DHCPOptions != nil && d.DHCPOptions.RouteMetric() != 0 {
metric = d.DHCPOptions.RouteMetric()
}
defRoute := &net.IPNet{
IP: net.IPv4zero,
Mask: net.IPv4Mask(0, 0, 0, 0),
}
for _, router := range d.Ack.Router() {
routes = append(routes, &Route{
Destination: defRoute,
Gateway: router,
Metric: metric,
})
}
// overwrite router option if classless routes were provided.
if len(d.Ack.ClasslessStaticRoute()) > 0 {
routes = []*Route{}
for _, dhcpRoute := range d.Ack.ClasslessStaticRoute() {
routes = append(routes, &Route{
Destination: dhcpRoute.Dest,
Gateway: dhcpRoute.Router,
Metric: metric,
})
}
}
// append any routes that were provided in config
for _, route := range d.RouteList {
_, ipnet, err := net.ParseCIDR(route.Network())
if err != nil {
// TODO: we should at least log this failure
continue
}
routes = append(routes, &Route{
Destination: ipnet,
Gateway: net.ParseIP(route.Gateway()),
Metric: staticRouteDefaultMetric,
})
}
return routes
}
// Resolvers returns the DNS resolvers from the DHCP offer.
func (d *DHCP4) Resolvers() []net.IP {
return d.Ack.DNS()
}
// Hostname returns the hostname from the DHCP offer.
func (d *DHCP4) Hostname() (hostname string) {
if d.Ack.HostName() == "" {
hostname = fmt.Sprintf("%s-%s", "talos", strings.ReplaceAll(d.Address().IP.String(), ".", "-"))
} else {
hostname = d.Ack.HostName()
}
if d.Ack.DomainName() != "" {
hostname = fmt.Sprintf("%s.%s", strings.Split(hostname, ".")[0], d.Ack.DomainName())
}
return hostname
}
// discover handles the actual DHCP conversation.
func (d *DHCP4) discover(ctx context.Context, logger *log.Logger) error {
opts := []dhcpv4.OptionCode{
dhcpv4.OptionClasslessStaticRoute,
dhcpv4.OptionDomainNameServer,
dhcpv4.OptionDNSDomainSearchList,
dhcpv4.OptionHostName,
// TODO: handle these options
dhcpv4.OptionNTPServers,
dhcpv4.OptionDomainName,
}
// <3 azure
// When including dhcp.OptionInterfaceMTU we don't get a dhcp offer back on azure.
// So we'll need to explicitly exclude adding this option for azure.
if p := procfs.ProcCmdline().Get(constants.KernelParamPlatform).First(); p != nil {
if *p != "azure" {
opts = append(opts, dhcpv4.OptionInterfaceMTU)
}
}
mods := []dhcpv4.Modifier{dhcpv4.WithRequestedOptions(opts...)}
clientOpts := []nclient4.ClientOpt{}
if d.Offer != nil {
// do not use broadcast, but send the packet to DHCP server directly
addr, err := net.ResolveUDPAddr("udp", d.Offer.ServerIPAddr.String()+":67")
if err != nil {
return err
}
// by default it's set to 0.0.0.0 which actually breaks lease renew
d.Offer.ClientIPAddr = d.Offer.YourIPAddr
clientOpts = append(clientOpts, nclient4.WithServerAddr(addr))
}
// TODO expose this ( nclient4.WithDebugLogger() ) with some
// debug logging option
cli, err := nclient4.New(d.NetIf.Name, clientOpts...)
if err != nil {
return err
}
//nolint:errcheck
defer cli.Close()
var lease *nclient4.Lease
if d.Offer != nil {
lease, err = cli.RequestFromOffer(ctx, d.Offer, mods...)
} else {
lease, err = cli.Request(ctx, mods...)
}
if err != nil {
// TODO: Make this a well defined error so we can make it not fatal
logger.Printf("failed dhcp request for %q: %v", d.NetIf.Name, err)
// clear offer if request fails to start with discover sequence next time
d.Offer = nil
return err
}
logger.Printf("DHCP ACK on %q: %s", d.NetIf.Name, collapseSummary(lease.ACK.Summary()))
d.Ack = lease.ACK
d.Offer = lease.Offer
return err
}
func collapseSummary(summary string) string {
lines := strings.Split(summary, "\n")[1:]
for i := range lines {
lines[i] = strings.TrimSpace(lines[i])
}
if len(lines) > 0 && lines[len(lines)-1] == "" {
lines = lines[:len(lines)-1]
}
return strings.Join(lines, ", ")
}

View File

@ -1,203 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package address
import (
"context"
"fmt"
"log"
"net"
"strings"
"time"
"github.com/insomniacslk/dhcp/dhcpv6"
"github.com/insomniacslk/dhcp/dhcpv6/nclient6"
"github.com/jsimonetti/rtnetlink"
"github.com/talos-systems/go-retry/retry"
"golang.org/x/sys/unix"
)
// DHCP6 implements the Addressing interface.
type DHCP6 struct {
Reply *dhcpv6.Message
NetIf *net.Interface
Mtu int
}
// Name returns back the name of the address method.
func (d *DHCP6) Name() string {
return "dhcp6"
}
// Link returns the underlying net.Interface that this address
// method is configured for.
func (d *DHCP6) Link() *net.Interface {
return d.NetIf
}
// Discover handles the DHCP client exchange stores the DHCP Ack.
func (d *DHCP6) Discover(ctx context.Context, logger *log.Logger, link *net.Interface) error {
d.NetIf = link
err := d.discover(ctx, logger)
return err
}
// Address returns back the IP address from the received DHCP offer.
func (d *DHCP6) Address() *net.IPNet {
if d.Reply.Options.OneIANA() == nil {
return nil
}
return &net.IPNet{
IP: d.Reply.Options.OneIANA().Options.OneAddress().IPv6Addr,
Mask: net.CIDRMask(128, 128),
}
}
// Mask returns the netmask from the DHCP offer.
func (d *DHCP6) Mask() net.IPMask {
return net.CIDRMask(128, 128)
}
// MTU returs the MTU size from the DHCP offer.
func (d *DHCP6) MTU() uint32 {
if d.Mtu > 0 {
return uint32(d.Mtu)
}
return uint32(d.NetIf.MTU)
}
// TTL denotes how long a DHCP offer is valid for.
func (d *DHCP6) TTL() time.Duration {
if d.Reply == nil {
return 0
}
return d.Reply.Options.OneIANA().Options.OneAddress().ValidLifetime
}
// Family qualifies the address as ipv4 or ipv6.
func (d *DHCP6) Family() int {
return unix.AF_INET6
}
// Scope sets the address scope.
func (d *DHCP6) Scope() uint8 {
return unix.RT_SCOPE_UNIVERSE
}
// Valid denotes if this address method should be used.
func (d *DHCP6) Valid() bool {
return d.Reply != nil && d.Reply.Options.OneIANA() != nil
}
// Routes is not supported on IPv6.
func (d *DHCP6) Routes() (routes []*Route) {
return nil
}
// Resolvers returns the DNS resolvers from the DHCP offer.
func (d *DHCP6) Resolvers() []net.IP {
return d.Reply.Options.DNS()
}
// Hostname returns the hostname from the DHCP offer.
func (d *DHCP6) Hostname() (hostname string) {
fqdn := d.Reply.Options.FQDN()
if fqdn != nil && fqdn.DomainName != nil {
hostname = strings.Join(fqdn.DomainName.Labels, ".")
} else {
hostname = fmt.Sprintf("%s-%s", "talos", strings.ReplaceAll(d.Address().IP.String(), ":", ""))
}
return hostname
}
// discover handles the actual DHCP conversation.
func (d *DHCP6) discover(ctx context.Context, logger *log.Logger) error {
if err := waitIPv6LinkReady(logger, d.NetIf); err != nil {
logger.Printf("failed waiting for IPv6 readiness: %s", err)
return err
}
cli, err := nclient6.New(d.NetIf.Name)
if err != nil {
logger.Printf("failed to create dhcp6 client: %s", err)
return err
}
//nolint:errcheck
defer cli.Close()
reply, err := cli.RapidSolicit(ctx)
if err != nil {
// TODO: Make this a well defined error so we can make it not fatal
logger.Printf("failed dhcp6 request for %q: %v", d.NetIf.Name, err)
return err
}
logger.Printf("DHCP6 REPLY on %q: %s", d.NetIf.Name, collapseSummary(reply.Summary()))
d.Reply = reply
return nil
}
func waitIPv6LinkReady(logger *log.Logger, iface *net.Interface) error {
conn, err := rtnetlink.Dial(nil)
if err != nil {
return err
}
defer conn.Close() //nolint:errcheck
return retry.Constant(30*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(func() error {
ready, err := isIPv6LinkReady(logger, iface, conn)
if err != nil {
return err
}
if !ready {
return retry.ExpectedError(fmt.Errorf("IPv6 address is still tentative"))
}
return nil
})
}
// isIPv6LinkReady returns true if the interface has a link-local address
// which is not tentative.
func isIPv6LinkReady(logger *log.Logger, iface *net.Interface, conn *rtnetlink.Conn) (bool, error) {
addrs, err := conn.Address.List()
if err != nil {
return false, err
}
for _, addr := range addrs {
if addr.Index != uint32(iface.Index) {
continue
}
if addr.Family != unix.AF_INET6 {
continue
}
if addr.Attributes.Address.IsLinkLocalUnicast() && (addr.Flags&unix.IFA_F_TENTATIVE == 0) {
if addr.Flags&unix.IFA_F_DADFAILED != 0 {
logger.Printf("DADFAILED for %v, continuing anyhow", addr.Attributes.Address)
}
return true, nil
}
}
return false, nil
}

View File

@ -1,145 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package address
import (
"context"
"log"
"net"
"time"
"golang.org/x/sys/unix"
"github.com/talos-systems/talos/pkg/machinery/config"
)
const staticRouteDefaultMetric uint32 = 10
// Static implements the Addressing interface.
type Static struct {
CIDR string
Mtu int
FQDN string
RouteList []config.Route
NetIf *net.Interface
NameServers []net.IP
}
// Discover doesnt do anything in the static configuration since all
// the necessary configuration data is supplied via config.
func (s *Static) Discover(ctx context.Context, logger *log.Logger, link *net.Interface) error {
s.NetIf = link
return nil
}
// Name returns back the name of the address method.
func (s *Static) Name() string {
return "static"
}
// Address returns the IP address.
func (s *Static) Address() *net.IPNet {
var ip net.IP
var ipn *net.IPNet
if s.CIDR != "" {
//nolint:errcheck
ip, ipn, _ = net.ParseCIDR(s.CIDR)
ipn.IP = ip
}
return ipn
}
// Mask returns the netmask.
func (s *Static) Mask() net.IPMask {
//nolint:errcheck
_, ipnet, _ := net.ParseCIDR(s.CIDR)
return ipnet.Mask
}
// MTU returns the specified MTU.
func (s *Static) MTU() uint32 {
mtu := uint32(s.Mtu)
if mtu == 0 {
mtu = uint32(s.NetIf.MTU)
}
return mtu
}
// TTL returns the address lifetime. Since this is static, there is
// no TTL (0).
func (s *Static) TTL() time.Duration {
return 0
}
// Family qualifies the address as ipv4 or ipv6.
func (s *Static) Family() int {
if s.Address() == nil {
panic("unable to determine address family as address is nil")
}
if s.Address().IP.To4() != nil {
return unix.AF_INET
}
return unix.AF_INET6
}
// Scope sets the address scope.
func (s *Static) Scope() uint8 {
return unix.RT_SCOPE_UNIVERSE
}
// Routes aggregates the specified routes for a given device configuration
// TODO: do we need to be explicit on route vs gateway?
func (s *Static) Routes() (routes []*Route) {
for _, route := range s.RouteList {
_, ipnet, err := net.ParseCIDR(route.Network())
if err != nil {
// TODO: we should at least log the error
continue
}
metric := staticRouteDefaultMetric
if route.Metric() != 0 {
metric = route.Metric()
}
routes = append(routes, &Route{
Destination: ipnet,
Gateway: net.ParseIP(route.Gateway()),
Metric: metric,
})
}
return routes
}
// Resolvers returns the DNS resolvers.
func (s *Static) Resolvers() []net.IP {
return s.NameServers
}
// Hostname returns the hostname.
func (s *Static) Hostname() string {
return s.FQDN
}
// Link returns the underlying net.Interface that this address
// method is configured for.
func (s Static) Link() *net.Interface {
return s.NetIf
}
// Valid denotes if this address method should be used.
func (s *Static) Valid() bool {
return true
}

View File

@ -1,154 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package networkd
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net"
"strings"
"text/template"
"github.com/jsimonetti/rtnetlink"
talosnet "github.com/talos-systems/net"
"golang.org/x/sys/unix"
"github.com/talos-systems/talos/pkg/machinery/config"
)
// filterInterfaces filters network links by name so we only mange links
// we need to.
//
//nolint:gocyclo
func filterInterfaces(logger *log.Logger, interfaces []net.Interface) (filtered []net.Interface, err error) {
var conn *rtnetlink.Conn
for _, iface := range interfaces {
switch {
case strings.HasPrefix(iface.Name, "en"):
filtered = append(filtered, iface)
case strings.HasPrefix(iface.Name, "eth"):
filtered = append(filtered, iface)
case strings.HasPrefix(iface.Name, "lo"):
filtered = append(filtered, iface)
case strings.HasPrefix(iface.Name, "bond"):
filtered = append(filtered, iface)
}
}
conn, err = rtnetlink.Dial(nil)
if err != nil {
return nil, err
}
//nolint:errcheck
defer conn.Close()
n := 0 //nolint:wsl
for _, iface := range filtered {
link, err := conn.Link.Get(uint32(iface.Index))
if err != nil {
logger.Printf("error getting link %q", iface.Name)
continue
}
if link.Flags&unix.IFF_UP == unix.IFF_UP && !(link.Flags&unix.IFF_RUNNING == unix.IFF_RUNNING) {
logger.Printf("no carrier for link %q", iface.Name)
} else {
logger.Printf("link %q has carrier signal", iface.Name)
filtered[n] = iface
n++
}
}
filtered = filtered[:n]
return filtered, nil
}
// writeResolvConf generates a /etc/resolv.conf with the specified nameservers.
func writeResolvConf(logger *log.Logger, resolvers []string) (err error) {
var resolvconf strings.Builder
for idx, resolver := range resolvers {
// Only allow the first 3 nameservers since that is all that will be used
if idx >= 3 {
break
}
if _, err = resolvconf.WriteString(fmt.Sprintf("nameserver %s\n", resolver)); err != nil {
logger.Println("failed to add some resolver to resolvconf:", resolver)
return err
}
}
if domain, err := talosnet.DomainName(); err == nil {
if domain != "" {
if _, err = resolvconf.WriteString(fmt.Sprintf("search %s\n", domain)); err != nil {
return fmt.Errorf("failed to add domain search line to resolvconf: %s", err)
}
}
}
logger.Println("writing resolvconf")
return ioutil.WriteFile("/etc/resolv.conf", []byte(resolvconf.String()), 0o644)
}
const hostsTemplate = `
127.0.0.1 localhost
{{ .IP }} {{ .Hostname }} {{ if ne .Hostname .Alias }}{{ .Alias }}{{ end }}
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
{{ with .ExtraHosts }}
{{ range . }}
{{ .IP }} {{ range .Aliases }}{{.}} {{ end }}
{{ end }}
{{ end }}
`
func writeHosts(hostname string, address net.IP, cfg config.Provider) (err error) {
extraHosts := []config.ExtraHost{}
if cfg != nil {
extraHosts = cfg.Machine().Network().ExtraHosts()
}
data := struct {
IP string
Hostname string
Alias string
ExtraHosts []config.ExtraHost
}{
IP: address.String(),
Hostname: hostname,
Alias: strings.Split(hostname, ".")[0],
ExtraHosts: extraHosts,
}
var tmpl *template.Template
tmpl, err = template.New("").Parse(hostsTemplate)
if err != nil {
return err
}
var buf []byte
writer := bytes.NewBuffer(buf)
err = tmpl.Execute(writer, data)
if err != nil {
return err
}
return ioutil.WriteFile("/etc/hosts", writer.Bytes(), 0o644)
}

View File

@ -1,323 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package networkd
import (
"fmt"
"log"
"net"
"strings"
"github.com/talos-systems/go-procfs/procfs"
"github.com/talos-systems/talos/internal/app/networkd/pkg/address"
"github.com/talos-systems/talos/internal/app/networkd/pkg/nic"
"github.com/talos-systems/talos/pkg/machinery/config"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1"
"github.com/talos-systems/talos/pkg/machinery/constants"
)
// buildOptions translates the supplied config to nic.Option used for
// configuring the interface.
//nolint:gocyclo,cyclop
func buildOptions(logger *log.Logger, device config.Device, hostname string) (name string, opts []nic.Option, err error) {
opts = append(opts, nic.WithName(device.Interface()))
if device.Ignore() || procfs.ProcCmdline().Get(constants.KernelParamNetworkInterfaceIgnore).Contains(device.Interface()) {
opts = append(opts, nic.WithIgnore())
return device.Interface(), opts, err
}
// Configure Addressing
switch {
case device.CIDR() != "":
s := &address.Static{CIDR: device.CIDR(), RouteList: device.Routes(), Mtu: device.MTU()}
// Set a default for the hostname to ensure we always have a valid
// ip + hostname pair
ip := s.Address().IP.String()
s.FQDN = fmt.Sprintf("%s-%s", "talos", strings.ReplaceAll(ip, ".", "-"))
if hostname != "" {
s.FQDN = hostname
}
opts = append(opts, nic.WithAddressing(s))
case device.DHCP():
if device.DHCPOptions().IPv4() {
d := &address.DHCP4{DHCPOptions: device.DHCPOptions(), RouteList: device.Routes(), Mtu: device.MTU()}
opts = append(opts, nic.WithAddressing(d))
}
if device.DHCPOptions().IPv6() {
d := &address.DHCP6{Mtu: device.MTU()}
opts = append(opts, nic.WithAddressing(d))
}
default:
// Allow master interface without any addressing if VLANs exist
if len(device.Vlans()) > 0 {
logger.Printf("no addressing for master device %s", device.Interface())
opts = append(opts, nic.WithNoAddressing())
} else {
// No CIDR and DHCP==false results in a static without an IP.
// This handles cases like slaac addressing.
s := &address.Static{RouteList: device.Routes(), Mtu: device.MTU()}
opts = append(opts, nic.WithAddressing(s))
}
}
// Configure Vlan interfaces
for _, vlan := range device.Vlans() {
opts = append(opts, nic.WithVlan(vlan.ID()))
if vlan.CIDR() != "" {
opts = append(opts, nic.WithVlanCIDR(vlan.ID(), vlan.CIDR(), vlan.Routes()))
}
if vlan.DHCP() {
opts = append(opts, nic.WithVlanDhcp(vlan.ID()))
}
}
// Handle dummy interface
if device.Dummy() {
opts = append(opts, nic.WithDummy())
}
if device.WireguardConfig() != nil {
opts = append(opts, nic.WithWireguardConfig(device.WireguardConfig()))
}
if device.VIPConfig() != nil {
opts = append(opts, nic.WithVIPConfig(device.VIPConfig()))
}
// Configure Bonding
if device.Bond() == nil {
return device.Interface(), opts, err
}
opts = append(opts, nic.WithBond(true))
if len(device.Bond().Interfaces()) == 0 {
return device.Interface(), opts, fmt.Errorf("invalid bond configuration for %s: must supply sub interfaces for bonded interface", device.Interface())
}
opts = append(opts, nic.WithSubInterface(device.Bond().Interfaces()...))
if device.Bond().Mode() != "" {
opts = append(opts, nic.WithBondMode(device.Bond().Mode()))
}
if device.Bond().HashPolicy() != "" {
opts = append(opts, nic.WithHashPolicy(device.Bond().HashPolicy()))
}
if device.Bond().LACPRate() != "" {
opts = append(opts, nic.WithLACPRate(device.Bond().LACPRate()))
}
if device.Bond().MIIMon() > 0 {
opts = append(opts, nic.WithMIIMon(device.Bond().MIIMon()))
}
if device.Bond().UpDelay() > 0 {
opts = append(opts, nic.WithUpDelay(device.Bond().UpDelay()))
}
if device.Bond().DownDelay() > 0 {
opts = append(opts, nic.WithDownDelay(device.Bond().DownDelay()))
}
if !device.Bond().UseCarrier() {
opts = append(opts, nic.WithUseCarrier(device.Bond().UseCarrier()))
}
if device.Bond().ARPInterval() > 0 {
opts = append(opts, nic.WithARPInterval(device.Bond().ARPInterval()))
}
// if device.Bond.ARPIPTarget {
// opts = append(opts, nic.WithARPIPTarget(device.Bond.ARPIPTarget))
//}
if device.Bond().ARPValidate() != "" {
opts = append(opts, nic.WithARPValidate(device.Bond().ARPValidate()))
}
if device.Bond().ARPAllTargets() != "" {
opts = append(opts, nic.WithARPAllTargets(device.Bond().ARPAllTargets()))
}
if device.Bond().Primary() != "" {
opts = append(opts, nic.WithPrimary(device.Bond().Primary()))
}
if device.Bond().PrimaryReselect() != "" {
opts = append(opts, nic.WithPrimaryReselect(device.Bond().PrimaryReselect()))
}
if device.Bond().FailOverMac() != "" {
opts = append(opts, nic.WithFailOverMAC(device.Bond().FailOverMac()))
}
if device.Bond().ResendIGMP() > 0 {
opts = append(opts, nic.WithResendIGMP(device.Bond().ResendIGMP()))
}
if device.Bond().NumPeerNotif() > 0 {
opts = append(opts, nic.WithNumPeerNotif(device.Bond().NumPeerNotif()))
}
if device.Bond().AllSlavesActive() > 0 {
opts = append(opts, nic.WithAllSlavesActive(device.Bond().AllSlavesActive()))
}
if device.Bond().MinLinks() > 0 {
opts = append(opts, nic.WithMinLinks(device.Bond().MinLinks()))
}
if device.Bond().LPInterval() > 0 {
opts = append(opts, nic.WithLPInterval(device.Bond().LPInterval()))
}
if device.Bond().PacketsPerSlave() > 0 {
opts = append(opts, nic.WithPacketsPerSlave(device.Bond().PacketsPerSlave()))
}
if device.Bond().ADSelect() != "" {
opts = append(opts, nic.WithADSelect(device.Bond().ADSelect()))
}
if device.Bond().ADActorSysPrio() > 0 {
opts = append(opts, nic.WithADActorSysPrio(device.Bond().ADActorSysPrio()))
}
if device.Bond().ADUserPortKey() > 0 {
opts = append(opts, nic.WithADUserPortKey(device.Bond().ADUserPortKey()))
}
// if device.Bond.ADActorSystem != "" {
// opts = append(opts, nic.WithADActorSystem(device.Bond.ADActorSystem))
//}
if device.Bond().TLBDynamicLB() > 0 {
opts = append(opts, nic.WithTLBDynamicLB(device.Bond().TLBDynamicLB()))
}
if device.Bond().PeerNotifyDelay() > 0 {
opts = append(opts, nic.WithPeerNotifyDelay(device.Bond().PeerNotifyDelay()))
}
return device.Interface(), opts, err
}
//nolint:gocyclo
func buildKernelOptions(cmdline string) (name string, opts []nic.Option) {
// https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt
// ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>:<ntp0-ip>
fields := strings.Split(cmdline, ":")
// If dhcp is specified, we'll handle it as a normal discovered
// interface
if len(fields) == 1 && fields[0] == "dhcp" {
return name, opts
}
// If there are not enough fields specified, we'll bail
if len(fields) < 4 {
return name, opts
}
var (
device = &v1alpha1.Device{}
hostname string
link *net.Interface
resolvers = []net.IP{}
)
for idx, field := range fields {
switch idx {
// Address
case 0:
device.DeviceCIDR = field
// NFS Server
// case 1:
// Gateway
case 2:
device.DeviceRoutes = []*v1alpha1.Route{
{
RouteNetwork: "0.0.0.0/0",
RouteGateway: field,
},
}
// Netmask
case 3:
mask := net.ParseIP(field).To4()
ipmask := net.IPv4Mask(mask[0], mask[1], mask[2], mask[3])
ones, _ := ipmask.Size()
device.DeviceCIDR = fmt.Sprintf("%s/%d", device.CIDR(), ones)
// Hostname
case 4:
hostname = field
// Interface name
case 5:
iface, err := net.InterfaceByName(field)
if err == nil {
link = iface
}
// Configuration method
// case 6:
// Primary DNS Resolver
case 7:
fallthrough
// Secondary DNS Resolver
case 8:
nameserverIP := net.ParseIP(field)
if nameserverIP != nil {
resolvers = append(resolvers, nameserverIP)
}
}
}
// NTP server
// case 9:
// // k.NTPServer = field
// Find the first non-loopback interface
if link == nil {
ifaces, err := net.Interfaces()
if err != nil {
return hostname, opts
}
for _, iface := range ifaces {
if iface.Flags&net.FlagLoopback != 0 {
continue
}
i := iface
link = &i
break
}
}
if device.DeviceInterface == "" {
opts = append(opts, nic.WithName(link.Name))
}
routes := make([]config.Route, len(device.DeviceRoutes))
for i := 0; i < len(device.DeviceRoutes); i++ {
routes[i] = device.DeviceRoutes[i]
}
s := &address.Static{Mtu: device.DeviceMTU, NameServers: resolvers, FQDN: hostname, NetIf: link, CIDR: device.DeviceCIDR, RouteList: routes}
opts = append(opts, nic.WithAddressing(s))
return link.Name, opts
}

View File

@ -1,147 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//nolint:testpackage
package networkd
import (
"log"
"net"
"os"
"testing"
"github.com/stretchr/testify/suite"
"github.com/talos-systems/talos/internal/app/networkd/pkg/nic"
"github.com/talos-systems/talos/pkg/machinery/config"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1"
)
type NetconfSuite struct {
suite.Suite
}
func TestNetconfSuite(t *testing.T) {
// Hide all our state transition messages
// log.SetOutput(ioutil.Discard)
suite.Run(t, new(NetconfSuite))
}
func (suite *NetconfSuite) TestBaseNetconf() {
for _, device := range sampleConfig() {
_, opts, err := buildOptions(log.New(os.Stderr, "", log.LstdFlags), device, "")
suite.Require().NoError(err)
_, err = nic.New(opts...)
suite.Require().NoError(err)
}
}
func (suite *NetconfSuite) TestKernelNetconf() {
name, opts := buildKernelOptions(sampleKernelIPParam())
iface, err := nic.New(opts...)
suite.Require().NoError(err)
suite.Assert().Equal(iface.Name, name)
suite.Assert().Equal(len(iface.AddressMethod), 1)
addr := iface.AddressMethod[0]
suite.Assert().Equal(addr.Name(), "static")
suite.Assert().Equal(addr.Hostname(), "hostname")
suite.Assert().Equal(addr.Address().IP, net.ParseIP("1.1.1.1"))
suite.Assert().Equal(len(addr.Resolvers()), 2)
suite.Assert().Equal(addr.Resolvers()[0], net.ParseIP("4.4.4.4"))
suite.Assert().Equal(addr.Resolvers()[1], net.ParseIP("5.5.5.5"))
suite.Assert().Equal(len(addr.Routes()), 1)
}
func (suite *NetconfSuite) TestKernelNetconfIncomplete() {
name, opts := buildKernelOptions("1.1.1.1::3.3.3.3:255.255.255.0::eth0:none:::")
iface, err := nic.New(opts...)
suite.Require().NoError(err)
suite.Assert().Equal(iface.Name, name)
suite.Assert().Equal(len(iface.AddressMethod), 1)
addr := iface.AddressMethod[0]
suite.Assert().Equal(addr.Name(), "static")
suite.Assert().Equal(addr.Hostname(), "")
suite.Assert().Equal(addr.Address().IP, net.ParseIP("1.1.1.1"))
suite.Assert().Len(addr.Resolvers(), 0)
suite.Assert().Equal(len(addr.Routes()), 1)
}
func sampleConfig() []config.Device {
return []config.Device{
&v1alpha1.Device{
DeviceInterface: "eth0",
DeviceCIDR: "192.168.0.10/24",
},
&v1alpha1.Device{
DeviceInterface: "bond0",
DeviceCIDR: "192.168.0.10/24",
DeviceBond: &v1alpha1.Bond{BondInterfaces: []string{"lo"}},
},
&v1alpha1.Device{
DeviceInterface: "bond0",
DeviceBond: &v1alpha1.Bond{BondInterfaces: []string{"lo"}, BondMode: "balance-rr"},
},
&v1alpha1.Device{
DeviceInterface: "eth0",
DeviceIgnore: true,
},
&v1alpha1.Device{
DeviceInterface: "eth0",
DeviceMTU: 9100,
DeviceCIDR: "192.168.0.10/24",
DeviceRoutes: []*v1alpha1.Route{{RouteNetwork: "10.0.0.0/8", RouteGateway: "10.0.0.1"}},
},
&v1alpha1.Device{
DeviceInterface: "bond0",
DeviceBond: &v1alpha1.Bond{
BondInterfaces: []string{"lo"},
BondMode: "balance-rr",
BondHashPolicy: "layer2",
BondLACPRate: "fast",
BondMIIMon: 200,
BondUpDelay: 100,
BondDownDelay: 100,
},
},
&v1alpha1.Device{
DeviceInterface: "bondyolo0",
DeviceBond: &v1alpha1.Bond{
BondInterfaces: []string{"lo"},
BondMode: "balance-rr",
BondHashPolicy: "layer2",
BondLACPRate: "fast",
BondMIIMon: 200,
BondUpDelay: 100,
BondDownDelay: 100,
BondUseCarrier: nil,
BondARPInterval: 230,
BondARPValidate: "all",
BondARPAllTargets: "all",
BondPrimary: "lo",
BondPrimaryReselect: "better",
BondFailOverMac: "none",
BondResendIGMP: 10,
BondNumPeerNotif: 5,
BondAllSlavesActive: 1,
BondMinLinks: 1,
BondLPInterval: 100,
BondPacketsPerSlave: 50,
BondADSelect: "bandwidth",
BondADActorSysPrio: 23,
BondADUserPortKey: 323,
BondTLBDynamicLB: 1,
BondPeerNotifyDelay: 200,
},
},
}
}
func sampleKernelIPParam() string {
return "1.1.1.1:2.2.2.2:3.3.3.3:255.255.255.0:hostname:eth0:none:4.4.4.4:5.5.5.5:6.6.6.6"
}

View File

@ -1,468 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Package networkd handles the network interface configuration on a host.
// If no configuration is provided, automatic configuration via dhcp will
// be performed on interfaces ( eth, en, bond ).
package networkd
import (
"context"
"fmt"
"log"
"net"
"sort"
"strings"
"sync"
"time"
multierror "github.com/hashicorp/go-multierror"
"github.com/talos-systems/go-procfs/procfs"
"golang.org/x/sync/errgroup"
"golang.org/x/sys/unix"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform"
"github.com/talos-systems/talos/internal/app/networkd/pkg/address"
"github.com/talos-systems/talos/internal/app/networkd/pkg/nic"
"github.com/talos-systems/talos/pkg/machinery/config"
"github.com/talos-systems/talos/pkg/machinery/constants"
)
// Set up default nameservers.
const (
DefaultPrimaryResolver = "1.1.1.1"
DefaultSecondaryResolver = "8.8.8.8"
)
// Networkd provides the high level interaction to configure network interfaces
// on a host system. This currently supports addressing configuration via dhcp
// and/or a specified configuration file.
type Networkd struct {
Interfaces map[string]*nic.NetworkInterface
Config config.Provider
hostname string
resolvers []string
sync.Mutex
ready bool
logger *log.Logger
}
// New takes the supplied configuration and creates an abstract representation
// of all interfaces (as nic.NetworkInterface).
//nolint:gocyclo,cyclop
func New(logger *log.Logger, config config.Provider) (*Networkd, error) {
var (
hostname string
option *string
result *multierror.Error
resolvers []string
)
netconf := make(map[string][]nic.Option)
if option = procfs.ProcCmdline().Get("ip").First(); option != nil {
if name, opts := buildKernelOptions(*option); name != "" {
netconf[name] = opts
}
}
// Gather settings for all config driven interfaces
if config != nil {
logger.Println("parsing configuration file")
for _, device := range config.Machine().Network().Devices() {
name, opts, err := buildOptions(logger, device, config.Machine().Network().Hostname())
if err != nil {
result = multierror.Append(result, err)
continue
}
if _, ok := netconf[name]; ok {
netconf[name] = append(netconf[name], opts...)
} else {
netconf[name] = opts
}
}
hostname = config.Machine().Network().Hostname()
if len(config.Machine().Network().Resolvers()) > 0 {
resolvers = config.Machine().Network().Resolvers()
}
}
logger.Println("discovering local interfaces")
// Gather already present interfaces
localInterfaces, err := net.Interfaces()
if err != nil {
result = multierror.Append(result, err)
return &Networkd{}, result.ErrorOrNil()
}
// Add locally discovered interfaces to our list of interfaces
// if they are not already present
filtered, err := filterInterfaces(logger, localInterfaces)
if err != nil {
result = multierror.Append(result, err)
return &Networkd{}, result.ErrorOrNil()
}
for _, device := range filtered {
if _, ok := netconf[device.Name]; !ok {
netconf[device.Name] = []nic.Option{nic.WithName(device.Name)}
// Explicitly ignore bonded interfaces if no configuration was specified
// This should speed up initial boot times since an unconfigured bond
// does not provide any value.
if strings.HasPrefix(device.Name, "bond") {
netconf[device.Name] = append(netconf[device.Name], nic.WithIgnore())
}
}
// Ensure lo has proper loopback address
// Ensure MTU for loopback is 64k
// ref: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0cf833aefaa85bbfce3ff70485e5534e09254773
if strings.HasPrefix(device.Name, "lo") {
netconf[device.Name] = append(netconf[device.Name], nic.WithAddressing(
&address.Static{
CIDR: "127.0.0.1/8",
Mtu: nic.MaximumMTU,
},
))
}
}
// add local interfaces which were filtered out with Ignore
for _, device := range localInterfaces {
if _, ok := netconf[device.Name]; !ok {
netconf[device.Name] = []nic.Option{nic.WithName(device.Name), nic.WithIgnore()}
}
}
interfaces := make(map[string]*nic.NetworkInterface)
// Create nic.NetworkInterface representation of the interface
for ifname, opts := range netconf {
netif, err := nic.New(opts...)
if err != nil {
result = multierror.Append(result, err)
continue
}
interfaces[ifname] = netif
}
// Set interfaces that are part of a bond to ignored
for _, netif := range interfaces {
if !netif.Bonded {
continue
}
for _, subif := range netif.SubInterfaces {
if _, ok := interfaces[subif.Name]; !ok {
result = multierror.Append(result, fmt.Errorf("bond subinterface %s does not exist", subif.Name))
continue
}
interfaces[subif.Name].Ignore = true
}
}
return &Networkd{
Interfaces: interfaces,
Config: config,
hostname: hostname,
resolvers: resolvers,
logger: logger,
}, result.ErrorOrNil()
}
// Configure handles the lifecycle for an interface. This includes creation,
// configuration, and any addressing that is needed. We care about ordering
// here so that we can ensure any links that make up a bond will be in
// the correct state when we get to bonding configuration.
//
//nolint:gocyclo
func (n *Networkd) Configure(ctx context.Context) (err error) {
// Configure non-bonded interfaces first so we can ensure basic
// interfaces exist prior to bonding
for _, bonded := range []bool{false, true} {
if bonded {
n.logger.Println("configuring bonded interfaces")
} else {
n.logger.Println("configuring non-bonded interfaces")
}
if err = n.configureLinks(ctx, bonded); err != nil {
// Treat errors as non-fatal
n.logger.Println(err)
}
}
// prefer resolvers from the configuration
resolvers := append([]string(nil), n.resolvers...)
// if no resolvers configured, use addressing method resolvers
if len(resolvers) == 0 {
for _, netif := range n.Interfaces {
for _, method := range netif.AddressMethod {
if !method.Valid() {
continue
}
for _, resolver := range method.Resolvers() {
resolvers = append(resolvers, resolver.String())
}
}
}
}
// use default resolvers if nothing is configured
if len(resolvers) == 0 {
resolvers = append(resolvers, DefaultPrimaryResolver, DefaultSecondaryResolver)
}
// Set hostname must be before the resolv configuration
// so we can ensure the hosts domainname is set properly
// before we write the search stanza
if err = n.Hostname(); err != nil {
return err
}
if err = writeResolvConf(n.logger, resolvers); err != nil {
return err
}
n.SetReady()
return nil
}
// Renew sets up a long running loop to refresh a network interfaces
// addressing configuration. Currently this only applies to interfaces
// configured by DHCP.
func (n *Networkd) Renew(ctx context.Context) {
for _, iface := range n.Interfaces {
iface.Renew(ctx, n.logger)
}
}
// Reset handles removing addresses from previously configured interfaces.
func (n *Networkd) Reset() {
for _, iface := range n.Interfaces {
iface.Reset()
}
}
// RunControllers spins up additional controllers in the errgroup.
func (n *Networkd) RunControllers(ctx context.Context, eg *errgroup.Group) error {
for _, iface := range n.Interfaces {
if err := iface.RunControllers(ctx, n.logger, eg); err != nil {
return err
}
}
return nil
}
// Hostname returns the first hostname found from the addressing methods.
// Create /etc/hosts and set hostname.
// Priority is:
// 1. Config (explicitly defined by the user)
// 2. Kernel arg
// 3. Platform
// 4. DHCP
// 5. Default with the format: talos-<ip addr>.
func (n *Networkd) Hostname() (err error) {
hostname, domainname, address, err := n.decideHostname()
if err != nil {
return err
}
if err = writeHosts(hostname, address, n.Config); err != nil {
return err
}
var p runtime.Platform
p, err = platform.CurrentPlatform()
if err != nil {
return err
}
// Skip hostname/domainname setting when running in container mode
if p.Mode() == runtime.ModeContainer {
return nil
}
if err = unix.Sethostname([]byte(hostname)); err != nil {
return err
}
return unix.Setdomainname([]byte(domainname))
}
//nolint:gocyclo
func (n *Networkd) decideHostname() (hostname, domainname string, address net.IP, err error) {
// Set hostname to default
address = net.ParseIP("127.0.1.1")
hostname = fmt.Sprintf("%s-%s", "talos", strings.ReplaceAll(address.String(), ".", "-"))
// Sort interface names alphabetically so we can ensure parsing order
interfaceNames := make([]string, 0, len(n.Interfaces))
for intName := range n.Interfaces {
interfaceNames = append(interfaceNames, intName)
}
sort.Strings(interfaceNames)
// Loop through address responses and use the first hostname
// and address response.
outer:
for _, intName := range interfaceNames {
iface := n.Interfaces[intName]
// Skip loopback interface because it will always have
// a hardcoded hostname of `talos-ip`
if iface.Link != nil && iface.Link.Flags&net.FlagLoopback != 0 {
continue
}
for _, method := range iface.AddressMethod {
if !method.Valid() {
continue
}
if method.Hostname() != "" {
hostname = method.Hostname()
address = method.Address().IP
break outer
}
}
}
// Platform
var p runtime.Platform
ctx, ctxCancel := context.WithTimeout(context.Background(), 30*time.Second)
defer ctxCancel()
p, err = platform.CurrentPlatform()
if err == nil {
var pHostname []byte
if pHostname, err = p.Hostname(ctx); err == nil && string(pHostname) != "" {
hostname = string(pHostname)
}
}
// Kernel
if kernelHostname := procfs.ProcCmdline().Get(constants.KernelParamHostname).First(); kernelHostname != nil {
hostname = *kernelHostname
}
// Allow user supplied hostname to win
if n.hostname != "" {
hostname = n.hostname
}
hostParts := strings.Split(hostname, ".")
if len(hostParts[0]) > 63 {
return "", "", net.IP{}, fmt.Errorf("hostname length longer than max allowed (63): %s", hostParts[0])
}
if len(hostname) > 253 {
return "", "", net.IP{}, fmt.Errorf("hostname fqdn length longer than max allowed (253): %s", hostname)
}
hostname = hostParts[0]
if len(hostParts) > 1 {
domainname = strings.Join(hostParts[1:], ".")
}
// Only return the hostname portion of the name ( strip domain bits off )
return hostname, domainname, address, nil
}
// Ready exposes the readiness state of networkd.
func (n *Networkd) Ready() bool {
n.Lock()
defer n.Unlock()
return n.ready
}
// SetReady sets the readiness state of networkd.
func (n *Networkd) SetReady() {
n.Lock()
defer n.Unlock()
n.ready = true
}
func (n *Networkd) configureLinks(ctx context.Context, bonded bool) error {
errCh := make(chan error, len(n.Interfaces))
count := 0
for _, iface := range n.Interfaces {
if iface.Bonded != bonded {
continue
}
count++
go func(netif *nic.NetworkInterface) {
if !netif.IsIgnored() {
n.logger.Printf("setting up %s", netif.Name)
}
errCh <- func() error {
// Ensure link exists
if err := netif.Create(); err != nil {
return fmt.Errorf("error creating nic %q: %w", netif.Name, err)
}
if err := netif.CreateSub(n.logger); err != nil {
return fmt.Errorf("error creating sub interface nic %q: %w", netif.Name, err)
}
if err := netif.Configure(ctx); err != nil {
return fmt.Errorf("error configuring nic %q: %w", netif.Name, err)
}
if err := netif.Addressing(n.logger); err != nil {
return fmt.Errorf("error configuring addressing %q: %w", netif.Name, err)
}
if err := netif.AddressingSub(n.logger); err != nil {
return fmt.Errorf("error configuring addressing %q: %w", netif.Name, err)
}
return nil
}()
}(iface)
}
var multiErr *multierror.Error
for i := 0; i < count; i++ {
multiErr = multierror.Append(multiErr, <-errCh)
}
return multiErr.ErrorOrNil()
}

View File

@ -1,213 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//nolint:testpackage
package networkd
import (
"log"
"net"
"os"
"testing"
"github.com/insomniacslk/dhcp/dhcpv4"
"github.com/stretchr/testify/suite"
"github.com/talos-systems/talos/internal/app/networkd/pkg/address"
"github.com/talos-systems/talos/pkg/machinery/config"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1"
)
type NetworkdSuite struct {
suite.Suite
}
func TestNetworkdSuite(t *testing.T) {
// Hide all our state transition messages
// log.SetOutput(ioutil.Discard)
suite.Run(t, new(NetworkdSuite))
}
func (suite *NetworkdSuite) TestNetworkd() {
nwd, err := New(log.New(os.Stderr, "", log.LstdFlags), sampleConfigFile())
suite.Require().NoError(err)
suite.Require().Contains(nwd.Interfaces, "eth0")
suite.Assert().False(nwd.Interfaces["eth0"].Bonded)
suite.Require().Contains(nwd.Interfaces, "bond0")
suite.Assert().True(nwd.Interfaces["bond0"].Bonded)
suite.Assert().Equal(1, len(nwd.Interfaces["bond0"].SubInterfaces))
suite.Require().Contains(nwd.Interfaces, "lo")
}
func (suite *NetworkdSuite) TestHostname() {
var (
addr net.IP
domainname string
err error
hostname string
nwd *Networkd
sampleConfig config.Provider
)
nwd, err = New(log.New(os.Stderr, "", log.LstdFlags), nil)
suite.Require().NoError(err)
// Default test
hostname, _, addr, err = nwd.decideHostname()
suite.Require().NoError(err)
suite.Assert().Equal("talos-127-0-1-1", hostname)
suite.Assert().Equal(addr, net.ParseIP("127.0.1.1"))
// Static addressing tests
// Static with hostname
sampleConfig = sampleConfigFile()
nwd, err = New(log.New(os.Stderr, "", log.LstdFlags), sampleConfig)
suite.Require().NoError(err)
hostname, _, addr, err = nwd.decideHostname()
suite.Require().NoError(err)
suite.Assert().Equal("myhostname", hostname)
suite.Assert().Equal(addr, net.ParseIP("192.168.0.10"))
// Static for computed hostname ( talos-ip )
sampleConfig.(*v1alpha1.Config).MachineConfig.MachineNetwork.NetworkHostname = ""
nwd, err = New(log.New(os.Stderr, "", log.LstdFlags), sampleConfig)
suite.Require().NoError(err)
hostname, _, addr, err = nwd.decideHostname()
suite.Require().NoError(err)
suite.Assert().Equal("talos-192-168-0-10", hostname)
suite.Assert().Equal(addr, net.ParseIP("192.168.0.10"))
// Static for hostname too long
sampleConfig.(*v1alpha1.Config).MachineConfig.MachineNetwork.NetworkHostname = "somereallyreallyreallylongstringthathasmorethan63charactersbecauseweneedtotestit"
nwd, err = New(log.New(os.Stderr, "", log.LstdFlags), sampleConfig)
suite.Require().NoError(err)
//nolint:dogsled
_, _, _, err = nwd.decideHostname()
suite.Require().Error(err)
// Static for hostname vs domain name
sampleConfig.(*v1alpha1.Config).MachineConfig.MachineNetwork.NetworkHostname = "dadjokes.biz.dev.com.org.io"
nwd, err = New(log.New(os.Stderr, "", log.LstdFlags), sampleConfig)
suite.Require().NoError(err)
hostname, domainname, _, err = nwd.decideHostname()
suite.Require().NoError(err)
suite.Assert().Equal("dadjokes", hostname)
suite.Assert().Equal("biz.dev.com.org.io", domainname)
// DHCP addressing tests
// DHCP with OptionHostName
nwd, err = New(log.New(os.Stderr, "", log.LstdFlags), dhcpConfigFile())
suite.Require().NoError(err)
nwd.Interfaces["eth0"].AddressMethod = []address.Addressing{
&address.DHCP4{
Ack: &dhcpv4.DHCPv4{
YourIPAddr: net.ParseIP("192.168.0.11"),
Options: dhcpv4.Options{
uint8(dhcpv4.OptionHostName): []byte("evenbetterdadjokes"),
uint8(dhcpv4.OptionSubnetMask): []byte{255, 255, 255, 0},
},
},
},
}
hostname, _, addr, err = nwd.decideHostname()
suite.Require().NoError(err)
suite.Assert().Equal("evenbetterdadjokes", hostname)
suite.Assert().Equal(addr.String(), "192.168.0.11")
// DHCP without OptionHostName
nwd, err = New(log.New(os.Stderr, "", log.LstdFlags), dhcpConfigFile())
suite.Require().NoError(err)
nwd.Interfaces["eth0"].AddressMethod = []address.Addressing{
&address.DHCP4{
Ack: &dhcpv4.DHCPv4{
YourIPAddr: net.ParseIP("192.168.0.11"),
Options: dhcpv4.Options{
uint8(dhcpv4.OptionSubnetMask): []byte{255, 255, 255, 0},
},
},
},
}
hostname, _, addr, err = nwd.decideHostname()
suite.Require().NoError(err)
suite.Assert().Equal("talos-192-168-0-11", hostname)
suite.Assert().Equal(addr, net.ParseIP("192.168.0.11"))
// DHCP without OptionHostname and with OptionDomainName
nwd.Interfaces["eth0"].AddressMethod = []address.Addressing{
&address.DHCP4{
Ack: &dhcpv4.DHCPv4{
YourIPAddr: net.ParseIP("192.168.0.11"),
Options: dhcpv4.Options{
uint8(dhcpv4.OptionDomainName): []byte("domain.tld"),
},
},
},
}
hostname, domainname, addr, err = nwd.decideHostname()
suite.Require().NoError(err)
suite.Assert().Equal("talos-192-168-0-11", hostname)
suite.Assert().Equal("domain.tld", domainname)
suite.Assert().Equal(addr, net.ParseIP("192.168.0.11"))
}
func sampleConfigFile() config.Provider {
return &v1alpha1.Config{
MachineConfig: &v1alpha1.MachineConfig{
MachineNetwork: &v1alpha1.NetworkConfig{
NameServers: []string{"1.2.3.4", "2.3.4.5"},
NetworkHostname: "myhostname",
NetworkInterfaces: []*v1alpha1.Device{
{
DeviceInterface: "eth0",
DeviceCIDR: "192.168.0.10/24",
DeviceMTU: 9100,
},
{
DeviceInterface: "bond0",
DeviceCIDR: "192.168.0.10/24",
DeviceBond: &v1alpha1.Bond{
BondInterfaces: []string{"lo"},
BondMode: "balance-rr",
},
},
},
},
},
}
}
func dhcpConfigFile() config.Provider {
return &v1alpha1.Config{
MachineConfig: &v1alpha1.MachineConfig{
MachineNetwork: &v1alpha1.NetworkConfig{
NetworkInterfaces: []*v1alpha1.Device{
{
DeviceInterface: "eth0",
},
{
DeviceInterface: "eth1",
DeviceCIDR: "192.168.0.10/24",
DeviceMTU: 9100,
},
},
},
},
}
}

View File

@ -1,373 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Additional information can be found
// https://www.kernel.org/doc/Documentation/networking/bonding.txt.
package nic
import (
"net"
)
// WithBond defines if the interface should be bonded.
func WithBond(o bool) Option {
return func(n *NetworkInterface) (err error) {
n.Bonded = o
return nil
}
}
// WithSubInterface defines which interfaces make up the bond.
func WithSubInterface(o ...string) Option {
return func(n *NetworkInterface) (err error) {
var found bool
for _, ifname := range o {
found = false
for _, subif := range n.SubInterfaces {
if ifname == subif.Name {
found = true
break
}
}
if found {
continue
}
var iface *net.Interface
iface, err = net.InterfaceByName(ifname)
if err != nil {
return err
}
n.SubInterfaces = append(n.SubInterfaces, iface)
}
return err
}
}
// WithBondMode sets the mode the bond should operate in.
func WithBondMode(o string) Option {
return func(n *NetworkInterface) (err error) {
var mode BondMode
if mode, err = BondModeByName(o); err != nil {
return err
}
n.BondSettings.Uint8(uint16(IFLA_BOND_MODE), uint8(mode))
return err
}
}
// WithHashPolicy configures the transmit hash policy for the bonded interface.
func WithHashPolicy(o string) Option {
return func(n *NetworkInterface) (err error) {
var policy BondXmitHashPolicy
if policy, err = BondXmitHashPolicyByName(o); err != nil {
return err
}
n.BondSettings.Uint8(uint16(IFLA_BOND_XMIT_HASH_POLICY), uint8(policy))
return err
}
}
// WithLACPRate configures the bond LACP rate.
func WithLACPRate(o string) Option {
return func(n *NetworkInterface) (err error) {
var rate LACPRate
if rate, err = LACPRateByName(o); err != nil {
return err
}
n.BondSettings.Uint8(uint16(IFLA_BOND_AD_LACP_RATE), uint8(rate))
return err
}
}
// WithUpDelay configures the up delay for interfaces that makes up a bond.
// The value is given in ms.
func WithUpDelay(o uint32) Option {
return func(n *NetworkInterface) (err error) {
n.BondSettings.Uint32(uint16(IFLA_BOND_UPDELAY), o)
return err
}
}
// WithDownDelay configures the down delay for interfaces that makes up a bond.
// The value is given in ms.
func WithDownDelay(o uint32) Option {
return func(n *NetworkInterface) (err error) {
n.BondSettings.Uint32(uint16(IFLA_BOND_DOWNDELAY), o)
return err
}
}
// WithMIIMon configures the miimon interval for a bond.
// The value is given in ms.
func WithMIIMon(o uint32) Option {
return func(n *NetworkInterface) (err error) {
n.BondSettings.Uint32(uint16(IFLA_BOND_MIIMON), o)
return err
}
}
// WithUseCarrier configures how miimon will determine the link status.
func WithUseCarrier(o bool) Option {
return func(n *NetworkInterface) (err error) {
// default to 1
var carrier uint8 = 1
if !o {
carrier = 0
}
n.BondSettings.Uint8(uint16(IFLA_BOND_USE_CARRIER), carrier)
return err
}
}
// WithARPInterval specifies the ARP link monitoring frequency in milliseconds.
func WithARPInterval(o uint32) Option {
return func(n *NetworkInterface) (err error) {
n.BondSettings.Uint32(uint16(IFLA_BOND_ARP_INTERVAL), o)
return err
}
}
// WithARPValidate specifies whether or not ARP probes and replies should be
// validated.
func WithARPValidate(o string) Option {
return func(n *NetworkInterface) (err error) {
var valid ARPValidate
if valid, err = ARPValidateByName(o); err != nil {
return err
}
n.BondSettings.Uint32(uint16(IFLA_BOND_ARP_VALIDATE), uint32(valid))
return err
}
}
// WithARPAllTargets specifies the quantity of arp_ip_targets that must be
// reachable in order for the ARP monitor to consider a slave as being up.
func WithARPAllTargets(o string) Option {
return func(n *NetworkInterface) (err error) {
var target ARPAllTargets
if target, err = ARPAllTargetsByName(o); err != nil {
return err
}
n.BondSettings.Uint32(uint16(IFLA_BOND_ARP_ALL_TARGETS), uint32(target))
return err
}
}
// WithPrimary specifies which slave is the primary device.
func WithPrimary(o string) Option {
return func(n *NetworkInterface) (err error) {
var iface *net.Interface
if iface, err = net.InterfaceByName(o); err != nil {
return err
}
n.BondSettings.Uint8(uint16(IFLA_BOND_PRIMARY_RESELECT), uint8(iface.Index))
return err
}
}
// WithPrimaryReselect specifies the reselection policy for the primary slave.
func WithPrimaryReselect(o string) Option {
return func(n *NetworkInterface) (err error) {
var primary PrimaryReselect
if primary, err = PrimaryReselectByName(o); err != nil {
return err
}
n.BondSettings.Uint8(uint16(IFLA_BOND_PRIMARY_RESELECT), uint8(primary))
return err
}
}
// WithFailOverMAC specifies whether active-backup mode should set all
// slaves to the same MAC address.
func WithFailOverMAC(o string) Option {
return func(n *NetworkInterface) (err error) {
var fo FailOverMAC
if fo, err = FailOverMACByName(o); err != nil {
return err
}
n.BondSettings.Uint8(uint16(IFLA_BOND_FAIL_OVER_MAC), uint8(fo))
return err
}
}
// WithResendIGMP specifies the number of IGMP membership reports to be issued
// after a failover event.
func WithResendIGMP(o uint32) Option {
return func(n *NetworkInterface) (err error) {
n.BondSettings.Uint32(uint16(IFLA_BOND_RESEND_IGMP), o)
return err
}
}
// WithNumPeerNotif specifies the number of peer notifications (gratuitous ARPs and
// unsolicited IPv6 Neighbor Advertisements) to be issued after a failover event.
func WithNumPeerNotif(o uint8) Option {
return func(n *NetworkInterface) (err error) {
n.BondSettings.Uint8(uint16(IFLA_BOND_NUM_PEER_NOTIF), o)
return err
}
}
// WithAllSlavesActive specifies that duplicate frames (received on inactive
// ports) should be dropped (0) or delivered (1).
func WithAllSlavesActive(o uint8) Option {
return func(n *NetworkInterface) (err error) {
n.BondSettings.Uint8(uint16(IFLA_BOND_ALL_SLAVES_ACTIVE), o)
return err
}
}
// WithMinLinks specifies the minimum number of links that must be active
// before asserting carrier.
func WithMinLinks(o uint32) Option {
return func(n *NetworkInterface) (err error) {
n.BondSettings.Uint32(uint16(IFLA_BOND_MIN_LINKS), o)
return err
}
}
// WithLPInterval specifies the number of seconds between instances where
// the bonding driver sends learning packets to each slaves peer switch.
func WithLPInterval(o uint32) Option {
return func(n *NetworkInterface) (err error) {
n.BondSettings.Uint32(uint16(IFLA_BOND_LP_INTERVAL), o)
return err
}
}
// WithPacketsPerSlave specify the number of packets to transmit through
// a slave before moving to the next one.
func WithPacketsPerSlave(o uint32) Option {
return func(n *NetworkInterface) (err error) {
n.BondSettings.Uint32(uint16(IFLA_BOND_PACKETS_PER_SLAVE), o)
return err
}
}
// WithADSelect specifies the 802.3ad aggregation selection logic to use.
func WithADSelect(o string) Option {
return func(n *NetworkInterface) (err error) {
var sel ADSelect
if sel, err = ADSelectByName(o); err != nil {
return err
}
n.BondSettings.Uint8(uint16(IFLA_BOND_AD_SELECT), uint8(sel))
return err
}
}
// WithADActorSysPrio in an AD system, this specifies the system priority.
func WithADActorSysPrio(o uint16) Option {
return func(n *NetworkInterface) (err error) {
n.BondSettings.Uint16(uint16(IFLA_BOND_AD_ACTOR_SYS_PRIO), o)
return err
}
}
// WithADUserPortKey specifies the upper 10 bits of the port key.
func WithADUserPortKey(o uint16) Option {
return func(n *NetworkInterface) (err error) {
n.BondSettings.Uint16(uint16(IFLA_BOND_AD_USER_PORT_KEY), o)
return err
}
}
// WithTLBDynamicLB specifies if dynamic shuffling of flows is enabled in
// tlb mode.
func WithTLBDynamicLB(o uint8) Option {
return func(n *NetworkInterface) (err error) {
n.BondSettings.Uint8(uint16(IFLA_BOND_TLB_DYNAMIC_LB), o)
return err
}
}
// WithPeerNotifyDelay specifies the delay between each peer notification
// (gratuitous ARP and unsolicited IPv6 Neighbor Advertisement) when they
// are issued after a failover event.
// The value is given in ms.
func WithPeerNotifyDelay(o uint32) Option {
return func(n *NetworkInterface) (err error) {
n.BondSettings.Uint32(uint16(IFLA_BOND_PEER_NOTIF_DELAY), o)
return err
}
}
/*
// o = mac addr
// [IFLA_BOND_AD_ACTOR_SYSTEM] = { .type = NLA_BINARY, .len = ETH_ALEN },
func WithADActorSystem(o string) Option {
return func(n *NetworkInterface) (err error) {
return err
}
}
// Not sure this is the right way to do nested attributes
func WithARPIPTarget(o []string) Option {
return func(n *NetworkInterface) (err error) {
n.BondSettings.Nested(uint16(IFLA_BOND_ARP_IP_TARGET), netlink.NewAttributeEncoder().String(uint16(IFLA_BOND_ARP_IP_TARGET), strings.Join(",", o)))
return err
}
}
// [IFLA_BOND_AD_INFO] = { .type = NLA_NESTED },
// func WithInfo(o []string) Option {
// Not adding Active Slave since this is more of a
// runtime adjustment versus config
// [IFLA_BOND_ACTIVE_SLAVE] = { .type = NLA_U32 },
*/

View File

@ -1,152 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package nic
import (
"net"
"github.com/jsimonetti/rtnetlink"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
"golang.zx2c4.com/wireguard/wgctrl"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// createLink creates an interface.
func (n *NetworkInterface) createLink(name string, info *rtnetlink.LinkInfo) error {
err := n.rtConn.Link.New(&rtnetlink.LinkMessage{
Family: unix.AF_UNSPEC,
Type: 0,
Attributes: &rtnetlink.LinkAttributes{
Name: name,
Info: info,
},
})
return err
}
// createLink creates an interface.
func (n *NetworkInterface) createSubLink(name string, info *rtnetlink.LinkInfo, master *uint32) error {
err := n.rtConn.Link.New(&rtnetlink.LinkMessage{
Family: unix.AF_UNSPEC,
Type: 0,
Attributes: &rtnetlink.LinkAttributes{
Name: name,
Info: info,
Type: *master,
},
})
return err
}
// setMTU sets the link MTU.
func (n *NetworkInterface) setMTU(idx int, mtu uint32) error {
msg, err := n.rtConn.Link.Get(uint32(idx))
if err != nil {
return err
}
if msg.Attributes != nil && msg.Attributes.MTU == mtu {
return nil
}
err = n.rtConn.Link.Set(&rtnetlink.LinkMessage{
Family: msg.Family,
Type: msg.Type,
Index: uint32(idx),
Flags: msg.Flags,
Change: 0,
Attributes: &rtnetlink.LinkAttributes{
MTU: mtu,
},
})
return err
}
func (n *NetworkInterface) configureBond(idx int, attrs *netlink.AttributeEncoder) error {
// Request the details of the interface
msg, err := n.rtConn.Link.Get(uint32(idx))
if err != nil {
return err
}
nlAttrBytes, err := attrs.Encode()
if err != nil {
return err
}
err = n.rtConn.Link.Set(&rtnetlink.LinkMessage{
Family: unix.AF_UNSPEC,
Type: msg.Type,
Index: msg.Index,
Change: 0,
Attributes: &rtnetlink.LinkAttributes{
Info: &rtnetlink.LinkInfo{
// https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/if_link.h#L612
Kind: "bond",
Data: nlAttrBytes,
},
},
})
if err != nil {
return err
}
return nil
}
func (n *NetworkInterface) configureWireguard(name string, config *wgtypes.Config) error {
c, err := wgctrl.New()
if err != nil {
return err
}
defer c.Close() //nolint:errcheck
return c.ConfigureDevice(name, *config)
}
func (n *NetworkInterface) enslaveLink(bondIndex *uint32, links ...*net.Interface) error {
// Set the interface operationally UP
for _, iface := range links {
// Request the details of the interface
msg, err := n.rtConn.Link.Get(uint32(iface.Index))
if err != nil {
return err
}
// rtnl.Down
err = n.rtConn.Link.Set(&rtnetlink.LinkMessage{
Family: msg.Family,
Type: msg.Type,
Index: uint32(iface.Index),
Flags: 0,
Change: unix.IFF_UP,
})
if err != nil {
return err
}
// Set link master to bond interface
err = n.rtConn.Link.Set(&rtnetlink.LinkMessage{
Family: msg.Family,
Type: msg.Type,
Index: uint32(iface.Index),
Change: 0,
Flags: 0,
Attributes: &rtnetlink.LinkAttributes{
Master: bondIndex,
},
})
if err != nil {
return err
}
}
return nil
}

View File

@ -1,486 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Package nic provides a way to describe and configure a network interface.
package nic
import (
"context"
"errors"
"fmt"
"log"
"net"
"os"
"strconv"
"syscall"
"time"
"github.com/hashicorp/go-multierror"
"github.com/jsimonetti/rtnetlink"
"github.com/jsimonetti/rtnetlink/rtnl"
"github.com/mdlayher/netlink"
"github.com/talos-systems/go-procfs/procfs"
"github.com/talos-systems/go-retry/retry"
"golang.org/x/sync/errgroup"
"golang.org/x/sys/unix"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"google.golang.org/protobuf/proto"
"github.com/talos-systems/talos/internal/app/networkd/pkg/address"
"github.com/talos-systems/talos/internal/app/networkd/pkg/vip"
"github.com/talos-systems/talos/pkg/machinery/constants"
)
const (
// ref: https://tools.ietf.org/html/rfc791
// MinimumMTU is the lowest allowed MTU for an interface.
MinimumMTU = 68
// MaximumMTU is the highest allowed MTU for an interface.
MaximumMTU = 65536
)
// NetworkInterface provides an abstract configuration representation for a
// network interface.
type NetworkInterface struct {
Name string
Type int
Ignore bool
Dummy bool
Bonded bool
Wireguard bool
MTU uint32
Link *net.Interface
SubInterfaces []*net.Interface
AddressMethod []address.Addressing
BondSettings *netlink.AttributeEncoder
Vlans []*Vlan
VirtualIP net.IP
WireguardConfig *wgtypes.Config
rtConn *rtnetlink.Conn
rtnlConn *rtnl.Conn
vipController vip.Controller
}
// New returns a NetworkInterface with all of the given setter options applied.
func New(setters ...Option) (*NetworkInterface, error) {
// Default interface setup
iface := defaultOptions()
// Configure interface with any specified options
var result *multierror.Error
for _, setter := range setters {
result = multierror.Append(result, setter(iface))
}
// TODO: May need to look at switching this around to filter by Interface.HardwareAddr
// Ensure we have an interface name defined
if iface.Name == "" {
result = multierror.Append(result, errors.New("interface must have a name"))
}
// If no addressing methods have been configured, default to DHCP.
// If VLANs exist do not force DHCP on master device
if len(iface.AddressMethod) == 0 && len(iface.Vlans) == 0 {
iface.AddressMethod = append(iface.AddressMethod, &address.DHCP4{}) // TODO: enable DHCPv6 by default?
}
// Handle netlink connection
conn, err := rtnl.Dial(nil)
if err != nil {
result = multierror.Append(result, err)
return nil, result.ErrorOrNil()
}
iface.rtnlConn = conn
// Need rtnetlink for MTU and bond settings
nlConn, err := rtnetlink.Dial(nil)
if err != nil {
result = multierror.Append(result, err)
return nil, result.ErrorOrNil()
}
iface.rtConn = nlConn
return iface, result.ErrorOrNil()
}
// IsIgnored checks the network interface to see if it should be ignored and not configured.
func (n *NetworkInterface) IsIgnored() bool {
if n.Ignore || procfs.ProcCmdline().Get(constants.KernelParamNetworkInterfaceIgnore).Contains(n.Name) {
return true
}
return false
}
// Create creates the underlying link if it does not already exist.
func (n *NetworkInterface) Create() error {
var info *rtnetlink.LinkInfo
iface, err := net.InterfaceByName(n.Name)
if err == nil {
n.Link = iface
return nil
}
switch {
case n.Bonded:
info = &rtnetlink.LinkInfo{Kind: "bond"}
case n.Dummy:
info = &rtnetlink.LinkInfo{Kind: "dummy"}
case n.Wireguard:
info = &rtnetlink.LinkInfo{Kind: "wireguard"}
default:
return fmt.Errorf("unknown device type")
}
if err = n.createLink(n.Name, info); err != nil {
return err
}
iface, err = net.InterfaceByName(n.Name)
if err != nil {
return err
}
n.Link = iface
return nil
}
// CreateSub create VLAN devices that belongs to a master device.
func (n *NetworkInterface) CreateSub(logger *log.Logger) error {
var info *rtnetlink.LinkInfo
// Create all the VLAN devices
for _, vlan := range n.Vlans {
name := n.Name + "." + strconv.Itoa(int(vlan.ID))
logger.Printf("setting up %s", name)
iface, err := net.InterfaceByName(name)
if err == nil {
vlan.Link = iface
continue
}
data, err := vlan.VlanSettings.Encode()
if err != nil {
logger.Println("failed to encode vlan link parameters: " + err.Error())
continue
}
// Vlan devices needs the master link index
masterIdx := uint32(n.Link.Index)
info = &rtnetlink.LinkInfo{Kind: "vlan", Data: data}
if err = n.createSubLink(name, info, &masterIdx); err != nil {
logger.Println("failed to create vlan link " + err.Error())
return err
}
iface, err = net.InterfaceByName(name)
if err != nil {
logger.Println("failed to get vlan interface ")
return err
}
vlan.Link = iface
}
return nil
}
// Configure is used to set the link state and configure any necessary
// bond settings ( ex, mode ).
//nolint:gocyclo
func (n *NetworkInterface) Configure(ctx context.Context) (err error) {
if n.IsIgnored() {
return err
}
if n.Bonded {
if err = n.configureBond(n.Link.Index, n.BondSettings); err != nil {
return err
}
bondIndex := proto.Uint32(uint32(n.Link.Index))
// TODO: Add check if link is already part of a bond
if err = n.enslaveLink(bondIndex, n.SubInterfaces...); err != nil {
return err
}
}
if n.Wireguard {
if err = n.configureWireguard(n.Name, n.WireguardConfig); err != nil {
return err
}
}
if err = n.rtnlConn.LinkUp(n.Link); err != nil {
return err
}
if err = n.waitForLinkToBeUp(n.Link); err != nil {
return fmt.Errorf("failed to bring up interface %q: %w", n.Link.Name, err)
}
// Create all the VLAN devices
for _, vlan := range n.Vlans {
if err = n.rtnlConn.LinkUp(vlan.Link); err != nil {
return err
}
if err = n.waitForLinkToBeUp(vlan.Link); err != nil {
return fmt.Errorf("failed to bring up interface %q: %w", vlan.Link.Name, err)
}
}
return nil
}
// RunControllers is used to run additional controllers per interface.
func (n *NetworkInterface) RunControllers(ctx context.Context, logger *log.Logger, eg *errgroup.Group) (err error) {
if n.VirtualIP != nil {
if n.vipController, err = vip.New(n.VirtualIP.String(), n.Link.Name); err != nil {
return fmt.Errorf("failed to create the VirtualIP controller for %q on %q: %w", n.VirtualIP, n.Link.Name, err)
}
if err = n.vipController.Start(ctx, logger, eg); err != nil {
return fmt.Errorf("failed to start the VirtualIP controller for %q on %q: %w", n.VirtualIP, n.Link.Name, err)
}
}
return nil
}
func (n *NetworkInterface) waitForLinkToBeUp(linkDev *net.Interface) error {
// Wait for link to report up
var link rtnetlink.LinkMessage
err := retry.Constant(30*time.Second, retry.WithUnits(250*time.Millisecond), retry.WithJitter(50*time.Millisecond)).Retry(func() error {
var err error
link, err = n.rtConn.Link.Get(uint32(linkDev.Index))
if err != nil {
return err
}
if link.Flags&unix.IFF_UP != unix.IFF_UP {
return retry.ExpectedError(fmt.Errorf("link is not up %s", n.Link.Name))
}
if link.Flags&unix.IFF_RUNNING != unix.IFF_RUNNING {
return retry.ExpectedError(fmt.Errorf("link is not ready %s", n.Link.Name))
}
return nil
})
return err
}
// Addressing handles the address method for a configured interface ( dhcp/static ).
// This is inclusive of the address itself as well as any defined routes.
func (n *NetworkInterface) Addressing(logger *log.Logger) error {
if n.IsIgnored() {
return nil
}
for _, method := range n.AddressMethod {
if err := n.configureInterface(logger, method, n.Link); err != nil {
// Treat as non fatal error when failing to configure an interface
continue
}
}
return nil
}
// AddressingSub handles the address method for a configured sub interface ( dhcp/static ).
// This is inclusive of the address itself as well as any defined routes.
func (n *NetworkInterface) AddressingSub(logger *log.Logger) error {
if n.IsIgnored() {
return nil
}
for _, vlan := range n.Vlans {
for _, method := range vlan.AddressMethod {
if err := n.configureInterface(logger, method, vlan.Link); err != nil {
logger.Println("failed to configure address on vlan link: " + err.Error())
// Treat as non fatal error when failing to configure an interface
continue
}
}
}
return nil
}
// Renew is the mechanism for keeping a dhcp lease active.
func (n *NetworkInterface) Renew(ctx context.Context, logger *log.Logger) {
for _, method := range n.AddressMethod {
if method.TTL() == 0 {
continue
}
go n.renew(ctx, logger, method)
}
}
// renew sets up the looping to ensure we keep the addressing information
// up to date. We attempt to do our first reconfiguration halfway through
// address TTL. If that fails, we'll continue to attempt to retry every
// halflife.
func (n *NetworkInterface) renew(ctx context.Context, logger *log.Logger, method address.Addressing) {
const minRenewDuration = 5 * time.Second // protect from renewing too often
renewDuration := method.TTL() / 2
var err error
for {
select {
case <-time.After(renewDuration):
case <-ctx.Done():
return
}
if err = n.configureInterface(logger, method, n.Link); err != nil {
logger.Printf("failure to renew address for %q: %s", n.Name, err)
renewDuration = (renewDuration / 2)
} else {
renewDuration = method.TTL() / 2
}
if renewDuration < minRenewDuration {
renewDuration = minRenewDuration
}
}
}
// configureInterface handles the actual address discovery mechanism and
// netlink interaction to configure the interface.
//nolint:gocyclo,cyclop
func (n *NetworkInterface) configureInterface(logger *log.Logger, method address.Addressing, link *net.Interface) error {
var err error
discoverErr := method.Discover(context.Background(), logger, link)
// Set link MTU in any case
if err = n.setMTU(method.Link().Index, method.MTU()); err != nil {
return fmt.Errorf("error setting MTU %d on %q: %w", method.MTU(), n.Name, err)
}
if discoverErr != nil {
return discoverErr
}
if method.Address() != nil {
// Check to see if we need to configure the address
var addrs []*net.IPNet
addrs, err = n.rtnlConn.Addrs(method.Link(), method.Family())
if err != nil {
return err
}
addressExists := false
for _, addr := range addrs {
if method.Address().String() == addr.String() {
addressExists = true
break
}
}
if !addressExists && method.Address() != nil {
if err = n.rtnlConn.AddrAdd(method.Link(), method.Address()); err != nil {
switch err := err.(type) {
case *netlink.OpError:
if !os.IsExist(err.Err) && err.Err != syscall.ESRCH {
return fmt.Errorf("error adding address %s on %q: %w", method.Address(), n.Name, err)
}
default:
return fmt.Errorf("failed to add address (already exists) %+v to %s: %v", method.Address(), method.Link().Name, err)
}
}
}
}
// Add any routes
for _, r := range method.Routes() {
// If gateway/router is 0.0.0.0 we'll set to nil so route scope decision will be correct
gw := r.Gateway
if net.IPv4zero.Equal(gw) || net.IPv6zero.Equal(gw) {
gw = nil
}
src := method.Address()
// if destination is the ipv6 default route,and gateway is LL do not pass a src address to set the default geteway
if net.IPv6zero.Equal(r.Destination.IP) && gw.IsLinkLocalUnicast() {
src = nil
}
attr := rtnetlink.RouteAttributes{
Dst: r.Destination.IP,
OutIface: uint32(method.Link().Index),
Priority: r.Metric,
}
if gw != nil {
attr.Gateway = gw
}
err = n.rtnlConn.RouteAdd(method.Link(), *r.Destination, gw, rtnl.WithRouteSrc(src), rtnl.WithRouteAttrs(attr))
if err != nil {
// ignore "EEXIST" errors for routes which are already present
if opErr, ok := err.(*netlink.OpError); !ok || !os.IsExist(opErr.Err) {
return fmt.Errorf("error adding route %s %s on %q: %s", *r.Destination, gw, n.Name, err)
}
}
}
return nil
}
// Reset removes addressing configuration from a given link.
func (n *NetworkInterface) Reset() {
var (
err error
link *net.Interface
nets []*net.IPNet
)
link, err = net.InterfaceByName(n.Name)
if err != nil {
return
}
if nets, err = n.rtnlConn.Addrs(link, 0); err != nil {
return
}
for _, ipnet := range nets {
if err = n.rtnlConn.AddrDel(link, ipnet); err != nil {
continue
}
}
//nolint:errcheck
n.rtnlConn.LinkDown(link)
}

View File

@ -1,128 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package nic_test
import (
"testing"
"github.com/stretchr/testify/suite"
"github.com/talos-systems/talos/internal/app/networkd/pkg/address"
"github.com/talos-systems/talos/internal/app/networkd/pkg/nic"
"github.com/talos-systems/talos/pkg/machinery/config"
)
type NicSuite struct {
suite.Suite
}
func TestNicSuite(t *testing.T) {
suite.Run(t, new(NicSuite))
}
func (suite *NicSuite) TestIgnoreNic() {
mynic, err := nic.New(nic.WithName("yolo"), nic.WithIgnore())
suite.Require().NoError(err)
suite.Assert().True(mynic.IsIgnored())
}
func (suite *NicSuite) TestNoName() {
_, err := nic.New()
suite.Require().Error(err)
}
func (suite *NicSuite) TestBond() {
testSettings := [][]nic.Option{
{
nic.WithName("yolobond"),
nic.WithBond(true),
},
{
nic.WithName("yolobond"),
nic.WithBond(true),
nic.WithBondMode("balance-xor"),
},
{
nic.WithName("yolobond"),
nic.WithBond(true),
nic.WithBondMode("802.3ad"),
nic.WithHashPolicy("layer3+4"),
},
{
nic.WithName("yolobond"),
nic.WithBond(true),
nic.WithBondMode("balance-tlb"),
nic.WithHashPolicy("encap3+4"),
nic.WithLACPRate("fast"),
},
{
nic.WithName("yolobond"),
nic.WithBond(true),
nic.WithBondMode("balance-alb"),
nic.WithHashPolicy("encap2+3"),
nic.WithLACPRate("slow"),
nic.WithUpDelay(200),
},
{
nic.WithName("yolobond"),
nic.WithBond(true),
nic.WithBondMode("broadcast"),
nic.WithHashPolicy("layer2+3"),
nic.WithLACPRate("fast"),
nic.WithUpDelay(300),
nic.WithDownDelay(400),
nic.WithMIIMon(500),
},
{
nic.WithName("yolobond"),
nic.WithBond(true),
nic.WithBondMode("balance-rr"),
nic.WithHashPolicy("layer2"),
nic.WithLACPRate("slow"),
nic.WithUpDelay(300),
nic.WithDownDelay(400),
nic.WithMIIMon(500),
nic.WithSubInterface("lo", "lo"),
},
{
nic.WithName("yolobond"),
nic.WithBond(true),
nic.WithBondMode("active-backup"),
nic.WithHashPolicy("layer2"),
nic.WithLACPRate("slow"),
nic.WithUpDelay(300),
nic.WithDownDelay(400),
nic.WithMIIMon(500),
nic.WithSubInterface("lo", "lo"),
nic.WithAddressing(&address.Static{}),
},
}
for _, test := range testSettings {
mynic, err := nic.New(test...)
suite.Require().NoError(err)
suite.Assert().True(mynic.Bonded)
}
}
func (suite *NicSuite) TestVlan() {
testSettings := [][]nic.Option{
{
nic.WithName("eth0"),
nic.WithVlan(100),
},
{
nic.WithName("eth0"),
nic.WithVlan(100),
nic.WithVlanCIDR(100, "172.21.10.101/28", []config.Route{}),
},
}
for _, test := range testSettings {
mynic, err := nic.New(test...)
suite.Require().NoError(err)
suite.Assert().True(len(mynic.Vlans) > 0)
}
}

View File

@ -1,71 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package nic
import (
"github.com/mdlayher/netlink"
"github.com/talos-systems/talos/internal/app/networkd/pkg/address"
)
// Option is the functional option func.
type Option func(*NetworkInterface) error
// defaultOptions defines our default network interface configuration.
func defaultOptions() *NetworkInterface {
return &NetworkInterface{
Bonded: false,
MTU: 1500,
AddressMethod: []address.Addressing{},
BondSettings: netlink.NewAttributeEncoder(),
}
}
// WithDummy indicates that the interface should be a virtual, dummy interface.
func WithDummy() Option {
return func(n *NetworkInterface) (err error) {
n.Dummy = true
return
}
}
// WithIgnore indicates that the interface should not be processed by talos.
func WithIgnore() Option {
return func(n *NetworkInterface) (err error) {
n.Ignore = true
return
}
}
// WithName sets the name of the interface to the given name.
func WithName(o string) Option {
return func(n *NetworkInterface) (err error) {
n.Name = o
return err
}
}
// WithAddressing defines how the addressing for a given interface
// should be configured.
func WithAddressing(a address.Addressing) Option {
return func(n *NetworkInterface) (err error) {
n.AddressMethod = append(n.AddressMethod, a)
return err
}
}
// WithNoAddressing defines how the addressing for a given interface
// should be configured.
func WithNoAddressing() Option {
return func(n *NetworkInterface) (err error) {
n.AddressMethod = []address.Addressing{}
return err
}
}

View File

@ -1,322 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//nolint:golint,stylecheck,revive
package nic
import "fmt"
// https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/if_link.h#L608
type BondSetting uint16
const (
IFLA_BOND_UNSPEC BondSetting = iota
IFLA_BOND_MODE
IFLA_BOND_ACTIVE_SLAVE
IFLA_BOND_MIIMON
IFLA_BOND_UPDELAY
IFLA_BOND_DOWNDELAY
IFLA_BOND_USE_CARRIER
IFLA_BOND_ARP_INTERVAL
IFLA_BOND_ARP_IP_TARGET
IFLA_BOND_ARP_VALIDATE
IFLA_BOND_ARP_ALL_TARGETS
IFLA_BOND_PRIMARY
IFLA_BOND_PRIMARY_RESELECT
IFLA_BOND_FAIL_OVER_MAC
IFLA_BOND_XMIT_HASH_POLICY
IFLA_BOND_RESEND_IGMP
IFLA_BOND_NUM_PEER_NOTIF
IFLA_BOND_ALL_SLAVES_ACTIVE
IFLA_BOND_MIN_LINKS
IFLA_BOND_LP_INTERVAL
IFLA_BOND_PACKETS_PER_SLAVE
IFLA_BOND_AD_LACP_RATE
IFLA_BOND_AD_SELECT
IFLA_BOND_AD_INFO
IFLA_BOND_AD_ACTOR_SYS_PRIO
IFLA_BOND_AD_USER_PORT_KEY
IFLA_BOND_AD_ACTOR_SYSTEM
IFLA_BOND_TLB_DYNAMIC_LB
IFLA_BOND_PEER_NOTIF_DELAY
)
func (b BondSetting) String() string {
return [...]string{
"unspec",
"mode",
"active slave",
"miimon",
"updelay",
"downdelay",
"use carrier",
"arp interval",
"arp ip target",
"arp validate",
"arp all targets",
"primary",
"primary reselect",
"fail over mac",
"xmit hash policy",
"resend igmp",
"num peer notif",
"all slaves active",
"min links",
"lp interval",
"packets per slave",
"ad lacp rate",
"ad select",
"ad innfo",
"ad actor sys prio",
"ad user port key",
"ad actor system",
"tlb dynamic lb",
"peer notif delay",
}[int(b)]
}
// https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/if_bonding.h
type BondMode uint8
const (
BOND_MODE_ROUNDROBIN BondMode = iota
BOND_MODE_ACTIVEBACKUP
BOND_MODE_XOR
BOND_MODE_BROADCAST
BOND_MODE_8023AD
BOND_MODE_TLB
BOND_MODE_ALB
)
func (b BondMode) String() string {
return [...]string{"balance-rr", "active-backup", "balance-xor", "broadcast", "802.3ad", "balance-tlb", "balance-alb"}[int(b)]
}
func BondModeByName(mode string) (bm BondMode, err error) {
switch mode {
case "balance-rr":
bm = BOND_MODE_ROUNDROBIN
case "active-backup":
bm = BOND_MODE_ACTIVEBACKUP
case "balance-xor":
bm = BOND_MODE_XOR
case "broadcast":
bm = BOND_MODE_BROADCAST
case "802.3ad":
bm = BOND_MODE_8023AD
case "balance-tlb":
bm = BOND_MODE_TLB
case "balance-alb":
bm = BOND_MODE_ALB
default:
err = fmt.Errorf("invalid bond type %s", mode)
}
return bm, err
}
type BondXmitHashPolicy uint8
const (
BOND_XMIT_POLICY_LAYER2 BondXmitHashPolicy = iota
BOND_XMIT_POLICY_LAYER34
BOND_XMIT_POLICY_LAYER23
BOND_XMIT_POLICY_ENCAP23
BOND_XMIT_POLICY_ENCAP34
)
func (b BondXmitHashPolicy) String() string {
return [...]string{"layer2", "layer3+4", "layer2+3", "encap2+3", "encap3+4"}[int(b)]
}
func BondXmitHashPolicyByName(policy string) (xmit BondXmitHashPolicy, err error) {
switch policy {
case "layer2":
xmit = BOND_XMIT_POLICY_LAYER2
case "layer3+4":
xmit = BOND_XMIT_POLICY_LAYER34
case "layer2+3":
xmit = BOND_XMIT_POLICY_LAYER23
case "encap2+3":
xmit = BOND_XMIT_POLICY_ENCAP23
case "encap3+4":
xmit = BOND_XMIT_POLICY_ENCAP34
default:
err = fmt.Errorf("invalid xmit hash policy %v", xmit)
}
return xmit, err
}
type LACPRate uint8
const (
LACP_RATE_SLOW LACPRate = iota
LACP_RATE_FAST
)
func (l LACPRate) String() string {
return [...]string{"slow", "fast"}[l]
}
func LACPRateByName(mode string) (rate LACPRate, err error) {
switch mode {
case "slow":
rate = LACP_RATE_SLOW
case "fast":
rate = LACP_RATE_FAST
default:
err = fmt.Errorf("invalid lacp rate %v", mode)
}
return rate, err
}
type ADSelect uint8
const (
AD_SELECT_STABLE ADSelect = iota
AD_SELECT_BANDWIDTH
AD_SELECT_COUNT
)
func (a ADSelect) String() string {
return [...]string{"stable", "bandwidth", "count"}[a]
}
func ADSelectByName(sel string) (adsel ADSelect, err error) {
switch sel {
case "stable":
adsel = AD_SELECT_STABLE
case "bandwidth":
adsel = AD_SELECT_BANDWIDTH
case "count":
adsel = AD_SELECT_COUNT
default:
err = fmt.Errorf("invalid ad_select mode %v", sel)
}
return adsel, err
}
type ARPValidate uint32
const (
ARP_VALIDATE_NONE ARPValidate = iota
ARP_VALIDATE_ACTIVE
ARP_VALIDATE_BACKUP
ARP_VALIDATE_ALL
)
func (a ARPValidate) String() string {
return [...]string{"none", "active", "backup", "all"}[a]
}
func ARPValidateByName(a string) (arpv ARPValidate, err error) {
switch a {
case "none":
arpv = ARP_VALIDATE_NONE
case "active":
arpv = ARP_VALIDATE_ACTIVE
case "backup":
arpv = ARP_VALIDATE_BACKUP
case "all":
arpv = ARP_VALIDATE_ALL
default:
err = fmt.Errorf("invalid arp_validate mode %v", a)
}
return arpv, err
}
type ARPAllTargets uint32
const (
ARP_ALL_TARGETS_ANY ARPAllTargets = iota
ARP_ALL_TARGETS_ALL
)
func (a ARPAllTargets) String() string {
return [...]string{"any", "all"}[a]
}
func ARPAllTargetsByName(a string) (arpa ARPAllTargets, err error) {
switch a {
case "any":
arpa = ARP_ALL_TARGETS_ANY
case "all":
arpa = ARP_ALL_TARGETS_ALL
default:
err = fmt.Errorf("invalid arp_all_targets mode %v", a)
}
return arpa, err
}
type PrimaryReselect uint8
const (
PRIMARY_RESELECT_ALWAYS PrimaryReselect = iota
PRIMARY_RESELECT_BETTER
PRIMARY_RESELECT_FAILURE
)
func (p PrimaryReselect) String() string {
return [...]string{"always", "better", "failure"}[p]
}
func PrimaryReselectByName(p string) (pr PrimaryReselect, err error) {
switch p {
case "always":
pr = PRIMARY_RESELECT_ALWAYS
case "better":
pr = PRIMARY_RESELECT_BETTER
case "failure":
pr = PRIMARY_RESELECT_FAILURE
default:
err = fmt.Errorf("invalid primary_reselect mode %v", p)
}
return pr, err
}
type FailOverMAC uint8
const (
FAIL_OVER_MAC_NONE FailOverMAC = iota
FAIL_OVER_MAC_ACTIVE
FAIL_OVER_MAC_FOLLOW
)
func FailOverMACByName(f string) (fo FailOverMAC, err error) {
switch f {
case "none":
fo = FAIL_OVER_MAC_NONE
case "active":
fo = FAIL_OVER_MAC_ACTIVE
case "follow":
fo = FAIL_OVER_MAC_FOLLOW
default:
err = fmt.Errorf("invalid fail_over_mac value %v", f)
}
return fo, err
}
const (
IFLA_VLAN_UNSPEC = iota
IFLA_VLAN_ID
IFLA_VLAN_FLAGS
IFLA_VLAN_EGRESS_QOS
IFLA_VLAN_INGRESS_QOS
IFLA_VLAN_PROTOCOL
IFLA_VLAN_MAX = IFLA_VLAN_PROTOCOL
)
// VlanProtocol possible values.
const (
VLAN_PROTOCOL_UNKNOWN = 0
VLAN_PROTOCOL_8021Q = 0x8100
VLAN_PROTOCOL_8021AD = 0x88A8
)

View File

@ -1,29 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Additional information can be found
// https://www.kernel.org/doc/Documentation/networking/bonding.txt.
package nic
import (
"fmt"
"net"
"github.com/talos-systems/talos/pkg/machinery/config"
)
// WithVIPConfig adapts a talosconfig VIP configuration to a networkd interface configuration option.
func WithVIPConfig(cfg config.VIPConfig) Option {
return func(n *NetworkInterface) (err error) {
sharedIP := net.ParseIP(cfg.IP())
if sharedIP == nil {
return fmt.Errorf("failed to parse shared IP %q as an IP address", cfg.IP())
}
n.VirtualIP = sharedIP
return nil
}
}

View File

@ -1,75 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package nic
import (
"fmt"
"net"
"github.com/mdlayher/netlink"
"github.com/talos-systems/talos/internal/app/networkd/pkg/address"
"github.com/talos-systems/talos/pkg/machinery/config"
)
// Vlan contins interface related parameters to a VLAN device.
type Vlan struct {
Parent string
ID uint16
Link *net.Interface
VlanSettings *netlink.AttributeEncoder
AddressMethod []address.Addressing
}
// WithVlan defines the VLAN id to use.
func WithVlan(id uint16) Option {
return func(n *NetworkInterface) (err error) {
for _, vlan := range n.Vlans {
if vlan.ID == id {
return fmt.Errorf("duplicate VLAN id %v given", vlan)
}
}
vlan := &Vlan{
ID: id,
VlanSettings: netlink.NewAttributeEncoder(),
}
vlan.VlanSettings.Uint16(uint16(IFLA_VLAN_ID), vlan.ID)
n.Vlans = append(n.Vlans, vlan)
return nil
}
}
// WithVlanDhcp sets a VLAN device with DHCP.
func WithVlanDhcp(id uint16) Option {
return func(n *NetworkInterface) (err error) {
for _, vlan := range n.Vlans {
if vlan.ID == id {
vlan.AddressMethod = append(vlan.AddressMethod, &address.DHCP4{}) // TODO: should we enable DHCP6 by default?
return nil
}
}
return fmt.Errorf("VLAN id not found for DHCP. Vlan ID %v given", id)
}
}
// WithVlanCIDR defines if the interface have static CIDRs added.
func WithVlanCIDR(id uint16, cidr string, routeList []config.Route) Option {
return func(n *NetworkInterface) (err error) {
for _, vlan := range n.Vlans {
if vlan.ID == id {
vlan.AddressMethod = append(vlan.AddressMethod, &address.Static{CIDR: cidr, RouteList: routeList})
return nil
}
}
return fmt.Errorf("VLAN id not found for CIDR setting %v given", id)
}
}

View File

@ -1,87 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Additional information can be found
// https://www.kernel.org/doc/Documentation/networking/bonding.txt.
package nic
import (
"net"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/talos-systems/talos/pkg/machinery/config"
)
// WithWireguardConfig defines if the interface should be a Wireguard interface and supplies Wireguard configs.
//nolint:gocyclo
func WithWireguardConfig(cfg config.WireguardConfig) Option {
return func(n *NetworkInterface) (err error) {
n.Wireguard = true
privateKey, err := wgtypes.ParseKey(cfg.PrivateKey())
if err != nil {
return err
}
config := &wgtypes.Config{
PrivateKey: &privateKey,
ReplacePeers: true,
}
firewallMark := cfg.FirewallMark()
if firewallMark > 0 {
config.FirewallMark = &firewallMark
}
listenPort := cfg.ListenPort()
if listenPort > 0 {
config.ListenPort = &listenPort
}
config.Peers = make([]wgtypes.PeerConfig, len(cfg.Peers()))
for i, peer := range cfg.Peers() {
publicKey, err := wgtypes.ParseKey(peer.PublicKey())
if err != nil {
return err
}
peerCfg := wgtypes.PeerConfig{
PublicKey: publicKey,
AllowedIPs: make([]net.IPNet, len(peer.AllowedIPs())),
}
if peer.Endpoint() != "" {
peerCfg.Endpoint, err = net.ResolveUDPAddr("", peer.Endpoint())
if err != nil {
return err
}
}
peerKeepaliveInterval := peer.PersistentKeepaliveInterval()
if peerKeepaliveInterval > 0 {
peerCfg.PersistentKeepaliveInterval = &peerKeepaliveInterval
}
for j, ip := range peer.AllowedIPs() {
_, ip, err := net.ParseCIDR(ip)
if err != nil {
return err
}
peerCfg.AllowedIPs[j] = *ip
}
config.Peers[i] = peerCfg
}
n.WireguardConfig = config
return nil
}
}

View File

@ -1,102 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Package reg provides the gRPC network service implementation.
package reg
import (
"context"
"errors"
"time"
"github.com/golang/protobuf/ptypes/empty"
"google.golang.org/grpc"
"github.com/talos-systems/talos/internal/app/networkd/pkg/networkd"
healthapi "github.com/talos-systems/talos/pkg/machinery/api/health"
)
// Registrator is the concrete type that implements the factory.Registrator and
// healthapi.HealthServer and networkapi.NetworkServiceServer interfaces.
type Registrator struct {
healthapi.UnimplementedHealthServer
Networkd *networkd.Networkd
}
// NewRegistrator builds new Registrator instance.
func NewRegistrator(n *networkd.Networkd) (*Registrator, error) {
return &Registrator{
Networkd: n,
}, nil
}
// Register implements the factory.Registrator interface.
func (r *Registrator) Register(s *grpc.Server) {
healthapi.RegisterHealthServer(s, r)
}
// Check implements the Health api and provides visibilty into the state of networkd.
func (r *Registrator) Check(ctx context.Context, in *empty.Empty) (reply *healthapi.HealthCheckResponse, err error) {
reply = &healthapi.HealthCheckResponse{
Messages: []*healthapi.HealthCheck{
{
Status: healthapi.HealthCheck_SERVING,
},
},
}
return reply, nil
}
// Watch implements the Health api and provides visibilty into the state of networkd.
// Ready signifies the daemon (api) is healthy and ready to serve requests.
func (r *Registrator) Watch(in *healthapi.HealthWatchRequest, srv healthapi.Health_WatchServer) (err error) {
if in == nil {
return errors.New("an input interval is required")
}
var (
resp *healthapi.HealthCheckResponse
ticker = time.NewTicker(time.Duration(in.IntervalSeconds) * time.Second)
)
defer ticker.Stop()
for {
select {
case <-srv.Context().Done():
return srv.Context().Err()
case <-ticker.C:
resp, err = r.Check(srv.Context(), &empty.Empty{})
if err != nil {
return err
}
if err = srv.Send(resp); err != nil {
return err
}
}
}
}
// Ready implements the Health api and provides visibility to the state of networkd.
// Ready signifies the initial network configuration ( interfaces, routes, hostname, resolv.conf )
// settings have been applied.
// Not Ready signifies that the initial network configuration still needs to happen.
func (r *Registrator) Ready(ctx context.Context, in *empty.Empty) (reply *healthapi.ReadyCheckResponse, err error) {
rdy := &healthapi.ReadyCheck{Status: healthapi.ReadyCheck_NOT_READY}
if r.Networkd.Ready() {
rdy.Status = healthapi.ReadyCheck_READY
}
reply = &healthapi.ReadyCheckResponse{
Messages: []*healthapi.ReadyCheck{
rdy,
},
}
return reply, nil
}

View File

@ -1,109 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//nolint:testpackage
package reg
import (
"context"
"fmt"
"io/ioutil"
"log"
"net"
"os"
"testing"
"github.com/golang/protobuf/ptypes/empty"
"github.com/stretchr/testify/suite"
"google.golang.org/grpc"
"github.com/talos-systems/talos/internal/app/networkd/pkg/networkd"
"github.com/talos-systems/talos/pkg/grpc/dialer"
"github.com/talos-systems/talos/pkg/grpc/factory"
healthapi "github.com/talos-systems/talos/pkg/machinery/api/health"
)
type NetworkdSuite struct {
suite.Suite
}
func TestNetworkdSuite(t *testing.T) {
suite.Run(t, new(NetworkdSuite))
}
func (suite *NetworkdSuite) fakeNetworkdRPC() (*networkd.Networkd, *grpc.Server, net.Listener) {
// Create networkd instance
n, err := networkd.New(log.New(os.Stderr, "", log.LstdFlags), nil)
suite.Assert().NoError(err)
// Create gRPC server
api, err := NewRegistrator(n)
suite.Require().NoError(err)
server := factory.NewServer(api)
tmpfile, err := ioutil.TempFile("", "networkd")
suite.Assert().NoError(err)
listener, err := factory.NewListener(
factory.Network("unix"),
factory.SocketPath(tmpfile.Name()),
)
suite.Assert().NoError(err)
return n, server, listener
}
func (suite *NetworkdSuite) TestHealthAPI() {
nwd, server, listener := suite.fakeNetworkdRPC()
//nolint:errcheck
defer os.Remove(listener.Addr().String())
defer server.Stop()
//nolint:errcheck
go server.Serve(listener)
conn, err := grpc.Dial(
fmt.Sprintf("%s://%s", "unix", listener.Addr().String()),
grpc.WithInsecure(),
grpc.WithContextDialer(dialer.DialUnix()),
)
suite.Assert().NoError(err)
// Verify base state
nClient := healthapi.NewHealthClient(conn)
hcResp, err := nClient.Check(context.Background(), &empty.Empty{})
suite.Assert().NoError(err)
// Can only check against unknown since its not guaranteed that
// the host the tests will run on will have an arp table populated.
suite.Assert().NotEqual(healthapi.HealthCheck_UNKNOWN, hcResp.Messages[0].Status)
rResp, err := nClient.Ready(context.Background(), &empty.Empty{})
suite.Assert().NoError(err)
suite.Assert().Equal(healthapi.ReadyCheck_NOT_READY, rResp.Messages[0].Status)
// Trigger ready condition
nwd.SetReady()
suite.Assert().NoError(err)
rResp, err = nClient.Ready(context.Background(), &empty.Empty{})
suite.Assert().NoError(err)
suite.Assert().Equal(healthapi.ReadyCheck_READY, rResp.Messages[0].Status)
// Test watch
ctx, cancel := context.WithCancel(context.Background())
stream, err := nClient.Watch(ctx, &healthapi.HealthWatchRequest{IntervalSeconds: 1})
suite.Require().NoError(err)
for i := 0; i < 2; i++ {
hcResp, err = stream.Recv()
suite.Assert().NoError(err)
suite.Assert().NotEqual(healthapi.HealthCheck_UNKNOWN, hcResp.Messages[0].Status)
}
cancel()
_, err = stream.Recv()
suite.Assert().Error(err)
}

View File

@ -2,7 +2,6 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Package server implements network API gRPC server.
package server
import (

View File

@ -1,197 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package vip
import (
"context"
"fmt"
"log"
"net"
"os"
"time"
"github.com/plunder-app/kube-vip/pkg/vip"
"go.etcd.io/etcd/client/v3/concurrency"
"golang.org/x/sync/errgroup"
"github.com/talos-systems/talos/internal/pkg/etcd"
"github.com/talos-systems/talos/pkg/machinery/constants"
)
const campaignRetryInterval = time.Second
// A Controller provides a control interface for Virtual IP addressing.
type Controller interface {
// Start activates the Virtual IP address controller.
Start(ctx context.Context, logger *log.Logger, eg *errgroup.Group) error
}
type vipController struct {
ip net.IP
iface *net.Interface
}
// New creates a new Virtual IP controller.
func New(ip, iface string) (Controller, error) {
ipaddr := net.ParseIP(ip)
if ipaddr == nil {
return nil, fmt.Errorf("failed to parse ip %q as an IP address", ip)
}
netIf, err := net.InterfaceByName(iface)
if err != nil || netIf == nil {
return nil, fmt.Errorf("failed to find interface %s by name: %w", iface, err)
}
return &vipController{
ip: ipaddr,
iface: netIf,
}, nil
}
// Start implements the Controller interface.
func (c *vipController) Start(ctx context.Context, logger *log.Logger, eg *errgroup.Group) error {
netController, err := vip.NewConfig(c.ip.String(), c.iface.Name, false)
if err != nil {
return err
}
eg.Go(func() error {
c.maintain(ctx, logger, netController)
return nil
})
return nil
}
func (c *vipController) etcdElectionKey() string {
return fmt.Sprintf("%s:vip:election:%s", constants.EtcdRootTalosKey, c.ip.String())
}
func (c *vipController) maintain(ctx context.Context, logger *log.Logger, netController vip.Network) {
for ctx.Err() == nil {
if err := c.campaign(ctx, logger, netController); err != nil {
logger.Printf("campaign failure: %s", err)
time.Sleep(campaignRetryInterval)
continue
}
}
}
//nolint:gocyclo,cyclop
func (c *vipController) campaign(ctx context.Context, logger *log.Logger, netController vip.Network) error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
hostname, err := os.Hostname()
if err != nil {
return fmt.Errorf("refusing to join election without a hostname")
}
ec, err := etcd.NewLocalClient()
if err != nil {
return fmt.Errorf("failed to create local etcd client: %w", err)
}
defer ec.Close() //nolint:errcheck
sess, err := concurrency.NewSession(ec.Client)
if err != nil {
return fmt.Errorf("failed to create concurrency session: %w", err)
}
defer sess.Close() //nolint:errcheck
election := concurrency.NewElection(sess, c.etcdElectionKey())
node, err := election.Leader(ctx)
if err != nil {
if err != concurrency.ErrElectionNoLeader {
return fmt.Errorf("failed getting current leader: %w", err)
}
} else if string(node.Kvs[0].Value) == hostname {
logger.Printf("vip: resigning from previous election")
// we are still leader from the previous election, attempt to resign to force new election
resumedElection := concurrency.ResumeElection(sess, c.etcdElectionKey(), string(node.Kvs[0].Key), node.Kvs[0].CreateRevision)
if err = resumedElection.Resign(ctx); err != nil {
return fmt.Errorf("failed resigning from previous elections: %w", err)
}
}
campaignErrCh := make(chan error)
go func() {
campaignErrCh <- election.Campaign(ctx, hostname)
}()
select {
case err = <-campaignErrCh:
if err != nil {
return fmt.Errorf("failed to conduct campaign: %w", err)
}
case <-sess.Done():
logger.Printf("vip: session closed")
}
defer func() {
// use a new context to resign, as `ctx` might be canceled
resignCtx, resignCancel := context.WithTimeout(context.Background(), 10*time.Second)
defer resignCancel()
election.Resign(resignCtx) //nolint:errcheck
}()
if err = netController.AddIP(); err != nil {
return fmt.Errorf("failed to add VIP %q to local interface %q: %w", c.ip.String(), c.iface.Name, err)
}
defer func() {
logger.Printf("vip: removing shared IP %q on interface %q", c.ip.String(), c.iface.Name)
if err = netController.DeleteIP(); err != nil {
logger.Printf("vip: error removing shared IP: %s", err)
}
}()
// ARP is only supported for IPv4
if c.ip.To4() != nil {
// Send gratuitous ARP to announce the change
if err = vip.ARPSendGratuitous(c.ip.String(), c.iface.Name); err != nil {
return fmt.Errorf("failed to send gratuitous ARP after winning election: %w", err)
}
}
logger.Printf("vip: enabled shared IP %q on interface %q", c.ip.String(), c.iface.Name)
observe := election.Observe(ctx)
observeLoop:
for {
select {
case <-sess.Done():
logger.Printf("vip: session closed")
break observeLoop
case <-ctx.Done():
break observeLoop
case resp, ok := <-observe:
if !ok {
break observeLoop
}
if string(resp.Kvs[0].Value) != hostname {
logger.Printf("vip: detected new leader %q", string(resp.Kvs[0].Value))
break observeLoop
}
}
}
return nil
}

View File

@ -91,6 +91,11 @@ func (r *StaticPodStatus) ResourceDefinition() meta.ResourceDefinitionSpec {
}
}
// Status gets pod status.
func (r *StaticPodStatus) Status() *v1.PodStatus {
return r.spec.PodStatus
}
// SetStatus sets pod status.
func (r *StaticPodStatus) SetStatus(status *v1.PodStatus) {
r.spec.PodStatus = status

View File

@ -35,7 +35,7 @@ func RouteID(destination netaddr.IPPrefix, gateway netaddr.IP) string {
dst, _ := destination.MarshalText() //nolint:errcheck
gw, _ := gateway.MarshalText() //nolint:errcheck
return fmt.Sprintf("%s/%s", string(dst), string(gw))
return fmt.Sprintf("%s/%s", string(gw), string(dst))
}
// OperatorID builds ID (primary key) for the operators.

View File

@ -27,6 +27,7 @@ type RouteSpec struct {
type RouteSpecSpec struct {
Family nethelpers.Family `yaml:"family"`
Destination netaddr.IPPrefix `yaml:"dst"`
Source netaddr.IPPrefix `yaml:"src"`
Gateway netaddr.IP `yaml:"gateway"`
OutLinkName string `yaml:"outLinkName,omitempty"`
Table nethelpers.RoutingTable `yaml:"table"`
@ -38,6 +39,40 @@ type RouteSpecSpec struct {
ConfigLayer ConfigLayer `yaml:"layer"`
}
var (
zero16 = netaddr.MustParseIP("::")
zero4 = netaddr.MustParseIP("0.0.0.0")
)
// Normalize converts 0.0.0.0 to zero value.
//
//nolint:gocyclo
func (route *RouteSpecSpec) Normalize() {
if route.Destination.Bits == 0 && (route.Destination.IP.Compare(zero4) == 0 || route.Destination.IP.Compare(zero16) == 0) {
// clear destination to be zero value to support "0.0.0.0/0" routes
route.Destination = netaddr.IPPrefix{}
}
if route.Gateway.Compare(zero4) == 0 || route.Gateway.Compare(zero16) == 0 {
route.Gateway = netaddr.IP{}
}
if route.Source.Bits == 0 && (route.Source.IP.Compare(zero4) == 0 || route.Source.IP.Compare(zero16) == 0) {
route.Source = netaddr.IPPrefix{}
}
switch {
case route.Gateway.IsZero():
route.Scope = nethelpers.ScopeLink
case route.Destination.IP.IsLinkLocalUnicast() || route.Destination.IP.IsLinkLocalMulticast():
route.Scope = nethelpers.ScopeLink
case route.Destination.IP.IsLoopback():
route.Scope = nethelpers.ScopeHost
default:
route.Scope = nethelpers.ScopeGlobal
}
}
// NewRouteSpec initializes a RouteSpec resource.
func NewRouteSpec(namespace resource.Namespace, id resource.ID) *RouteSpec {
r := &RouteSpec{