toolbox dump: support dumping only k8s resources

Because metal does not support cloud-resource discovery, we need to
skip this in our metal tests.
This commit is contained in:
justinsb 2024-11-10 18:31:14 -05:00
parent 6c421cc6e7
commit b124625c62
3 changed files with 64 additions and 45 deletions

View File

@ -70,6 +70,9 @@ type ToolboxDumpOptions struct {
SSHUser string SSHUser string
MaxNodes int MaxNodes int
K8sResources bool K8sResources bool
// CloudResources controls whether we dump the cloud resources
CloudResources bool
} }
func (o *ToolboxDumpOptions) InitDefaults() { func (o *ToolboxDumpOptions) InitDefaults() {
@ -78,6 +81,7 @@ func (o *ToolboxDumpOptions) InitDefaults() {
o.SSHUser = "ubuntu" o.SSHUser = "ubuntu"
o.MaxNodes = 500 o.MaxNodes = 500
o.K8sResources = k8sResources != "" o.K8sResources = k8sResources != ""
o.CloudResources = true
} }
func NewCmdToolboxDump(f commandutils.Factory, out io.Writer) *cobra.Command { func NewCmdToolboxDump(f commandutils.Factory, out io.Writer) *cobra.Command {
@ -104,6 +108,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.Flags().StringVar(&options.Dir, "dir", options.Dir, "Target directory; if specified will collect logs and other information.")
cmd.MarkFlagDirname("dir") cmd.MarkFlagDirname("dir")
cmd.Flags().BoolVar(&options.K8sResources, "k8s-resources", options.K8sResources, "Include k8s resources in the dump") cmd.Flags().BoolVar(&options.K8sResources, "k8s-resources", options.K8sResources, "Include k8s resources in the dump")
cmd.Flags().BoolVar(&options.CloudResources, "cloud-resources", options.CloudResources, "Include cloud resources in the dump")
cmd.Flags().IntVar(&options.MaxNodes, "max-nodes", options.MaxNodes, "The maximum number of nodes from which to dump logs") 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.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.Flags().StringVar(&options.SSHUser, "ssh-user", options.SSHUser, "The remote user for SSH access to instances")
@ -132,6 +137,8 @@ func RunToolboxDump(ctx context.Context, f commandutils.Factory, out io.Writer,
return err return err
} }
var cloudResources *resources.Dump
if options.CloudResources {
resourceMap, err := resourceops.ListResources(cloud, cluster) resourceMap, err := resourceops.ListResources(cloud, cluster)
if err != nil { if err != nil {
return err return err
@ -140,6 +147,8 @@ func RunToolboxDump(ctx context.Context, f commandutils.Factory, out io.Writer,
if err != nil { if err != nil {
return err return err
} }
cloudResources = d
}
if options.Dir != "" { if options.Dir != "" {
privateKeyPath := options.PrivateKey privateKeyPath := options.PrivateKey
@ -213,23 +222,26 @@ func RunToolboxDump(ctx context.Context, f commandutils.Factory, out io.Writer,
// look for a bastion instance and use it if exists // look for a bastion instance and use it if exists
// Prefer a bastion load balancer if exists // Prefer a bastion load balancer if exists
bastionAddress := "" bastionAddress := ""
for _, lb := range d.LoadBalancers { if cloudResources != nil {
for _, lb := range cloudResources.LoadBalancers {
if strings.Contains(lb.Name, "bastion") && lb.DNSName != "" { if strings.Contains(lb.Name, "bastion") && lb.DNSName != "" {
bastionAddress = lb.DNSName bastionAddress = lb.DNSName
} }
} }
if bastionAddress == "" { if bastionAddress == "" {
for _, instance := range d.Instances { for _, instance := range cloudResources.Instances {
if strings.Contains(instance.Name, "bastion") { if strings.Contains(instance.Name, "bastion") {
bastionAddress = instance.PublicAddresses[0] bastionAddress = instance.PublicAddresses[0]
} }
} }
} }
}
dumper := dump.NewLogDumper(bastionAddress, sshConfig, keyRing, options.Dir) dumper := dump.NewLogDumper(bastionAddress, sshConfig, keyRing, options.Dir)
var additionalIPs []string var additionalIPs []string
var additionalPrivateIPs []string var additionalPrivateIPs []string
for _, instance := range d.Instances { if cloudResources != nil {
for _, instance := range cloudResources.Instances {
if len(instance.PublicAddresses) != 0 { if len(instance.PublicAddresses) != 0 {
additionalIPs = append(additionalIPs, instance.PublicAddresses[0]) additionalIPs = append(additionalIPs, instance.PublicAddresses[0])
} else if len(instance.PrivateAddresses) != 0 { } else if len(instance.PrivateAddresses) != 0 {
@ -238,6 +250,7 @@ func RunToolboxDump(ctx context.Context, f commandutils.Factory, out io.Writer,
klog.Warningf("no IP for instance %q", instance.Name) klog.Warningf("no IP for instance %q", instance.Name)
} }
} }
}
if err := dumper.DumpAllNodes(ctx, nodes, options.MaxNodes, additionalIPs, additionalPrivateIPs); err != nil { if err := dumper.DumpAllNodes(ctx, nodes, options.MaxNodes, additionalIPs, additionalPrivateIPs); err != nil {
klog.Warningf("error dumping nodes: %v", err) klog.Warningf("error dumping nodes: %v", err)
@ -262,9 +275,10 @@ func RunToolboxDump(ctx context.Context, f commandutils.Factory, out io.Writer,
} }
} }
if cloudResources != nil {
switch options.Output { switch options.Output {
case OutputYaml: case OutputYaml:
b, err := kops.ToRawYaml(d) b, err := kops.ToRawYaml(cloudResources)
if err != nil { if err != nil {
return fmt.Errorf("error marshaling yaml: %v", err) return fmt.Errorf("error marshaling yaml: %v", err)
} }
@ -275,7 +289,7 @@ func RunToolboxDump(ctx context.Context, f commandutils.Factory, out io.Writer,
return nil return nil
case OutputJSON: case OutputJSON:
b, err := json.MarshalIndent(d, "", " ") b, err := json.MarshalIndent(cloudResources, "", " ")
if err != nil { if err != nil {
return fmt.Errorf("error marshaling json: %v", err) return fmt.Errorf("error marshaling json: %v", err)
} }
@ -289,6 +303,8 @@ func RunToolboxDump(ctx context.Context, f commandutils.Factory, out io.Writer,
return fmt.Errorf("unsupported output format: %q", options.Output) return fmt.Errorf("unsupported output format: %q", options.Output)
} }
} }
return nil
}
func truncateNodeList(nodes *corev1.NodeList, max int) error { func truncateNodeList(nodes *corev1.NodeList, max int) error {
if max < 0 { if max < 0 {

View File

@ -23,6 +23,7 @@ kops toolbox dump [CLUSTER] [flags]
### Options ### Options
``` ```
--cloud-resources Include cloud resources in the dump (default true)
--dir string Target directory; if specified will collect logs and other information. --dir string Target directory; if specified will collect logs and other information.
-h, --help help for dump -h, --help help for dump
--k8s-resources Include k8s resources in the dump --k8s-resources Include k8s resources in the dump

View File

@ -73,10 +73,12 @@ for ns in kube-system; do
done done
# Use `kops toolbox dump` to dump a lot of useful information # Use `kops toolbox dump` to dump a lot of useful information
# We pass --cloud-resources=false because dumping cloud resources is not implemented on metal
mkdir -p ${ARTIFACTS}/dump mkdir -p ${ARTIFACTS}/dump
${KOPS} toolbox dump \ ${KOPS} toolbox dump \
--dir ${ARTIFACTS}/dump \ --dir ${ARTIFACTS}/dump \
--k8s-resources \ --k8s-resources \
--cloud-resources=false \
--private-key ${REPO_ROOT}/.build/.ssh/id_ed25519 \ --private-key ${REPO_ROOT}/.build/.ssh/id_ed25519 \
--ssh-user root \ --ssh-user root \
--name metal.k8s.local --name metal.k8s.local