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. // 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( func New(
caCreds nebula.CACredentials, caCreds nebula.CACredentials,
adminCreationParams CreationParams, adminCreationParams CreationParams,

View File

@ -3,20 +3,37 @@ package main
import ( import (
"encoding" "encoding"
"fmt" "fmt"
"isle/nebula"
"net/netip"
) )
type textUnmarshalerFlag struct { type textUnmarshaler[T any] interface {
inner interface { encoding.TextUnmarshaler
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 { func (f *textUnmarshalerFlag[T, P]) Type() string { return "string" }
return f.inner.UnmarshalText([]byte(v))
}
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/bootstrap"
"isle/daemon" "isle/daemon"
"isle/jsonutil" "isle/jsonutil"
"isle/nebula"
"net/netip"
"os" "os"
"sort" "sort"
) )
@ -19,19 +17,17 @@ var subCmdHostsCreate = subCmd{
do: func(subCmdCtx subCmdCtx) error { do: func(subCmdCtx subCmdCtx) error {
var ( var (
flags = subCmdCtx.flagSet(false) flags = subCmdCtx.flagSet(false)
hostName nebula.HostName hostName hostNameFlag
ip netip.Addr ip ipFlag
) )
hostNameF := flags.VarPF( hostNameF := flags.VarPF(
textUnmarshalerFlag{&hostName}, &hostName,
"hostname", "h", "hostname", "h",
"Name of the host to generate bootstrap.json for", "Name of the host to generate bootstrap.json for",
) )
flags.VarP( flags.VarP(&ip, "ip", "i", "IP of the new host. An available IP will be chosen if none is given.")
textUnmarshalerFlag{&ip}, "ip", "i", "IP of the new host",
)
canCreateHosts := flags.Bool( canCreateHosts := flags.Bool(
"can-create-hosts", "can-create-hosts",
@ -53,9 +49,9 @@ var subCmdHostsCreate = subCmd{
&res, &res,
"CreateHost", "CreateHost",
daemon.CreateHostRequest{ daemon.CreateHostRequest{
HostName: hostName, HostName: hostName.V,
Opts: daemon.CreateHostOpts{ Opts: daemon.CreateHostOpts{
IP: ip, IP: ip.V,
CanCreateHosts: *canCreateHosts, CanCreateHosts: *canCreateHosts,
}, },
}, },
@ -110,11 +106,11 @@ var subCmdHostsRemove = subCmd{
do: func(subCmdCtx subCmdCtx) error { do: func(subCmdCtx subCmdCtx) error {
var ( var (
flags = subCmdCtx.flagSet(false) flags = subCmdCtx.flagSet(false)
hostName nebula.HostName hostName hostNameFlag
) )
hostNameF := flags.VarPF( hostNameF := flags.VarPF(
textUnmarshalerFlag{&hostName}, &hostName,
"hostname", "h", "hostname", "h",
"Name of the host to remove", "Name of the host to remove",
) )
@ -129,7 +125,7 @@ var subCmdHostsRemove = subCmd{
err := subCmdCtx.daemonRCPClient.Call( err := subCmdCtx.daemonRCPClient.Call(
subCmdCtx.ctx, nil, "RemoveHost", daemon.RemoveHostRequest{ subCmdCtx.ctx, nil, "RemoveHost", daemon.RemoveHostRequest{
HostName: hostName, HostName: hostName.V,
}, },
) )
if err != nil { if err != nil {

View File

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

View File

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

View File

@ -48,6 +48,8 @@ func (ctx subCmdCtx) flagSet(withPassthrough bool) *pflag.FlagSet {
passthroughStr = " [--] [args...]" 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( fmt.Fprintf(
os.Stderr, "%s[-h|--help] [%s flags...]%s\n\n", os.Stderr, "%s[-h|--help] [%s flags...]%s\n\n",
ctx.usagePrefix(), ctx.subCmd.name, passthroughStr, ctx.usagePrefix(), ctx.subCmd.name, passthroughStr,

View File

@ -32,6 +32,8 @@ type CreateHostOpts struct {
// CanCreateHosts indicates that the bootstrap produced by CreateHost should // CanCreateHosts indicates that the bootstrap produced by CreateHost should
// give the new host the ability to create new hosts as well. // give the new host the ability to create new hosts as well.
CanCreateHosts bool CanCreateHosts bool
// TODO add nebula cert tags
} }
// Daemon presents all functionality required for client frontends to interact // 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 // existing host, given the public key for that host. This is currently
// mostly useful for creating certs for mobile devices. // 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: // Errors:
// - ErrHostNotFound // - ErrHostNotFound
CreateNebulaCertificate( 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( caSigningPrivateKey, err := getNebulaCASigningPrivateKey(
ctx, d.secretsStore, 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. // lowercase letters, numbers, and hyphens, and must start with a letter.
type HostName string 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. // UnmarshalText parses and validates a HostName from a text string.
func (h *HostName) UnmarshalText(b []byte) error { func (h *HostName) UnmarshalText(b []byte) error {
if !hostNameRegexp.Match(b) { if !hostNameRegexp.Match(b) {