211 lines
4.9 KiB
Go
211 lines
4.9 KiB
Go
package main
|
|
|
|
import (
|
|
"cmp"
|
|
"errors"
|
|
"fmt"
|
|
"isle/bootstrap"
|
|
"isle/daemon"
|
|
"isle/daemon/network"
|
|
"isle/jsonutil"
|
|
"isle/nebula"
|
|
"slices"
|
|
)
|
|
|
|
var subCmdNetworkCreate = subCmd{
|
|
name: "create",
|
|
descr: "Create's a new network, with this host being the first host in that network.",
|
|
do: func(ctx subCmdCtx) error {
|
|
var (
|
|
ipNet ipNetFlag
|
|
hostName hostNameFlag
|
|
)
|
|
|
|
name := ctx.flags.StringP(
|
|
"name", "N", "",
|
|
"Human-readable name to identify the network as.",
|
|
)
|
|
|
|
domain := ctx.flags.StringP(
|
|
"domain", "d", "",
|
|
"Domain name that should be used as the root domain in the network.",
|
|
)
|
|
|
|
ipNetF := ctx.flags.VarPF(
|
|
&ipNet, "ip-net", "i",
|
|
`An IP subnet, in CIDR form, which will be the overall range of`+
|
|
` possible IPs in the network. The first IP in this network`+
|
|
` range will become this first host's IP.`,
|
|
)
|
|
|
|
hostNameF := ctx.flags.VarPF(
|
|
&hostName,
|
|
"hostname", "n",
|
|
"Name of this host, which will be the first host in the network",
|
|
)
|
|
|
|
ctx, err := ctx.withParsedFlags(&withParsedFlagsOpts{
|
|
noNetwork: true,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("parsing flags: %w", err)
|
|
}
|
|
|
|
if *name == "" ||
|
|
*domain == "" ||
|
|
!ipNetF.Changed ||
|
|
!hostNameF.Changed {
|
|
return errors.New("--name, --domain, --ip-net, and --hostname are required")
|
|
}
|
|
|
|
err = ctx.daemonRPC.CreateNetwork(
|
|
ctx, *name, *domain, ipNet.V, hostName.V,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("creating network: %w", err)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var subCmdNetworkJoin = subCmd{
|
|
name: "join",
|
|
descr: "Joins this host to an existing network",
|
|
do: func(ctx subCmdCtx) error {
|
|
bootstrapPath := ctx.flags.StringP(
|
|
"bootstrap-path", "b", "", "Path to a bootstrap.json file.",
|
|
)
|
|
|
|
ctx, err := ctx.withParsedFlags(&withParsedFlagsOpts{
|
|
noNetwork: true,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("parsing flags: %w", err)
|
|
}
|
|
|
|
if *bootstrapPath == "" {
|
|
return errors.New("--bootstrap-path is required")
|
|
}
|
|
|
|
var newBootstrap network.JoiningBootstrap
|
|
if err := jsonutil.LoadFile(&newBootstrap, *bootstrapPath); err != nil {
|
|
return fmt.Errorf(
|
|
"loading bootstrap from %q: %w", *bootstrapPath, err,
|
|
)
|
|
}
|
|
|
|
return ctx.daemonRPC.JoinNetwork(ctx, newBootstrap)
|
|
},
|
|
}
|
|
|
|
var subCmdNetworkList = subCmd{
|
|
name: "list",
|
|
descr: "Lists all networks which have been joined",
|
|
do: doWithOutput(func(ctx subCmdCtx) (any, error) {
|
|
ctx, err := ctx.withParsedFlags(&withParsedFlagsOpts{
|
|
noNetwork: true,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parsing flags: %w", err)
|
|
}
|
|
|
|
networkCreationParams, err := ctx.daemonRPC.GetNetworks(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("calling GetNetworks: %w", err)
|
|
}
|
|
|
|
type lighthouseView struct {
|
|
PublicAddr string `yaml:"public_addr,omitempty"`
|
|
IP string `yaml:"ip"`
|
|
}
|
|
|
|
type networkView struct {
|
|
bootstrap.CreationParams `yaml:",inline"`
|
|
CACert nebula.Certificate `yaml:"ca_cert"`
|
|
SubnetCIDR string `yaml:"subnet_cidr"`
|
|
Lighthouses []lighthouseView `yaml:"lighthouses"`
|
|
}
|
|
|
|
var (
|
|
daemonRPC = ctx.daemonRPC
|
|
networkViews = make([]networkView, len(networkCreationParams))
|
|
)
|
|
|
|
for i, creationParams := range networkCreationParams {
|
|
ctx := daemon.WithNetwork(ctx, creationParams.ID)
|
|
|
|
networkBootstrap, err := daemonRPC.GetBootstrap(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"calling GetBootstrap with network:%+v: %w",
|
|
networkCreationParams,
|
|
err,
|
|
)
|
|
}
|
|
|
|
var (
|
|
caCert = networkBootstrap.CAPublicCredentials.Cert
|
|
caCertDetails = caCert.Unwrap().Details
|
|
subnet = caCertDetails.Subnets[0]
|
|
|
|
lighthouseViews []lighthouseView
|
|
)
|
|
|
|
for _, h := range networkBootstrap.HostsOrdered() {
|
|
if h.Nebula.PublicAddr == "" {
|
|
continue
|
|
}
|
|
|
|
lighthouseViews = append(lighthouseViews, lighthouseView{
|
|
PublicAddr: h.Nebula.PublicAddr,
|
|
IP: h.IP().String(),
|
|
})
|
|
}
|
|
|
|
networkViews[i] = networkView{
|
|
CreationParams: creationParams,
|
|
CACert: caCert,
|
|
SubnetCIDR: subnet.String(),
|
|
Lighthouses: lighthouseViews,
|
|
}
|
|
}
|
|
|
|
slices.SortFunc(networkViews, func(a, b networkView) int {
|
|
return cmp.Or(
|
|
cmp.Compare(a.Name, b.Name),
|
|
cmp.Compare(a.ID, b.ID),
|
|
)
|
|
})
|
|
|
|
return networkViews, nil
|
|
}),
|
|
}
|
|
|
|
var subCmdNetworkGetConfig = subCmd{
|
|
name: "get-config",
|
|
descr: "Displays the currently active configuration for a joined network",
|
|
do: doWithOutput(func(ctx subCmdCtx) (any, error) {
|
|
ctx, err := ctx.withParsedFlags(nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parsing flags: %w", err)
|
|
}
|
|
|
|
return ctx.daemonRPC.GetConfig(ctx)
|
|
}),
|
|
}
|
|
|
|
var subCmdNetwork = subCmd{
|
|
name: "network",
|
|
descr: "Sub-commands related to network membership",
|
|
plural: "s",
|
|
do: func(ctx subCmdCtx) error {
|
|
return ctx.doSubCmd(
|
|
subCmdNetworkCreate,
|
|
subCmdNetworkJoin,
|
|
subCmdNetworkList,
|
|
subCmdNetworkGetConfig,
|
|
)
|
|
},
|
|
}
|