diff --git a/go/cmd/entrypoint/network.go b/go/cmd/entrypoint/network.go index 8d43673..4269eca 100644 --- a/go/cmd/entrypoint/network.go +++ b/go/cmd/entrypoint/network.go @@ -5,6 +5,7 @@ import ( "fmt" "isle/daemon/network" "isle/jsonutil" + "os" ) var subCmdNetworkCreate = subCmd{ @@ -92,13 +93,34 @@ var subCmdNetworkJoin = subCmd{ }, } +var subCmdNetworkList = subCmd{ + name: "list", + descr: "Lists all networks which have been joined", + noNetwork: true, + do: func(ctx subCmdCtx) error { + ctx, err := ctx.withParsedFlags() + if err != nil { + return fmt.Errorf("parsing flags: %w", err) + } + + creationParams, err := newDaemonRPCClient().GetNetworks(ctx) + if err != nil { + return fmt.Errorf("getting joined networks: %w", err) + } + + return jsonutil.WriteIndented(os.Stdout, creationParams) + }, +} + var subCmdNetwork = subCmd{ - name: "network", - descr: "Sub-commands related to network membership", + name: "network", + descr: "Sub-commands related to network membership", + plural: "s", do: func(ctx subCmdCtx) error { return ctx.doSubCmd( subCmdNetworkCreate, subCmdNetworkJoin, + subCmdNetworkList, ) }, } diff --git a/go/daemon/client.go b/go/daemon/client.go index 77dda78..a57a167 100644 --- a/go/daemon/client.go +++ b/go/daemon/client.go @@ -86,6 +86,15 @@ func (c *rpcClient) GetNebulaCAPublicCredentials(ctx context.Context) (c2 nebula return } +func (c *rpcClient) GetNetworks(ctx context.Context) (ca1 []bootstrap.CreationParams, err error) { + err = c.client.Call( + ctx, + &ca1, + "GetNetworks", + ) + return +} + func (c *rpcClient) JoinNetwork(ctx context.Context, j1 network.JoiningBootstrap) (err error) { err = c.client.Call( ctx, diff --git a/go/daemon/daemon.go b/go/daemon/daemon.go index d4d71cb..062f909 100644 --- a/go/daemon/daemon.go +++ b/go/daemon/daemon.go @@ -12,6 +12,7 @@ import ( "isle/daemon/network" "isle/nebula" "isle/toolkit" + "sort" "sync" "dev.mediocregopher.com/mediocre-go-lib.git/mctx" @@ -302,6 +303,35 @@ func withNetwork[Res any]( return fn(ctx, network) } +// GetNetworks returns all networks which have been joined by the Daemon, +// ordered by their name. +func (d *Daemon) GetNetworks( + ctx context.Context, +) ( + []bootstrap.CreationParams, error, +) { + d.l.RLock() + defer d.l.RUnlock() + + res := make([]bootstrap.CreationParams, 0, len(d.networks)) + for id, network := range d.networks { + creationParams, err := network.GetNetworkCreationParams(ctx) + if err != nil { + return nil, fmt.Errorf( + "getting network creation params of network %q: %w", id, err, + ) + } + + res = append(res, creationParams) + } + + sort.Slice(res, func(i, j int) bool { + return res[i].Name < res[j].Name + }) + + return res, nil +} + // GetHost implements the method for the network.RPC interface. func (d *Daemon) GetHosts(ctx context.Context) ([]bootstrap.Host, error) { return withNetwork( diff --git a/go/daemon/rpc.go b/go/daemon/rpc.go index ac35969..3165246 100644 --- a/go/daemon/rpc.go +++ b/go/daemon/rpc.go @@ -2,6 +2,7 @@ package daemon import ( "context" + "isle/bootstrap" "isle/daemon/jsonrpc2" "isle/daemon/network" "isle/nebula" @@ -22,6 +23,8 @@ type RPC interface { JoinNetwork(context.Context, network.JoiningBootstrap) error + GetNetworks(context.Context) ([]bootstrap.CreationParams, error) + // All network.RPC methods are automatically implemented by Daemon using the // currently joined network. If no network is joined then any call to these // methods will return ErrNoNetwork. @@ -30,6 +33,7 @@ type RPC interface { // WithNetwork, in order to choose the network. These methods may return // these errors, in addition to those documented on the individual methods: // + // Errors: // - ErrNoMatchingNetworks // - ErrMultipleMatchingNetworks network.RPC