isle/go/cmd/entrypoint/network.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,
)
},
}