Skip to content

Commit

Permalink
Merge pull request kubernetes#16160 from rifelpet/limit-node-dump
Browse files Browse the repository at this point in the history
Add --max-nodes flag to toolbox dump, default to 500
  • Loading branch information
k8s-ci-robot authored Dec 7, 2023
2 parents 2dfb93f + 2956f37 commit e26b0f9
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 0 deletions.
28 changes: 28 additions & 0 deletions cmd/kops/toolbox_dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"slices"
"strings"

"github.com/spf13/cobra"
Expand All @@ -34,6 +36,7 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/apis/kops/util"
"k8s.io/kops/pkg/commands/commandutils"
"k8s.io/kops/pkg/dump"
"k8s.io/kops/pkg/resources"
Expand Down Expand Up @@ -63,12 +66,14 @@ type ToolboxDumpOptions struct {
Dir string
PrivateKey string
SSHUser string
MaxNodes int
}

func (o *ToolboxDumpOptions) InitDefaults() {
o.Output = OutputYaml
o.PrivateKey = "~/.ssh/id_rsa"
o.SSHUser = "ubuntu"
o.MaxNodes = 500
}

func NewCmdToolboxDump(f commandutils.Factory, out io.Writer) *cobra.Command {
Expand All @@ -94,6 +99,7 @@ func NewCmdToolboxDump(f commandutils.Factory, out io.Writer) *cobra.Command {

cmd.Flags().StringVar(&options.Dir, "dir", options.Dir, "Target directory; if specified will collect logs and other information.")
cmd.MarkFlagDirname("dir")
cmd.Flags().IntVar(&options.MaxNodes, "max-nodes", options.MaxNodes, "The maximum number of nodes from which to dump logs")
cmd.Flags().StringVar(&options.PrivateKey, "private-key", options.PrivateKey, "File containing private key to use for SSH access to instances")
cmd.Flags().StringVar(&options.SSHUser, "ssh-user", options.SSHUser, "The remote user for SSH access to instances")
cmd.RegisterFlagCompletionFunc("ssh-user", cobra.NoFileCompletions)
Expand Down Expand Up @@ -174,6 +180,11 @@ func RunToolboxDump(ctx context.Context, f commandutils.Factory, out io.Writer,
}
}

err = truncateNodeList(&nodes, options.MaxNodes)
if err != nil {
klog.Warningf("not limiting number of nodes dumped: %v", err)
}

sshConfig := &ssh.ClientConfig{
Config: ssh.Config{},
User: options.SSHUser,
Expand Down Expand Up @@ -240,3 +251,20 @@ func RunToolboxDump(ctx context.Context, f commandutils.Factory, out io.Writer,
return fmt.Errorf("unsupported output format: %q", options.Output)
}
}

func truncateNodeList(nodes *corev1.NodeList, max int) error {
if max < 0 {
return errors.New("--max-nodes must be greater than zero")
}
// Move control plane nodes to the start of the list and truncate the remainder
slices.SortFunc[[]corev1.Node](nodes.Items, func(a corev1.Node, e corev1.Node) int {
if role := util.GetNodeRole(&a); role == "control-plane" || role == "apiserver" {
return -1
}
return 1
})
if len(nodes.Items) > max {
nodes.Items = nodes.Items[:max]
}
return nil
}
111 changes: 111 additions & 0 deletions cmd/kops/toolbox_dump_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
"testing"

"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestTruncateNodeList(t *testing.T) {
cases := []struct {
name string
input []corev1.Node
max int
expected []corev1.Node
err bool
}{
{
name: "less than max",
input: []corev1.Node{
makeNode(),
makeNode(),
makeControlPlaneNode(),
},
max: 5,
expected: []corev1.Node{
makeControlPlaneNode(),
makeNode(),
makeNode(),
},
},
{
name: "truncate",
input: []corev1.Node{
makeNode(),
makeNode(),
makeNode(),
makeControlPlaneNode(),
makeNode(),
},
max: 4,
expected: []corev1.Node{
makeControlPlaneNode(),
makeNode(),
makeNode(),
makeNode(),
},
},
{
name: "less than zero",
input: []corev1.Node{
makeNode(),
makeNode(),
makeNode(),
makeControlPlaneNode(),
makeNode(),
},
max: -1,
err: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
nodeList := corev1.NodeList{Items: tc.input}
err := truncateNodeList(&nodeList, tc.max)
if tc.err {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, tc.expected, nodeList.Items)
}
})
}
}

func makeControlPlaneNode() corev1.Node {
return corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"node-role.kubernetes.io/control-plane": "",
},
},
}
}

func makeNode() corev1.Node {
return corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"node-role.kubernetes.io/node": "",
},
},
}
}
1 change: 1 addition & 0 deletions docs/cli/kops_toolbox_dump.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions docs/releases/1.29-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ instances.

## Other breaking changes

* `kops toolbox dump` limits the number of nodes dumped to 500 by default. Use `--max-nodes` to override.

* Support for Kubernetes version 1.23 has been removed.

# Known Issues
Expand Down

0 comments on commit e26b0f9

Please sign in to comment.