cmd/kgctl: add output options for showconf

This commit adds several output options to the `showconf` command of the
`kgctl` binary:
* `--as-peer`: this can be used to generate a peer configuration, which
can be used to configure the selected resource as a peer of another
WireGuard interface
* `--output`: this can be used to select the desired output format of
the peer resource, available options are: WireGuard, YAML, and JSON.
This commit is contained in:
Lucas Servén Marín 2019-05-08 01:31:36 +02:00
parent 5914a9468f
commit 90e68c7735
No known key found for this signature in database
GPG Key ID: 586FEAF680DA74AD
5 changed files with 185 additions and 17 deletions

View File

@ -25,7 +25,6 @@ func graph() *cobra.Command {
return &cobra.Command{
Use: "graph",
Short: "Generates a graph of the Kilo network",
Long: "",
RunE: runGraph,
}
}

View File

@ -17,16 +17,42 @@ package main
import (
"errors"
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer/json"
"github.com/squat/kilo/pkg/k8s/apis/kilo/v1alpha1"
"github.com/squat/kilo/pkg/mesh"
"github.com/squat/kilo/pkg/wireguard"
)
const (
outputFormatJSON = "json"
outputFormatWireGuard = "wireguard"
outputFormatYAML = "yaml"
)
var (
availableOutputFormats = strings.Join([]string{
outputFormatJSON,
outputFormatWireGuard,
outputFormatYAML,
}, ", ")
asPeer bool
output string
serializer *json.Serializer
)
func showConf() *cobra.Command {
cmd := &cobra.Command{
Use: "showconf",
Short: "Show the WireGuard configuration for a node or peer in the Kilo network",
Long: "",
Use: "showconf",
Short: "Show the WireGuard configuration for a node or peer in the Kilo network",
PersistentPreRunE: runShowConf,
}
for _, subCmd := range []*cobra.Command{
@ -35,15 +61,29 @@ func showConf() *cobra.Command {
} {
cmd.AddCommand(subCmd)
}
cmd.PersistentFlags().BoolVar(&asPeer, "as-peer", false, "Should the resource be shown as a peer? Useful to configure this resource as a peer of another WireGuard interface.")
cmd.PersistentFlags().StringVarP(&output, "output", "o", "wireguard", fmt.Sprintf("The output format of the resource. Only valid when combined with 'as-peer'. Possible values: %s", availableOutputFormats))
return cmd
}
func runShowConf(c *cobra.Command, args []string) error {
switch output {
case outputFormatJSON:
serializer = json.NewSerializer(json.DefaultMetaFactory, peerCreatorTyper{}, peerCreatorTyper{}, true)
case outputFormatWireGuard:
case outputFormatYAML:
serializer = json.NewYAMLSerializer(json.DefaultMetaFactory, peerCreatorTyper{}, peerCreatorTyper{})
default:
return fmt.Errorf("output format %v unknown; posible values are: %s", output, availableOutputFormats)
}
return runRoot(c, args)
}
func showConfNode() *cobra.Command {
return &cobra.Command{
Use: "node",
Use: "node [name]",
Short: "Show the WireGuard configuration for a node in the Kilo network",
Long: "",
RunE: runShowConfNode,
Args: cobra.ExactArgs(1),
}
@ -51,9 +91,8 @@ func showConfNode() *cobra.Command {
func showConfPeer() *cobra.Command {
return &cobra.Command{
Use: "peer",
Use: "peer [name]",
Short: "Show the WireGuard configuration for a peer in the Kilo network",
Long: "",
RunE: runShowConfPeer,
Args: cobra.ExactArgs(1),
}
@ -93,11 +132,35 @@ func runShowConfNode(_ *cobra.Command, args []string) error {
if err != nil {
return fmt.Errorf("failed to create topology: %v", err)
}
c, err := t.Conf().Bytes()
if err != nil {
return fmt.Errorf("failed to generate configuration: %v", err)
if !asPeer {
c, err := t.Conf().Bytes()
if err != nil {
return fmt.Errorf("failed to generate configuration: %v", err)
}
_, err = os.Stdout.Write(c)
return err
}
switch output {
case outputFormatJSON:
fallthrough
case outputFormatYAML:
p := translatePeer(t.AsPeer())
p.Name = hostname
return serializer.Encode(p, os.Stdout)
case outputFormatWireGuard:
c, err := (&wireguard.Conf{
Peers: []*wireguard.Peer{
t.AsPeer(),
},
}).Bytes()
if err != nil {
return fmt.Errorf("failed to generate configuration: %v", err)
}
_, err = os.Stdout.Write(c)
return err
}
fmt.Printf(string(c))
return nil
}
@ -137,10 +200,89 @@ func runShowConfPeer(_ *cobra.Command, args []string) error {
if err != nil {
return fmt.Errorf("failed to create topology: %v", err)
}
c, err := t.PeerConf(peer).Bytes()
if err != nil {
return fmt.Errorf("failed to generate configuration: %v", err)
if !asPeer {
c, err := t.PeerConf(peer).Bytes()
if err != nil {
return fmt.Errorf("failed to generate configuration: %v", err)
}
_, err = os.Stdout.Write(c)
return err
}
switch output {
case outputFormatJSON:
fallthrough
case outputFormatYAML:
p := translatePeer(t.AsPeer())
p.Name = peer
return serializer.Encode(p, os.Stdout)
case outputFormatWireGuard:
c, err := (&wireguard.Conf{
Peers: []*wireguard.Peer{
&peers[peer].Peer,
},
}).Bytes()
if err != nil {
return fmt.Errorf("failed to generate configuration: %v", err)
}
_, err = os.Stdout.Write(c)
return err
}
fmt.Printf(string(c))
return nil
}
// translatePeer translates a wireguard.Peer to a Peer CRD.
func translatePeer(peer *wireguard.Peer) *v1alpha1.Peer {
if peer == nil {
return &v1alpha1.Peer{}
}
var aips []string
for _, aip := range peer.AllowedIPs {
// Skip any invalid IPs.
if aip == nil {
continue
}
aips = append(aips, aip.String())
}
var endpoint *v1alpha1.PeerEndpoint
if peer.Endpoint != nil && peer.Endpoint.Port > 0 && peer.Endpoint.IP != nil {
endpoint = &v1alpha1.PeerEndpoint{
IP: peer.Endpoint.IP.String(),
Port: peer.Endpoint.Port,
}
}
var key string
if len(peer.PublicKey) > 0 {
key = string(peer.PublicKey)
}
var pka int
if peer.PersistentKeepalive > 0 {
pka = peer.PersistentKeepalive
}
return &v1alpha1.Peer{
TypeMeta: metav1.TypeMeta{
Kind: v1alpha1.PeerKind,
APIVersion: v1alpha1.SchemeGroupVersion.String(),
},
Spec: v1alpha1.PeerSpec{
AllowedIPs: aips,
Endpoint: endpoint,
PublicKey: key,
PersistentKeepalive: pka,
},
}
}
type peerCreatorTyper struct{}
func (p peerCreatorTyper) New(_ schema.GroupVersionKind) (runtime.Object, error) {
return &v1alpha1.Peer{}, nil
}
func (p peerCreatorTyper) ObjectKinds(_ runtime.Object) ([]schema.GroupVersionKind, bool, error) {
return []schema.GroupVersionKind{v1alpha1.PeerGVK}, false, nil
}
func (p peerCreatorTyper) Recognizes(_ schema.GroupVersionKind) bool {
return true
}

View File

@ -31,7 +31,7 @@ var (
const GroupName = "kilo.squat.ai"
// SchemeGroupVersion is the group version used to register these objects.
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: Version}
// Resource takes an unqualified resource and returns a Group-qualified GroupResource.
func Resource(resource string) schema.GroupResource {

View File

@ -21,15 +21,23 @@ import (
"net"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const (
// Version is the version of this API.
Version = "v1alpha1"
// PeerKind is the API kind for the peer resource.
PeerKind = "Peer"
// PeerPlural is the plural name for the peer resource.
PeerPlural = "peers"
)
var (
// PeerGVK is the GroupVersionKind for Peers.
PeerGVK = schema.GroupVersionKind{Group: GroupName, Version: Version, Kind: PeerKind}
)
// PeerShortNames are convenient shortnames for the peer resource.
var PeerShortNames = []string{"peer"}

View File

@ -338,6 +338,25 @@ func (t *Topology) Conf() *wireguard.Conf {
return c
}
// AsPeer generates the WireGuard peer configuration for the local location of the given Topology.
// This configuration can be used to configure this location as a peer of another WireGuard interface.
func (t *Topology) AsPeer() *wireguard.Peer {
for _, s := range t.segments {
if s.location != t.location {
continue
}
return &wireguard.Peer{
AllowedIPs: s.allowedIPs,
Endpoint: &wireguard.Endpoint{
IP: s.endpoint,
Port: uint32(t.port),
},
PublicKey: s.key,
}
}
return nil
}
// PeerConf generates a WireGuard configuration file for a given peer in a Topology.
func (t *Topology) PeerConf(name string) *wireguard.Conf {
c := &wireguard.Conf{}