Fix rendering of text flag defaults

This commit is contained in:
Brian Picciano 2024-07-22 10:42:25 +02:00
parent af69f1cfba
commit ca62a37692
8 changed files with 78 additions and 35 deletions

View File

@ -47,6 +47,10 @@ type Bootstrap struct {
}
// New initializes and returns a new Bootstrap file for a new host.
//
// TODO in the resulting bootstrap only include this host and hosts which are
// necessary for connecting to nebula/garage. Remember to immediately re-poll
// garage for the full hosts list during network joining.
func New(
caCreds nebula.CACredentials,
adminCreationParams CreationParams,

View File

@ -3,20 +3,37 @@ package main
import (
"encoding"
"fmt"
"isle/nebula"
"net/netip"
)
type textUnmarshalerFlag struct {
inner interface {
type textUnmarshaler[T any] interface {
encoding.TextUnmarshaler
*T
}
type textUnmarshalerFlag[T encoding.TextMarshaler, P textUnmarshaler[T]] struct {
V T
}
func (f *textUnmarshalerFlag[T, P]) Set(v string) error {
return P(&(f.V)).UnmarshalText([]byte(v))
}
func (f *textUnmarshalerFlag[T, P]) String() string {
b, err := f.V.MarshalText()
if err != nil {
panic(fmt.Sprintf("calling MarshalText on %#v: %v", f.V, err))
}
return string(b)
}
func (f textUnmarshalerFlag) Set(v string) error {
return f.inner.UnmarshalText([]byte(v))
}
func (f *textUnmarshalerFlag[T, P]) Type() string { return "string" }
func (f textUnmarshalerFlag) String() string {
return fmt.Sprint(f.inner)
}
////////////////////////////////////////////////////////////////////////////////
func (f textUnmarshalerFlag) Type() string { return "string" }
type (
hostNameFlag = textUnmarshalerFlag[nebula.HostName, *nebula.HostName]
ipNetFlag = textUnmarshalerFlag[nebula.IPNet, *nebula.IPNet]
ipFlag = textUnmarshalerFlag[netip.Addr, *netip.Addr]
)

View File

@ -7,8 +7,6 @@ import (
"isle/bootstrap"
"isle/daemon"
"isle/jsonutil"
"isle/nebula"
"net/netip"
"os"
"sort"
)
@ -19,19 +17,17 @@ var subCmdHostsCreate = subCmd{
do: func(subCmdCtx subCmdCtx) error {
var (
flags = subCmdCtx.flagSet(false)
hostName nebula.HostName
ip netip.Addr
hostName hostNameFlag
ip ipFlag
)
hostNameF := flags.VarPF(
textUnmarshalerFlag{&hostName},
&hostName,
"hostname", "h",
"Name of the host to generate bootstrap.json for",
)
flags.VarP(
textUnmarshalerFlag{&ip}, "ip", "i", "IP of the new host",
)
flags.VarP(&ip, "ip", "i", "IP of the new host. An available IP will be chosen if none is given.")
canCreateHosts := flags.Bool(
"can-create-hosts",
@ -53,9 +49,9 @@ var subCmdHostsCreate = subCmd{
&res,
"CreateHost",
daemon.CreateHostRequest{
HostName: hostName,
HostName: hostName.V,
Opts: daemon.CreateHostOpts{
IP: ip,
IP: ip.V,
CanCreateHosts: *canCreateHosts,
},
},
@ -110,11 +106,11 @@ var subCmdHostsRemove = subCmd{
do: func(subCmdCtx subCmdCtx) error {
var (
flags = subCmdCtx.flagSet(false)
hostName nebula.HostName
hostName hostNameFlag
)
hostNameF := flags.VarPF(
textUnmarshalerFlag{&hostName},
&hostName,
"hostname", "h",
"Name of the host to remove",
)
@ -129,7 +125,7 @@ var subCmdHostsRemove = subCmd{
err := subCmdCtx.daemonRCPClient.Call(
subCmdCtx.ctx, nil, "RemoveHost", daemon.RemoveHostRequest{
HostName: hostName,
HostName: hostName.V,
},
)
if err != nil {

View File

@ -15,11 +15,11 @@ var subCmdNebulaCreateCert = subCmd{
do: func(subCmdCtx subCmdCtx) error {
var (
flags = subCmdCtx.flagSet(false)
hostName nebula.HostName
hostName hostNameFlag
)
hostNameF := flags.VarPF(
textUnmarshalerFlag{&hostName},
&hostName,
"hostname", "h",
"Name of the host to generate a certificate for",
)
@ -53,7 +53,7 @@ var subCmdNebulaCreateCert = subCmd{
&res,
"CreateNebulaCertificate",
daemon.CreateNebulaCertificateRequest{
HostName: hostName,
HostName: hostName.V,
HostEncryptingPublicKey: hostPub,
},
)

View File

@ -5,6 +5,7 @@ import (
"fmt"
"isle/daemon"
"isle/jsonutil"
"log"
)
var subCmdNetworkCreate = subCmd{
@ -14,28 +15,30 @@ var subCmdNetworkCreate = subCmd{
var (
ctx = subCmdCtx.ctx
flags = subCmdCtx.flagSet(false)
req daemon.CreateNetworkRequest
ipNet ipNetFlag
hostName hostNameFlag
)
flags.StringVarP(
&req.Name, "name", "n", "",
name := flags.StringP(
"name", "n", "",
"Human-readable name to identify the network as.",
)
flags.StringVarP(
&req.Domain, "domain", "d", "",
domain := flags.StringP(
"domain", "d", "",
"Domain name that should be used as the root domain in the network.",
)
ipNetF := flags.VarPF(
textUnmarshalerFlag{&req.IPNet}, "ip-net", "i",
&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 := flags.VarPF(
textUnmarshalerFlag{&req.HostName},
&hostName,
"hostname", "h",
"Name of this host, which will be the first host in the network",
)
@ -44,13 +47,23 @@ var subCmdNetworkCreate = subCmd{
return fmt.Errorf("parsing flags: %w", err)
}
if req.Name == "" ||
req.Domain == "" ||
if *name == "" ||
*domain == "" ||
!ipNetF.Changed ||
!hostNameF.Changed {
return errors.New("--name, --domain, --ip-net, and --hostname are required")
}
req := daemon.CreateNetworkRequest{
Name: *name,
Domain: *domain,
IPNet: ipNet.V,
HostName: hostName.V,
}
log.Printf("req:%+v", req)
return nil
err := subCmdCtx.daemonRCPClient.Call(ctx, nil, "CreateNetwork", req)
if err != nil {
return fmt.Errorf("creating network: %w", err)

View File

@ -48,6 +48,8 @@ func (ctx subCmdCtx) flagSet(withPassthrough bool) *pflag.FlagSet {
passthroughStr = " [--] [args...]"
}
// TODO don't allow -h/--help flag to be set by sub-commands (or better,
// somehow check that a flag hasn't been set twice).
fmt.Fprintf(
os.Stderr, "%s[-h|--help] [%s flags...]%s\n\n",
ctx.usagePrefix(), ctx.subCmd.name, passthroughStr,

View File

@ -32,6 +32,8 @@ type CreateHostOpts struct {
// CanCreateHosts indicates that the bootstrap produced by CreateHost should
// give the new host the ability to create new hosts as well.
CanCreateHosts bool
// TODO add nebula cert tags
}
// Daemon presents all functionality required for client frontends to interact
@ -85,6 +87,9 @@ type Daemon interface {
// existing host, given the public key for that host. This is currently
// mostly useful for creating certs for mobile devices.
//
// TODO replace this with CreateHostBootstrap, and the
// CreateNebulaCertificate RPC method can just pull cert out of that.
//
// Errors:
// - ErrHostNotFound
CreateNebulaCertificate(
@ -685,6 +690,7 @@ func (d *daemon) CreateHost(
)
}
}
// TODO if the ip is given, check that it's not already in use.
caSigningPrivateKey, err := getNebulaCASigningPrivateKey(
ctx, d.secretsStore,

View File

@ -11,6 +11,11 @@ var hostNameRegexp = regexp.MustCompile(`^[a-z][a-z0-9\-]*$`)
// lowercase letters, numbers, and hyphens, and must start with a letter.
type HostName string
// MarshalText casts the HostName to a byte string and returns it.
func (h HostName) MarshalText() ([]byte, error) {
return []byte(h), nil
}
// UnmarshalText parses and validates a HostName from a text string.
func (h *HostName) UnmarshalText(b []byte) error {
if !hostNameRegexp.Match(b) {