talos/internal/pkg/tui/installer/connection.go
Andrey Smirnov 9baca49662
refactor: implement COSI resource API for Talos
Overview: deprecate existing Talos resource API, and introduce new COSI
API.

Consequences:

* COSI API can only go via one-2-one proxy (`client.WithNode`)
* client-side API access is way easier with `state.State` wrappers
* lots of small changes on the client side to use new APIs

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
2022-08-12 22:31:54 +04:00

164 lines
4.3 KiB
Go

// 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 installer
import (
"context"
"net"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"github.com/talos-systems/talos/pkg/machinery/api/machine"
"github.com/talos-systems/talos/pkg/machinery/api/storage"
"github.com/talos-systems/talos/pkg/machinery/client"
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
"github.com/talos-systems/talos/pkg/machinery/resources/network"
)
// Connection unifies clients for bootstrap node and the node which is being configured.
type Connection struct {
nodeEndpoint string
bootstrapEndpoint string
nodeClient *client.Client
bootstrapClient *client.Client
nodeCtx context.Context //nolint:containedctx
bootstrapCtx context.Context //nolint:containedctx
dryRun bool
}
// NewConnection creates new installer connection.
func NewConnection(ctx context.Context, nodeClient *client.Client, endpoint string, options ...Option) (
*Connection,
error,
) {
c := &Connection{
nodeEndpoint: endpoint,
nodeClient: nodeClient,
nodeCtx: ctx,
}
for _, opt := range options {
err := opt(c)
if err != nil {
return nil, err
}
}
return c, nil
}
// GenerateConfiguration calls GenerateConfiguration on the target/bootstrap node.
func (c *Connection) GenerateConfiguration(
req *machine.GenerateConfigurationRequest,
callOptions ...grpc.CallOption,
) (*machine.GenerateConfigurationResponse, error) {
if c.bootstrapClient != nil {
return c.bootstrapClient.GenerateConfiguration(c.bootstrapCtx, req, callOptions...)
}
return c.nodeClient.GenerateConfiguration(c.nodeCtx, req, callOptions...)
}
// ApplyConfiguration calls ApplyConfiguration on the target node using appropriate node context.
func (c *Connection) ApplyConfiguration(
req *machine.ApplyConfigurationRequest,
callOptions ...grpc.CallOption,
) (*machine.ApplyConfigurationResponse, error) {
return c.nodeClient.ApplyConfiguration(c.nodeCtx, req, callOptions...)
}
// Disks get disks list from the target node.
func (c *Connection) Disks(callOptions ...grpc.CallOption) (*storage.DisksResponse, error) {
return c.nodeClient.Disks(c.nodeCtx, callOptions...)
}
// Link a subset of fields from LinkStatus resource.
type Link struct {
Name string
Physical bool
Up bool
HardwareAddr net.HardwareAddr
MTU int
}
// Links gets a list of network interfaces.
//
//nolint:gocyclo
func (c *Connection) Links() ([]Link, error) {
ctx := c.nodeCtx
md, _ := metadata.FromOutgoingContext(c.nodeCtx)
if nodes := md["nodes"]; len(nodes) > 0 {
ctx = client.WithNode(ctx, nodes[0])
}
items, err := safe.StateList[*network.LinkStatus](
ctx,
c.nodeClient.COSI,
resource.NewMetadata(network.NamespaceName, network.LinkStatusType, "", resource.VersionUndefined),
)
if err != nil {
return nil, err
}
it := safe.IteratorFromList(items)
var links []Link
for it.Next() {
var link Link
link.Name = it.Value().Metadata().ID()
link.Physical = it.Value().TypedSpec().Physical()
link.MTU = int(it.Value().TypedSpec().MTU)
switch it.Value().TypedSpec().OperationalState { //nolint:exhaustive
case nethelpers.OperStateUnknown:
link.Up = true
case nethelpers.OperStateUp:
link.Up = true
default:
link.Up = false
}
link.HardwareAddr = net.HardwareAddr(it.Value().TypedSpec().HardwareAddr)
links = append(links, link)
}
return links, nil
}
// ExpandingCluster check if bootstrap node is set.
func (c *Connection) ExpandingCluster() bool {
return c.bootstrapClient != nil
}
// Option represents a single connection option.
type Option func(c *Connection) error
// WithBootstrapNode configures bootstrap node endpoint.
func WithBootstrapNode(ctx context.Context, bootstrapClient *client.Client, bootstrapNode string) Option {
return func(c *Connection) error {
c.bootstrapEndpoint = bootstrapNode
c.bootstrapClient = bootstrapClient
c.bootstrapCtx = ctx
return nil
}
}
// WithDryRun enables dry run mode in the installer.
func WithDryRun(dryRun bool) Option {
return func(c *Connection) error {
c.dryRun = dryRun
return nil
}
}