diff --git a/default.nix b/default.nix index 242f9ef..5eb7a76 100644 --- a/default.nix +++ b/default.nix @@ -69,7 +69,7 @@ in rec { ''; }; - vendorHash = "sha256-heXFvEea3u3zvdUvVxlI+FTxqEeImoXd1sqdJJTASoc="; + vendorHash = "sha256-fBCwaZLusixNnD1QG0XtDe/NpNvonI7wBhqWbRrLFAg="; subPackages = [ "./cmd/entrypoint" diff --git a/go/bootstrap/hosts.go b/go/bootstrap/hosts.go index b03a61d..7ab4dbe 100644 --- a/go/bootstrap/hosts.go +++ b/go/bootstrap/hosts.go @@ -78,5 +78,5 @@ type Host struct { // This assumes that the Host and its data has already been verified against the // CA signing key. func (h Host) IP() net.IP { - return h.PublicCredentials.Cert.Details.Ips[0].IP + return h.PublicCredentials.Cert.Unwrap().Details.Ips[0].IP } diff --git a/go/cmd/entrypoint/admin.go b/go/cmd/entrypoint/admin.go index fbb1448..711105f 100644 --- a/go/cmd/entrypoint/admin.go +++ b/go/cmd/entrypoint/admin.go @@ -372,7 +372,7 @@ var subCmdAdminCreateNebulaCert = subCmd{ return fmt.Errorf("creating cert: %w", err) } - nebulaHostCertPEM, err := nebulaHostCert.MarshalToPEM() + nebulaHostCertPEM, err := nebulaHostCert.Unwrap().MarshalToPEM() if err != nil { return fmt.Errorf("marshaling cert to PEM: %w", err) } diff --git a/go/cmd/entrypoint/daemon.go b/go/cmd/entrypoint/daemon.go index 3a3f956..2567fc2 100644 --- a/go/cmd/entrypoint/daemon.go +++ b/go/cmd/entrypoint/daemon.go @@ -115,6 +115,23 @@ func runDaemonPmuxOnce( } }() + { + logger := logger.WithNamespace("http") + httpSrv, err := newHTTPServer( + ctx, logger, daemon.NewRPC(daemonInst), + ) + if err != nil { + return bootstrap.Bootstrap{}, fmt.Errorf("starting HTTP server: %w", err) + } + defer func() { + // see comment in daemonInst shutdown logic regarding background + // context. + if err := httpSrv.Shutdown(context.Background()); err != nil { + logger.Error(ctx, "Failed to cleanly shutdown http server", err) + } + }() + } + ticker := time.NewTicker(3 * time.Minute) defer ticker.Stop() diff --git a/go/cmd/entrypoint/daemon_util.go b/go/cmd/entrypoint/daemon_util.go index ea25076..d64b8cc 100644 --- a/go/cmd/entrypoint/daemon_util.go +++ b/go/cmd/entrypoint/daemon_util.go @@ -1,10 +1,18 @@ package main import ( + "context" + "errors" "fmt" "isle/bootstrap" "isle/daemon" + "isle/daemon/jsonrpc2" "isle/garage/garagesrv" + "net" + "net/http" + + "dev.mediocregopher.com/mediocre-go-lib.git/mctx" + "dev.mediocregopher.com/mediocre-go-lib.git/mlog" ) func coalesceDaemonConfigAndBootstrap( @@ -50,3 +58,41 @@ func coalesceDaemonConfigAndBootstrap( return hostBootstrap, nil } + +const daemonHTTPRPCPath = "/rpc/v0.json" + +func newHTTPServer( + ctx context.Context, logger *mlog.Logger, rpc *daemon.RPC, +) ( + *http.Server, error, +) { + l, err := net.Listen("unix", envSocketPath) + if err != nil { + return nil, fmt.Errorf( + "failed to listen on socket %q: %w", envSocketPath, err, + ) + } + + ctx = mctx.Annotate(ctx, "httpAddr", l.Addr().String()) + logger.Info(ctx, "HTTP server socket created") + + rpcHandler := jsonrpc2.Chain( + jsonrpc2.NewMLogMiddleware(logger.WithNamespace("rpc")), + jsonrpc2.ExposeServerSideErrorsMiddleware, + )( + jsonrpc2.NewDispatchHandler(&rpc), + ) + + httpMux := http.NewServeMux() + httpMux.Handle(daemonHTTPRPCPath, jsonrpc2.NewHTTPHandler(rpcHandler)) + + srv := &http.Server{Handler: httpMux} + + go func() { + if err := srv.Serve(l); !errors.Is(err, http.ErrServerClosed) { + logger.Fatal(ctx, "HTTP server unexpectedly shut down", err) + } + }() + + return srv, nil +} diff --git a/go/cmd/entrypoint/hosts.go b/go/cmd/entrypoint/hosts.go index 856128a..63c4fd9 100644 --- a/go/cmd/entrypoint/hosts.go +++ b/go/cmd/entrypoint/hosts.go @@ -1,15 +1,15 @@ package main import ( + "encoding/json" "errors" "fmt" "isle/bootstrap" + "isle/daemon" "isle/jsonutil" "os" "regexp" "sort" - - "dev.mediocregopher.com/mediocre-go-lib.git/mlog" ) var hostNameRegexp = regexp.MustCompile(`^[a-z][a-z0-9\-]*$`) @@ -24,39 +24,21 @@ func validateHostName(name string) error { } var subCmdHostsList = subCmd{ - name: "list", - descr: "Lists all hosts in the network, and their IPs", - checkLock: true, + name: "list", + descr: "Lists all hosts in the network, and their IPs", do: func(subCmdCtx subCmdCtx) error { - flags := subCmdCtx.flagSet(false) - - logLevelStr := flags.StringP( - "log-level", "l", "info", - `Maximum log level which should be output. Values can be "debug", "info", "warn", "error", "fatal". Does not apply to sub-processes`, - ) - - if err := flags.Parse(subCmdCtx.args); err != nil { - return fmt.Errorf("parsing flags: %w", err) - } - ctx := subCmdCtx.ctx - logLevel := mlog.LevelFromString(*logLevelStr) - if logLevel == nil { - return fmt.Errorf("couldn't parse log level %q", *logLevelStr) + var resRaw json.RawMessage + err := subCmdCtx.daemonRCPClient.Call(ctx, &resRaw, "GetHosts", nil) + if err != nil { + return fmt.Errorf("calling GetHosts: %w", err) } - logger := subCmdCtx.logger.WithMaxLevel(logLevel.Int()) - - hostBootstrap, err := loadHostBootstrap() - if err != nil { - return fmt.Errorf("loading host bootstrap: %w", err) - } - - hostsMap, err := hostBootstrap.GetGarageBootstrapHosts(ctx, logger) - if err != nil { - return fmt.Errorf("retrieving hosts from garage: %w", err) + var res daemon.GetHostsResult + if err := json.Unmarshal(resRaw, &res); err != nil { + return fmt.Errorf("unmarshaling %s into %T: %w", string(resRaw), res, err) } type host struct { @@ -67,8 +49,8 @@ var subCmdHostsList = subCmd{ Storage bootstrap.GarageHost `json:",omitempty"` } - hosts := make([]host, 0, len(hostsMap)) - for _, h := range hostsMap { + hosts := make([]host, 0, len(res.Hosts)) + for _, h := range res.Hosts { host := host{ Name: h.Name, diff --git a/go/cmd/entrypoint/main.go b/go/cmd/entrypoint/main.go index 7be5129..88d0f2a 100644 --- a/go/cmd/entrypoint/main.go +++ b/go/cmd/entrypoint/main.go @@ -8,9 +8,9 @@ import ( "path/filepath" "syscall" - "github.com/adrg/xdg" "dev.mediocregopher.com/mediocre-go-lib.git/mctx" "dev.mediocregopher.com/mediocre-go-lib.git/mlog" + "github.com/adrg/xdg" ) // The purpose of this binary is to act as the entrypoint of the isle diff --git a/go/cmd/entrypoint/nebula.go b/go/cmd/entrypoint/nebula.go index 3b3094c..d87da86 100644 --- a/go/cmd/entrypoint/nebula.go +++ b/go/cmd/entrypoint/nebula.go @@ -21,7 +21,7 @@ var subCmdNebulaShow = subCmd{ return fmt.Errorf("loading host bootstrap: %w", err) } - caCert := hostBootstrap.CAPublicCredentials.Cert + caCert := hostBootstrap.CAPublicCredentials.Cert.Unwrap() caCertPEM, err := caCert.MarshalToPEM() if err != nil { return fmt.Errorf("marshaling CA cert to PEM: %w", err) diff --git a/go/cmd/entrypoint/sub_cmd.go b/go/cmd/entrypoint/sub_cmd.go index de3e09f..cf15281 100644 --- a/go/cmd/entrypoint/sub_cmd.go +++ b/go/cmd/entrypoint/sub_cmd.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "isle/daemon/jsonrpc2" "os" "strings" @@ -16,8 +17,9 @@ type subCmdCtx struct { args []string // command-line arguments, excluding the subCmd itself. subCmdNames []string // names of subCmds so far, including this one - ctx context.Context - logger *mlog.Logger + ctx context.Context + logger *mlog.Logger + daemonRCPClient jsonrpc2.Client } type subCmd struct { @@ -107,12 +109,17 @@ func (ctx subCmdCtx) doSubCmd(subCmds ...subCmd) error { } } + daemonRCPClient := jsonrpc2.NewUnixHTTPClient( + envSocketPath, daemonHTTPRPCPath, + ) + err := subCmd.do(subCmdCtx{ - subCmd: subCmd, - args: args, - subCmdNames: append(ctx.subCmdNames, subCmdName), - ctx: ctx.ctx, - logger: ctx.logger, + subCmd: subCmd, + args: args, + subCmdNames: append(ctx.subCmdNames, subCmdName), + ctx: ctx.ctx, + logger: ctx.logger, + daemonRCPClient: daemonRCPClient, }) if err != nil { diff --git a/go/daemon/child_nebula.go b/go/daemon/child_nebula.go index b9f2b7a..d0f6770 100644 --- a/go/daemon/child_nebula.go +++ b/go/daemon/child_nebula.go @@ -59,14 +59,14 @@ func nebulaPmuxProcConfig( staticHostMap[ip] = []string{host.Nebula.PublicAddr} } - caCertPEM, err := hostBootstrap.CAPublicCredentials.Cert.MarshalToPEM() + caCertPEM, err := hostBootstrap.CAPublicCredentials.Cert.Unwrap().MarshalToPEM() if err != nil { return pmuxlib.ProcessConfig{}, fmt.Errorf( "marshaling CA cert to PEM: :%w", err, ) } - hostCertPEM, err := hostBootstrap.PublicCredentials.Cert.MarshalToPEM() + hostCertPEM, err := hostBootstrap.PublicCredentials.Cert.Unwrap().MarshalToPEM() if err != nil { return pmuxlib.ProcessConfig{}, fmt.Errorf( "marshaling host cert to PEM: :%w", err, diff --git a/go/daemon/jsonrpc2/client_http.go b/go/daemon/jsonrpc2/client_http.go index 878d0ac..187267b 100644 --- a/go/daemon/jsonrpc2/client_http.go +++ b/go/daemon/jsonrpc2/client_http.go @@ -6,6 +6,9 @@ import ( "encoding/json" "fmt" "net/http" + "net/url" + + "github.com/tv42/httpunix" ) type httpClient struct { @@ -15,12 +18,33 @@ type httpClient struct { // NewHTTPClient returns a Client which will use HTTP POST requests against the // given URL as a transport for JSONRPC2 method calls. -func NewHTTPClient(url string) Client { +func NewHTTPClient(urlStr string) Client { return &httpClient{ c: &http.Client{ Transport: http.DefaultTransport.(*http.Transport).Clone(), }, - url: url, + url: urlStr, + } +} + +// NewUnixHTTPClient returns a Client which will use HTTP POST requests against +// the given unix socket as a transport for JSONRPC2 method calls. The given +// path will be used as the path portion of the HTTP request. +func NewUnixHTTPClient(unixSocketPath, reqPath string) Client { + const host = "uds" + + u := &url.URL{ + Scheme: httpunix.Scheme, + Host: host, + Path: reqPath, + } + + transport := new(httpunix.Transport) + transport.RegisterLocation(host, unixSocketPath) + + return &httpClient{ + c: &http.Client{Transport: transport}, + url: u.String(), } } diff --git a/go/daemon/jsonrpc2/jsonrpc2_test.go b/go/daemon/jsonrpc2/jsonrpc2_test.go index 02a9427..9735df0 100644 --- a/go/daemon/jsonrpc2/jsonrpc2_test.go +++ b/go/daemon/jsonrpc2/jsonrpc2_test.go @@ -4,7 +4,9 @@ import ( "context" "errors" "io" + "net" "net/http/httptest" + "path/filepath" "strings" "sync" "testing" @@ -156,3 +158,20 @@ func TestHTTP(t *testing.T) { t.Cleanup(server.Close) testClient(t, NewHTTPClient(server.URL)) } + +func TestUnixHTTP(t *testing.T) { + var ( + unixSocketPath = filepath.Join(t.TempDir(), "test.sock") + server = httptest.NewUnstartedServer(NewHTTPHandler(testHandler)) + ) + + var err error + if server.Listener, err = net.Listen("unix", unixSocketPath); err != nil { + t.Fatal(err) + } + + server.Start() + t.Cleanup(server.Close) + + testClient(t, NewUnixHTTPClient(unixSocketPath, "/")) +} diff --git a/go/daemon/jsonrpc2/server_http.go b/go/daemon/jsonrpc2/server_http.go index 9e425d7..d179bd0 100644 --- a/go/daemon/jsonrpc2/server_http.go +++ b/go/daemon/jsonrpc2/server_http.go @@ -18,6 +18,11 @@ func NewHTTPHandler(h Handler) http.Handler { } func (h httpHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + rw.WriteHeader(http.StatusMethodNotAllowed) + return + } + var ( ctx = r.Context() enc = json.NewEncoder(rw) diff --git a/go/daemon/rpc.go b/go/daemon/rpc.go new file mode 100644 index 0000000..d41325f --- /dev/null +++ b/go/daemon/rpc.go @@ -0,0 +1,46 @@ +package daemon + +import ( + "cmp" + "context" + "fmt" + "isle/bootstrap" + "slices" + + "golang.org/x/exp/maps" +) + +// GetHostsResult wraps the results from the GetHosts RPC method. +type GetHostsResult struct { + Hosts []bootstrap.Host +} + +// RPC exposes all RPC methods which are available to be called over the RPC +// interface. +type RPC struct { + daemon Daemon +} + +// NewRPC initializes and returns an RPC instance. +func NewRPC(daemon Daemon) *RPC { + return &RPC{daemon} +} + +// GetHosts returns all hosts known to the cluster, sorted by their name. +func (r *RPC) GetHosts( + ctx context.Context, req struct{}, +) ( + GetHostsResult, error, +) { + hostsMap, err := r.daemon.GetGarageBootstrapHosts(ctx) + if err != nil { + return GetHostsResult{}, fmt.Errorf("retrieving hosts: %w", err) + } + + hosts := maps.Values(hostsMap) + slices.SortFunc(hosts, func(a, b bootstrap.Host) int { + return cmp.Compare(a.Name, b.Name) + }) + + return GetHostsResult{hosts}, nil +} diff --git a/go/go.mod b/go/go.mod index 8c1aa46..5361bfc 100644 --- a/go/go.mod +++ b/go/go.mod @@ -12,13 +12,14 @@ require ( github.com/shirou/gopsutil v3.21.11+incompatible github.com/slackhq/nebula v1.6.1 github.com/spf13/pflag v1.0.5 + github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/dustin/go-humanize v1.0.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect - github.com/google/go-cmp v0.5.6 // indirect github.com/google/uuid v1.1.1 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/json-iterator/go v1.1.12 // indirect diff --git a/go/go.sum b/go/go.sum index 1fc8a4b..657100c 100644 --- a/go/go.sum +++ b/go/go.sum @@ -13,8 +13,8 @@ github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -77,10 +77,14 @@ github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03O github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= +github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ= +github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o= golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/net v0.0.0-20220403103023-749bd193bc2b h1:vI32FkLJNAWtGD4BwkThwEy6XS7ZLLMHkSkYfF8M0W0= golang.org/x/net v0.0.0-20220403103023-749bd193bc2b/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -92,8 +96,6 @@ golang.org/x/sys v0.0.0-20220406155245-289d7a0edf71/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y= golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= diff --git a/go/nebula/certificate.go b/go/nebula/certificate.go new file mode 100644 index 0000000..95f1168 --- /dev/null +++ b/go/nebula/certificate.go @@ -0,0 +1,29 @@ +package nebula + +import "github.com/slackhq/nebula/cert" + +// Certificate wraps a NebulaCertificate to provide convenient (and consistent) +// text (un)marshaling methods. +type Certificate struct { + inner cert.NebulaCertificate +} + +// Unwrap returns the wrapped NebulaCertificate type. +func (c Certificate) Unwrap() *cert.NebulaCertificate { + return &c.inner +} + +// MarshalText implements the encoding.TextMarshaler interface. +func (c Certificate) MarshalText() ([]byte, error) { + return c.inner.MarshalToPEM() +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (c *Certificate) UnmarshalText(b []byte) error { + nebCrt, _, err := cert.UnmarshalNebulaCertificateFromPEM(b) + if err != nil { + return err + } + c.inner = *nebCrt + return nil +} diff --git a/go/nebula/nebula.go b/go/nebula/nebula.go index 78bc460..33a0027 100644 --- a/go/nebula/nebula.go +++ b/go/nebula/nebula.go @@ -13,7 +13,7 @@ import ( // HostPublicCredentials contains certificate and signing public keys which are // able to be broadcast publicly. type HostPublicCredentials struct { - Cert cert.NebulaCertificate + Cert Certificate SigningKey SigningPublicKey } @@ -28,7 +28,7 @@ type HostPrivateCredentials struct { // able to be broadcast publicly. The signing public key is the same one which // is embedded into the certificate. type CAPublicCredentials struct { - Cert cert.NebulaCertificate + Cert Certificate SigningKey SigningPublicKey } @@ -47,20 +47,20 @@ func NewHostCert( hostName string, ip net.IP, ) ( - cert.NebulaCertificate, error, + Certificate, error, ) { caCert := caCreds.Public.Cert - issuer, err := caCert.Sha256Sum() + issuer, err := caCert.inner.Sha256Sum() if err != nil { - return cert.NebulaCertificate{}, fmt.Errorf("getting ca.crt issuer: %w", err) + return Certificate{}, fmt.Errorf("getting ca.crt issuer: %w", err) } - expireAt := caCert.Details.NotAfter.Add(-1 * time.Second) + expireAt := caCert.inner.Details.NotAfter.Add(-1 * time.Second) - subnet := caCert.Details.Subnets[0] + subnet := caCert.inner.Details.Subnets[0] if !subnet.Contains(ip) { - return cert.NebulaCertificate{}, fmt.Errorf("invalid ip %q, not contained by network subnet %q", ip, subnet) + return Certificate{}, fmt.Errorf("invalid ip %q, not contained by network subnet %q", ip, subnet) } hostCert := cert.NebulaCertificate{ @@ -78,15 +78,15 @@ func NewHostCert( }, } - if err := hostCert.CheckRootConstrains(&caCert); err != nil { - return cert.NebulaCertificate{}, fmt.Errorf("validating certificate constraints: %w", err) + if err := hostCert.CheckRootConstrains(&caCert.inner); err != nil { + return Certificate{}, fmt.Errorf("validating certificate constraints: %w", err) } if err := signCert(&hostCert, caCreds.SigningPrivateKey); err != nil { - return cert.NebulaCertificate{}, fmt.Errorf("signing host cert with ca.key: %w", err) + return Certificate{}, fmt.Errorf("signing host cert with ca.key: %w", err) } - return hostCert, nil + return Certificate{hostCert}, nil } // NewHostCredentials generates a new key/cert for a nebula host using the CA @@ -149,7 +149,7 @@ func NewCACredentials(domain string, subnet *net.IPNet) (CACredentials, error) { return CACredentials{ Public: CAPublicCredentials{ - Cert: caCert, + Cert: Certificate{caCert}, SigningKey: signingPubKey, }, SigningPrivateKey: signingPrivKey, diff --git a/go/nebula/signing_key_test.go b/go/nebula/signing_key_test.go index 13e140f..812ff19 100644 --- a/go/nebula/signing_key_test.go +++ b/go/nebula/signing_key_test.go @@ -16,8 +16,8 @@ func TestSigningKeysJSON(t *testing.T) { t.Fatal(err) } - if !strings.HasPrefix(string(pubJSON), `"-----BEGIN `) { - t.Fatalf("pub key didn't marshal to PEM: %q", pubJSON) + if !strings.HasPrefix(string(pubJSON), `"S0`) { + t.Fatalf("pub key didn't marshal with prefix: %q", pubJSON) } var pub2 SigningPublicKey @@ -36,8 +36,8 @@ func TestSigningKeysJSON(t *testing.T) { t.Fatal(err) } - if !strings.HasPrefix(string(privJSON), `"-----BEGIN `) { - t.Fatalf("priv key didn't marshal to PEM: %q", privJSON) + if !strings.HasPrefix(string(privJSON), `"s0`) { + t.Fatalf("priv key didn't marshal with prefix: %q", privJSON) } var priv2 SigningPrivateKey diff --git a/tests/cases/hosts/00-list.sh b/tests/cases/hosts/00-list.sh new file mode 100644 index 0000000..210558a --- /dev/null +++ b/tests/cases/hosts/00-list.sh @@ -0,0 +1,13 @@ +# shellcheck source=../../utils/with-1-data-1-empty-node-cluster.sh +source "$UTILS"/with-1-data-1-empty-node-cluster.sh + +as_primus +hosts="$(isle hosts list)" + +[ "$(echo "$hosts" | jq -r '.[0].Name')" = "primus" ] +[ "$(echo "$hosts" | jq -r '.[0].VPN.IP')" = "10.6.9.1" ] +[ "$(echo "$hosts" | jq -r '.[0].Storage.Instances|length')" = "3" ] + +[ "$(echo "$hosts" | jq -r '.[1].Name')" = "secondus" ] +[ "$(echo "$hosts" | jq -r '.[1].VPN.IP')" = "10.6.9.2" ] +[ "$(echo "$hosts" | jq -r '.[1].Storage.Instances|length')" = "0" ] diff --git a/tests/utils/shared-daemon-env.sh b/tests/utils/shared-daemon-env.sh index bc7a2cd..d4b8115 100644 --- a/tests/utils/shared-daemon-env.sh +++ b/tests/utils/shared-daemon-env.sh @@ -12,5 +12,6 @@ cat </dev/null; do sleep 1; done - $SHELL "$UTILS/register-cleanup.sh" "$pid" "1-data-1-empty-node-cluster/primus" + while ! isle hosts list >/dev/null; do sleep 1; done + echo "Creating secondus bootstrap" isle admin create-bootstrap \ --admin-path admin.json \ @@ -86,8 +86,7 @@ EOF pid="$!" echo "Waiting for secondus daemon (process $!) to initialize" - while ! isle hosts list >/dev/null; do sleep 1; done - $SHELL "$UTILS/register-cleanup.sh" "$pid" "1-data-1-empty-node-cluster/secondus" + while ! isle hosts list >/dev/null; do sleep 1; done ) fi