chore: add endpoints balancer controller

This PR adds support for creating a list of API endpoints (each is pair of host and port).

It gets them from
- Machine config cluster endpoint.
- Localhost with LocalAPIServerPort if machine is control panel.
- netip.Addr[0] and port from affiliates if they are control panels.

For #7191

Signed-off-by: Dmitriy Matrenichev <dmitry.matrenichev@siderolabs.com>
This commit is contained in:
Dmitriy Matrenichev 2023-06-01 06:19:46 -04:00
parent 423a31ac9d
commit 8a02ecd4cb
No known key found for this signature in database
GPG Key ID: D3363CF894E68892
55 changed files with 2686 additions and 1277 deletions

View File

@ -131,7 +131,6 @@ linters-settings:
replace-local: true
replace-allow-list:
- gopkg.in/yaml.v3
- inet.af/tcpproxy
retract-allow-no-explanation: false
exclude-forbidden: true

View File

@ -872,7 +872,7 @@ COPY --from=rootfs / /
ARG TESTPKGS
ENV PLATFORM container
ARG GO_LDFLAGS
RUN --security=insecure --mount=type=cache,id=testspace,target=/tmp --mount=type=cache,target=/.cache go test -v \
RUN --security=insecure --mount=type=cache,id=testspace,target=/tmp --mount=type=cache,target=/.cache go test -failfast -v \
-ldflags "${GO_LDFLAGS}" \
-covermode=atomic -coverprofile=coverage.txt -coverpkg=${TESTPKGS} -count 1 -p 4 ${TESTPKGS}
FROM scratch AS unit-tests

View File

@ -16,6 +16,7 @@ message AffiliateSpec {
string operating_system = 5;
talos.resource.definitions.enums.MachineType machine_type = 6;
KubeSpanAffiliateSpec kube_span = 7;
ControlPlane control_plane = 8;
}
// ConfigSpec describes KubeSpan configuration.
@ -29,6 +30,11 @@ message ConfigSpec {
string service_cluster_id = 7;
}
// ControlPlane describes ControlPlane data if any.
message ControlPlane {
int64 api_server_port = 1;
}
// IdentitySpec describes status of rendered secrets.
//
// Note: IdentitySpec is persisted on disk in the STATE partition,
@ -58,5 +64,6 @@ message MemberSpec {
string hostname = 3;
talos.resource.definitions.enums.MachineType machine_type = 4;
string operating_system = 5;
ControlPlane control_plane = 6;
}

View File

