Skip to content

Commit

Permalink
Add wiring export to cli (#697)
Browse files Browse the repository at this point in the history
  • Loading branch information
Frostman authored Dec 10, 2024
1 parent 2e68154 commit 89a7d13
Show file tree
Hide file tree
Showing 3 changed files with 246 additions and 0 deletions.
41 changes: 41 additions & 0 deletions cmd/hhfctl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,47 @@ func main() {
},
},
},
{
Name: "wiring",
Usage: "general wiring diagram helpers",
Flags: []cli.Flag{
verboseFlag,
},
Subcommands: []*cli.Command{
{
Name: "export",
Usage: "export wiring diagram (incl. switches, connections, vpcs, externals, etc.)",
Flags: []cli.Flag{
verboseFlag,
&cli.BoolFlag{
Name: "vpcs",
Usage: "include VPCs",
Value: true,
},
&cli.BoolFlag{
Name: "externals",
Usage: "include Externals",
Value: true,
},
&cli.BoolFlag{
Name: "switch-profiles",
Usage: "include SwitchProfiles (may cause issues on importing)",
Value: false,
},
},
Before: func(_ *cli.Context) error {
return setupLogger(verbose)
},
Action: func(cCtx *cli.Context) error {
return errors.Wrapf(hhfctl.WiringExport(ctx, hhfctl.WiringExportOptions{
VPCs: cCtx.Bool("vpcs"),
Externals: cCtx.Bool("externals"),
SwitchProfiles: cCtx.Bool("switch-profiles"),
}), "failed to export wiring")
},
},
},
},
{
Name: "inspect",
Aliases: []string{"i"},
Expand Down
96 changes: 96 additions & 0 deletions pkg/hhfctl/wiring.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright 2024 Hedgehog
// SPDX-License-Identifier: Apache-2.0

package hhfctl

import (
"context"
"fmt"
"os"

vpcapi "go.githedgehog.com/fabric/api/vpc/v1beta1"
wiringapi "go.githedgehog.com/fabric/api/wiring/v1beta1"
"go.githedgehog.com/fabric/pkg/util/kubeutil"
)

type WiringExportOptions struct {
VPCs bool
Externals bool
SwitchProfiles bool
}

func WiringExport(ctx context.Context, opts WiringExportOptions) error {
kube, err := kubeutil.NewClient(ctx, "", wiringapi.SchemeBuilder, vpcapi.SchemeBuilder)
if err != nil {
return fmt.Errorf("creating kube client: %w", err)
}

out := os.Stdout
objs := new(int)

if opts.SwitchProfiles {
if err := kubeutil.PrintObjectList(ctx, kube, out, &wiringapi.SwitchProfileList{}, objs); err != nil {
return fmt.Errorf("printing switch profiles: %w", err)
}
}

if err := kubeutil.PrintObjectList(ctx, kube, out, &wiringapi.VLANNamespaceList{}, objs); err != nil {
return fmt.Errorf("printing vlan namespaces: %w", err)
}

if err := kubeutil.PrintObjectList(ctx, kube, out, &wiringapi.SwitchGroupList{}, objs); err != nil {
return fmt.Errorf("printing switch groups: %w", err)
}

if err := kubeutil.PrintObjectList(ctx, kube, out, &wiringapi.SwitchList{}, objs); err != nil {
return fmt.Errorf("printing switches: %w", err)
}

if err := kubeutil.PrintObjectList(ctx, kube, out, &wiringapi.ServerList{}, objs); err != nil {
return fmt.Errorf("printing servers: %w", err)
}

if err := kubeutil.PrintObjectList(ctx, kube, out, &wiringapi.ConnectionList{}, objs); err != nil {
return fmt.Errorf("printing connections: %w", err)
}

if err := kubeutil.PrintObjectList(ctx, kube, out, &wiringapi.ServerProfileList{}, objs); err != nil {
return fmt.Errorf("printing server profiles: %w", err)
}

if opts.VPCs || opts.Externals {
if err := kubeutil.PrintObjectList(ctx, kube, out, &vpcapi.IPv4NamespaceList{}, objs); err != nil {
return fmt.Errorf("printing ipv4 namespaces: %w", err)
}
}

if opts.VPCs {
if err := kubeutil.PrintObjectList(ctx, kube, out, &vpcapi.VPCList{}, objs); err != nil {
return fmt.Errorf("printing vpcs: %w", err)
}

if err := kubeutil.PrintObjectList(ctx, kube, out, &vpcapi.VPCAttachmentList{}, objs); err != nil {
return fmt.Errorf("printing vpc attachments: %w", err)
}

if err := kubeutil.PrintObjectList(ctx, kube, out, &vpcapi.VPCPeeringList{}, objs); err != nil {
return fmt.Errorf("printing vpc peerings: %w", err)
}
}

if opts.Externals {
if err := kubeutil.PrintObjectList(ctx, kube, out, &vpcapi.ExternalList{}, objs); err != nil {
return fmt.Errorf("printing externals: %w", err)
}

if err := kubeutil.PrintObjectList(ctx, kube, out, &vpcapi.ExternalAttachmentList{}, objs); err != nil {
return fmt.Errorf("printing external attachments: %w", err)
}

if err := kubeutil.PrintObjectList(ctx, kube, out, &vpcapi.ExternalPeeringList{}, objs); err != nil {
return fmt.Errorf("printing external peerings: %w", err)
}
}

return nil
}
109 changes: 109 additions & 0 deletions pkg/util/kubeutil/print.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright 2024 Hedgehog
// SPDX-License-Identifier: Apache-2.0

package kubeutil

import (
"context"
"fmt"
"io"
"reflect"

"go.githedgehog.com/fabric/api/meta"
wiringapi "go.githedgehog.com/fabric/api/wiring/v1beta1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
)

func PrintObjectList(ctx context.Context, kube client.Reader, w io.Writer, objList meta.ObjectList, objs *int) error {
if objs == nil {
objs = new(int)
}

if err := kube.List(ctx, objList); err != nil {
return fmt.Errorf("listing objects: %w", err)
}

if len(objList.GetItems()) > 0 {
_, err := fmt.Fprintf(w, "#\n# %s\n#\n", reflect.TypeOf(objList).Elem().Name())
if err != nil {
return fmt.Errorf("writing comment: %w", err)
}
}

for _, obj := range objList.GetItems() {
if *objs > 0 {
_, err := fmt.Fprintf(w, "---\n")
if err != nil {
return fmt.Errorf("writing separator: %w", err)
}
}
*objs++

if err := PrintObject(obj, w, false); err != nil {
return fmt.Errorf("printing object: %w", err)
}
}

return nil
}

type printObj struct {
APIVersion string `json:"apiVersion,omitempty"`
Kind string `json:"kind,omitempty"`
Meta printObjMeta `json:"metadata,omitempty"`
Spec any `json:"spec,omitempty"`
Status any `json:"status,omitempty"`
}

type printObjMeta struct {
Name string `json:"name,omitempty"`
Namespace string `json:"namespace,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
}

func PrintObject(obj client.Object, w io.Writer, withStatus bool) error {
labels := obj.GetLabels()
wiringapi.CleanupFabricLabels(labels)
if len(labels) == 0 {
labels = nil
}

annotations := obj.GetAnnotations()
for key := range annotations {
if key == "kubectl.kubernetes.io/last-applied-configuration" {
delete(annotations, key)
}
}
if len(annotations) == 0 {
annotations = nil
}

p := printObj{
APIVersion: obj.GetObjectKind().GroupVersionKind().GroupVersion().String(),
Kind: obj.GetObjectKind().GroupVersionKind().Kind,
Meta: printObjMeta{
Name: obj.GetName(),
Namespace: obj.GetNamespace(),
Labels: labels,
Annotations: annotations,
},
Spec: reflect.ValueOf(obj).Elem().FieldByName("Spec").Interface(),
}

if withStatus {
p.Status = reflect.ValueOf(obj).Elem().FieldByName("Status").Interface()
}

buf, err := yaml.Marshal(p)
if err != nil {
return fmt.Errorf("marshalling: %w", err)
}
_, err = w.Write(buf)
if err != nil {
return fmt.Errorf("writing: %w", err)
}

return nil
}

0 comments on commit 89a7d13

Please sign in to comment.