isle/go/cmd/entrypoint/host.go

172 lines
3.8 KiB
Go

package main
import (
"encoding/json"
"errors"
"fmt"
"isle/daemon/network"
"os"
)
var subCmdHostCreate = subCmd{
name: "create",
descr: "Creates a new host in the network, writing its new bootstrap.json to stdout",
do: func(ctx subCmdCtx) error {
var (
hostName hostNameFlag
ip ipFlag
)
hostNameF := ctx.flags.VarPF(
&hostName,
"hostname", "n",
"Name of the host to generate bootstrap.json for",
)
ctx.flags.VarP(&ip, "ip", "i", "IP of the new host. An available IP will be chosen if none is given.")
canCreateHosts := ctx.flags.Bool(
"can-create-hosts",
false,
"The new host should have the ability to create hosts too",
)
ctx, err := ctx.withParsedFlags(nil)
if err != nil {
return fmt.Errorf("parsing flags: %w", err)
}
if !hostNameF.Changed {
return errors.New("--hostname is required")
}
daemonRPC, err := ctx.newDaemonRPC()
if err != nil {
return fmt.Errorf("creating daemon RPC client: %w", err)
}
defer daemonRPC.Close()
res, err := daemonRPC.CreateHost(
ctx, hostName.V, network.CreateHostOpts{
IP: ip.V,
CanCreateHosts: *canCreateHosts,
},
)
if err != nil {
return fmt.Errorf("calling CreateHost: %w", err)
}
return json.NewEncoder(os.Stdout).Encode(res)
},
}
var subCmdHostList = subCmd{
name: "list",
descr: "Lists all hosts in the network, and their IPs",
do: doWithOutput(func(ctx subCmdCtx) (any, error) {
ctx, err := ctx.withParsedFlags(nil)
if err != nil {
return nil, fmt.Errorf("parsing flags: %w", err)
}
daemonRPC, err := ctx.newDaemonRPC()
if err != nil {
return nil, fmt.Errorf("creating daemon RPC client: %w", err)
}
defer daemonRPC.Close()
currBoostrap, err := daemonRPC.GetBootstrap(ctx)
if err != nil {
return nil, fmt.Errorf("calling GetBootstrap: %w", err)
}
hosts := currBoostrap.HostsOrdered()
type storageAllocationView struct {
ID string `yaml:"id"`
RPCPort int `yaml:"rpc_port"`
S3APIPort int `yaml:"s3_api_port"`
}
type hostView struct {
Name string `yaml:"name"`
VPN struct {
IP string `yaml:"ip"`
PublicAddr string `yaml:"public_addr,omitempty"`
}
Storage struct {
Allocations []storageAllocationView `yaml:"allocations"`
} `yaml:",omitempty"`
}
hostViews := make([]hostView, len(hosts))
for i, host := range hosts {
storageAllocViews := make([]storageAllocationView, len(host.Garage.Instances))
for i := range host.Garage.Instances {
storageAllocViews[i] = storageAllocationView(host.Garage.Instances[i])
}
hostView := hostView{
Name: string(host.Name),
}
hostView.VPN.IP = host.IP().String()
hostView.VPN.PublicAddr = host.Nebula.PublicAddr
hostView.Storage.Allocations = storageAllocViews
hostViews[i] = hostView
}
return hostViews, nil
}),
}
var subCmdHostRemove = subCmd{
name: "remove",
descr: "Removes a host from the network",
do: func(ctx subCmdCtx) error {
var (
hostName hostNameFlag
)
hostNameF := ctx.flags.VarPF(
&hostName,
"hostname", "n",
"Name of the host to remove",
)
ctx, err := ctx.withParsedFlags(nil)
if err != nil {
return fmt.Errorf("parsing flags: %w", err)
}
if !hostNameF.Changed {
return errors.New("--hostname is required")
}
daemonRPC, err := ctx.newDaemonRPC()
if err != nil {
return fmt.Errorf("creating daemon RPC client: %w", err)
}
defer daemonRPC.Close()
if err := daemonRPC.RemoveHost(ctx, hostName.V); err != nil {
return fmt.Errorf("calling RemoveHost: %w", err)
}
return nil
},
}
var subCmdHost = subCmd{
name: "host",
plural: "s",
descr: "Sub-commands having to do with configuration of hosts in the network",
do: func(ctx subCmdCtx) error {
return ctx.doSubCmd(
subCmdHostCreate,
subCmdHostRemove,
subCmdHostList,
)
},
}