@ -23,6 +23,17 @@ message APIServerConfigSpec {
string advertised_address = 11;
}
// APIServerEndpoint holds data for control plane endpoint.
message APIServerEndpoint {
string host = 1;
uint32 port = 2;
}
// APIServerEndpointsSpec describes APIServerEndpoints configuration.
message APIServerEndpointsSpec {
repeated APIServerEndpoint endpoints = 1;
}
// AdmissionControlConfigSpec is configuration for kube-apiserver.
message AdmissionControlConfigSpec {
repeated AdmissionPluginSpec config = 1;

8
go.mod
View File

@ -37,7 +37,7 @@ require (
github.com/containernetworking/plugins v1.3.0
github.com/coreos/go-iptables v0.6.0
github.com/coreos/go-semver v0.3.1
github.com/cosi-project/runtime v0.3.1-alpha.3
github.com/cosi-project/runtime v0.3.1-alpha.4
github.com/docker/distribution v2.8.2+incompatible
github.com/docker/docker v24.0.2+incompatible
github.com/docker/go-connections v0.4.0
@ -87,8 +87,8 @@ require (
github.com/safchain/ethtool v0.3.0
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17
github.com/siderolabs/crypto v0.4.0
github.com/siderolabs/discovery-api v0.1.2
github.com/siderolabs/discovery-client v0.1.4
github.com/siderolabs/discovery-api v0.1.3
github.com/siderolabs/discovery-client v0.1.5
github.com/siderolabs/gen v0.4.5
github.com/siderolabs/go-blockdevice v0.4.5
github.com/siderolabs/go-circular v0.1.0
@ -110,7 +110,7 @@ require (
github.com/siderolabs/talos/pkg/machinery v1.5.0-alpha.0
github.com/spf13/cobra v1.7.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.3
github.com/stretchr/testify v1.8.4
github.com/u-root/u-root v0.11.0
github.com/ulikunitz/xz v0.5.11
github.com/vishvananda/netlink v1.2.1-beta.2

16
go.sum
View File

@ -458,8 +458,8 @@ github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cosi-project/runtime v0.3.1-alpha.3 h1:8IDeG7b64OCTMHlngEKWTHTYKCfJ1oMVDxP3y0yuKbU=
github.com/cosi-project/runtime v0.3.1-alpha.3/go.mod h1:g+0MZ3+2MIUkUL7JYTqgYeo5f4j7dAuGem6apjBJ1XU=
github.com/cosi-project/runtime v0.3.1-alpha.4 h1:TXrn1ka+pw2YeywKZjA9b4mNoz4a9HBX9ovR4YtkDGc=
github.com/cosi-project/runtime v0.3.1-alpha.4/go.mod h1:GubXzK7vQFe4VyrWL/eXaMU4T1pXBx9KoDj0GJz6miw=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
@ -1134,10 +1134,10 @@ github.com/sethgrid/pester v1.2.0/go.mod h1:hEUINb4RqvDxtoCaU0BNT/HV4ig5kfgOasrf
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/siderolabs/crypto v0.4.0 h1:o1KIR1KyevUcY9nbJlSyQAj7+p+rveGGF8LjAAFMtjc=
github.com/siderolabs/crypto v0.4.0/go.mod h1:itZpBsJ9i0aH8jiHAuSlKCal7hni7X1aDYo6vGVl5LY=
github.com/siderolabs/discovery-api v0.1.2 h1:PxJhl9s2qpPgjO65bgOdRovBvRg/RuZu935nbYszIYc=
github.com/siderolabs/discovery-api v0.1.2/go.mod h1:JnJg4h1HbAhOazQl0lYHEjrg63rg/cf9r2te6/DqUxo=
github.com/siderolabs/discovery-client v0.1.4 h1:5e/4tVsCMTdz1YlynI2ebhSJ9PHhmD3y+aGIsKjDlSA=
github.com/siderolabs/discovery-client v0.1.4/go.mod h1:TmrvPz89JyhQT4vrIzAqx/v89zxikdYmhpC0GQsiH3Y=
github.com/siderolabs/discovery-api v0.1.3 h1:37ue+0w2A7Q2FrhyuDbfdhL4VPvDTpCzUYGvibhMwv0=
github.com/siderolabs/discovery-api v0.1.3/go.mod h1:fC6DOJwYQy2QsMCLLTvoScKmBCMNza+VwK2/RHLsoHU=
github.com/siderolabs/discovery-client v0.1.5 h1:CyaOOynanZdB29v46lyEOaNfPoBnKjjEBwdYbyCZEh4=
github.com/siderolabs/discovery-client v0.1.5/go.mod h1:XFSNX7ADu+4r3j/m299V6pP7f4vEDnSJJhgc5yZE73g=
github.com/siderolabs/gen v0.4.5 h1:rwXUVJlL7hYza1LrSVXfT905ZC9Rgei37jMKKs/+eP0=
github.com/siderolabs/gen v0.4.5/go.mod h1:wS8tFq7sn5vqKAuyS30vJUig3tX5v6q79VG4KfUnILM=
github.com/siderolabs/go-api-signature v0.2.4 h1:s+K0GtaPHql4LdZzL72QvwPMzffY+KB0mszORDK+5/w=
@ -1238,8 +1238,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=

View File

@ -10,6 +10,8 @@ import (
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/siderolabs/gen/channel"
"go.uber.org/zap"
"github.com/siderolabs/talos/pkg/machinery/resources/cluster"
@ -47,23 +49,21 @@ func (ctrl *AffiliateMergeController) Outputs() []controller.Output {
// Run implements controller.Controller interface.
//
//nolint:gocyclo
func (ctrl *AffiliateMergeController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
func (ctrl *AffiliateMergeController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error {
for {
select {
case <-ctx.Done():
return nil
case <-r.EventCh():
if _, ok := channel.RecvWithContext(ctx, r.EventCh()); !ok && ctx.Err() != nil {
return nil //nolint:nilerr
}
mergedAffiliates := make(map[resource.ID]*cluster.AffiliateSpec)
rawAffiliates, err := r.List(ctx, resource.NewMetadata(cluster.RawNamespaceName, cluster.AffiliateType, "", resource.VersionUndefined))
rawAffiliates, err := safe.ReaderList[*cluster.Affiliate](ctx, r, resource.NewMetadata(cluster.RawNamespaceName, cluster.AffiliateType, "", resource.VersionUndefined))
if err != nil {
return fmt.Errorf("error listing affiliates")
}
for _, rawAffiliate := range rawAffiliates.Items {
affiliateSpec := rawAffiliate.(*cluster.Affiliate).TypedSpec()
mergedAffiliates := make(map[resource.ID]*cluster.AffiliateSpec, rawAffiliates.Len())
for it := safe.IteratorFromList(rawAffiliates); it.Next(); {
affiliateSpec := it.Value().TypedSpec()
id := affiliateSpec.NodeID
if affiliate, ok := mergedAffiliates[id]; ok {
@ -73,13 +73,13 @@ func (ctrl *AffiliateMergeController) Run(ctx context.Context, r controller.Runt
}
}
touchedIDs := make(map[resource.ID]struct{})
touchedIDs := make(map[resource.ID]struct{}, len(mergedAffiliates))
for id, affiliateSpec := range mergedAffiliates {
affiliateSpec := affiliateSpec
if err = r.Modify(ctx, cluster.NewAffiliate(cluster.NamespaceName, id), func(res resource.Resource) error {
*res.(*cluster.Affiliate).TypedSpec() = *affiliateSpec
if err = safe.WriterModify(ctx, r, cluster.NewAffiliate(cluster.NamespaceName, id), func(res *cluster.Affiliate) error {
*res.TypedSpec() = *affiliateSpec
return nil
}); err != nil {
@ -90,12 +90,14 @@ func (ctrl *AffiliateMergeController) Run(ctx context.Context, r controller.Runt
}
// list keys for cleanup
list, err := r.List(ctx, resource.NewMetadata(cluster.NamespaceName, cluster.AffiliateType, "", resource.VersionUndefined))
list, err := safe.ReaderListAll[*cluster.Affiliate](ctx, r)
if err != nil {
return fmt.Errorf("error listing resources: %w", err)
}
for _, res := range list.Items {
for it := safe.IteratorFromList(list); it.Next(); {
res := it.Value()
if res.Metadata().Owner() != ctrl.Name() {
continue
}

View File

@ -7,13 +7,13 @@ package cluster_test
import (
"net/netip"
"testing"
"time"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/siderolabs/go-retry/retry"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
clusterctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cluster"
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest"
"github.com/siderolabs/talos/pkg/machinery/config/machine"
"github.com/siderolabs/talos/pkg/machinery/resources/cluster"
)
@ -40,6 +40,7 @@ func (suite *AffiliateMergeSuite) TestReconcileDefault() {
AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")},
Endpoints: []netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")},
},
ControlPlane: &cluster.ControlPlane{APIServerPort: 6443},
}
affiliate2 := cluster.NewAffiliate(cluster.RawNamespaceName, "service/7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC")
@ -65,67 +66,63 @@ func (suite *AffiliateMergeSuite) TestReconcileDefault() {
}
// there should be two merged affiliates: one from affiliate1+affiliate2, and another from affiliate3
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertResource(*cluster.NewAffiliate(cluster.NamespaceName, affiliate1.TypedSpec().NodeID).Metadata(), func(r resource.Resource) error {
spec := r.(*cluster.Affiliate).TypedSpec()
ctest.AssertResource(
suite,
affiliate1.TypedSpec().NodeID,
func(r *cluster.Affiliate, asrt *assert.Assertions) {
spec := r.TypedSpec()
suite.Assert().Equal(affiliate1.TypedSpec().NodeID, spec.NodeID)
suite.Assert().Equal([]netip.Addr{netip.MustParseAddr("192.168.3.4"), netip.MustParseAddr("10.5.0.2")}, spec.Addresses)
suite.Assert().Equal("foo.com", spec.Hostname)
suite.Assert().Equal("bar", spec.Nodename)
suite.Assert().Equal(machine.TypeControlPlane, spec.MachineType)
suite.Assert().Equal(netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0"), spec.KubeSpan.Address)
suite.Assert().Equal("PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", spec.KubeSpan.PublicKey)
suite.Assert().Equal([]netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")}, spec.KubeSpan.AdditionalAddresses)
suite.Assert().Equal([]netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")}, spec.KubeSpan.Endpoints)
asrt.Equal(affiliate1.TypedSpec().NodeID, spec.NodeID)
asrt.Equal([]netip.Addr{netip.MustParseAddr("192.168.3.4"), netip.MustParseAddr("10.5.0.2")}, spec.Addresses)
asrt.Equal("foo.com", spec.Hostname)
asrt.Equal("bar", spec.Nodename)
asrt.Equal(machine.TypeControlPlane, spec.MachineType)
asrt.Equal(netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0"), spec.KubeSpan.Address)
asrt.Equal("PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", spec.KubeSpan.PublicKey)
asrt.Equal([]netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")}, spec.KubeSpan.AdditionalAddresses)
asrt.Equal([]netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")}, spec.KubeSpan.Endpoints)
asrt.Equal(&cluster.ControlPlane{APIServerPort: 6443}, spec.ControlPlane)
},
)
return nil
}),
))
ctest.AssertResource(
suite,
affiliate3.TypedSpec().NodeID,
func(r *cluster.Affiliate, asrt *assert.Assertions) {
spec := r.TypedSpec()
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertResource(*cluster.NewAffiliate(cluster.NamespaceName, affiliate3.TypedSpec().NodeID).Metadata(), func(r resource.Resource) error {
spec := r.(*cluster.Affiliate).TypedSpec()
suite.Assert().Equal(affiliate3.TypedSpec().NodeID, spec.NodeID)
suite.Assert().Equal([]netip.Addr{netip.MustParseAddr("192.168.3.5")}, spec.Addresses)
suite.Assert().Equal("worker-1", spec.Hostname)
suite.Assert().Equal("worker-1", spec.Nodename)
suite.Assert().Equal(machine.TypeWorker, spec.MachineType)
suite.Assert().Zero(spec.KubeSpan.PublicKey)
return nil
}),
))
asrt.Equal(affiliate3.TypedSpec().NodeID, spec.NodeID)
asrt.Equal([]netip.Addr{netip.MustParseAddr("192.168.3.5")}, spec.Addresses)
asrt.Equal("worker-1", spec.Hostname)
asrt.Equal("worker-1", spec.Nodename)
asrt.Equal(machine.TypeWorker, spec.MachineType)
asrt.Zero(spec.KubeSpan.PublicKey)
asrt.Nil(spec.ControlPlane)
},
)
// remove affiliate2, KubeSpan information should eventually go away
suite.Require().NoError(suite.state.Destroy(suite.ctx, affiliate1.Metadata()))
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertResource(*cluster.NewAffiliate(cluster.NamespaceName, affiliate1.TypedSpec().NodeID).Metadata(), func(r resource.Resource) error {
spec := r.(*cluster.Affiliate).TypedSpec()
ctest.AssertResource(
suite,
affiliate1.TypedSpec().NodeID,
func(r *cluster.Affiliate, asrt *assert.Assertions) {
spec := r.TypedSpec()
suite.Assert().Equal(affiliate1.TypedSpec().NodeID, spec.NodeID)
if spec.KubeSpan.PublicKey != "" {
return retry.ExpectedErrorf("not reconciled yet")
}
suite.Assert().Zero(spec.KubeSpan.Address)
suite.Assert().Zero(spec.KubeSpan.PublicKey)
suite.Assert().Zero(spec.KubeSpan.AdditionalAddresses)
suite.Assert().Zero(spec.KubeSpan.Endpoints)
return nil
}),
))
asrt.Equal(affiliate1.TypedSpec().NodeID, spec.NodeID)
asrt.Zero(spec.KubeSpan.Address)
asrt.Zero(spec.KubeSpan.PublicKey)
asrt.Zero(spec.KubeSpan.AdditionalAddresses)
asrt.Zero(spec.KubeSpan.Endpoints)
asrt.Nil(spec.ControlPlane)
},
)
// remove affiliate3, merged affiliate should be removed
suite.Require().NoError(suite.state.Destroy(suite.ctx, affiliate3.Metadata()))
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertNoResource(*cluster.NewAffiliate(cluster.NamespaceName, affiliate3.TypedSpec().NodeID).Metadata()),
))
ctest.AssertNoResource[*cluster.Affiliate](suite, affiliate3.TypedSpec().NodeID)
}
func TestAffiliateMergeSuite(t *testing.T) {

View File

@ -11,18 +11,24 @@ import (
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/siderolabs/talos/pkg/machinery/resources/cluster"
)
func cleanupAffiliates(ctx context.Context, ctrl controller.Controller, r controller.Runtime, touchedIDs map[resource.ID]struct{}) error {
// list keys for cleanup
list, err := r.List(ctx, resource.NewMetadata(cluster.RawNamespaceName, cluster.AffiliateType, "", resource.VersionUndefined))
list, err := safe.ReaderList[*cluster.Affiliate](
ctx,
r,
resource.NewMetadata(cluster.RawNamespaceName, cluster.AffiliateType, "", resource.VersionUndefined),
)
if err != nil {
return fmt.Errorf("error listing resources: %w", err)
}
for _, res := range list.Items {
for it := safe.IteratorFromList(list); it.Next(); {
res := it.Value()
if res.Metadata().Owner() != ctrl.Name() {
continue
}

View File

@ -71,21 +71,6 @@ func (suite *ClusterSuite) assertResource(md resource.Metadata, check func(res r
}
}
func (suite *ClusterSuite) assertNoResource(md resource.Metadata) func() error {
return func() error {
_, err := suite.state.Get(suite.ctx, md)
if err == nil {
return retry.ExpectedErrorf("resource %s still exists", md)
}
if state.IsNotFoundError(err) {
return nil
}
return err
}
}
func (suite *ClusterSuite) TearDownTest() {
suite.T().Log("tear down")

View File

@ -13,6 +13,7 @@ import (
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/go-pointer"
"go.uber.org/zap"
@ -67,17 +68,17 @@ func (ctrl *ConfigController) Run(ctx context.Context, r controller.Runtime, log
}
}
touchedIDs := make(map[resource.ID]struct{})
touchedIDs := map[resource.ID]struct{}{}
if cfg != nil {
c := cfg.(*config.MachineConfig).Config()
if err = r.Modify(ctx, cluster.NewConfig(config.NamespaceName, cluster.ConfigID), func(res resource.Resource) error {
res.(*cluster.Config).TypedSpec().DiscoveryEnabled = c.Cluster().Discovery().Enabled()
if err = safe.WriterModify(ctx, r, cluster.NewConfig(config.NamespaceName, cluster.ConfigID), func(res *cluster.Config) error {
res.TypedSpec().DiscoveryEnabled = c.Cluster().Discovery().Enabled()
if c.Cluster().Discovery().Enabled() {
res.(*cluster.Config).TypedSpec().RegistryKubernetesEnabled = c.Cluster().Discovery().Registries().Kubernetes().Enabled()
res.(*cluster.Config).TypedSpec().RegistryServiceEnabled = c.Cluster().Discovery().Registries().Service().Enabled()
res.TypedSpec().RegistryKubernetesEnabled = c.Cluster().Discovery().Registries().Kubernetes().Enabled()
res.TypedSpec().RegistryServiceEnabled = c.Cluster().Discovery().Registries().Service().Enabled()
if c.Cluster().Discovery().Registries().Service().Enabled() {
var u *url.URL
@ -98,24 +99,24 @@ func (ctrl *ConfigController) Run(ctx context.Context, r controller.Runtime, log
}
}
res.(*cluster.Config).TypedSpec().ServiceEndpoint = net.JoinHostPort(host, port)
res.(*cluster.Config).TypedSpec().ServiceEndpointInsecure = u.Scheme == "http"
res.TypedSpec().ServiceEndpoint = net.JoinHostPort(host, port)
res.TypedSpec().ServiceEndpointInsecure = u.Scheme == "http"
res.(*cluster.Config).TypedSpec().ServiceEncryptionKey, err = base64.StdEncoding.DecodeString(c.Cluster().Secret())
res.TypedSpec().ServiceEncryptionKey, err = base64.StdEncoding.DecodeString(c.Cluster().Secret())
if err != nil {
return err
}
res.(*cluster.Config).TypedSpec().ServiceClusterID = c.Cluster().ID()
res.TypedSpec().ServiceClusterID = c.Cluster().ID()
} else {
res.(*cluster.Config).TypedSpec().ServiceEndpoint = ""
res.(*cluster.Config).TypedSpec().ServiceEndpointInsecure = false
res.(*cluster.Config).TypedSpec().ServiceEncryptionKey = nil
res.(*cluster.Config).TypedSpec().ServiceClusterID = ""
res.TypedSpec().ServiceEndpoint = ""
res.TypedSpec().ServiceEndpointInsecure = false
res.TypedSpec().ServiceEncryptionKey = nil
res.TypedSpec().ServiceClusterID = ""
}
} else {
res.(*cluster.Config).TypedSpec().RegistryKubernetesEnabled = false
res.(*cluster.Config).TypedSpec().RegistryServiceEnabled = false
res.TypedSpec().RegistryKubernetesEnabled = false
res.TypedSpec().RegistryServiceEnabled = false
}
return nil

View File

@ -133,7 +133,7 @@ func (ctrl *DiscoveryServiceController) Run(ctx context.Context, r controller.Ru
}
}
discoveryConfig, err := r.Get(ctx, resource.NewMetadata(config.NamespaceName, cluster.ConfigType, cluster.ConfigID, resource.VersionUndefined))
discoveryConfig, err := safe.ReaderGetByID[*cluster.Config](ctx, r, cluster.ConfigID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting discovery config: %w", err)
@ -142,7 +142,7 @@ func (ctrl *DiscoveryServiceController) Run(ctx context.Context, r controller.Ru
continue
}
if !discoveryConfig.(*cluster.Config).TypedSpec().RegistryServiceEnabled {
if !discoveryConfig.TypedSpec().RegistryServiceEnabled {
// if discovery is disabled cleanup existing resources
if err = cleanupAffiliates(ctx, ctrl, r, nil); err != nil {
return err
@ -164,7 +164,7 @@ func (ctrl *DiscoveryServiceController) Run(ctx context.Context, r controller.Ru
continue
}
identity, err := r.Get(ctx, resource.NewMetadata(cluster.NamespaceName, cluster.IdentityType, cluster.LocalIdentity, resource.VersionUndefined))
identity, err := safe.ReaderGetByID[*cluster.Identity](ctx, r, cluster.LocalIdentity)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting local identity: %w", err)
@ -173,7 +173,7 @@ func (ctrl *DiscoveryServiceController) Run(ctx context.Context, r controller.Ru
continue
}
localAffiliateID := identity.(*cluster.Identity).TypedSpec().NodeID
localAffiliateID := identity.TypedSpec().NodeID
if ctrl.localAffiliateID != localAffiliateID {
ctrl.localAffiliateID = localAffiliateID
@ -203,7 +203,7 @@ func (ctrl *DiscoveryServiceController) Run(ctx context.Context, r controller.Ru
}
}
affiliate, err := r.Get(ctx, resource.NewMetadata(cluster.NamespaceName, cluster.AffiliateType, ctrl.localAffiliateID, resource.VersionUndefined))
affiliate, err := safe.ReaderGetByID[*cluster.Affiliate](ctx, r, ctrl.localAffiliateID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting local affiliate: %w", err)
@ -212,9 +212,9 @@ func (ctrl *DiscoveryServiceController) Run(ctx context.Context, r controller.Ru
continue
}
affiliateSpec := affiliate.(*cluster.Affiliate).TypedSpec()
affiliateSpec := affiliate.TypedSpec()
otherEndpointsList, err := r.List(ctx, resource.NewMetadata(kubespan.NamespaceName, kubespan.EndpointType, "", resource.VersionUndefined))
otherEndpointsList, err := safe.ReaderListAll[*kubespan.Endpoint](ctx, r)
if err != nil {
return fmt.Errorf("error listing endpoints: %w", err)
}
@ -225,20 +225,20 @@ func (ctrl *DiscoveryServiceController) Run(ctx context.Context, r controller.Ru
}
if client == nil {
var cipher cipher.Block
var cipherBlock cipher.Block
cipher, err = aes.NewCipher(discoveryConfig.(*cluster.Config).TypedSpec().ServiceEncryptionKey)
cipherBlock, err = aes.NewCipher(discoveryConfig.TypedSpec().ServiceEncryptionKey)
if err != nil {
return fmt.Errorf("error initializing AES cipher: %w", err)
}
client, err = discoveryclient.NewClient(discoveryclient.Options{
Cipher: cipher,
Endpoint: discoveryConfig.(*cluster.Config).TypedSpec().ServiceEndpoint,
ClusterID: discoveryConfig.(*cluster.Config).TypedSpec().ServiceClusterID,
Cipher: cipherBlock,
Endpoint: discoveryConfig.TypedSpec().ServiceEndpoint,
ClusterID: discoveryConfig.TypedSpec().ServiceClusterID,
AffiliateID: localAffiliateID,
TTL: defaultDiscoveryTTL,
Insecure: discoveryConfig.(*cluster.Config).TypedSpec().ServiceEndpointInsecure,
Insecure: discoveryConfig.TypedSpec().ServiceEndpointInsecure,
ClientVersion: version.Tag,
})
if err != nil {
@ -306,8 +306,8 @@ func (ctrl *DiscoveryServiceController) Run(ctx context.Context, r controller.Ru
discoveredAffiliate := discoveredAffiliate
if err = r.Modify(ctx, cluster.NewAffiliate(cluster.RawNamespaceName, id), func(res resource.Resource) error {
*res.(*cluster.Affiliate).TypedSpec() = specAffiliate(discoveredAffiliate.Affiliate, discoveredAffiliate.Endpoints)
if err = safe.WriterModify(ctx, r, cluster.NewAffiliate(cluster.RawNamespaceName, id), func(res *cluster.Affiliate) error {
*res.TypedSpec() = specAffiliate(discoveredAffiliate.Affiliate, discoveredAffiliate.Endpoints)
return nil
}); err != nil {
@ -327,9 +327,7 @@ func (ctrl *DiscoveryServiceController) Run(ctx context.Context, r controller.Ru
func pbAffiliate(affiliate *cluster.AffiliateSpec) *pb.Affiliate {
addresses := slices.Map(affiliate.Addresses, func(address netip.Addr) []byte {
result, _ := address.MarshalBinary() //nolint:errcheck // doesn't fail
return result
return takeResult(address.MarshalBinary())
})
var kubeSpan *pb.KubeSpan
@ -337,21 +335,14 @@ func pbAffiliate(affiliate *cluster.AffiliateSpec) *pb.Affiliate {
if affiliate.KubeSpan.PublicKey != "" {
kubeSpan = &pb.KubeSpan{
PublicKey: affiliate.KubeSpan.PublicKey,
Address: takeResult(affiliate.KubeSpan.Address.MarshalBinary()),
AdditionalAddresses: slices.Map(affiliate.KubeSpan.AdditionalAddresses, func(address netip.Prefix) *pb.IPPrefix {
return &pb.IPPrefix{
Bits: uint32(address.Bits()),
Ip: takeResult(address.Addr().MarshalBinary()),
}
}),
}
kubeSpan.Address, _ = affiliate.KubeSpan.Address.MarshalBinary() //nolint:errcheck // doesn't fail
additionalAddresses := make([]*pb.IPPrefix, len(affiliate.KubeSpan.AdditionalAddresses))
for i := range additionalAddresses {
additionalAddresses[i] = &pb.IPPrefix{
Bits: uint32(affiliate.KubeSpan.AdditionalAddresses[i].Bits()),
}
additionalAddresses[i].Ip, _ = affiliate.KubeSpan.AdditionalAddresses[i].Addr().MarshalBinary() //nolint:errcheck // doesn't fail
}
kubeSpan.AdditionalAddresses = additionalAddresses
}
return &pb.Affiliate{
@ -362,47 +353,48 @@ func pbAffiliate(affiliate *cluster.AffiliateSpec) *pb.Affiliate {
MachineType: affiliate.MachineType.String(),
OperatingSystem: affiliate.OperatingSystem,
Kubespan: kubeSpan,
ControlPlane: toPlane(affiliate.ControlPlane),
}
}
func toPlane(data *cluster.ControlPlane) *pb.ControlPlane {
if data == nil {
return nil
}
return &pb.ControlPlane{ApiServerPort: uint32(data.APIServerPort)}
}
func pbEndpoints(affiliate *cluster.AffiliateSpec) []*pb.Endpoint {
if affiliate.KubeSpan.PublicKey == "" || len(affiliate.KubeSpan.Endpoints) == 0 {
return nil
}
result := make([]*pb.Endpoint, len(affiliate.KubeSpan.Endpoints))
for i := range result {
result[i] = &pb.Endpoint{
Port: uint32(affiliate.KubeSpan.Endpoints[i].Port()),
return slices.Map(affiliate.KubeSpan.Endpoints, func(endpoint netip.AddrPort) *pb.Endpoint {
return &pb.Endpoint{
Port: uint32(endpoint.Port()),
Ip: takeResult(endpoint.Addr().MarshalBinary()),
}
result[i].Ip, _ = affiliate.KubeSpan.Endpoints[i].Addr().MarshalBinary() //nolint:errcheck // doesn't fail
}
return result
})
}
func pbOtherEndpoints(otherEndpointsList resource.List) []discoveryclient.Endpoint {
if len(otherEndpointsList.Items) == 0 {
func pbOtherEndpoints(otherEndpointsList safe.List[*kubespan.Endpoint]) []discoveryclient.Endpoint {
if otherEndpointsList.Len() == 0 {
return nil
}
result := make([]discoveryclient.Endpoint, 0, len(otherEndpointsList.Items))
result := make([]discoveryclient.Endpoint, 0, otherEndpointsList.Len())
for _, res := range otherEndpointsList.Items {
endpoint := res.(*kubespan.Endpoint).TypedSpec()
encodedEndpoint := &pb.Endpoint{
Port: uint32(endpoint.Endpoint.Port()),
}
encodedEndpoint.Ip, _ = endpoint.Endpoint.Addr().MarshalBinary() //nolint:errcheck // doesn't fail
for it := safe.IteratorFromList(otherEndpointsList); it.Next(); {
endpoint := it.Value().TypedSpec()
result = append(result, discoveryclient.Endpoint{
AffiliateID: endpoint.AffiliateID,
Endpoints: []*pb.Endpoint{
encodedEndpoint,
{
Port: uint32(endpoint.Endpoint.Port()),
Ip: takeResult(endpoint.Endpoint.Addr().MarshalBinary()),
},
},
})
}
@ -456,10 +448,10 @@ func specAffiliate(affiliate *pb.Affiliate, endpoints []*pb.Endpoint) cluster.Af
Hostname: affiliate.Hostname,
Nodename: affiliate.Nodename,
OperatingSystem: affiliate.OperatingSystem,
MachineType: takeResult(machine.ParseType(affiliate.MachineType)), // ignore parse error (machine.TypeUnknown)
ControlPlane: fromControlPlane(affiliate.ControlPlane),
}
result.MachineType, _ = machine.ParseType(affiliate.MachineType) //nolint:errcheck // ignore parse error (machine.TypeUnknown)
result.Addresses = make([]netip.Addr, 0, len(affiliate.Addresses))
for i := range affiliate.Addresses {
@ -497,3 +489,15 @@ func specAffiliate(affiliate *pb.Affiliate, endpoints []*pb.Endpoint) cluster.Af
return result
}
func fromControlPlane(plane *pb.ControlPlane) *cluster.ControlPlane {
if plane == nil {
return nil
}
return &cluster.ControlPlane{APIServerPort: int(plane.ApiServerPort)}
}
func takeResult[T any](arg1 T, _ error) T {
return arg1
}

View File

@ -16,10 +16,11 @@ import (
"testing"
"time"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/resource/rtestutils"
"github.com/siderolabs/discovery-api/api/v1alpha1/client/pb"
"github.com/siderolabs/discovery-client/pkg/client"
"github.com/siderolabs/go-retry/retry"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
clusteradapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/cluster"
@ -88,6 +89,7 @@ func (suite *DiscoveryServiceSuite) TestReconcile() {
AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")},
Endpoints: []netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")},
},
ControlPlane: &cluster.ControlPlane{APIServerPort: 6443},
}
suite.Require().NoError(suite.state.Create(suite.ctx, localAffiliate))
@ -141,6 +143,7 @@ func (suite *DiscoveryServiceSuite) TestReconcile() {
},
},
},
ControlPlane: &pb.ControlPlane{ApiServerPort: 6443},
}, affiliates[0].Affiliate))
suite.Assert().True(proto.Equal(
&pb.Endpoint{
@ -187,9 +190,11 @@ func (suite *DiscoveryServiceSuite) TestReconcile() {
},
}, nil))
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertResource(*cluster.NewAffiliate(cluster.RawNamespaceName, "service/7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC").Metadata(), func(r resource.Resource) error {
spec := r.(*cluster.Affiliate).TypedSpec()
ctest.AssertResource(
suite,
"service/7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC",
func(r *cluster.Affiliate, asrt *assert.Assertions) {
spec := r.TypedSpec()
suite.Assert().Equal("7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", spec.NodeID)
suite.Assert().Equal([]netip.Addr{netip.MustParseAddr("192.168.3.5")}, spec.Addresses)
@ -201,22 +206,18 @@ func (suite *DiscoveryServiceSuite) TestReconcile() {
suite.Assert().Equal("1CXkdhWBm58c36kTpchR8iGlXHG1ruHa5W8gsFqD8Qs=", spec.KubeSpan.PublicKey)
suite.Assert().Equal([]netip.Prefix{netip.MustParsePrefix("10.244.4.1/24")}, spec.KubeSpan.AdditionalAddresses)
suite.Assert().Equal([]netip.AddrPort{netip.MustParseAddrPort("192.168.3.5:51820")}, spec.KubeSpan.Endpoints)
return nil
}),
))
suite.Assert().Zero(spec.ControlPlane)
},
rtestutils.WithNamespace(cluster.RawNamespaceName),
)
// controller should publish public IP
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertResource(*network.NewAddressStatus(cluster.NamespaceName, "service").Metadata(), func(r resource.Resource) error {
spec := r.(*network.AddressStatus).TypedSpec()
ctest.AssertResource(suite, "service", func(r *network.AddressStatus, assertions *assert.Assertions) {
spec := r.TypedSpec()
suite.Assert().True(spec.Address.IsValid())
suite.Assert().True(spec.Address.IsSingleIP())
return nil
}),
))
assertions.True(spec.Address.IsValid())
assertions.True(spec.Address.IsSingleIP())
}, rtestutils.WithNamespace(cluster.NamespaceName))
// make controller inject additional endpoint via kubespan.Endpoint
endpoint := kubespan.NewEndpoint(kubespan.NamespaceName, "1CXkdhWBm58c36kTpchR8iGlXHG1ruHa5W8gsFqD8Qs=")
@ -226,22 +227,19 @@ func (suite *DiscoveryServiceSuite) TestReconcile() {
}
suite.Require().NoError(suite.state.Create(suite.ctx, endpoint))
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertResource(*cluster.NewAffiliate(cluster.RawNamespaceName, "service/7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC").Metadata(), func(r resource.Resource) error {
spec := r.(*cluster.Affiliate).TypedSpec()
ctest.AssertResource(suite,
"service/7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC",
func(r *cluster.Affiliate, assertions *assert.Assertions) {
spec := r.TypedSpec()
if len(spec.KubeSpan.Endpoints) != 2 {
return retry.ExpectedErrorf("waiting for 2 endpoints, got %d", len(spec.KubeSpan.Endpoints))
}
suite.Assert().Equal([]netip.AddrPort{
assertions.Len(spec.KubeSpan.Endpoints, 2)
assertions.Equal([]netip.AddrPort{
netip.MustParseAddrPort("192.168.3.5:51820"),
netip.MustParseAddrPort("1.1.1.1:343"),
}, spec.KubeSpan.Endpoints)
return nil
}),
))
},
rtestutils.WithNamespace(cluster.RawNamespaceName),
)
// pretend that machine is being reset
machineStatus := runtime.NewMachineStatus()
@ -341,15 +339,14 @@ func (suite *DiscoveryServiceSuite) TestDisable() {
},
}, nil))
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertResource(*cluster.NewAffiliate(cluster.RawNamespaceName, "service/7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC").Metadata(), func(r resource.Resource) error {
spec := r.(*cluster.Affiliate).TypedSpec()
suite.Assert().Equal("7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", spec.NodeID)
return nil
}),
))
ctest.AssertResource(
suite,
"service/7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC",
func(r *cluster.Affiliate, asrt *assert.Assertions) {
suite.Assert().Equal("7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", r.TypedSpec().NodeID)
},
rtestutils.WithNamespace(cluster.RawNamespaceName),
)
// now disable the service registry
ctest.UpdateWithConflicts(suite, discoveryConfig, func(r *cluster.Config) error {
@ -358,9 +355,11 @@ func (suite *DiscoveryServiceSuite) TestDisable() {
return nil
})
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertNoResource(*cluster.NewAffiliate(cluster.RawNamespaceName, "service/7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC").Metadata()),
))
ctest.AssertNoResource[*cluster.Affiliate](
suite,
"service/7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC",
rtestutils.WithNamespace(cluster.RawNamespaceName),
)
cliCtxCancel()
suite.Assert().NoError(<-errCh)

View File

@ -5,16 +5,16 @@
package cluster_test
import (
"fmt"
"net/netip"
"testing"
"time"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/siderolabs/go-retry/retry"
"github.com/siderolabs/gen/slices"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
clusterctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cluster"
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest"
"github.com/siderolabs/talos/pkg/machinery/config/machine"
"github.com/siderolabs/talos/pkg/machinery/resources/cluster"
"github.com/siderolabs/talos/pkg/machinery/resources/k8s"
@ -61,15 +61,19 @@ func (suite *EndpointSuite) TestReconcileDefault() {
}
// control plane members should be translated to Endpoints
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertResource(*k8s.NewEndpoint(k8s.ControlPlaneNamespaceName, k8s.ControlPlaneDiscoveredEndpointsID).Metadata(), func(r resource.Resource) error {
spec := r.(*k8s.Endpoint).TypedSpec()
ctest.AssertResource(suite, k8s.ControlPlaneDiscoveredEndpointsID, func(r *k8s.Endpoint, asrt *assert.Assertions) {
spec := r.TypedSpec()
suite.Assert().Equal(`["172.20.0.2" "172.20.0.3" "fd50:8d60:4238:6302:f857:23ff:fe21:d1e0" "fd50:8d60:4238:6302:f857:23ff:fe21:d1e1"]`, fmt.Sprintf("%q", spec.Addresses))
return nil
}),
))
asrt.Equal(
[]string{
"172.20.0.2",
"172.20.0.3",
"fd50:8d60:4238:6302:f857:23ff:fe21:d1e0",
"fd50:8d60:4238:6302:f857:23ff:fe21:d1e1",
},
slices.Map(spec.Addresses, netip.Addr.String),
)
})
}
func TestEndpointSuite(t *testing.T) {

View File

@ -9,7 +9,6 @@ import (
"fmt"
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/go-pointer"
@ -58,7 +57,7 @@ func (ctrl *InfoController) Run(ctx context.Context, r controller.Runtime, logge
case <-r.EventCh():
}
cfg, err := safe.ReaderGet[*config.MachineConfig](ctx, r, resource.NewMetadata(config.NamespaceName, config.MachineConfigType, config.V1Alpha1ID, resource.VersionUndefined))
cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.V1Alpha1ID)
if err != nil {
if state.IsNotFoundError(err) {
continue

View File

@ -10,6 +10,7 @@ import (
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/go-pointer"
"go.uber.org/zap"
@ -93,7 +94,7 @@ func (ctrl *KubernetesPullController) Run(ctx context.Context, r controller.Runt
case <-notifyCh:
}
discoveryConfig, err := r.Get(ctx, resource.NewMetadata(config.NamespaceName, cluster.ConfigType, cluster.ConfigID, resource.VersionUndefined))
discoveryConfig, err := safe.ReaderGetByID[*cluster.Config](ctx, r, cluster.ConfigID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting discovery config: %w", err)
@ -102,7 +103,7 @@ func (ctrl *KubernetesPullController) Run(ctx context.Context, r controller.Runt
continue
}
if !discoveryConfig.(*cluster.Config).TypedSpec().RegistryKubernetesEnabled {
if !discoveryConfig.TypedSpec().RegistryKubernetesEnabled {
// if discovery is disabled cleanup existing resources
if err = cleanupAffiliates(ctx, ctrl, r, nil); err != nil {
return err
@ -115,7 +116,7 @@ func (ctrl *KubernetesPullController) Run(ctx context.Context, r controller.Runt
return err
}
nodename, err := r.Get(ctx, resource.NewMetadata(k8s.NamespaceName, k8s.NodenameType, k8s.NodenameID, resource.VersionUndefined))
nodename, err := safe.ReaderGetByID[*k8s.Nodename](ctx, r, k8s.NodenameID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting nodename: %w", err)
@ -145,7 +146,7 @@ func (ctrl *KubernetesPullController) Run(ctx context.Context, r controller.Runt
}
}
affiliateSpecs, err := kubernetesRegistry.List(nodename.(*k8s.Nodename).TypedSpec().Nodename)
affiliateSpecs, err := kubernetesRegistry.List(nodename.TypedSpec().Nodename)
if err != nil {
return fmt.Errorf("error listing affiliates: %w", err)
}
@ -157,8 +158,8 @@ func (ctrl *KubernetesPullController) Run(ctx context.Context, r controller.Runt
affilateSpec := affilateSpec
if err = r.Modify(ctx, cluster.NewAffiliate(cluster.RawNamespaceName, id), func(res resource.Resource) error {
*res.(*cluster.Affiliate).TypedSpec() = *affilateSpec
if err = safe.WriterModify(ctx, r, cluster.NewAffiliate(cluster.RawNamespaceName, id), func(res *cluster.Affiliate) error {
*res.TypedSpec() = *affilateSpec
return nil
}); err != nil {

View File

@ -10,6 +10,7 @@ import (
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/go-pointer"
"go.uber.org/zap"
@ -73,7 +74,7 @@ func (ctrl *KubernetesPushController) Run(ctx context.Context, r controller.Runt
case <-ctx.Done():
return nil
case <-r.EventCh():
discoveryConfig, err := r.Get(ctx, resource.NewMetadata(config.NamespaceName, cluster.ConfigType, cluster.ConfigID, resource.VersionUndefined))
discoveryConfig, err := safe.ReaderGetByID[*cluster.Config](ctx, r, cluster.ConfigID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting discovery config: %w", err)
@ -82,7 +83,7 @@ func (ctrl *KubernetesPushController) Run(ctx context.Context, r controller.Runt
continue
}
if !discoveryConfig.(*cluster.Config).TypedSpec().RegistryKubernetesEnabled {
if !discoveryConfig.TypedSpec().RegistryKubernetesEnabled {
continue
}
@ -90,7 +91,7 @@ func (ctrl *KubernetesPushController) Run(ctx context.Context, r controller.Runt
return err
}
identity, err := r.Get(ctx, resource.NewMetadata(cluster.NamespaceName, cluster.IdentityType, cluster.LocalIdentity, resource.VersionUndefined))
identity, err := safe.ReaderGetByID[*cluster.Identity](ctx, r, cluster.LocalIdentity)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting local identity: %w", err)
@ -99,7 +100,7 @@ func (ctrl *KubernetesPushController) Run(ctx context.Context, r controller.Runt
continue
}
localAffiliateID := identity.(*cluster.Identity).TypedSpec().NodeID
localAffiliateID := identity.TypedSpec().NodeID
if ctrl.localAffiliateID != localAffiliateID {
ctrl.localAffiliateID = localAffiliateID
@ -116,7 +117,7 @@ func (ctrl *KubernetesPushController) Run(ctx context.Context, r controller.Runt
}
}
affiliate, err := r.Get(ctx, resource.NewMetadata(cluster.NamespaceName, cluster.AffiliateType, ctrl.localAffiliateID, resource.VersionUndefined))
affiliate, err := safe.ReaderGetByID[*cluster.Affiliate](ctx, r, ctrl.localAffiliateID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting local affiliate: %w", err)
@ -132,7 +133,7 @@ func (ctrl *KubernetesPushController) Run(ctx context.Context, r controller.Runt
}
}
if err = registry.NewKubernetes(ctrl.kubernetesClient).Push(ctx, affiliate.(*cluster.Affiliate)); err != nil {
if err = registry.NewKubernetes(ctrl.kubernetesClient).Push(ctx, affiliate); err != nil {
// reset client connection
ctrl.kubernetesClient.Close() //nolint:errcheck
ctrl.kubernetesClient = nil

View File

@ -13,6 +13,7 @@ import (
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/gen/channel"
"github.com/siderolabs/gen/slices"
"github.com/siderolabs/go-pointer"
"github.com/siderolabs/net"
@ -90,6 +91,11 @@ func (ctrl *LocalAffiliateController) Inputs() []controller.Input {
Type: network.AddressStatusType,
Kind: controller.InputWeak,
},
{
Namespace: config.NamespaceName,
Type: config.MachineConfigType,
Kind: controller.InputWeak,
},
}
}
@ -108,188 +114,202 @@ func (ctrl *LocalAffiliateController) Outputs() []controller.Output {
//nolint:gocyclo,cyclop
func (ctrl *LocalAffiliateController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
for {
select {
case <-ctx.Done():
return nil
case <-r.EventCh():
// mandatory resources to be fetched
discoveryConfig, err := safe.ReaderGetByID[*cluster.Config](ctx, r, cluster.ConfigID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting discovery config: %w", err)
if _, ok := channel.RecvWithContext(ctx, r.EventCh()); !ok && ctx.Err() != nil {
return nil //nolint:nilerr
}
// mandatory resources to be fetched
discoveryConfig, err := safe.ReaderGetByID[*cluster.Config](ctx, r, cluster.ConfigID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting discovery config: %w", err)
}
continue
}
identity, err := safe.ReaderGetByID[*cluster.Identity](ctx, r, cluster.LocalIdentity)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting local identity: %w", err)
}
continue
}
hostname, err := safe.ReaderGetByID[*network.HostnameStatus](ctx, r, network.HostnameID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting hostname: %w", err)
}
continue
}
nodename, err := safe.ReaderGetByID[*k8s.Nodename](ctx, r, k8s.NodenameID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting nodename: %w", err)
}
continue
}
routedAddresses, err := safe.ReaderGetByID[*network.NodeAddress](ctx, r, network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s))
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting addresses: %w", err)
}
continue
}
currentAddresses, err := safe.ReaderGetByID[*network.NodeAddress](ctx, r, network.FilteredNodeAddressID(network.NodeAddressCurrentID, k8s.NodeAddressFilterNoK8s))
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting addresses: %w", err)
}
continue
}
machineType, err := safe.ReaderGetByID[*config.MachineType](ctx, r, config.MachineTypeID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting machine type: %w", err)
}
continue
}
machineConfig, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.V1Alpha1ID)
if err != nil && !state.IsNotFoundError(err) {
return fmt.Errorf("error getting machine config: %w", err)
}
// optional resources (kubespan)
kubespanIdentity, err := safe.ReaderGetByID[*kubespan.Identity](ctx, r, kubespan.LocalIdentity)
if err != nil && !state.IsNotFoundError(err) {
return fmt.Errorf("error getting kubespan identity: %w", err)
}
kubespanConfig, err := safe.ReaderGetByID[*kubespan.Config](ctx, r, kubespan.ConfigID)
if err != nil && !state.IsNotFoundError(err) {
return fmt.Errorf("error getting kubespan config: %w", err)
}
ksAdditionalAddresses, err := safe.ReaderGetByID[*network.NodeAddress](ctx, r, network.FilteredNodeAddressID(network.NodeAddressCurrentID, k8s.NodeAddressFilterOnlyK8s))
if err != nil && !state.IsNotFoundError(err) {
return fmt.Errorf("error getting kubespan additional addresses: %w", err)
}
discoveredPublicIPs, err := safe.ReaderList[*network.AddressStatus](ctx, r, resource.NewMetadata(cluster.NamespaceName, network.AddressStatusType, "", resource.VersionUndefined))
if err != nil {
return fmt.Errorf("error getting discovered public IP: %w", err)
}
localID := identity.TypedSpec().NodeID
touchedIDs := map[resource.ID]struct{}{}
if discoveryConfig.TypedSpec().DiscoveryEnabled {
if err = safe.WriterModify(ctx, r, cluster.NewAffiliate(cluster.NamespaceName, localID), func(res *cluster.Affiliate) error {
spec := res.TypedSpec()
spec.NodeID = localID
spec.Hostname = hostname.TypedSpec().FQDN()
spec.Nodename = nodename.TypedSpec().Nodename
spec.MachineType = machineType.MachineType()
spec.OperatingSystem = fmt.Sprintf("%s (%s)", version.Name, version.Tag)
if machineType.MachineType().IsControlPlane() && machineConfig != nil {
spec.ControlPlane = &cluster.ControlPlane{
APIServerPort: machineConfig.Config().Cluster().LocalAPIServerPort(),
}
} else {
spec.ControlPlane = nil
}
continue
}
routedNodeIPs := routedAddresses.TypedSpec().IPs()
currentNodeIPs := currentAddresses.TypedSpec().IPs()
identity, err := safe.ReaderGetByID[*cluster.Identity](ctx, r, cluster.LocalIdentity)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting local identity: %w", err)
}
spec.Addresses = routedNodeIPs
continue
}
spec.KubeSpan = cluster.KubeSpanAffiliateSpec{}
hostname, err := safe.ReaderGetByID[*network.HostnameStatus](ctx, r, network.HostnameID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting hostname: %w", err)
}
if kubespanIdentity != nil && kubespanConfig != nil {
spec.KubeSpan.Address = kubespanIdentity.TypedSpec().Address.Addr()
spec.KubeSpan.PublicKey = kubespanIdentity.TypedSpec().PublicKey
continue
}
nodename, err := safe.ReaderGetByID[*k8s.Nodename](ctx, r, k8s.NodenameID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting nodename: %w", err)
}
continue
}
routedAddresses, err := safe.ReaderGetByID[*network.NodeAddress](ctx, r, network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s))
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting addresses: %w", err)
}
continue
}
currentAddresses, err := safe.ReaderGetByID[*network.NodeAddress](ctx, r, network.FilteredNodeAddressID(network.NodeAddressCurrentID, k8s.NodeAddressFilterNoK8s))
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting addresses: %w", err)
}
continue
}
machineType, err := safe.ReaderGetByID[*config.MachineType](ctx, r, config.MachineTypeID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting machine type: %w", err)
}
continue
}
// optional resources (kubespan)
kubespanIdentity, err := safe.ReaderGetByID[*kubespan.Identity](ctx, r, kubespan.LocalIdentity)
if err != nil && !state.IsNotFoundError(err) {
return fmt.Errorf("error getting kubespan identity: %w", err)
}
kubespanConfig, err := safe.ReaderGetByID[*kubespan.Config](ctx, r, kubespan.ConfigID)
if err != nil && !state.IsNotFoundError(err) {
return fmt.Errorf("error getting kubespan config: %w", err)
}
ksAdditionalAddresses, err := safe.ReaderGetByID[*network.NodeAddress](ctx, r, network.FilteredNodeAddressID(network.NodeAddressCurrentID, k8s.NodeAddressFilterOnlyK8s))
if err != nil && !state.IsNotFoundError(err) {
return fmt.Errorf("error getting kubespan additional addresses: %w", err)
}
discoveredPublicIPs, err := safe.ReaderList[*network.AddressStatus](ctx, r, resource.NewMetadata(cluster.NamespaceName, network.AddressStatusType, "", resource.VersionUndefined))
if err != nil {
return fmt.Errorf("error getting discovered public IP: %w", err)
}
localID := identity.TypedSpec().NodeID
touchedIDs := map[resource.ID]struct{}{}
if discoveryConfig.TypedSpec().DiscoveryEnabled {
if err = safe.WriterModify(ctx, r, cluster.NewAffiliate(cluster.NamespaceName, localID), func(res *cluster.Affiliate) error {
spec := res.TypedSpec()
spec.NodeID = localID
spec.Hostname = hostname.TypedSpec().FQDN()
spec.Nodename = nodename.TypedSpec().Nodename
spec.MachineType = machineType.MachineType()
spec.OperatingSystem = fmt.Sprintf("%s (%s)", version.Name, version.Tag)
routedNodeIPs := routedAddresses.TypedSpec().IPs()
currentNodeIPs := currentAddresses.TypedSpec().IPs()
spec.Addresses = routedNodeIPs
spec.KubeSpan = cluster.KubeSpanAffiliateSpec{}
if kubespanIdentity != nil && kubespanConfig != nil {
spec.KubeSpan.Address = kubespanIdentity.TypedSpec().Address.Addr()
spec.KubeSpan.PublicKey = kubespanIdentity.TypedSpec().PublicKey
if kubespanConfig.TypedSpec().AdvertiseKubernetesNetworks && ksAdditionalAddresses != nil {
spec.KubeSpan.AdditionalAddresses = slices.Clone(ksAdditionalAddresses.TypedSpec().Addresses)
} else {
spec.KubeSpan.AdditionalAddresses = nil
}
endpointIPs := slices.Filter(currentNodeIPs, func(ip netip.Addr) bool {
if ip == spec.KubeSpan.Address {
// skip kubespan local address
return false
}
if network.IsULA(ip, network.ULASideroLink) {
// ignore SideroLink addresses, as they are point-to-point addresses
return false
}
return true
})
// mix in discovered public IPs
for iter := safe.IteratorFromList(discoveredPublicIPs); iter.Next(); {
addr := iter.Value().TypedSpec().Address.Addr()
if slices.Contains(endpointIPs, func(a netip.Addr) bool { return addr == a }) {
// this address is already published
continue
}
endpointIPs = append(endpointIPs, addr)
}
// filter endpoints if configured
if kubespanConfig.TypedSpec().EndpointFilters != nil {
endpointIPs, err = net.FilterIPs(endpointIPs, kubespanConfig.TypedSpec().EndpointFilters)
if err != nil {
return fmt.Errorf("error filtering KubeSpan endpoints: %w", err)
}
}
spec.KubeSpan.Endpoints = slices.Map(endpointIPs, func(addr netip.Addr) netip.AddrPort {
return netip.AddrPortFrom(addr, constants.KubeSpanDefaultPort)
})
if kubespanConfig.TypedSpec().AdvertiseKubernetesNetworks && ksAdditionalAddresses != nil {
spec.KubeSpan.AdditionalAddresses = slices.Clone(ksAdditionalAddresses.TypedSpec().Addresses)
} else {
spec.KubeSpan.AdditionalAddresses = nil
}
return nil
}); err != nil {
return err
}
endpointIPs := slices.Filter(currentNodeIPs, func(ip netip.Addr) bool {
if ip == spec.KubeSpan.Address {
// skip kubespan local address
return false
}
touchedIDs[localID] = struct{}{}
}
if network.IsULA(ip, network.ULASideroLink) {
// ignore SideroLink addresses, as they are point-to-point addresses
return false
}
// list keys for cleanup
list, err := r.List(ctx, resource.NewMetadata(cluster.NamespaceName, cluster.AffiliateType, "", resource.VersionUndefined))
if err != nil {
return fmt.Errorf("error listing resources: %w", err)
}
return true
})
for _, res := range list.Items {
if res.Metadata().Owner() != ctrl.Name() {
continue
}
// mix in discovered public IPs
for iter := safe.IteratorFromList(discoveredPublicIPs); iter.Next(); {
addr := iter.Value().TypedSpec().Address.Addr()
if _, ok := touchedIDs[res.Metadata().ID()]; !ok {
if err = r.Destroy(ctx, res.Metadata()); err != nil {
return fmt.Errorf("error cleaning up specs: %w", err)
if slices.Contains(endpointIPs, func(a netip.Addr) bool { return addr == a }) {
// this address is already published
continue
}
endpointIPs = append(endpointIPs, addr)
}
// filter endpoints if configured
if kubespanConfig.TypedSpec().EndpointFilters != nil {
endpointIPs, err = net.FilterIPs(endpointIPs, kubespanConfig.TypedSpec().EndpointFilters)
if err != nil {
return fmt.Errorf("error filtering KubeSpan endpoints: %w", err)
}
}
spec.KubeSpan.Endpoints = slices.Map(endpointIPs, func(addr netip.Addr) netip.AddrPort {
return netip.AddrPortFrom(addr, constants.KubeSpanDefaultPort)
})
}
return nil
}); err != nil {
return err
}
touchedIDs[localID] = struct{}{}
}
// list keys for cleanup
list, err := safe.ReaderListAll[*cluster.Affiliate](ctx, r)
if err != nil {
return fmt.Errorf("error listing resources: %w", err)
}
for it := safe.IteratorFromList(list); it.Next(); {
res := it.Value()
if res.Metadata().Owner() != ctrl.Name() {
continue
}
if _, ok := touchedIDs[res.Metadata().ID()]; !ok {
if err = r.Destroy(ctx, res.Metadata()); err != nil {
return fmt.Errorf("error cleaning up specs: %w", err)
}
}
}

View File

@ -7,17 +7,20 @@ package cluster_test
import (
"net"
"net/netip"
"net/url"
"testing"
"time"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/siderolabs/go-retry/retry"
"github.com/siderolabs/gen/slices"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
clusteradapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/cluster"
kubespanadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/kubespan"
clusterctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cluster"
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest"
"github.com/siderolabs/talos/pkg/machinery/config/container"
"github.com/siderolabs/talos/pkg/machinery/config/machine"
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
"github.com/siderolabs/talos/pkg/machinery/resources/cluster"
"github.com/siderolabs/talos/pkg/machinery/resources/config"
"github.com/siderolabs/talos/pkg/machinery/resources/k8s"
@ -35,6 +38,167 @@ func (suite *LocalAffiliateSuite) TestGeneration() {
suite.Require().NoError(suite.runtime.RegisterController(&clusterctrl.LocalAffiliateController{}))
nodeIdentity, nonK8sRoutedAddresses, discoveryConfig := suite.createResources()
machineType := config.NewMachineType()
machineType.SetMachineType(machine.TypeWorker)
suite.Require().NoError(suite.state.Create(suite.ctx, machineType))
ctest.AssertResource(suite, nodeIdentity.TypedSpec().NodeID, func(r *cluster.Affiliate, asrt *assert.Assertions) {
spec := r.TypedSpec()
asrt.Equal([]string{
"172.20.0.2",
"10.5.0.1",
"192.168.192.168",
"2001:123:4567::1",
}, slices.Map(spec.Addresses, netip.Addr.String))
asrt.Equal("example1", spec.Hostname)
asrt.Equal("example1.com", spec.Nodename)
asrt.Equal(machine.TypeWorker, spec.MachineType)
asrt.Equal("Talos ("+version.Tag+")", spec.OperatingSystem)
asrt.Equal(cluster.KubeSpanAffiliateSpec{}, spec.KubeSpan)
})
// enable kubespan
mac, err := net.ParseMAC("ea:71:1b:b2:cc:ee")
suite.Require().NoError(err)
ksIdentity := kubespan.NewIdentity(kubespan.NamespaceName, kubespan.LocalIdentity)
suite.Require().NoError(kubespanadapter.IdentitySpec(ksIdentity.TypedSpec()).GenerateKey())
suite.Require().NoError(kubespanadapter.IdentitySpec(ksIdentity.TypedSpec()).UpdateAddress("8XuV9TZHW08DOk3bVxQjH9ih_TBKjnh-j44tsCLSBzo=", mac))
suite.Require().NoError(suite.state.Create(suite.ctx, ksIdentity))
ksConfig := kubespan.NewConfig(config.NamespaceName, kubespan.ConfigID)
ksConfig.TypedSpec().EndpointFilters = []string{"0.0.0.0/0", "!192.168.0.0/16", "2001::/16"}
ksConfig.TypedSpec().AdvertiseKubernetesNetworks = true
suite.Require().NoError(suite.state.Create(suite.ctx, ksConfig))
// add KS address to the list of node addresses, it should be ignored in the endpoints
nonK8sRoutedAddresses.TypedSpec().Addresses = append(nonK8sRoutedAddresses.TypedSpec().Addresses, ksIdentity.TypedSpec().Address)
suite.Require().NoError(suite.state.Update(suite.ctx, nonK8sRoutedAddresses))
onlyK8sAddresses := network.NewNodeAddress(network.NamespaceName, network.FilteredNodeAddressID(network.NodeAddressCurrentID, k8s.NodeAddressFilterOnlyK8s))
onlyK8sAddresses.TypedSpec().Addresses = []netip.Prefix{netip.MustParsePrefix("10.244.1.0/24")}
suite.Require().NoError(suite.state.Create(suite.ctx, onlyK8sAddresses))
// add discovered public IPs
for _, addr := range []netip.Addr{
netip.MustParseAddr("1.1.1.1"),
netip.MustParseAddr("2001:123:4567::1"), // duplicate, will be ignored
} {
discoveredAddr := network.NewAddressStatus(cluster.NamespaceName, addr.String())
discoveredAddr.TypedSpec().Address = netip.PrefixFrom(addr, addr.BitLen())
suite.Require().NoError(suite.state.Create(suite.ctx, discoveredAddr))
}
ctest.AssertResource(suite, nodeIdentity.TypedSpec().NodeID, func(r *cluster.Affiliate, asrt *assert.Assertions) {
spec := r.TypedSpec()
asrt.False(len(spec.Addresses) < 5)
asrt.Equal([]netip.Addr{
netip.MustParseAddr("172.20.0.2"),
netip.MustParseAddr("10.5.0.1"),
netip.MustParseAddr("192.168.192.168"),
netip.MustParseAddr("2001:123:4567::1"),
ksIdentity.TypedSpec().Address.Addr(),
}, spec.Addresses)
asrt.Equal("example1", spec.Hostname)
asrt.Equal("example1.com", spec.Nodename)
asrt.Equal(machine.TypeWorker, spec.MachineType)
asrt.NotZero(spec.KubeSpan.PublicKey)
asrt.NotZero(spec.KubeSpan.AdditionalAddresses)
asrt.Len(spec.KubeSpan.Endpoints, 4)
asrt.Equal(ksIdentity.TypedSpec().Address.Addr(), spec.KubeSpan.Address)
asrt.Equal(ksIdentity.TypedSpec().PublicKey, spec.KubeSpan.PublicKey)
asrt.Equal([]netip.Prefix{netip.MustParsePrefix("10.244.1.0/24")}, spec.KubeSpan.AdditionalAddresses)
asrt.Equal(
[]string{
"172.20.0.2:51820",
"10.5.0.1:51820",
"1.1.1.1:51820",
"[2001:123:4567::1]:51820",
},
slices.Map(spec.KubeSpan.Endpoints, netip.AddrPort.String),
)
})
// disable advertising K8s addresses
ksConfig.TypedSpec().AdvertiseKubernetesNetworks = false
suite.Require().NoError(suite.state.Update(suite.ctx, ksConfig))
ctest.AssertResource(suite, nodeIdentity.TypedSpec().NodeID, func(r *cluster.Affiliate, asrt *assert.Assertions) {
asrt.Empty(r.TypedSpec().KubeSpan.AdditionalAddresses)
})
// disable discovery, local affiliate should be removed
discoveryConfig.TypedSpec().DiscoveryEnabled = false
suite.Require().NoError(suite.state.Update(suite.ctx, discoveryConfig))
ctest.AssertNoResource[*cluster.Affiliate](suite, nodeIdentity.TypedSpec().NodeID)
}
func (suite *LocalAffiliateSuite) TestCPGeneration() {
suite.startRuntime()
suite.Require().NoError(suite.runtime.RegisterController(&clusterctrl.LocalAffiliateController{}))
nodeIdentity, _, discoveryConfig := suite.createResources()
machineType := config.NewMachineType()
machineType.SetMachineType(machine.TypeControlPlane)
suite.Require().NoError(suite.state.Create(suite.ctx, machineType))
u, err := url.Parse("https://foo:6443")
suite.Require().NoError(err)
mc := config.NewMachineConfig(
container.NewV1Alpha1(
&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{},
ClusterConfig: &v1alpha1.ClusterConfig{
ControlPlane: &v1alpha1.ControlPlaneConfig{
Endpoint: &v1alpha1.Endpoint{
URL: u,
},
LocalAPIServerPort: 6445,
},
},
},
),
)
suite.Require().NoError(suite.state.Create(suite.ctx, mc))
ctest.AssertResource(suite, nodeIdentity.TypedSpec().NodeID, func(r *cluster.Affiliate, asrt *assert.Assertions) {
spec := r.TypedSpec()
asrt.Equal([]string{
"172.20.0.2",
"10.5.0.1",
"192.168.192.168",
"2001:123:4567::1",
}, slices.Map(spec.Addresses, netip.Addr.String))
asrt.Equal("example1", spec.Hostname)
asrt.Equal("example1.com", spec.Nodename)
asrt.Equal(machine.TypeControlPlane, spec.MachineType)
asrt.Equal("Talos ("+version.Tag+")", spec.OperatingSystem)
asrt.Equal(cluster.KubeSpanAffiliateSpec{}, spec.KubeSpan)
asrt.NotNil(spec.ControlPlane)
asrt.Equal(6445, spec.ControlPlane.APIServerPort)
})
discoveryConfig.TypedSpec().DiscoveryEnabled = false
suite.Require().NoError(suite.state.Update(suite.ctx, discoveryConfig))
ctest.AssertNoResource[*cluster.Affiliate](suite, nodeIdentity.TypedSpec().NodeID)
}
func (suite *LocalAffiliateSuite) createResources() (*cluster.Identity, *network.NodeAddress, *cluster.Config) {
// regular discovery affiliate
discoveryConfig := cluster.NewConfig(config.NamespaceName, cluster.ConfigID)
discoveryConfig.TypedSpec().DiscoveryEnabled = true
@ -73,133 +237,7 @@ func (suite *LocalAffiliateSuite) TestGeneration() {
}
suite.Require().NoError(suite.state.Create(suite.ctx, nonK8sRoutedAddresses))
machineType := config.NewMachineType()
machineType.SetMachineType(machine.TypeWorker)
suite.Require().NoError(suite.state.Create(suite.ctx, machineType))
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertResource(*cluster.NewAffiliate(cluster.NamespaceName, nodeIdentity.TypedSpec().NodeID).Metadata(), func(r resource.Resource) error {
spec := r.(*cluster.Affiliate).TypedSpec()
suite.Assert().Equal([]netip.Addr{
netip.MustParseAddr("172.20.0.2"),
netip.MustParseAddr("10.5.0.1"),
netip.MustParseAddr("192.168.192.168"),
netip.MustParseAddr("2001:123:4567::1"),
}, spec.Addresses)
suite.Assert().Equal("example1", spec.Hostname)
suite.Assert().Equal("example1.com", spec.Nodename)
suite.Assert().Equal(machine.TypeWorker, spec.MachineType)
suite.Assert().Equal("Talos ("+version.Tag+")", spec.OperatingSystem)
suite.Assert().Equal(cluster.KubeSpanAffiliateSpec{}, spec.KubeSpan)
return nil
}),
))
// enable kubespan
mac, err := net.ParseMAC("ea:71:1b:b2:cc:ee")
suite.Require().NoError(err)
ksIdentity := kubespan.NewIdentity(kubespan.NamespaceName, kubespan.LocalIdentity)
suite.Require().NoError(kubespanadapter.IdentitySpec(ksIdentity.TypedSpec()).GenerateKey())
suite.Require().NoError(kubespanadapter.IdentitySpec(ksIdentity.TypedSpec()).UpdateAddress("8XuV9TZHW08DOk3bVxQjH9ih_TBKjnh-j44tsCLSBzo=", mac))
suite.Require().NoError(suite.state.Create(suite.ctx, ksIdentity))
ksConfig := kubespan.NewConfig(config.NamespaceName, kubespan.ConfigID)
ksConfig.TypedSpec().EndpointFilters = []string{"0.0.0.0/0", "!192.168.0.0/16", "2001::/16"}
ksConfig.TypedSpec().AdvertiseKubernetesNetworks = true
suite.Require().NoError(suite.state.Create(suite.ctx, ksConfig))
// add KS address to the list of node addresses, it should be ignored in the endpoints
nonK8sRoutedAddresses.TypedSpec().Addresses = append(nonK8sRoutedAddresses.TypedSpec().Addresses, ksIdentity.TypedSpec().Address)
suite.Require().NoError(suite.state.Update(suite.ctx, nonK8sRoutedAddresses))
onlyK8sAddresses := network.NewNodeAddress(network.NamespaceName, network.FilteredNodeAddressID(network.NodeAddressCurrentID, k8s.NodeAddressFilterOnlyK8s))
onlyK8sAddresses.TypedSpec().Addresses = []netip.Prefix{netip.MustParsePrefix("10.244.1.0/24")}
suite.Require().NoError(suite.state.Create(suite.ctx, onlyK8sAddresses))
// add discovered public IPs
for _, addr := range []netip.Addr{
netip.MustParseAddr("1.1.1.1"),
netip.MustParseAddr("2001:123:4567::1"), // duplicate, will be ignored
} {
discoveredAddr := network.NewAddressStatus(cluster.NamespaceName, addr.String())
discoveredAddr.TypedSpec().Address = netip.PrefixFrom(addr, addr.BitLen())
suite.Require().NoError(suite.state.Create(suite.ctx, discoveredAddr))
}
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertResource(*cluster.NewAffiliate(cluster.NamespaceName, nodeIdentity.TypedSpec().NodeID).Metadata(), func(r resource.Resource) error {
spec := r.(*cluster.Affiliate).TypedSpec()
if len(spec.Addresses) < 5 {
return retry.ExpectedErrorf("not reconciled yet")
}
suite.Assert().Equal([]netip.Addr{
netip.MustParseAddr("172.20.0.2"),
netip.MustParseAddr("10.5.0.1"),
netip.MustParseAddr("192.168.192.168"),
netip.MustParseAddr("2001:123:4567::1"),
ksIdentity.TypedSpec().Address.Addr(),
}, spec.Addresses)
suite.Assert().Equal("example1", spec.Hostname)
suite.Assert().Equal("example1.com", spec.Nodename)
suite.Assert().Equal(machine.TypeWorker, spec.MachineType)
if spec.KubeSpan.PublicKey == "" {
return retry.ExpectedErrorf("kubespan is not filled in yet")
}
if spec.KubeSpan.AdditionalAddresses == nil {
return retry.ExpectedErrorf("kubespan is not filled in yet")
}
if len(spec.KubeSpan.Endpoints) != 4 {
return retry.ExpectedErrorf("kubespan endpoints are not reconciled yet")
}
suite.Assert().Equal(ksIdentity.TypedSpec().Address.Addr(), spec.KubeSpan.Address)
suite.Assert().Equal(ksIdentity.TypedSpec().PublicKey, spec.KubeSpan.PublicKey)
suite.Assert().Equal([]netip.Prefix{netip.MustParsePrefix("10.244.1.0/24")}, spec.KubeSpan.AdditionalAddresses)
suite.Assert().Equal([]netip.AddrPort{
netip.MustParseAddrPort("172.20.0.2:51820"),
netip.MustParseAddrPort("10.5.0.1:51820"),
netip.MustParseAddrPort("1.1.1.1:51820"),
netip.MustParseAddrPort("[2001:123:4567::1]:51820"),
}, spec.KubeSpan.Endpoints)
return nil
}),
))
// disable advertising K8s addresses
ksConfig.TypedSpec().AdvertiseKubernetesNetworks = false
suite.Require().NoError(suite.state.Update(suite.ctx, ksConfig))
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertResource(*cluster.NewAffiliate(cluster.NamespaceName, nodeIdentity.TypedSpec().NodeID).Metadata(), func(r resource.Resource) error {
spec := r.(*cluster.Affiliate).TypedSpec()
if spec.KubeSpan.AdditionalAddresses != nil {
return retry.ExpectedErrorf("additional addresses are not cleared yet")
}
suite.Assert().Empty(spec.KubeSpan.AdditionalAddresses)
return nil
}),
))
// disable discovery, local affiliate should be removed
discoveryConfig.TypedSpec().DiscoveryEnabled = false
suite.Require().NoError(suite.state.Update(suite.ctx, discoveryConfig))
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertNoResource(*cluster.NewAffiliate(cluster.NamespaceName, nodeIdentity.TypedSpec().NodeID).Metadata()),
))
return nodeIdentity, nonK8sRoutedAddresses, discoveryConfig
}
func TestLocalAffiliateSuite(t *testing.T) {

View File

@ -10,6 +10,8 @@ import (
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/siderolabs/gen/channel"
"github.com/siderolabs/gen/slices"
"go.uber.org/zap"
@ -48,36 +50,35 @@ func (ctrl *MemberController) Outputs() []controller.Output {
// Run implements controller.Controller interface.
//
//nolint:gocyclo
func (ctrl *MemberController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
func (ctrl *MemberController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error {
for {
select {
case <-ctx.Done():
return nil
case <-r.EventCh():
if _, ok := channel.RecvWithContext(ctx, r.EventCh()); !ok && ctx.Err() != nil {
return nil //nolint:nilerr
}
affiliates, err := r.List(ctx, resource.NewMetadata(cluster.NamespaceName, cluster.AffiliateType, "", resource.VersionUndefined))
affiliates, err := safe.ReaderListAll[*cluster.Affiliate](ctx, r)
if err != nil {
return fmt.Errorf("error listing affiliates")
}
touchedIDs := make(map[resource.ID]struct{})
for _, affiliate := range affiliates.Items {
affiliateSpec := affiliate.(*cluster.Affiliate).TypedSpec()
for it := safe.IteratorFromList(affiliates); it.Next(); {
affiliateSpec := it.Value().TypedSpec()
if affiliateSpec.Nodename == "" {
// not a cluster member
continue
}
if err = r.Modify(ctx, cluster.NewMember(cluster.NamespaceName, affiliateSpec.Nodename), func(res resource.Resource) error {
spec := res.(*cluster.Member).TypedSpec()
if err = safe.WriterModify(ctx, r, cluster.NewMember(cluster.NamespaceName, affiliateSpec.Nodename), func(res *cluster.Member) error {
spec := res.TypedSpec()
spec.Addresses = slices.Clone(affiliateSpec.Addresses)
spec.Hostname = affiliateSpec.Hostname
spec.MachineType = affiliateSpec.MachineType
spec.OperatingSystem = affiliateSpec.OperatingSystem
spec.NodeID = affiliateSpec.NodeID
spec.ControlPlane = affiliateSpec.ControlPlane
return nil
}); err != nil {
@ -88,12 +89,14 @@ func (ctrl *MemberController) Run(ctx context.Context, r controller.Runtime, log
}
// list keys for cleanup
list, err := r.List(ctx, resource.NewMetadata(cluster.NamespaceName, cluster.MemberType, "", resource.VersionUndefined))
list, err := safe.ReaderListAll[*cluster.Member](ctx, r)
if err != nil {
return fmt.Errorf("error listing resources: %w", err)
}
for _, res := range list.Items {
for it := safe.IteratorFromList(list); it.Next(); {
res := it.Value()
if res.Metadata().Owner() != ctrl.Name() {
continue
}

View File

@ -7,13 +7,13 @@ package cluster_test
import (
"net/netip"
"testing"
"time"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/siderolabs/go-retry/retry"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
clusterctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cluster"
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest"
"github.com/siderolabs/talos/pkg/machinery/config/machine"
"github.com/siderolabs/talos/pkg/machinery/resources/cluster"
)
@ -41,6 +41,7 @@ func (suite *MemberSuite) TestReconcileDefault() {
AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")},
Endpoints: []netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")},
},
ControlPlane: &cluster.ControlPlane{APIServerPort: 6443},
}
affiliate2 := cluster.NewAffiliate(cluster.NamespaceName, "9dwHNUViZlPlIervqX9Qo256RUhrfhgO0xBBnKcKl4F")
@ -64,39 +65,38 @@ func (suite *MemberSuite) TestReconcileDefault() {
}
// affiliates with non-empty Nodename should be translated to Members
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertResource(*cluster.NewMember(cluster.NamespaceName, affiliate1.TypedSpec().Nodename).Metadata(), func(r resource.Resource) error {
spec := r.(*cluster.Member).TypedSpec()
ctest.AssertResource(
suite,
affiliate1.TypedSpec().Nodename,
func(r *cluster.Member, asrt *assert.Assertions) {
spec := r.TypedSpec()
suite.Assert().Equal(affiliate1.TypedSpec().NodeID, spec.NodeID)
suite.Assert().Equal([]netip.Addr{netip.MustParseAddr("192.168.3.4")}, spec.Addresses)
suite.Assert().Equal("foo.com", spec.Hostname)
suite.Assert().Equal(machine.TypeControlPlane, spec.MachineType)
suite.Assert().Equal("Talos (v1.0.0)", spec.OperatingSystem)
asrt.Equal(affiliate1.TypedSpec().NodeID, spec.NodeID)
asrt.Equal([]netip.Addr{netip.MustParseAddr("192.168.3.4")}, spec.Addresses)
asrt.Equal("foo.com", spec.Hostname)
asrt.Equal(machine.TypeControlPlane, spec.MachineType)
asrt.Equal("Talos (v1.0.0)", spec.OperatingSystem)
asrt.Equal(6443, spec.ControlPlane.APIServerPort)
},
)
return nil
}),
))
ctest.AssertResource(
suite,
affiliate2.TypedSpec().Nodename,
func(r *cluster.Member, asrt *assert.Assertions) {
spec := r.TypedSpec()
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertResource(*cluster.NewMember(cluster.NamespaceName, affiliate2.TypedSpec().Nodename).Metadata(), func(r resource.Resource) error {
spec := r.(*cluster.Member).TypedSpec()
suite.Assert().Equal(affiliate2.TypedSpec().NodeID, spec.NodeID)
suite.Assert().Equal([]netip.Addr{netip.MustParseAddr("192.168.3.5")}, spec.Addresses)
suite.Assert().Equal("worker-1", spec.Hostname)
suite.Assert().Equal(machine.TypeWorker, spec.MachineType)
return nil
}),
))
asrt.Equal(affiliate2.TypedSpec().NodeID, spec.NodeID)
asrt.Equal([]netip.Addr{netip.MustParseAddr("192.168.3.5")}, spec.Addresses)
asrt.Equal("worker-1", spec.Hostname)
asrt.Equal(machine.TypeWorker, spec.MachineType)
},
)
// remove affiliate2, member information should eventually go away
suite.Require().NoError(suite.state.Destroy(suite.ctx, affiliate2.Metadata()))
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertNoResource(*cluster.NewMember(cluster.NamespaceName, affiliate2.TypedSpec().Nodename).Metadata()),
))
ctest.AssertNoResource[*cluster.Member](suite, affiliate2.TypedSpec().Nodename)
}
func TestMemberSuite(t *testing.T) {

View File

@ -9,7 +9,6 @@ import (
"fmt"
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/go-pointer"
@ -60,7 +59,7 @@ func (ctrl *SeccompProfileController) Run(ctx context.Context, r controller.Runt
case <-r.EventCh():
}
cfg, err := safe.ReaderGet[*config.MachineConfig](ctx, r, resource.NewMetadata(config.NamespaceName, config.MachineConfigType, config.V1Alpha1ID, resource.VersionUndefined))
cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.V1Alpha1ID)
if err != nil {
if state.IsNotFoundError(err) {
continue

View File

@ -14,11 +14,13 @@ import (
"github.com/cosi-project/runtime/pkg/controller/runtime"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/resource/rtestutils"
"github.com/cosi-project/runtime/pkg/safe"
"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/siderolabs/go-retry/retry"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
@ -108,6 +110,11 @@ func (suite *DefaultSuite) TearDownTest() {
}
}
// Create creates a new resource in the state of the suite.
func (suite *DefaultSuite) Create(res resource.Resource, opts ...state.CreateOption) {
suite.Require().NoError(suite.State().Create(suite.Ctx(), res, opts...))
}
// Suite is a type which describes the suite type.
type Suite interface {
T() *testing.T
@ -134,3 +141,51 @@ func GetUsingResource[T resource.Resource](suite Suite, res T, options ...state.
func Get[T resource.Resource](suite Suite, ptr resource.Pointer, options ...state.GetOption) (T, error) { //nolint:ireturn
return safe.StateGet[T](suite.Ctx(), suite.State(), ptr, options...)
}
// Suiter is like Suite but do not require Require() method.
type Suiter interface {
T() *testing.T
State() state.State
Ctx() context.Context
}
// AssertResources asserts on a resource list.
func AssertResources[R rtestutils.ResourceWithRD](
suiter Suiter,
requiredIDs []resource.ID,
check func(R, *assert.Assertions),
opts ...rtestutils.Option,
) {
ctx, cancel := context.WithTimeout(suiter.Ctx(), 10*time.Second)
defer cancel()
rtestutils.AssertResources(ctx, suiter.T(), suiter.State(), requiredIDs, check, opts...)
}
// AssertResource asserts on a single resource.
func AssertResource[R rtestutils.ResourceWithRD](
suiter Suiter,
requiredIDs resource.ID,
check func(R, *assert.Assertions),
opts ...rtestutils.Option,
) {
AssertResources(suiter, []resource.ID{requiredIDs}, check, opts...)
}
// AssertNoResource asserts that a resource no longer exists.
func AssertNoResource[R rtestutils.ResourceWithRD](
suiter Suiter,
id string,
opts ...rtestutils.Option,
) {
ctx, cancel := context.WithTimeout(suiter.Ctx(), 10*time.Second)
defer cancel()
rtestutils.AssertNoResource[R](
ctx,
suiter.T(),
suiter.State(),
id,
opts...,
)
}

View File

@ -83,7 +83,7 @@ func (ctrl *ConfigController) Run(ctx context.Context, r controller.Runtime, log
continue
}
machineConfig, err := safe.ReaderGet[*config.MachineConfig](ctx, r, resource.NewMetadata(config.NamespaceName, config.MachineConfigType, config.V1Alpha1ID, resource.VersionUndefined))
machineConfig, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.V1Alpha1ID)
if err != nil {
if state.IsNotFoundError(err) {
continue

View File

@ -0,0 +1,170 @@
// 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 k8s
import (
"context"
"fmt"
"strconv"
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/gen/channel"
"github.com/siderolabs/go-pointer"
"go.uber.org/zap"
"github.com/siderolabs/talos/pkg/machinery/config/machine"
"github.com/siderolabs/talos/pkg/machinery/resources/cluster"
"github.com/siderolabs/talos/pkg/machinery/resources/config"
"github.com/siderolabs/talos/pkg/machinery/resources/k8s"
)
// APIServerEndpointsController creates a list of API server endpoints.
type APIServerEndpointsController struct{}
// Name implements controller.Controller interface.
func (ctrl *APIServerEndpointsController) Name() string {
return "cluster.APIServerEndpointsController"
}
// Inputs implements controller.Controller interface.
func (ctrl *APIServerEndpointsController) Inputs() []controller.Input {
return []controller.Input{
{
Namespace: config.NamespaceName,
Type: config.MachineTypeType,
ID: pointer.To(config.MachineTypeID),
Kind: controller.InputWeak,
},
safe.Input[*cluster.Member](controller.InputWeak),
safe.Input[*config.MachineConfig](controller.InputWeak),
}
}
// Outputs implements controller.Controller interface.
func (ctrl *APIServerEndpointsController) Outputs() []controller.Output {
return []controller.Output{
{
Type: k8s.APIServerEndpointsType,
Kind: controller.OutputExclusive,
},
}
}
// Run implements controller.Controller interface.
//
//nolint:gocyclo,cyclop
func (ctrl *APIServerEndpointsController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
for {
if _, ok := channel.RecvWithContext(ctx, r.EventCh()); !ok && ctx.Err() != nil {
return nil //nolint:nilerr
}
machineConfig, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.V1Alpha1ID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting machine config: %w", err)
}
continue
}
machineType, err := safe.ReaderGetByID[*config.MachineType](ctx, r, config.MachineTypeID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting machine type: %w", err)
}
continue
}
members, err := safe.ReaderListAll[*cluster.Member](ctx, r)
if err != nil {
return fmt.Errorf("error listing affiliates: %w", err)
}
var endpoints []k8s.APIServerEndpoint
ce := machineConfig.Config().Cluster().Endpoint()
if ce != nil {
endpoints = append(endpoints, k8s.APIServerEndpoint{
Host: ce.Hostname(),
Port: toPort(ce.Port()),
})
}
if machineType.MachineType() == machine.TypeControlPlane {
endpoints = append(endpoints, k8s.APIServerEndpoint{
Host: "localhost",
Port: uint32(machineConfig.Config().Cluster().LocalAPIServerPort()),
})
}
for it := safe.IteratorFromList(members); it.Next(); {
memberSpec := it.Value().TypedSpec()
if len(memberSpec.Addresses) > 0 && memberSpec.ControlPlane != nil {
for _, addr := range memberSpec.Addresses {
endpoints = append(endpoints, k8s.APIServerEndpoint{
Host: addr.String(),
Port: uint32(memberSpec.ControlPlane.APIServerPort),
})
}
}
}
err = safe.WriterModify[*k8s.APIServerEndpoints](
ctx,
r,
k8s.NewEndpoints(k8s.NamespaceName, k8s.APIServerEndpointsID),
func(res *k8s.APIServerEndpoints) error {
res.TypedSpec().Endpoints = endpoints
return nil
},
)
if err != nil {
return fmt.Errorf("error updating endpoints: %w", err)
}
// list keys for cleanup
list, err := safe.ReaderListAll[*k8s.APIServerEndpoints](ctx, r)
if err != nil {
return fmt.Errorf("error listing resources: %w", err)
}
for it := safe.IteratorFromList(list); it.Next(); {
res := it.Value()
if res.Metadata().Owner() != ctrl.Name() {
continue
}
if res.Metadata().ID() != k8s.APIServerEndpointsID {
if err = r.Destroy(ctx, res.Metadata()); err != nil {
return fmt.Errorf("error cleaning up specs: %w", err)
}
logger.Info("removed endpoints resource", zap.String("id", res.Metadata().ID()))
}
}
r.ResetRestartBackoff()
}
}
func toPort(port string) uint32 {
if port == "" {
return 443
}
p, err := strconv.ParseUint(port, 10, 32)
if err != nil {
return 443
}
return uint32(p)
}

View File

@ -0,0 +1,130 @@
// 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 k8s_test
import (
"net/netip"
"net/url"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
clusteradapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/cluster"
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest"
clusterctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s"
"github.com/siderolabs/talos/pkg/machinery/config/container"
"github.com/siderolabs/talos/pkg/machinery/config/machine"
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
"github.com/siderolabs/talos/pkg/machinery/resources/cluster"
"github.com/siderolabs/talos/pkg/machinery/resources/config"
"github.com/siderolabs/talos/pkg/machinery/resources/k8s"
)
type EndpointsBalancerControllerSuite struct {
ctest.DefaultSuite
}
func (suite *EndpointsBalancerControllerSuite) TestGeneration() {
nodeIdentity := cluster.NewIdentity(cluster.NamespaceName, cluster.LocalIdentity)
suite.Require().NoError(clusteradapter.IdentitySpec(nodeIdentity.TypedSpec()).Generate())
suite.Create(nodeIdentity)
mc := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{},
ClusterConfig: &v1alpha1.ClusterConfig{
ControlPlane: &v1alpha1.ControlPlaneConfig{
Endpoint: &v1alpha1.Endpoint{
URL: must(url.Parse("https://example.com"))(suite.Require()),
},
LocalAPIServerPort: 6445,
},
},
}))
suite.Create(mc)
machineType := config.NewMachineType()
machineType.SetMachineType(machine.TypeControlPlane)
suite.Create(machineType)
member1 := cluster.NewMember(cluster.NamespaceName, "service/7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC")
*member1.TypedSpec() = cluster.MemberSpec{
NodeID: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC",
Hostname: "foo.com",
MachineType: machine.TypeControlPlane,
Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.4")},
ControlPlane: &cluster.ControlPlane{APIServerPort: 6446},
}
suite.Create(member1)
member2 := cluster.NewMember(cluster.NamespaceName, "service/xCnFFfxylOf9i5ynhAkt6ZbfcqaLDGKfIa3gwpuaxe7F")
*member2.TypedSpec() = cluster.MemberSpec{
NodeID: nodeIdentity.TypedSpec().NodeID,
Hostname: "foo2.com",
MachineType: machine.TypeControlPlane,
Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.6")},
ControlPlane: &cluster.ControlPlane{APIServerPort: 6443},
}
suite.Create(member2)
member3 := cluster.NewMember(cluster.NamespaceName, "service/9dwHNUViZlPlIervqX9Qo256RUhrfhgO0xBBnKcKl4F")
*member3.TypedSpec() = cluster.MemberSpec{
NodeID: "9dwHNUViZlPlIervqX9Qo256RUhrfhgO0xBBnKcKl4F",
Hostname: "worker-1",
MachineType: machine.TypeWorker,
Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.5")},
}
suite.Create(member3)
ctest.AssertResource(suite, k8s.APIServerEndpointsID, func(e *k8s.APIServerEndpoints, asrt *assert.Assertions) {
asrt.Equal(
&k8s.APIServerEndpointsSpec{
Endpoints: []k8s.APIServerEndpoint{
{
Host: "example.com",
Port: 443,
},
{
Host: "localhost",
Port: 6445,
},
{
Host: "192.168.3.4",
Port: 6446,
},
{
Host: "192.168.3.6",
Port: 6443,
},
},
},
e.TypedSpec(),
)
})
}
func must[T any](res T, err error) func(t *require.Assertions) T {
return func(t *require.Assertions) T {
t.NoError(err)
return res
}
}
func TestEndpointsBalancerControllerSuite(t *testing.T) {
suite.Run(t, &EndpointsBalancerControllerSuite{
DefaultSuite: ctest.DefaultSuite{
AfterSetup: func(suite *ctest.DefaultSuite) {
suite.Require().NoError(suite.Runtime().RegisterController(&clusterctrl.APIServerEndpointsController{}))
},
},
})
}

View File

@ -97,7 +97,7 @@ func (ctrl *ControlPlaneController) Run(ctx context.Context, r controller.Runtim
case <-r.EventCh():
}
cfg, err := safe.ReaderGet[*config.MachineConfig](ctx, r, resource.NewMetadata(config.NamespaceName, config.MachineConfigType, config.V1Alpha1ID, resource.VersionUndefined))
cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.V1Alpha1ID)
if err != nil {
if state.IsNotFoundError(err) {
if err = ctrl.teardownAll(ctx, r); err != nil {

View File

@ -78,7 +78,7 @@ func (ctrl *KubeletConfigController) Run(ctx context.Context, r controller.Runti
return fmt.Errorf("error accessing static pod server status resource: %w", err)
}
cfg, err := safe.ReaderGet[*config.MachineConfig](ctx, r, resource.NewMetadata(config.NamespaceName, config.MachineConfigType, config.V1Alpha1ID, resource.VersionUndefined))
cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.V1Alpha1ID)
if err != nil {
if state.IsNotFoundError(err) {
continue

View File

@ -9,7 +9,6 @@ import (
"fmt"
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/go-pointer"
@ -62,7 +61,7 @@ func (ctrl *NodeLabelSpecController) Run(ctx context.Context, r controller.Runti
var nodeLabels map[string]string
cfg, err := safe.ReaderGet[*config.MachineConfig](ctx, r, resource.NewMetadata(config.NamespaceName, config.MachineConfigType, config.V1Alpha1ID, resource.VersionUndefined))
cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.V1Alpha1ID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting config: %w", err)

View File

@ -11,7 +11,6 @@ import (
"net/netip"
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/gen/slices"
@ -61,7 +60,7 @@ func (ctrl *StaticEndpointController) Run(ctx context.Context, r controller.Runt
case <-r.EventCh():
}
machineConfig, err := safe.ReaderGet[*config.MachineConfig](ctx, r, resource.NewMetadata(config.NamespaceName, config.MachineConfigType, config.V1Alpha1ID, resource.VersionUndefined))
machineConfig, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.V1Alpha1ID)
if err != nil {
if state.IsNotFoundError(err) {
continue

View File

@ -10,6 +10,8 @@ import (
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/siderolabs/gen/channel"
"github.com/siderolabs/gen/value"
"go.uber.org/zap"
@ -56,18 +58,16 @@ func (ctrl *EndpointController) Outputs() []controller.Output {
//nolint:gocyclo
func (ctrl *EndpointController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
for {
select {
case <-ctx.Done():
return nil
case <-r.EventCh():
if _, ok := channel.RecvWithContext(ctx, r.EventCh()); !ok && ctx.Err() != nil {
return nil //nolint:nilerr
}
peerStatuses, err := r.List(ctx, resource.NewMetadata(kubespan.NamespaceName, kubespan.PeerStatusType, "", resource.VersionUndefined))
peerStatuses, err := safe.ReaderListAll[*kubespan.PeerStatus](ctx, r)
if err != nil {
return fmt.Errorf("error listing cluster affiliates: %w", err)
}
affiliates, err := r.List(ctx, resource.NewMetadata(cluster.NamespaceName, cluster.AffiliateType, "", resource.VersionUndefined))
affiliates, err := safe.ReaderListAll[*cluster.Affiliate](ctx, r)
if err != nil {
return fmt.Errorf("error listing cluster affiliates: %w", err)
}
@ -75,8 +75,8 @@ func (ctrl *EndpointController) Run(ctx context.Context, r controller.Runtime, l
// build lookup table of affiliate's kubespan public key back to affiliate ID
affiliateLookup := make(map[string]string)
for _, res := range affiliates.Items {
affiliate := res.(*cluster.Affiliate).TypedSpec()
for it := safe.IteratorFromList(affiliates); it.Next(); {
affiliate := it.Value().TypedSpec()
if affiliate.KubeSpan.PublicKey != "" {
affiliateLookup[affiliate.KubeSpan.PublicKey] = affiliate.NodeID
@ -86,8 +86,9 @@ func (ctrl *EndpointController) Run(ctx context.Context, r controller.Runtime, l
// for every kubespan peer, if it's up and has endpoint, harvest that endpoint
touchedIDs := make(map[resource.ID]struct{})
for _, res := range peerStatuses.Items {
peerStatus := res.(*kubespan.PeerStatus).TypedSpec()
for it := safe.IteratorFromList(peerStatuses); it.Next(); {
res := it.Value()
peerStatus := res.TypedSpec()
if peerStatus.State != kubespan.PeerStateUp {
continue
@ -102,8 +103,8 @@ func (ctrl *EndpointController) Run(ctx context.Context, r controller.Runtime, l
continue
}
if err = r.Modify(ctx, kubespan.NewEndpoint(kubespan.NamespaceName, res.Metadata().ID()), func(res resource.Resource) error {
*res.(*kubespan.Endpoint).TypedSpec() = kubespan.EndpointSpec{
if err = safe.WriterModify(ctx, r, kubespan.NewEndpoint(kubespan.NamespaceName, res.Metadata().ID()), func(res *kubespan.Endpoint) error {
*res.TypedSpec() = kubespan.EndpointSpec{
AffiliateID: affiliateID,
Endpoint: peerStatus.Endpoint,
}
@ -117,12 +118,14 @@ func (ctrl *EndpointController) Run(ctx context.Context, r controller.Runtime, l
}
// list keys for cleanup
list, err := r.List(ctx, resource.NewMetadata(kubespan.NamespaceName, kubespan.EndpointType, "", resource.VersionUndefined))
list, err := safe.ReaderListAll[*kubespan.Endpoint](ctx, r)
if err != nil {
return fmt.Errorf("error listing resources: %w", err)
}
for _, res := range list.Items {
for it := safe.IteratorFromList(list); it.Next(); {
res := it.Value()
if res.Metadata().Owner() != ctrl.Name() {
continue
}

View File

@ -10,7 +10,9 @@ import (
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/gen/channel"
"github.com/siderolabs/gen/slices"
"github.com/siderolabs/go-pointer"
"go.uber.org/zap"
@ -67,128 +69,131 @@ func (ctrl *PeerSpecController) Outputs() []controller.Output {
//nolint:gocyclo,cyclop
func (ctrl *PeerSpecController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
for {
select {
case <-ctx.Done():
return nil
case <-r.EventCh():
cfg, err := r.Get(ctx, resource.NewMetadata(config.NamespaceName, kubespan.ConfigType, kubespan.ConfigID, resource.VersionUndefined))
if err != nil && !state.IsNotFoundError(err) {
return fmt.Errorf("error getting kubespan configuration: %w", err)
}
if _, ok := channel.RecvWithContext(ctx, r.EventCh()); !ok && ctx.Err() != nil {
return nil //nolint:nilerr
}
localIdentity, err := r.Get(ctx, resource.NewMetadata(cluster.NamespaceName, cluster.IdentityType, cluster.LocalIdentity, resource.VersionUndefined))
if err != nil && !state.IsNotFoundError(err) {
return fmt.Errorf("error getting first MAC address: %w", err)
}
cfg, err := safe.ReaderGetByID[*kubespan.Config](ctx, r, kubespan.ConfigID)
if err != nil && !state.IsNotFoundError(err) {
return fmt.Errorf("error getting kubespan configuration: %w", err)
}
affiliates, err := r.List(ctx, resource.NewMetadata(cluster.NamespaceName, cluster.AffiliateType, "", resource.VersionUndefined))
if err != nil {
return fmt.Errorf("error listing cluster affiliates: %w", err)
}
localIdentity, err := safe.ReaderGetByID[*cluster.Identity](ctx, r, cluster.LocalIdentity)
if err != nil && !state.IsNotFoundError(err) {
return fmt.Errorf("error getting first MAC address: %w", err)
}
touchedIDs := make(map[resource.ID]struct{})
affiliates, err := safe.ReaderListAll[*cluster.Affiliate](ctx, r)
if err != nil {
return fmt.Errorf("error listing cluster affiliates: %w", err)
}
if cfg != nil && localIdentity != nil && cfg.(*kubespan.Config).TypedSpec().Enabled {
localAffiliateID := localIdentity.(*cluster.Identity).TypedSpec().NodeID
touchedIDs := map[resource.ID]struct{}{}
peerIPSets := make(map[string]*netipx.IPSet, len(affiliates.Items))
if cfg != nil && localIdentity != nil && cfg.TypedSpec().Enabled {
localAffiliateID := localIdentity.TypedSpec().NodeID
affiliateLoop:
for _, affiliate := range affiliates.Items {
if affiliate.Metadata().ID() == localAffiliateID {
// skip local affiliate, it's not a peer
continue
}
peerIPSets := make(map[string]*netipx.IPSet, affiliates.Len())
spec := affiliate.(*cluster.Affiliate).TypedSpec()
affiliateLoop:
for it := safe.IteratorFromList(affiliates); it.Next(); {
affiliate := it.Value()
if spec.KubeSpan.PublicKey == "" {
// no kubespan information, skip it
continue
}
var builder netipx.IPSetBuilder
for _, ipPrefix := range spec.KubeSpan.AdditionalAddresses {
builder.AddPrefix(ipPrefix)
}
for _, ip := range spec.Addresses {
builder.Add(ip)
}
builder.Add(spec.KubeSpan.Address)
var ipSet *netipx.IPSet
ipSet, err = builder.IPSet()
if err != nil {
logger.Warn("failed building list of IP ranges for the peer", zap.String("ignored_peer", spec.KubeSpan.PublicKey), zap.String("label", spec.Nodename), zap.Error(err))
continue
}
for otherPublicKey, otherIPSet := range peerIPSets {
if otherIPSet.Overlaps(ipSet) {
logger.Warn("peer address overlap", zap.String("this_peer", spec.KubeSpan.PublicKey), zap.String("other_peer", otherPublicKey),
zap.Strings("this_ips", dumpSet(ipSet)), zap.Strings("other_ips", dumpSet(otherIPSet)))
// exclude overlapping IPs from the ipSet
var bldr netipx.IPSetBuilder
// ipSet = ipSet & ~otherIPSet
bldr.AddSet(otherIPSet)
bldr.Complement()
bldr.Intersect(ipSet)
ipSet, err = bldr.IPSet()
if err != nil {
logger.Warn("failed building list of IP ranges for the peer", zap.String("ignored_peer", spec.KubeSpan.PublicKey), zap.String("label", spec.Nodename), zap.Error(err))
continue affiliateLoop
}
if len(ipSet.Ranges()) == 0 {
logger.Warn("conflict resolution removed all ranges", zap.String("this_peer", spec.KubeSpan.PublicKey), zap.String("other_peer", otherPublicKey))
}
}
}
peerIPSets[spec.KubeSpan.PublicKey] = ipSet
if err = r.Modify(ctx, kubespan.NewPeerSpec(kubespan.NamespaceName, spec.KubeSpan.PublicKey), func(res resource.Resource) error {
*res.(*kubespan.PeerSpec).TypedSpec() = kubespan.PeerSpecSpec{
Address: spec.KubeSpan.Address,
AllowedIPs: ipSet.Prefixes(),
Endpoints: slices.Clone(spec.KubeSpan.Endpoints),
Label: spec.Nodename,
}
return nil
}); err != nil {
return err
}
touchedIDs[spec.KubeSpan.PublicKey] = struct{}{}
}
}
// list keys for cleanup
list, err := r.List(ctx, resource.NewMetadata(kubespan.NamespaceName, kubespan.PeerSpecType, "", resource.VersionUndefined))
if err != nil {
return fmt.Errorf("error listing resources: %w", err)
}
for _, res := range list.Items {
if res.Metadata().Owner() != ctrl.Name() {
if affiliate.Metadata().ID() == localAffiliateID {
// skip local affiliate, it's not a peer
continue
}
if _, ok := touchedIDs[res.Metadata().ID()]; !ok {
if err = r.Destroy(ctx, res.Metadata()); err != nil {
return fmt.Errorf("error cleaning up specs: %w", err)
spec := affiliate.TypedSpec()
if spec.KubeSpan.PublicKey == "" {
// no kubespan information, skip it
continue
}
var builder netipx.IPSetBuilder
for _, ipPrefix := range spec.KubeSpan.AdditionalAddresses {
builder.AddPrefix(ipPrefix)
}
for _, ip := range spec.Addresses {
builder.Add(ip)
}
builder.Add(spec.KubeSpan.Address)
var ipSet *netipx.IPSet
ipSet, err = builder.IPSet()
if err != nil {
logger.Warn("failed building list of IP ranges for the peer", zap.String("ignored_peer", spec.KubeSpan.PublicKey), zap.String("label", spec.Nodename), zap.Error(err))
continue
}
for otherPublicKey, otherIPSet := range peerIPSets {
if otherIPSet.Overlaps(ipSet) {
logger.Warn("peer address overlap", zap.String("this_peer", spec.KubeSpan.PublicKey), zap.String("other_peer", otherPublicKey),
zap.Strings("this_ips", dumpSet(ipSet)), zap.Strings("other_ips", dumpSet(otherIPSet)))
// exclude overlapping IPs from the ipSet
var bldr netipx.IPSetBuilder
// ipSet = ipSet & ~otherIPSet
bldr.AddSet(otherIPSet)
bldr.Complement()
bldr.Intersect(ipSet)
ipSet, err = bldr.IPSet()
if err != nil {
logger.Warn("failed building list of IP ranges for the peer", zap.String("ignored_peer", spec.KubeSpan.PublicKey), zap.String("label", spec.Nodename), zap.Error(err))
continue affiliateLoop
}
if len(ipSet.Ranges()) == 0 {
logger.Warn("conflict resolution removed all ranges", zap.String("this_peer", spec.KubeSpan.PublicKey), zap.String("other_peer", otherPublicKey))
}
}
}
peerIPSets[spec.KubeSpan.PublicKey] = ipSet
if err = safe.WriterModify(ctx, r, kubespan.NewPeerSpec(kubespan.NamespaceName, spec.KubeSpan.PublicKey), func(res *kubespan.PeerSpec) error {
*res.TypedSpec() = kubespan.PeerSpecSpec{
Address: spec.KubeSpan.Address,
AllowedIPs: ipSet.Prefixes(),
Endpoints: slices.Clone(spec.KubeSpan.Endpoints),
Label: spec.Nodename,
}
return nil
}); err != nil {
return err
}
touchedIDs[spec.KubeSpan.PublicKey] = struct{}{}
}
}
// list keys for cleanup
list, err := safe.ReaderListAll[*kubespan.PeerSpec](ctx, r)
if err != nil {
return fmt.Errorf("error listing resources: %w", err)
}
for it := safe.IteratorFromList(list); it.Next(); {
res := it.Value()
if res.Metadata().Owner() != ctrl.Name() {
continue
}
if _, ok := touchedIDs[res.Metadata().ID()]; !ok {
if err = r.Destroy(ctx, res.Metadata()); err != nil {
return fmt.Errorf("error cleaning up specs: %w", err)
}
}
}

View File

@ -90,7 +90,7 @@ func (ctrl *DeviceConfigController) Run(ctx context.Context, r controller.Runtim
var cfgProvider talosconfig.Config
cfg, err := safe.ReaderGet[*config.MachineConfig](ctx, r, resource.NewMetadata(config.NamespaceName, config.MachineConfigType, config.V1Alpha1ID, resource.VersionUndefined))
cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.V1Alpha1ID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting config: %w", err)

View File

@ -89,6 +89,7 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error
&cluster.ConfigController{},
&cluster.DiscoveryServiceController{},
&cluster.EndpointController{},
&k8s.APIServerEndpointsController{},
&cluster.InfoController{},
&cluster.KubernetesPullController{},
&cluster.KubernetesPushController{},

View File

@ -97,6 +97,7 @@ func NewState() (*State, error) {
&v1alpha1.Service{},
&cluster.Affiliate{},
&cluster.Config{},
&k8s.APIServerEndpoints{},
&cluster.Identity{},
&cluster.Info{},
&cluster.Member{},

View File

@ -13,7 +13,6 @@ import (
"testing"
"time"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/hashicorp/go-multierror"
"github.com/siderolabs/go-pointer"
@ -476,7 +475,7 @@ func (suite *ApplyConfigSuite) TestApplyTry() {
nodeCtx := client.WithNode(suite.ctx, node)
getMachineConfig := func(ctx context.Context) (*mc.MachineConfig, error) {
cfg, err := safe.StateGet[*mc.MachineConfig](ctx, suite.Client.COSI, resource.NewMetadata(mc.NamespaceName, mc.MachineConfigType, mc.V1Alpha1ID, resource.VersionUndefined))
cfg, err := safe.StateGetByID[*mc.MachineConfig](ctx, suite.Client.COSI, mc.V1Alpha1ID)
if err != nil {
return nil, err
}

View File

@ -307,11 +307,7 @@ func (suite *DiscoverySuite) getAffiliates(nodeCtx context.Context, namespace re
items, err := safe.StateList[*cluster.Affiliate](nodeCtx, suite.Client.COSI, resource.NewMetadata(namespace, cluster.AffiliateType, "", resource.VersionUndefined))
suite.Require().NoError(err)
it := safe.IteratorFromList(items)
for it.Next() {
result = append(result, it.Value())
}
items.ForEach(func(item *cluster.Affiliate) { result = append(result, item) })
return result
}

View File

@ -8,7 +8,6 @@ import (
"context"
"fmt"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/siderolabs/talos/pkg/machinery/api/machine"
@ -29,7 +28,7 @@ func patchNodeConfig(ctx context.Context, cluster UpgradeProvider, node string,
ctx = client.WithNode(ctx, node)
mc, err := safe.StateGet[*config.MachineConfig](ctx, c.COSI, resource.NewMetadata(config.NamespaceName, config.MachineConfigType, config.V1Alpha1ID, resource.VersionUndefined))
mc, err := safe.StateGetByID[*config.MachineConfig](ctx, c.COSI, config.V1Alpha1ID)
if err != nil {
return fmt.Errorf("error fetching config resource: %w", err)
}

View File

@ -37,6 +37,7 @@ type AffiliateSpec struct {
OperatingSystem string `protobuf:"bytes,5,opt,name=operating_system,json=operatingSystem,proto3" json:"operating_system,omitempty"`
MachineType enums.MachineType `protobuf:"varint,6,opt,name=machine_type,json=machineType,proto3,enum=talos.resource.definitions.enums.MachineType" json:"machine_type,omitempty"`
KubeSpan *KubeSpanAffiliateSpec `protobuf:"bytes,7,opt,name=kube_span,json=kubeSpan,proto3" json:"kube_span,omitempty"`
ControlPlane *ControlPlane `protobuf:"bytes,8,opt,name=control_plane,json=controlPlane,proto3" json:"control_plane,omitempty"`
}
func (x *AffiliateSpec) Reset() {
@ -120,6 +121,13 @@ func (x *AffiliateSpec) GetKubeSpan() *KubeSpanAffiliateSpec {
return nil
}
func (x *AffiliateSpec) GetControlPlane() *ControlPlane {
if x != nil {
return x.ControlPlane
}
return nil
}
// ConfigSpec describes KubeSpan configuration.
type ConfigSpec struct {
state protoimpl.MessageState
@ -216,6 +224,54 @@ func (x *ConfigSpec) GetServiceClusterId() string {
return ""
}
// ControlPlane describes ControlPlane data if any.
type ControlPlane struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ApiServerPort int64 `protobuf:"varint,1,opt,name=api_server_port,json=apiServerPort,proto3" json:"api_server_port,omitempty"`
}
func (x *ControlPlane) Reset() {
*x = ControlPlane{}
if protoimpl.UnsafeEnabled {
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ControlPlane) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ControlPlane) ProtoMessage() {}
func (x *ControlPlane) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ControlPlane.ProtoReflect.Descriptor instead.
func (*ControlPlane) Descriptor() ([]byte, []int) {
return file_resource_definitions_cluster_cluster_proto_rawDescGZIP(), []int{2}
}
func (x *ControlPlane) GetApiServerPort() int64 {
if x != nil {
return x.ApiServerPort
}
return 0
}
// IdentitySpec describes status of rendered secrets.
//
// Note: IdentitySpec is persisted on disk in the STATE partition,
@ -231,7 +287,7 @@ type IdentitySpec struct {
func (x *IdentitySpec) Reset() {
*x = IdentitySpec{}
if protoimpl.UnsafeEnabled {
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[2]
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -244,7 +300,7 @@ func (x *IdentitySpec) String() string {
func (*IdentitySpec) ProtoMessage() {}
func (x *IdentitySpec) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[2]
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -257,7 +313,7 @@ func (x *IdentitySpec) ProtoReflect() protoreflect.Message {
// Deprecated: Use IdentitySpec.ProtoReflect.Descriptor instead.
func (*IdentitySpec) Descriptor() ([]byte, []int) {
return file_resource_definitions_cluster_cluster_proto_rawDescGZIP(), []int{2}
return file_resource_definitions_cluster_cluster_proto_rawDescGZIP(), []int{3}
}
func (x *IdentitySpec) GetNodeId() string {
@ -280,7 +336,7 @@ type InfoSpec struct {
func (x *InfoSpec) Reset() {
*x = InfoSpec{}
if protoimpl.UnsafeEnabled {
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[3]
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -293,7 +349,7 @@ func (x *InfoSpec) String() string {
func (*InfoSpec) ProtoMessage() {}
func (x *InfoSpec) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[3]
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -306,7 +362,7 @@ func (x *InfoSpec) ProtoReflect() protoreflect.Message {
// Deprecated: Use InfoSpec.ProtoReflect.Descriptor instead.
func (*InfoSpec) Descriptor() ([]byte, []int) {
return file_resource_definitions_cluster_cluster_proto_rawDescGZIP(), []int{3}
return file_resource_definitions_cluster_cluster_proto_rawDescGZIP(), []int{4}
}
func (x *InfoSpec) GetClusterId() string {
@ -338,7 +394,7 @@ type KubeSpanAffiliateSpec struct {
func (x *KubeSpanAffiliateSpec) Reset() {
*x = KubeSpanAffiliateSpec{}
if protoimpl.UnsafeEnabled {
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[4]
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -351,7 +407,7 @@ func (x *KubeSpanAffiliateSpec) String() string {
func (*KubeSpanAffiliateSpec) ProtoMessage() {}
func (x *KubeSpanAffiliateSpec) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[4]
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -364,7 +420,7 @@ func (x *KubeSpanAffiliateSpec) ProtoReflect() protoreflect.Message {
// Deprecated: Use KubeSpanAffiliateSpec.ProtoReflect.Descriptor instead.
func (*KubeSpanAffiliateSpec) Descriptor() ([]byte, []int) {
return file_resource_definitions_cluster_cluster_proto_rawDescGZIP(), []int{4}
return file_resource_definitions_cluster_cluster_proto_rawDescGZIP(), []int{5}
}
func (x *KubeSpanAffiliateSpec) GetPublicKey() string {
@ -406,12 +462,13 @@ type MemberSpec struct {
Hostname string `protobuf:"bytes,3,opt,name=hostname,proto3" json:"hostname,omitempty"`
MachineType enums.MachineType `protobuf:"varint,4,opt,name=machine_type,json=machineType,proto3,enum=talos.resource.definitions.enums.MachineType" json:"machine_type,omitempty"`
OperatingSystem string `protobuf:"bytes,5,opt,name=operating_system,json=operatingSystem,proto3" json:"operating_system,omitempty"`
ControlPlane *ControlPlane `protobuf:"bytes,6,opt,name=control_plane,json=controlPlane,proto3" json:"control_plane,omitempty"`
}
func (x *MemberSpec) Reset() {
*x = MemberSpec{}
if protoimpl.UnsafeEnabled {
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[5]
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -424,7 +481,7 @@ func (x *MemberSpec) String() string {
func (*MemberSpec) ProtoMessage() {}
func (x *MemberSpec) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[5]
mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -437,7 +494,7 @@ func (x *MemberSpec) ProtoReflect() protoreflect.Message {
// Deprecated: Use MemberSpec.ProtoReflect.Descriptor instead.
func (*MemberSpec) Descriptor() ([]byte, []int) {
return file_resource_definitions_cluster_cluster_proto_rawDescGZIP(), []int{5}
return file_resource_definitions_cluster_cluster_proto_rawDescGZIP(), []int{6}
}
func (x *MemberSpec) GetNodeId() string {
@ -475,6 +532,13 @@ func (x *MemberSpec) GetOperatingSystem() string {
return ""
}
func (x *MemberSpec) GetControlPlane() *ControlPlane {
if x != nil {
return x.ControlPlane
}
return nil
}
var File_resource_definitions_cluster_cluster_proto protoreflect.FileDescriptor
var file_resource_definitions_cluster_cluster_proto_rawDesc = []byte{
@ -486,7 +550,7 @@ var file_resource_definitions_cluster_cluster_proto_rawDesc = []byte{
0x1a, 0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x26, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f,
0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x65, 0x6e, 0x75, 0x6d,
0x73, 0x2f, 0x65, 0x6e, 0x75, 0x6d, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe2, 0x02,
0x73, 0x2f, 0x65, 0x6e, 0x75, 0x6d, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb9, 0x03,
0x0a, 0x0d, 0x41, 0x66, 0x66, 0x69, 0x6c, 0x69, 0x61, 0x74, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12,
0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72,
@ -509,72 +573,86 @@ var file_resource_definitions_cluster_cluster_proto_rawDesc = []byte{
0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74,
0x65, 0x72, 0x2e, 0x4b, 0x75, 0x62, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x41, 0x66, 0x66, 0x69, 0x6c,
0x69, 0x61, 0x74, 0x65, 0x53, 0x70, 0x65, 0x63, 0x52, 0x08, 0x6b, 0x75, 0x62, 0x65, 0x53, 0x70,
0x61, 0x6e, 0x22, 0xfe, 0x02, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x70, 0x65,
0x63, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x65,
0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x64, 0x69,
0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x3e,
0x0a, 0x1b, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x5f, 0x6b, 0x75, 0x62, 0x65, 0x72,
0x6e, 0x65, 0x74, 0x65, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20,
0x01, 0x28, 0x08, 0x52, 0x19, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x4b, 0x75, 0x62,
0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x38,
0x0a, 0x18, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08,
0x52, 0x16, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f,
0x69, 0x6e, 0x74, 0x12, 0x3a, 0x0a, 0x19, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x65,
0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65,
0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45,
0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12,
0x34, 0x0a, 0x16, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x65, 0x6e, 0x63, 0x72, 0x79,
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x14, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69,
0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28,
0x09, 0x52, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65,
0x72, 0x49, 0x64, 0x22, 0x27, 0x0a, 0x0c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53,
0x61, 0x6e, 0x12, 0x55, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c,
0x61, 0x6e, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x74, 0x61, 0x6c, 0x6f,
0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e,
0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x43,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x52, 0x0c, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x22, 0xfe, 0x02, 0x0a, 0x0a, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x53, 0x70, 0x65, 0x63, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x69, 0x73, 0x63,
0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x08, 0x52, 0x10, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x45, 0x6e,
0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x3e, 0x0a, 0x1b, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72,
0x79, 0x5f, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x5f, 0x65, 0x6e, 0x61,
0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x72, 0x65, 0x67, 0x69,
0x73, 0x74, 0x72, 0x79, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x45, 0x6e,
0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x38, 0x0a, 0x18, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72,
0x79, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72,
0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12,
0x29, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f,
0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x3a, 0x0a, 0x19, 0x73, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x69,
0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x73,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x49, 0x6e,
0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x5f, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79,
0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45,
0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x12,
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f,
0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x36, 0x0a, 0x0c, 0x43, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x70,
0x69, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20,
0x01, 0x28, 0x03, 0x52, 0x0d, 0x61, 0x70, 0x69, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x6f,
0x72, 0x74, 0x22, 0x27, 0x0a, 0x0c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x70,
0x65, 0x63, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x22, 0x4c, 0x0a, 0x08, 0x49,
0x6e, 0x66, 0x6f, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74,
0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75,
0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65,
0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c,
0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xd8, 0x01, 0x0a, 0x15, 0x4b, 0x75,
0x62, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x41, 0x66, 0x66, 0x69, 0x6c, 0x69, 0x61, 0x74, 0x65, 0x53,
0x70, 0x65, 0x63, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65,
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b,
0x65, 0x79, 0x12, 0x27, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4e, 0x65, 0x74,
0x49, 0x50, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x46, 0x0a, 0x14, 0x61,
0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
0x6f, 0x6e, 0x2e, 0x4e, 0x65, 0x74, 0x49, 0x50, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x52, 0x13,
0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73,
0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
0x4e, 0x65, 0x74, 0x49, 0x50, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x70, 0x6f,
0x69, 0x6e, 0x74, 0x73, 0x22, 0xc2, 0x02, 0x0a, 0x0a, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53,
0x70, 0x65, 0x63, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x22, 0x4c, 0x0a, 0x08,
0x49, 0x6e, 0x66, 0x6f, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73,
0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c,
0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74,
0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63,
0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xd8, 0x01, 0x0a, 0x15, 0x4b,
0x75, 0x62, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x41, 0x66, 0x66, 0x69, 0x6c, 0x69, 0x61, 0x74, 0x65,
0x53, 0x70, 0x65, 0x63, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b,
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
0x4b, 0x65, 0x79, 0x12, 0x27, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4e, 0x65,
0x74, 0x49, 0x50, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x46, 0x0a, 0x14,
0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65,
0x73, 0x73, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x6f, 0x6e, 0x2e, 0x4e, 0x65, 0x74, 0x49, 0x50, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x52,
0x13, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x65,
0x73, 0x73, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
0x2e, 0x4e, 0x65, 0x74, 0x49, 0x50, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x70,
0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0xeb, 0x01, 0x0a, 0x0a, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72,
0x53, 0x70, 0x65, 0x63, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x2b, 0x0a,
0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4e, 0x65, 0x74, 0x49, 0x50, 0x52,
0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f,
0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f,
0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x50, 0x0a, 0x0c, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e,
0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x74,
0x61, 0x6c, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, 0x65,
0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x65, 0x6e, 0x75, 0x6d, 0x73, 0x2e,
0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x6d, 0x61, 0x63,
0x68, 0x69, 0x6e, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x72,
0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x05, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x73,
0x74, 0x65, 0x6d, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x73, 0x69, 0x64, 0x65, 0x72, 0x6f, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x6c,
0x6f, 0x73, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x72, 0x79,
0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x64, 0x65,
0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65,
0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x09,
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4e, 0x65, 0x74, 0x49, 0x50, 0x52, 0x09,
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73,
0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73,
0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x50, 0x0a, 0x0c, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x74, 0x61,
0x6c, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, 0x65, 0x66,
0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x65, 0x6e, 0x75, 0x6d, 0x73, 0x2e, 0x4d,
0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x6d, 0x61, 0x63, 0x68,
0x69, 0x6e, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x72, 0x61,
0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74,
0x65, 0x6d, 0x12, 0x55, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c,
0x61, 0x6e, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x74, 0x61, 0x6c, 0x6f,
0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e,
0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x43,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x52, 0x0c, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x69, 0x64, 0x65, 0x72, 0x6f, 0x6c, 0x61,
0x62, 0x73, 0x2f, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6d, 0x61, 0x63,
0x68, 0x69, 0x6e, 0x65, 0x72, 0x79, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x2f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f,
0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -589,33 +667,36 @@ func file_resource_definitions_cluster_cluster_proto_rawDescGZIP() []byte {
return file_resource_definitions_cluster_cluster_proto_rawDescData
}
var file_resource_definitions_cluster_cluster_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_resource_definitions_cluster_cluster_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_resource_definitions_cluster_cluster_proto_goTypes = []interface{}{
(*AffiliateSpec)(nil), // 0: talos.resource.definitions.cluster.AffiliateSpec
(*ConfigSpec)(nil), // 1: talos.resource.definitions.cluster.ConfigSpec
(*IdentitySpec)(nil), // 2: talos.resource.definitions.cluster.IdentitySpec
(*InfoSpec)(nil), // 3: talos.resource.definitions.cluster.InfoSpec
(*KubeSpanAffiliateSpec)(nil), // 4: talos.resource.definitions.cluster.KubeSpanAffiliateSpec
(*MemberSpec)(nil), // 5: talos.resource.definitions.cluster.MemberSpec
(*common.NetIP)(nil), // 6: common.NetIP
(enums.MachineType)(0), // 7: talos.resource.definitions.enums.MachineType
(*common.NetIPPrefix)(nil), // 8: common.NetIPPrefix
(*common.NetIPPort)(nil), // 9: common.NetIPPort
(*ControlPlane)(nil), // 2: talos.resource.definitions.cluster.ControlPlane
(*IdentitySpec)(nil), // 3: talos.resource.definitions.cluster.IdentitySpec
(*InfoSpec)(nil), // 4: talos.resource.definitions.cluster.InfoSpec
(*KubeSpanAffiliateSpec)(nil), // 5: talos.resource.definitions.cluster.KubeSpanAffiliateSpec
(*MemberSpec)(nil), // 6: talos.resource.definitions.cluster.MemberSpec
(*common.NetIP)(nil), // 7: common.NetIP
(enums.MachineType)(0), // 8: talos.resource.definitions.enums.MachineType
(*common.NetIPPrefix)(nil), // 9: common.NetIPPrefix
(*common.NetIPPort)(nil), // 10: common.NetIPPort
}
var file_resource_definitions_cluster_cluster_proto_depIdxs = []int32{
6, // 0: talos.resource.definitions.cluster.AffiliateSpec.addresses:type_name -> common.NetIP
7, // 1: talos.resource.definitions.cluster.AffiliateSpec.machine_type:type_name -> talos.resource.definitions.enums.MachineType
4, // 2: talos.resource.definitions.cluster.AffiliateSpec.kube_span:type_name -> talos.resource.definitions.cluster.KubeSpanAffiliateSpec
6, // 3: talos.resource.definitions.cluster.KubeSpanAffiliateSpec.address:type_name -> common.NetIP
8, // 4: talos.resource.definitions.cluster.KubeSpanAffiliateSpec.additional_addresses:type_name -> common.NetIPPrefix
9, // 5: talos.resource.definitions.cluster.KubeSpanAffiliateSpec.endpoints:type_name -> common.NetIPPort
6, // 6: talos.resource.definitions.cluster.MemberSpec.addresses:type_name -> common.NetIP
7, // 7: talos.resource.definitions.cluster.MemberSpec.machine_type:type_name -> talos.resource.definitions.enums.MachineType
8, // [8:8] is the sub-list for method output_type
8, // [8:8] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
7, // 0: talos.resource.definitions.cluster.AffiliateSpec.addresses:type_name -> common.NetIP
8, // 1: talos.resource.definitions.cluster.AffiliateSpec.machine_type:type_name -> talos.resource.definitions.enums.MachineType
5, // 2: talos.resource.definitions.cluster.AffiliateSpec.kube_span:type_name -> talos.resource.definitions.cluster.KubeSpanAffiliateSpec
2, // 3: talos.resource.definitions.cluster.AffiliateSpec.control_plane:type_name -> talos.resource.definitions.cluster.ControlPlane
7, // 4: talos.resource.definitions.cluster.KubeSpanAffiliateSpec.address:type_name -> common.NetIP
9, // 5: talos.resource.definitions.cluster.KubeSpanAffiliateSpec.additional_addresses:type_name -> common.NetIPPrefix
10, // 6: talos.resource.definitions.cluster.KubeSpanAffiliateSpec.endpoints:type_name -> common.NetIPPort
7, // 7: talos.resource.definitions.cluster.MemberSpec.addresses:type_name -> common.NetIP
8, // 8: talos.resource.definitions.cluster.MemberSpec.machine_type:type_name -> talos.resource.definitions.enums.MachineType
2, // 9: talos.resource.definitions.cluster.MemberSpec.control_plane:type_name -> talos.resource.definitions.cluster.ControlPlane
10, // [10:10] is the sub-list for method output_type
10, // [10:10] is the sub-list for method input_type
10, // [10:10] is the sub-list for extension type_name
10, // [10:10] is the sub-list for extension extendee
0, // [0:10] is the sub-list for field type_name
}
func init() { file_resource_definitions_cluster_cluster_proto_init() }
@ -649,7 +730,7 @@ func file_resource_definitions_cluster_cluster_proto_init() {
}
}
file_resource_definitions_cluster_cluster_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*IdentitySpec); i {
switch v := v.(*ControlPlane); i {
case 0:
return &v.state
case 1:
@ -661,7 +742,7 @@ func file_resource_definitions_cluster_cluster_proto_init() {
}
}
file_resource_definitions_cluster_cluster_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*InfoSpec); i {
switch v := v.(*IdentitySpec); i {
case 0:
return &v.state
case 1:
@ -673,7 +754,7 @@ func file_resource_definitions_cluster_cluster_proto_init() {
}
}
file_resource_definitions_cluster_cluster_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*KubeSpanAffiliateSpec); i {
switch v := v.(*InfoSpec); i {
case 0:
return &v.state
case 1:
@ -685,6 +766,18 @@ func file_resource_definitions_cluster_cluster_proto_init() {
}
}
file_resource_definitions_cluster_cluster_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*KubeSpanAffiliateSpec); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_resource_definitions_cluster_cluster_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*MemberSpec); i {
case 0:
return &v.state
@ -703,7 +796,7 @@ func file_resource_definitions_cluster_cluster_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_resource_definitions_cluster_cluster_proto_rawDesc,
NumEnums: 0,
NumMessages: 6,
NumMessages: 7,
NumExtensions: 0,
NumServices: 0,
},

View File

@ -53,6 +53,16 @@ func (m *AffiliateSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if m.ControlPlane != nil {
size, err := m.ControlPlane.MarshalToSizedBufferVT(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarint(dAtA, i, uint64(size))
i--
dAtA[i] = 0x42
}
if m.KubeSpan != nil {
size, err := m.KubeSpan.MarshalToSizedBufferVT(dAtA[:i])
if err != nil {
@ -217,6 +227,44 @@ func (m *ConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
func (m *ControlPlane) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
}
size := m.SizeVT()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *ControlPlane) MarshalToVT(dAtA []byte) (int, error) {
size := m.SizeVT()
return m.MarshalToSizedBufferVT(dAtA[:size])
}
func (m *ControlPlane) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
if m == nil {
return 0, nil
}
i := len(dAtA)
_ = i
var l int
_ = l
if m.unknownFields != nil {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if m.ApiServerPort != 0 {
i = encodeVarint(dAtA, i, uint64(m.ApiServerPort))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *IdentitySpec) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
@ -444,6 +492,16 @@ func (m *MemberSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if m.ControlPlane != nil {
size, err := m.ControlPlane.MarshalToSizedBufferVT(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarint(dAtA, i, uint64(size))
i--
dAtA[i] = 0x32
}
if len(m.OperatingSystem) > 0 {
i -= len(m.OperatingSystem)
copy(dAtA[i:], m.OperatingSystem)
@ -549,6 +607,10 @@ func (m *AffiliateSpec) SizeVT() (n int) {
l = m.KubeSpan.SizeVT()
n += 1 + l + sov(uint64(l))
}
if m.ControlPlane != nil {
l = m.ControlPlane.SizeVT()
n += 1 + l + sov(uint64(l))
}
n += len(m.unknownFields)
return n
}
@ -587,6 +649,19 @@ func (m *ConfigSpec) SizeVT() (n int) {
return n
}
func (m *ControlPlane) SizeVT() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ApiServerPort != 0 {
n += 1 + sov(uint64(m.ApiServerPort))
}
n += len(m.unknownFields)
return n
}
func (m *IdentitySpec) SizeVT() (n int) {
if m == nil {
return 0
@ -700,6 +775,10 @@ func (m *MemberSpec) SizeVT() (n int) {
if l > 0 {
n += 1 + l + sov(uint64(l))
}
if m.ControlPlane != nil {
l = m.ControlPlane.SizeVT()
n += 1 + l + sov(uint64(l))
}
n += len(m.unknownFields)
return n
}
@ -964,6 +1043,42 @@ func (m *AffiliateSpec) UnmarshalVT(dAtA []byte) error {
return err
}
iNdEx = postIndex
case 8:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ControlPlane", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLength
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.ControlPlane == nil {
m.ControlPlane = &ControlPlane{}
}
if err := m.ControlPlane.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skip(dAtA[iNdEx:])
@ -1215,6 +1330,76 @@ func (m *ConfigSpec) UnmarshalVT(dAtA []byte) error {
}
return nil
}
func (m *ControlPlane) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: ControlPlane: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: ControlPlane: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ApiServerPort", wireType)
}
m.ApiServerPort = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ApiServerPort |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skip(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLength
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *IdentitySpec) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
@ -1810,6 +1995,42 @@ func (m *MemberSpec) UnmarshalVT(dAtA []byte) error {
}
m.OperatingSystem = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 6:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ControlPlane", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLength
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.ControlPlane == nil {
m.ControlPlane = &ControlPlane{}
}
if err := m.ControlPlane.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skip(dAtA[iNdEx:])

File diff suppressed because it is too large Load Diff

View File

@ -168,6 +168,96 @@ func (m *APIServerConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
func (m *APIServerEndpoint) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
}
size := m.SizeVT()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *APIServerEndpoint) MarshalToVT(dAtA []byte) (int, error) {
size := m.SizeVT()
return m.MarshalToSizedBufferVT(dAtA[:size])
}
func (m *APIServerEndpoint) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
if m == nil {
return 0, nil
}
i := len(dAtA)
_ = i
var l int
_ = l
if m.unknownFields != nil {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if m.Port != 0 {
i = encodeVarint(dAtA, i, uint64(m.Port))
i--
dAtA[i] = 0x10
}
if len(m.Host) > 0 {
i -= len(m.Host)
copy(dAtA[i:], m.Host)
i = encodeVarint(dAtA, i, uint64(len(m.Host)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *APIServerEndpointsSpec) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
}
size := m.SizeVT()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *APIServerEndpointsSpec) MarshalToVT(dAtA []byte) (int, error) {
size := m.SizeVT()
return m.MarshalToSizedBufferVT(dAtA[:size])
}
func (m *APIServerEndpointsSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
if m == nil {
return 0, nil
}
i := len(dAtA)
_ = i
var l int
_ = l
if m.unknownFields != nil {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if len(m.Endpoints) > 0 {
for iNdEx := len(m.Endpoints) - 1; iNdEx >= 0; iNdEx-- {
size, err := m.Endpoints[iNdEx].MarshalToSizedBufferVT(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarint(dAtA, i, uint64(size))
i--
dAtA[i] = 0xa
}
}
return len(dAtA) - i, nil
}
func (m *AdmissionControlConfigSpec) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
@ -1909,6 +1999,39 @@ func (m *APIServerConfigSpec) SizeVT() (n int) {
return n
}
func (m *APIServerEndpoint) SizeVT() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Host)
if l > 0 {
n += 1 + l + sov(uint64(l))
}
if m.Port != 0 {
n += 1 + sov(uint64(m.Port))
}
n += len(m.unknownFields)
return n
}
func (m *APIServerEndpointsSpec) SizeVT() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if len(m.Endpoints) > 0 {
for _, e := range m.Endpoints {
l = e.SizeVT()
n += 1 + l + sov(uint64(l))
}
}
n += len(m.unknownFields)
return n
}
func (m *AdmissionControlConfigSpec) SizeVT() (n int) {
if m == nil {
return 0
@ -3144,6 +3267,193 @@ func (m *APIServerConfigSpec) UnmarshalVT(dAtA []byte) error {
}
return nil
}
func (m *APIServerEndpoint) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: APIServerEndpoint: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: APIServerEndpoint: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Host", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Host = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Port", wireType)
}
m.Port = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Port |= uint32(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skip(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLength
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *APIServerEndpointsSpec) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: APIServerEndpointsSpec: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: APIServerEndpointsSpec: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Endpoints", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLength
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Endpoints = append(m.Endpoints, &APIServerEndpoint{})
if err := m.Endpoints[len(m.Endpoints)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skip(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLength
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AdmissionControlConfigSpec) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0

View File

@ -8,7 +8,7 @@ replace gopkg.in/yaml.v3 => github.com/unix4ever/yaml v0.0.0-20220527175918-f17b
require (
github.com/containerd/go-cni v1.1.9
github.com/cosi-project/runtime v0.3.1-alpha.3
github.com/cosi-project/runtime v0.3.1-alpha.4
github.com/dustin/go-humanize v1.0.1
github.com/evanphx/json-patch v5.6.0+incompatible
github.com/ghodss/yaml v1.0.0
@ -27,7 +27,7 @@ require (
github.com/siderolabs/go-pointer v1.0.0
github.com/siderolabs/net v0.4.0
github.com/siderolabs/protoenc v0.2.0
github.com/stretchr/testify v1.8.3
github.com/stretchr/testify v1.8.4
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19
google.golang.org/grpc v1.55.0
google.golang.org/protobuf v1.30.0

View File

@ -21,8 +21,8 @@ github.com/containerd/go-cni v1.1.9 h1:ORi7P1dYzCwVM6XPN4n3CbkuOx/NZ2DOqy+SHRdo9
github.com/containerd/go-cni v1.1.9/go.mod h1:XYrZJ1d5W6E2VOvjffL3IZq0Dz6bsVlERHbekNK90PM=
github.com/containernetworking/cni v1.1.2 h1:wtRGZVv7olUHMOqouPpn3cXJWpJgM6+EUl31EQbXALQ=
github.com/containernetworking/cni v1.1.2/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw=
github.com/cosi-project/runtime v0.3.1-alpha.3 h1:8IDeG7b64OCTMHlngEKWTHTYKCfJ1oMVDxP3y0yuKbU=
github.com/cosi-project/runtime v0.3.1-alpha.3/go.mod h1:g+0MZ3+2MIUkUL7JYTqgYeo5f4j7dAuGem6apjBJ1XU=
github.com/cosi-project/runtime v0.3.1-alpha.4 h1:TXrn1ka+pw2YeywKZjA9b4mNoz4a9HBX9ovR4YtkDGc=
github.com/cosi-project/runtime v0.3.1-alpha.4/go.mod h1:GubXzK7vQFe4VyrWL/eXaMU4T1pXBx9KoDj0GJz6miw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -131,8 +131,8 @@ github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/unix4ever/yaml v0.0.0-20220527175918-f17b0f05cf2c h1:Vn6nVVu9MdOYvXPkJP83iX5jVIfvxFC9v9xIKb+DlaQ=
github.com/unix4ever/yaml v0.0.0-20220527175918-f17b0f05cf2c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=

View File

@ -82,6 +82,14 @@ type AffiliateSpec struct {
OperatingSystem string `yaml:"operatingSystem" protobuf:"5"`
MachineType machine.Type `yaml:"machineType" protobuf:"6"`
KubeSpan KubeSpanAffiliateSpec `yaml:"kubespan,omitempty" protobuf:"7"`
ControlPlane *ControlPlane `yaml:"controlPlane,omitempty" protobuf:"8"`
}
// ControlPlane describes ControlPlane data if any.
//
//gotagsrewrite:gen
type ControlPlane struct {
APIServerPort int `yaml:"port" protobuf:"1"`
}
// Merge two AffiliateSpecs.
@ -104,6 +112,10 @@ func (spec *AffiliateSpec) Merge(other *AffiliateSpec) {
}
}
if other.ControlPlane != nil {
spec.ControlPlane = other.ControlPlane
}
if other.Hostname != "" {
spec.Hostname = other.Hostname
}

View File

@ -8,9 +8,13 @@ import (
"net/netip"
"testing"
"github.com/siderolabs/protoenc"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
cluster2 "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/cluster"
"github.com/siderolabs/talos/pkg/machinery/config/machine"
"github.com/siderolabs/talos/pkg/machinery/proto"
"github.com/siderolabs/talos/pkg/machinery/resources/cluster"
)
@ -25,10 +29,11 @@ func TestAffiliateSpec_Merge(t *testing.T) {
{
name: "merge kubespan",
a: cluster.AffiliateSpec{
Hostname: "foo.com",
Nodename: "bar",
MachineType: machine.TypeControlPlane,
Addresses: []netip.Addr{netip.MustParseAddr("10.0.0.2")},
Hostname: "foo.com",
Nodename: "bar",
MachineType: machine.TypeControlPlane,
Addresses: []netip.Addr{netip.MustParseAddr("10.0.0.2")},
ControlPlane: &cluster.ControlPlane{APIServerPort: 6443},
},
b: cluster.AffiliateSpec{
Hostname: "foo.com",
@ -53,6 +58,7 @@ func TestAffiliateSpec_Merge(t *testing.T) {
AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")},
Endpoints: []netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")},
},
ControlPlane: &cluster.ControlPlane{APIServerPort: 6443},
},
},
{
@ -76,6 +82,7 @@ func TestAffiliateSpec_Merge(t *testing.T) {
AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")},
Endpoints: []netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")},
},
ControlPlane: &cluster.ControlPlane{APIServerPort: 6443},
},
expected: cluster.AffiliateSpec{
Hostname: "foo.com",
@ -88,6 +95,7 @@ func TestAffiliateSpec_Merge(t *testing.T) {
AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")},
Endpoints: []netip.AddrPort{netip.MustParseAddrPort("192.168.3.4:51820"), netip.MustParseAddrPort("10.0.0.2:51820")},
},
ControlPlane: &cluster.ControlPlane{APIServerPort: 6443},
},
},
} {
@ -101,3 +109,38 @@ func TestAffiliateSpec_Merge(t *testing.T) {
})
}
}
func TestAffiliateSpecMarshal(t *testing.T) {
original := &cluster.AffiliateSpec{
NodeID: "myNodeID",
Hostname: "foo.com",
MachineType: machine.TypeControlPlane,
ControlPlane: &cluster.ControlPlane{APIServerPort: 6443},
}
wire, err := protoenc.Marshal(original)
require.NoError(t, err)
var unmarshaled cluster2.AffiliateSpec
err = proto.Unmarshal(wire, &unmarshaled)
require.NoError(t, err)
require.EqualValues(t, original.NodeID, unmarshaled.NodeId)
require.EqualValues(t, original.Hostname, unmarshaled.Hostname)
require.EqualValues(t, original.MachineType, unmarshaled.MachineType)
require.EqualValues(t, original.ControlPlane.APIServerPort, unmarshaled.ControlPlane.ApiServerPort)
unmarshaled.ControlPlane = nil
wire, err = proto.Marshal(&unmarshaled)
require.NoError(t, err)
spec := &cluster.AffiliateSpec{}
err = protoenc.Unmarshal(wire, spec)
require.NoError(t, err)
original.ControlPlane = nil
require.Equal(t, original, spec)
}

View File

@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/siderolabs/talos/pkg/machinery/resources/cluster"
"github.com/siderolabs/talos/pkg/machinery/resources/k8s"
)
func TestRegisterResource(t *testing.T) {
@ -27,6 +28,7 @@ func TestRegisterResource(t *testing.T) {
for _, resource := range []resource.Resource{
&cluster.Affiliate{},
&cluster.Config{},
&k8s.APIServerEndpoints{},
&cluster.Identity{},
&cluster.Member{},
} {

View File

@ -25,6 +25,10 @@ func (o AffiliateSpec) DeepCopy() AffiliateSpec {
cp.KubeSpan.Endpoints = make([]netip.AddrPort, len(o.KubeSpan.Endpoints))
copy(cp.KubeSpan.Endpoints, o.KubeSpan.Endpoints)
}
if o.ControlPlane != nil {
cp.ControlPlane = new(ControlPlane)
*cp.ControlPlane = *o.ControlPlane
}
return cp
}
@ -51,6 +55,10 @@ func (o MemberSpec) DeepCopy() MemberSpec {
cp.Addresses = make([]netip.Addr, len(o.Addresses))
copy(cp.Addresses, o.Addresses)
}
if o.ControlPlane != nil {
cp.ControlPlane = new(ControlPlane)
*cp.ControlPlane = *o.ControlPlane
}
return cp
}

View File

@ -28,11 +28,12 @@ type Member = typed.Resource[MemberSpec, MemberExtension]
//
//gotagsrewrite:gen
type MemberSpec struct {
NodeID string `yaml:"nodeId" protobuf:"1"`
Addresses []netip.Addr `yaml:"addresses" protobuf:"2"`
Hostname string `yaml:"hostname" protobuf:"3"`
MachineType machine.Type `yaml:"machineType" protobuf:"4"`
OperatingSystem string `yaml:"operatingSystem" protobuf:"5"`
NodeID string `yaml:"nodeId" protobuf:"1"`
Addresses []netip.Addr `yaml:"addresses" protobuf:"2"`
Hostname string `yaml:"hostname" protobuf:"3"`
MachineType machine.Type `yaml:"machineType" protobuf:"4"`
OperatingSystem string `yaml:"operatingSystem" protobuf:"5"`
ControlPlane *ControlPlane `yaml:"controlPlane,omitempty" protobuf:"6"`
}
// NewMember initializes a Member resource.

View File

@ -0,0 +1,84 @@
// 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 k8s
import (
"fmt"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/resource/meta"
"github.com/cosi-project/runtime/pkg/resource/protobuf"
"github.com/cosi-project/runtime/pkg/resource/typed"
"github.com/siderolabs/talos/pkg/machinery/proto"
)
// APIServerEndpointsType is type of APIServerEndpoints resource.
const APIServerEndpointsType = resource.Type("APIServerEndpoints.cluster.talos.dev")
// APIServerEndpointsID the singleton balancer data resource ID.
const APIServerEndpointsID = resource.ID("k8s-cluster")
// APIServerEndpoints resource holds endpoints data.
type APIServerEndpoints = typed.Resource[APIServerEndpointsSpec, APIServerEndpointsExtension]
// NewEndpoints initializes an APIServerEndpoints resource.
func NewEndpoints(namespace resource.Namespace, id resource.ID) *APIServerEndpoints {
return typed.NewResource[APIServerEndpointsSpec, APIServerEndpointsExtension](
resource.NewMetadata(namespace, APIServerEndpointsType, id, resource.VersionUndefined),
APIServerEndpointsSpec{},
)
}
// APIServerEndpointsSpec describes APIServerEndpoints configuration.
//
//gotagsrewrite:gen
type APIServerEndpointsSpec struct {
Endpoints []APIServerEndpoint `yaml:"endpoints" protobuf:"1"`
}
// APIServerEndpoint holds data for control plane endpoint.
//
//gotagsrewrite:gen
type APIServerEndpoint struct {
Host string `yaml:"host" protobuf:"1"`
Port uint32 `yaml:"port" protobuf:"2"`
}
// String returns string representation of APIServerEndpoint.
func (e APIServerEndpoint) String() string {
return fmt.Sprintf("host: %s, port: %d", e.Host, e.Port)
}
// APIServerEndpointsExtension provides auxiliary methods for APIServerEndpoints.
type APIServerEndpointsExtension struct{}
// ResourceDefinition implements [typed.Extension] interface.
func (APIServerEndpointsExtension) ResourceDefinition() meta.ResourceDefinitionSpec {
return meta.ResourceDefinitionSpec{
Type: APIServerEndpointsType,
Aliases: []resource.Type{},
DefaultNamespace: NamespaceName,
PrintColumns: []meta.PrintColumn{
{
Name: "Hosts",
JSONPath: ".endpoints[*].host",
},
{
Name: "Ports",
JSONPath: ".endpoints[*].port",
},
},
}
}
func init() {
proto.RegisterDefaultTypes()
err := protobuf.RegisterDynamic[APIServerEndpointsSpec](APIServerEndpointsType, &APIServerEndpoints{})
if err != nil {
panic(err)
}
}

View File

@ -2,7 +2,7 @@
// 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/.
// Code generated by "deep-copy -type AdmissionControlConfigSpec -type APIServerConfigSpec -type AuditPolicyConfigSpec -type BootstrapManifestsConfigSpec -type ConfigStatusSpec -type ControllerManagerConfigSpec -type EndpointSpec -type ExtraManifestsConfigSpec -type KubeletLifecycleSpec -type KubeletSpecSpec -type ManifestSpec -type ManifestStatusSpec -type NodeLabelSpecSpec -type KubeletConfigSpec -type NodeIPSpec -type NodeIPConfigSpec -type NodenameSpec -type SchedulerConfigSpec -type SecretsStatusSpec -type StaticPodSpec -type StaticPodStatusSpec -type StaticPodServerStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT.
// Code generated by "deep-copy -type AdmissionControlConfigSpec -type APIServerEndpointsSpec -type APIServerConfigSpec -type AuditPolicyConfigSpec -type BootstrapManifestsConfigSpec -type ConfigStatusSpec -type ControllerManagerConfigSpec -type EndpointSpec -type ExtraManifestsConfigSpec -type KubeletLifecycleSpec -type KubeletSpecSpec -type ManifestSpec -type ManifestStatusSpec -type NodeLabelSpecSpec -type KubeletConfigSpec -type NodeIPSpec -type NodeIPConfigSpec -type NodenameSpec -type SchedulerConfigSpec -type SecretsStatusSpec -type StaticPodSpec -type StaticPodStatusSpec -type StaticPodServerStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT.
package k8s
@ -30,6 +30,16 @@ func (o AdmissionControlConfigSpec) DeepCopy() AdmissionControlConfigSpec {
return cp
}
// DeepCopy generates a deep copy of APIServerEndpointsSpec.
func (o APIServerEndpointsSpec) DeepCopy() APIServerEndpointsSpec {
var cp APIServerEndpointsSpec = o
if o.Endpoints != nil {
cp.Endpoints = make([]APIServerEndpoint, len(o.Endpoints))
copy(cp.Endpoints, o.Endpoints)
}
return cp
}
// DeepCopy generates a deep copy of APIServerConfigSpec.
func (o APIServerConfigSpec) DeepCopy() APIServerConfigSpec {
var cp APIServerConfigSpec = o
@ -310,7 +320,7 @@ func (o SecretsStatusSpec) DeepCopy() SecretsStatusSpec {
func (o StaticPodSpec) DeepCopy() StaticPodSpec {
var cp StaticPodSpec = o
if o.Pod != nil {
cp.Pod = make(map[string]interface{}, len(o.Pod))
cp.Pod = make(map[string]any, len(o.Pod))
for k2, v2 := range o.Pod {
cp.Pod[k2] = v2
}
@ -322,7 +332,7 @@ func (o StaticPodSpec) DeepCopy() StaticPodSpec {
func (o StaticPodStatusSpec) DeepCopy() StaticPodStatusSpec {
var cp StaticPodStatusSpec = o
if o.PodStatus != nil {
cp.PodStatus = make(map[string]interface{}, len(o.PodStatus))
cp.PodStatus = make(map[string]any, len(o.PodStatus))
for k2, v2 := range o.PodStatus {
cp.PodStatus[k2] = v2
}

View File

@ -8,7 +8,7 @@ package k8s
import "github.com/cosi-project/runtime/pkg/resource"
//nolint:lll
//go:generate deep-copy -type AdmissionControlConfigSpec -type APIServerConfigSpec -type AuditPolicyConfigSpec -type BootstrapManifestsConfigSpec -type ConfigStatusSpec -type ControllerManagerConfigSpec -type EndpointSpec -type ExtraManifestsConfigSpec -type KubeletLifecycleSpec -type KubeletSpecSpec -type ManifestSpec -type ManifestStatusSpec -type NodeLabelSpecSpec -type KubeletConfigSpec -type NodeIPSpec -type NodeIPConfigSpec -type NodenameSpec -type SchedulerConfigSpec -type SecretsStatusSpec -type StaticPodSpec -type StaticPodStatusSpec -type StaticPodServerStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go .
//go:generate deep-copy -type AdmissionControlConfigSpec -type APIServerEndpointsSpec -type APIServerConfigSpec -type AuditPolicyConfigSpec -type BootstrapManifestsConfigSpec -type ConfigStatusSpec -type ControllerManagerConfigSpec -type EndpointSpec -type ExtraManifestsConfigSpec -type KubeletLifecycleSpec -type KubeletSpecSpec -type ManifestSpec -type ManifestStatusSpec -type NodeLabelSpecSpec -type KubeletConfigSpec -type NodeIPSpec -type NodeIPConfigSpec -type NodenameSpec -type SchedulerConfigSpec -type SecretsStatusSpec -type StaticPodSpec -type StaticPodStatusSpec -type StaticPodServerStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go .
// NamespaceName contains resources supporting Kubernetes components on all node types.
const NamespaceName resource.Namespace = "k8s"

View File

@ -23,7 +23,7 @@ type StaticPod = typed.Resource[StaticPodSpec, StaticPodExtension]
//
//gotagsrewrite:gen
type StaticPodSpec struct {
Pod map[string]interface{} `protobuf:"1"`
Pod map[string]any `protobuf:"1"`
}
// MarshalYAML implements yaml.Marshaler.

View File

@ -23,7 +23,7 @@ type StaticPodStatus = typed.Resource[StaticPodStatusSpec, StaticPodStatusExtens
//
//gotagsrewrite:gen
type StaticPodStatusSpec struct {
PodStatus map[string]interface{} `protobuf:"1"`
PodStatus map[string]any `protobuf:"1"`
}
// MarshalYAML implements yaml.Marshaler.

View File

@ -27,6 +27,7 @@ description: Talos gRPC API reference.
- [resource/definitions/cluster/cluster.proto](#resource/definitions/cluster/cluster.proto)
- [AffiliateSpec](#talos.resource.definitions.cluster.AffiliateSpec)
- [ConfigSpec](#talos.resource.definitions.cluster.ConfigSpec)
- [ControlPlane](#talos.resource.definitions.cluster.ControlPlane)
- [IdentitySpec](#talos.resource.definitions.cluster.IdentitySpec)
- [InfoSpec](#talos.resource.definitions.cluster.InfoSpec)
- [KubeSpanAffiliateSpec](#talos.resource.definitions.cluster.KubeSpanAffiliateSpec)
@ -89,6 +90,8 @@ description: Talos gRPC API reference.
- [APIServerConfigSpec](#talos.resource.definitions.k8s.APIServerConfigSpec)
- [APIServerConfigSpec.EnvironmentVariablesEntry](#talos.resource.definitions.k8s.APIServerConfigSpec.EnvironmentVariablesEntry)
- [APIServerConfigSpec.ExtraArgsEntry](#talos.resource.definitions.k8s.APIServerConfigSpec.ExtraArgsEntry)
- [APIServerEndpoint](#talos.resource.definitions.k8s.APIServerEndpoint)
- [APIServerEndpointsSpec](#talos.resource.definitions.k8s.APIServerEndpointsSpec)
- [AdmissionControlConfigSpec](#talos.resource.definitions.k8s.AdmissionControlConfigSpec)
- [AdmissionPluginSpec](#talos.resource.definitions.k8s.AdmissionPluginSpec)
- [AuditPolicyConfigSpec](#talos.resource.definitions.k8s.AuditPolicyConfigSpec)
@ -686,6 +689,7 @@ AffiliateSpec describes Affiliate state.
| operating_system | [string](#string) | | |
| machine_type | [talos.resource.definitions.enums.MachineType](#talos.resource.definitions.enums.MachineType) | | |
| kube_span | [KubeSpanAffiliateSpec](#talos.resource.definitions.cluster.KubeSpanAffiliateSpec) | | |
| control_plane | [ControlPlane](#talos.resource.definitions.cluster.ControlPlane) | | |
@ -713,6 +717,21 @@ ConfigSpec describes KubeSpan configuration.
<a name="talos.resource.definitions.cluster.ControlPlane"></a>
### ControlPlane
ControlPlane describes ControlPlane data if any.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| api_server_port | [int64](#int64) | | |
<a name="talos.resource.definitions.cluster.IdentitySpec"></a>
### IdentitySpec
@ -778,6 +797,7 @@ MemberSpec describes Member state.
| hostname | [string](#string) | | |
| machine_type | [talos.resource.definitions.enums.MachineType](#talos.resource.definitions.enums.MachineType) | | |
| operating_system | [string](#string) | | |
| control_plane | [ControlPlane](#talos.resource.definitions.cluster.ControlPlane) | | |
@ -1706,6 +1726,37 @@ APIServerConfigSpec is configuration for kube-apiserver.
<a name="talos.resource.definitions.k8s.APIServerEndpoint"></a>
### APIServerEndpoint
APIServerEndpoint holds data for control plane endpoint.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| host | [string](#string) | | |
| port | [uint32](#uint32) | | |
<a name="talos.resource.definitions.k8s.APIServerEndpointsSpec"></a>
### APIServerEndpointsSpec
APIServerEndpointsSpec describes APIServerEndpoints configuration.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| endpoints | [APIServerEndpoint](#talos.resource.definitions.k8s.APIServerEndpoint) | repeated | |
<a name="talos.resource.definitions.k8s.AdmissionControlConfigSpec"></a>
### AdmissionControlConfigSpec