Skip to content

Commit

Permalink
Log a message if the cluster is unitialized (#1015)
Browse files Browse the repository at this point in the history
* Log a message if the cluster is unitialized

Users coming from microk8s may not be used to having to bootstrap
the cluster.

We'll check k8sd errors and if the message contains "Database is
not yet initialized", we'll ask the users to either bootstrap
a new cluster or join an existing one.

We're adding this check to the query function of the k8sd client
so that all the k8s commands may benefit from it.

Implements: KU-2481

* Clean up the log messages

* Copy the "bootstrapped" checked to each individual command

To improve the error messages and avoid having too many nested
errors, we'll have each individual command check if the cluster
was initialized.

That being considered, we'll make the k8sd client error less verbose.

* Fix unit tests, updating k8sd mock
  • Loading branch information
petrutlucian94 authored Jan 29, 2025
1 parent 39292f9 commit 1c6739a
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 2 deletions.
11 changes: 11 additions & 0 deletions src/k8s/cmd/k8s/k8s_disable.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,17 @@ func newDisableCmd(env cmdutil.ExecutionEnvironment) *cobra.Command {
cmd.PrintErrf("Disabling %s from the cluster. This may take a few seconds, please wait.\n", strings.Join(args, ", "))
ctx, cancel := context.WithTimeout(cmd.Context(), opts.timeout)
cobra.OnFinalize(cancel)

if _, initialized, err := client.NodeStatus(cmd.Context()); err != nil {
cmd.PrintErrf("Error: Failed to check the current node status.\n\nThe error was: %v\n", err)
env.Exit(1)
return
} else if !initialized {
cmd.PrintErrln("Error: The node is not part of a Kubernetes cluster. You can bootstrap a new cluster with:\n\n sudo k8s bootstrap")
env.Exit(1)
return
}

if err := client.SetClusterConfig(ctx, apiv1.SetClusterConfigRequest{Config: config}); err != nil {
cmd.PrintErrf("Error: Failed to disable %s from the cluster.\n\nThe error was: %v\n", strings.Join(args, ", "), err)
env.Exit(1)
Expand Down
4 changes: 3 additions & 1 deletion src/k8s/cmd/k8s/k8s_disable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ func TestDisableCmd(t *testing.T) {

stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
mockClient := &k8sdmock.Mock{}
mockClient := &k8sdmock.Mock{
NodeStatusInitialized: true,
}
var returnCode int
env := cmdutil.ExecutionEnvironment{
Stdout: stdout,
Expand Down
11 changes: 11 additions & 0 deletions src/k8s/cmd/k8s/k8s_enable.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,17 @@ func newEnableCmd(env cmdutil.ExecutionEnvironment) *cobra.Command {
cmd.PrintErrf("Enabling %s on the cluster. This may take a few seconds, please wait.\n", strings.Join(args, ", "))
ctx, cancel := context.WithTimeout(cmd.Context(), opts.timeout)
cobra.OnFinalize(cancel)

if _, initialized, err := client.NodeStatus(cmd.Context()); err != nil {
cmd.PrintErrf("Error: Failed to check the current node status.\n\nThe error was: %v\n", err)
env.Exit(1)
return
} else if !initialized {
cmd.PrintErrln("Error: The node is not part of a Kubernetes cluster. You can bootstrap a new cluster with:\n\n sudo k8s bootstrap")
env.Exit(1)
return
}

if err := client.SetClusterConfig(ctx, apiv1.SetClusterConfigRequest{Config: config}); err != nil {
cmd.PrintErrf("Error: Failed to enable %s on the cluster.\n\nThe error was: %v\n", strings.Join(args, ", "), err)
env.Exit(1)
Expand Down
4 changes: 3 additions & 1 deletion src/k8s/cmd/k8s/k8s_enable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ func TestK8sEnableCmd(t *testing.T) {

stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
mockClient := &k8sdmock.Mock{}
mockClient := &k8sdmock.Mock{
NodeStatusInitialized: true,
}
var returnCode int
env := cmdutil.ExecutionEnvironment{
Stdout: stdout,
Expand Down
10 changes: 10 additions & 0 deletions src/k8s/cmd/k8s/k8s_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ func newGetCmd(env cmdutil.ExecutionEnvironment) *cobra.Command {
ctx, cancel := context.WithTimeout(cmd.Context(), opts.timeout)
cobra.OnFinalize(cancel)

if _, initialized, err := client.NodeStatus(cmd.Context()); err != nil {
cmd.PrintErrf("Error: Failed to check the current node status.\n\nThe error was: %v\n", err)
env.Exit(1)
return
} else if !initialized {
cmd.PrintErrln("Error: The node is not part of a Kubernetes cluster. You can bootstrap a new cluster with:\n\n sudo k8s bootstrap")
env.Exit(1)
return
}

response, err := client.GetClusterConfig(ctx)
if err != nil {
cmd.PrintErrf("Error: Failed to get the current cluster configuration.\n\nThe error was: %v\n", err)
Expand Down
11 changes: 11 additions & 0 deletions src/k8s/cmd/k8s/k8s_get_join_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@ func newGetJoinTokenCmd(env cmdutil.ExecutionEnvironment) *cobra.Command {

ctx, cancel := context.WithTimeout(cmd.Context(), opts.timeout)
cobra.OnFinalize(cancel)

if _, initialized, err := client.NodeStatus(cmd.Context()); err != nil {
cmd.PrintErrf("Error: Failed to check the current node status.\n\nThe error was: %v\n", err)
env.Exit(1)
return
} else if !initialized {
cmd.PrintErrln("Error: The node is not part of a Kubernetes cluster. You can bootstrap a new cluster with:\n\n sudo k8s bootstrap")
env.Exit(1)
return
}

token, err := client.GetJoinToken(ctx, apiv1.GetJoinTokenRequest{Name: name, Worker: opts.worker, TTL: opts.ttl})
if err != nil {
cmd.PrintErrf("Error: Could not generate a join token for %q.\n\nThe error was: %v\n", name, err)
Expand Down
10 changes: 10 additions & 0 deletions src/k8s/cmd/k8s/k8s_refresh_certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ func newRefreshCertsCmd(env cmdutil.ExecutionEnvironment) *cobra.Command {
ctx, cancel := context.WithTimeout(cmd.Context(), opts.timeout)
cobra.OnFinalize(cancel)

if _, initialized, err := client.NodeStatus(cmd.Context()); err != nil {
cmd.PrintErrf("Error: Failed to check the current node status.\n\nThe error was: %v\n", err)
env.Exit(1)
return
} else if !initialized {
cmd.PrintErrln("Error: The node is not part of a Kubernetes cluster. You can bootstrap a new cluster with:\n\n sudo k8s bootstrap")
env.Exit(1)
return
}

plan, err := client.RefreshCertificatesPlan(ctx, apiv1.RefreshCertificatesPlanRequest{})
if err != nil {
cmd.PrintErrf("Error: Failed to get the certificates refresh plan.\n\nThe error was: %v\n", err)
Expand Down
10 changes: 10 additions & 0 deletions src/k8s/cmd/k8s/k8s_remove_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ func newRemoveNodeCmd(env cmdutil.ExecutionEnvironment) *cobra.Command {
return
}

if _, initialized, err := client.NodeStatus(cmd.Context()); err != nil {
cmd.PrintErrf("Error: Failed to check the current node status.\n\nThe error was: %v\n", err)
env.Exit(1)
return
} else if !initialized {
cmd.PrintErrln("Error: The node is not part of a Kubernetes cluster. You can bootstrap a new cluster with:\n\n sudo k8s bootstrap")
env.Exit(1)
return
}

name := args[0]

cmd.PrintErrf("Removing %q from the Kubernetes cluster. This may take a few seconds, please wait.\n", name)
Expand Down
10 changes: 10 additions & 0 deletions src/k8s/cmd/k8s/k8s_x_capi.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ func newXCAPICmd(env cmdutil.ExecutionEnvironment) *cobra.Command {
return
}

if _, initialized, err := client.NodeStatus(cmd.Context()); err != nil {
cmd.PrintErrf("Error: Failed to check the current node status.\n\nThe error was: %v\n", err)
env.Exit(1)
return
} else if !initialized {
cmd.PrintErrln("Error: The node is not part of a Kubernetes cluster. You can bootstrap a new cluster with:\n\n sudo k8s bootstrap")
env.Exit(1)
return
}

err = client.SetClusterAPIAuthToken(cmd.Context(), apiv1.ClusterAPISetAuthTokenRequest{Token: token})
if err != nil {
cmd.PrintErrf("Error: Failed to set the CAPI auth token.\n\nThe error was: %v\n", err)
Expand Down
11 changes: 11 additions & 0 deletions src/k8s/pkg/client/k8sd/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ func query[T any](ctx context.Context, c *k8sd, method, path string, in any, out
log.FromContext(ctx).Info("Temporary error from k8sd: %v", err)
return false, nil
}
if bootstrapPending(err) {
return false, fmt.Errorf(
"the cluster hasn't been initialized yet. "+
" Attempted k8sd query: %s /%s: %w", method, path, err)
}
return false, fmt.Errorf("failed to %s /%s: %w", method, path, err)
}
return true, nil
Expand All @@ -49,3 +54,9 @@ func isTemporary(err error) bool {
}
return false
}

// bootstrapPending checks if an error was caused by the fact that the cluster
// has not been initialized yet.
func bootstrapPending(err error) bool {
return strings.Contains(err.Error(), "Database is not yet initialized")
}

0 comments on commit 1c6739a

Please sign in to comment.