2020-08-19 23:18:19 +03:00
// 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 cluster
import (
"context"
2021-06-29 22:01:01 +03:00
"errors"
2020-08-19 23:18:19 +03:00
"fmt"
"io"
"sort"
2020-10-28 17:09:40 +03:00
"strings"
2020-08-19 23:18:19 +03:00
"time"
2022-11-01 12:06:37 +04:00
"github.com/siderolabs/go-retry/retry"
2020-10-28 17:09:40 +03:00
"google.golang.org/grpc/backoff"
2021-06-22 19:23:41 +03:00
"google.golang.org/grpc/codes"
2020-09-02 20:14:25 +03:00
2022-11-02 15:06:45 +04:00
machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine"
"github.com/siderolabs/talos/pkg/machinery/client"
2023-05-25 17:14:01 +04:00
"github.com/siderolabs/talos/pkg/machinery/config/machine"
2020-08-19 23:18:19 +03:00
)
2021-01-19 15:45:50 +03:00
// APIBootstrapper bootstraps cluster via Talos API.
type APIBootstrapper struct {
2020-08-19 23:18:19 +03:00
ClientProvider
Info
}
// Bootstrap the cluster via the API.
//
// Bootstrap implements Bootstrapper interface.
2022-08-02 22:43:31 +04:00
//
2022-03-14 20:14:11 +03:00
//nolint:gocyclo
2021-01-19 15:45:50 +03:00
func ( s * APIBootstrapper ) Bootstrap ( ctx context . Context , out io . Writer ) error {
2020-08-19 23:18:19 +03:00
cli , err := s . Client ( )
if err != nil {
return err
}
controlPlaneNodes := s . NodesByType ( machine . TypeControlPlane )
2022-06-08 13:22:55 +02:00
2020-08-19 23:18:19 +03:00
if len ( controlPlaneNodes ) == 0 {
2024-02-13 15:32:06 +03:00
return errors . New ( "no control plane nodes to bootstrap" )
2020-08-19 23:18:19 +03:00
}
2022-06-08 13:22:55 +02:00
sort . Slice ( controlPlaneNodes , func ( i , j int ) bool {
return controlPlaneNodes [ i ] . IPs [ 0 ] . String ( ) < controlPlaneNodes [ j ] . IPs [ 0 ] . String ( )
} )
2020-08-19 23:18:19 +03:00
2022-06-08 13:22:55 +02:00
nodeIP := controlPlaneNodes [ 0 ] . IPs [ 0 ]
nodeCtx := client . WithNodes ( ctx , nodeIP . String ( ) )
2020-08-19 23:18:19 +03:00
fmt . Fprintln ( out , "waiting for API" )
2023-06-01 07:22:44 -04:00
err = retry . Constant ( 10 * time . Minute , retry . WithUnits ( 500 * time . Millisecond ) ) . RetryWithContext ( nodeCtx , func ( nodeCtx context . Context ) error {
retryCtx , cancel := context . WithTimeout ( nodeCtx , 2 * time . Second )
2020-08-19 23:18:19 +03:00
defer cancel ( )
if _ , err = cli . Version ( retryCtx ) ; err != nil {
return retry . ExpectedError ( err )
}
return nil
} )
if err != nil {
return err
}
fmt . Fprintln ( out , "bootstrapping cluster" )
2021-07-07 20:42:19 +03:00
return retry . Constant ( backoff . DefaultConfig . MaxDelay , retry . WithUnits ( 100 * time . Millisecond ) ) . RetryWithContext ( nodeCtx , func ( nodeCtx context . Context ) error {
2023-06-01 07:22:44 -04:00
retryCtx , cancel := context . WithTimeout ( nodeCtx , 2 * time . Second )
2020-10-28 17:09:40 +03:00
defer cancel ( )
2020-08-19 23:18:19 +03:00
2021-04-06 21:36:30 +03:00
if err = cli . Bootstrap ( retryCtx , & machineapi . BootstrapRequest { } ) ; err != nil {
2021-06-29 22:01:01 +03:00
switch {
2021-07-07 20:42:19 +03:00
// deadline exceeded in case it's verbatim context error
2021-06-29 22:01:01 +03:00
case errors . Is ( err , context . DeadlineExceeded ) :
return retry . ExpectedError ( err )
2021-07-07 20:42:19 +03:00
// FailedPrecondition when time is not in sync yet on the server
// DeadlineExceeded when the call fails in the gRPC stack either on the server or client side
case client . StatusCode ( err ) == codes . FailedPrecondition || client . StatusCode ( err ) == codes . DeadlineExceeded :
2021-06-29 22:01:01 +03:00
return retry . ExpectedError ( err )
2021-07-07 20:42:19 +03:00
// connection refused, including proxied connection refused via the endpoint to the node
2021-06-29 22:01:01 +03:00
case strings . Contains ( err . Error ( ) , "connection refused" ) :
2020-10-28 17:09:40 +03:00
return retry . ExpectedError ( err )
2022-03-14 20:14:11 +03:00
// connection timeout
case strings . Contains ( err . Error ( ) , "error reading from server: EOF" ) :
return retry . ExpectedError ( err )
2020-10-28 17:09:40 +03:00
}
2021-05-24 16:34:38 +03:00
return err
2020-10-28 17:09:40 +03:00
}
return nil
} )
2020-08-19 23:18:19 +03:00
}