From 2cd95772f871992575215e389cdad0efc6322386 Mon Sep 17 00:00:00 2001 From: "R.I.Pienaar" Date: Fri, 6 Dec 2024 10:32:17 +0100 Subject: [PATCH] Adds a connect-only natscontext (#5) This adds a version of natscontext that supports only the parts needed to connect to nats and none of the management features the CLI needs, this is in order to keep the usage API and dependencies low Signed-off-by: R.I.Pienaar --- .github/workflows/natscontext.yaml | 59 +++++ README.md | 7 +- natscontext/README.md | 37 +++ natscontext/context.go | 337 ++++++++++++++++++++++++++ natscontext/dialer.go | 52 ++++ natscontext/expansion_test.go | 36 +++ natscontext/go.mod | 16 ++ natscontext/go.sum | 14 ++ natscontext/go.work | 6 + natscontext/go.work.sum | 13 + natscontext/test/context_test.go | 100 ++++++++ natscontext/test/go.mod | 17 ++ natscontext/test/go.sum | 23 ++ natscontext/test/testdata/server.conf | 6 + natsext/go.work.sum | 4 + 15 files changed, 724 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/natscontext.yaml create mode 100644 natscontext/README.md create mode 100644 natscontext/context.go create mode 100644 natscontext/dialer.go create mode 100644 natscontext/expansion_test.go create mode 100644 natscontext/go.mod create mode 100644 natscontext/go.sum create mode 100644 natscontext/go.work create mode 100644 natscontext/go.work.sum create mode 100644 natscontext/test/context_test.go create mode 100644 natscontext/test/go.mod create mode 100644 natscontext/test/go.sum create mode 100644 natscontext/test/testdata/server.conf diff --git a/.github/workflows/natscontext.yaml b/.github/workflows/natscontext.yaml new file mode 100644 index 0000000..5a617b8 --- /dev/null +++ b/.github/workflows/natscontext.yaml @@ -0,0 +1,59 @@ +name: natscontext +on: + push: + paths: + - 'natscontext/**' + + pull_request: + paths: + - 'natscontext/**' + + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: 'stable' + + - name: Install deps + working-directory: natscontext + shell: bash --noprofile --norc -x -eo pipefail {0} + run: | + go get -t ./... + go install honnef.co/go/tools/cmd/staticcheck@latest + go install github.com/client9/misspell/cmd/misspell@latest + + - name: Run linters + working-directory: natscontext + shell: bash --noprofile --norc -x -eo pipefail {0} + run: | + $(exit $(go fmt ./... | wc -l)) + go vet ./... + go vet ./test/... + staticcheck ./... + staticcheck ./test/... + find . -type f -name "*.go" | xargs misspell -error -locale US + + test: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: 'stable' + + - name: Run tests + working-directory: natscontext + shell: bash --noprofile --norc -x -eo pipefail {0} + run: | + go test -v -count=1 ./... + go test -v -count=1 ./test/... diff --git a/README.md b/README.md index a02b72b..04f22a1 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ You can use the library as a whole, or pick just what you need. # Utilities -| Module | Description | Docs | -|----------------------|----------------------|--------------------------------| -| Core NATS Extensions | Core NATS extensions | [README.md](natsext/README.md) | +| Module | Description | Docs | +|----------------------|----------------------------------------------|------------------------------------| +| Core NATS Extensions | Core NATS extensions | [README.md](natsext/README.md) | +| `natscontext` | Allow connecting to NATS using NATS Contexts | [README.md](natscontext/README.md) | diff --git a/natscontext/README.md b/natscontext/README.md new file mode 100644 index 0000000..d1ea34a --- /dev/null +++ b/natscontext/README.md @@ -0,0 +1,37 @@ +# NATS Context Connection Helper + +This is a package that helps Go developers connect to NATS using a NATS Context as featured in the `nats` Command Line +Tool. + +## Installation + +```bash +go get github.com/synadia-io/orbit.go/natscontext +``` + +## Usage + +Using the `nats` command line create a Context that can connect to your server, here we use a credential in a file: + +```bash +nats context add staging --creds /home/user/staging.creds --js-domain STAGING +``` + +We can now use the context called `staging` from Go: + +```go +nc, settings, err := natscontext.Connect("staging", nats.Name("my application")) +if err != nil { + // handle error +} + +// Get a JetStream handle using the domain in the context +js, err := jetstream.NewWithDomain(nc, settings.JSDomain) +if err != nil { + // handle error +} +``` + +If the full path to a context JSON file is given instead of the friendly name then that file will be used. + +All context settings are supported except Windows Certificate Store related settings. \ No newline at end of file diff --git a/natscontext/context.go b/natscontext/context.go new file mode 100644 index 0000000..43317b4 --- /dev/null +++ b/natscontext/context.go @@ -0,0 +1,337 @@ +// Copyright 2022-2024 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package natscontext + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "os/user" + "path/filepath" + "strings" + + "github.com/nats-io/nats.go" +) + +type context struct { + Name string `json:"-"` + config *Settings + path string +} + +type nscCreds struct { + UserCreds string `json:"user_creds"` + Operator struct { + Service []string `json:"service"` + } `json:"operator"` +} + +type Settings struct { + Name string `json:"name,omitempty"` + Description string `json:"description"` + URL string `json:"url"` + nscUrl string + SocksProxy string `json:"socks_proxy"` + Token string `json:"token"` + User string `json:"user"` + Password string `json:"password"` + Creds string `json:"creds"` + nscCreds string + NKey string `json:"nkey"` + Cert string `json:"cert"` + Key string `json:"key"` + CA string `json:"ca"` + NSCLookup string `json:"nsc"` + JSDomain string `json:"jetstream_domain"` + JSAPIPrefix string `json:"jetstream_api_prefix"` + JSEventPrefix string `json:"jetstream_event_prefix"` + InboxPrefix string `json:"inbox_prefix"` + UserJwt string `json:"user_jwt"` + ColorScheme string `json:"color_scheme"` + TLSFirst bool `json:"tls_first"` + WinCertStoreType string `json:"windows_cert_store"` + WinCertStoreMatchBy string `json:"windows_cert_match_by"` + WinCertStoreMatch string `json:"windows_cert_match"` + WinCertStoreCaMatch []string `json:"windows_ca_certs_match"` +} + +// Connect connects to the NATS server configured by the named context, empty name connects to selected context. +// If the name is a absolute name to a context configuration that will be used. The settings that are returned +// will include all the values from the Context so values like the JetStream Domain etc can be used later +func Connect(name string, opts ...nats.Option) (*nats.Conn, Settings, error) { + var nctx *context + var err error + + absName, _ := filepath.Abs(name) + if name != "" && absName == name { + nctx, err = newFromFile(name) + } else { + nctx, err = newFromName(name) + } + if err != nil { + return nil, Settings{}, err + } + + nc, err := nctx.connect(opts...) + if err != nil { + return nil, Settings{}, err + } + + return nc, *nctx.config, nil +} + +func (c *context) connect(opts ...nats.Option) (*nats.Conn, error) { + nopts, err := c.natsOptions(opts...) + if err != nil { + return nil, err + } + + return nats.Connect(c.config.URL, nopts...) +} + +func (c *context) natsOptions(opts ...nats.Option) ([]nats.Option, error) { + var nopts []nats.Option + + switch { + case c.config.User != "": + nopts = append(nopts, nats.UserInfo(c.config.User, c.config.Password)) + case c.config.Creds != "": + nopts = append(nopts, nats.UserCredentials(expandHomedir(c.config.Creds))) + + case c.config.NKey != "": + nko, err := nats.NkeyOptionFromSeed(expandHomedir(c.config.NKey)) + if err != nil { + return nil, err + } + + nopts = append(nopts, nko) + } + + if c.config.Token != "" { + nopts = append(nopts, nats.Token(expandHomedir(c.config.Token))) + } + + if c.config.Cert != "" && c.config.Key != "" { + nopts = append(nopts, nats.ClientCert(expandHomedir(c.config.Cert), expandHomedir(c.config.Key))) + } + + if c.config.CA != "" { + nopts = append(nopts, nats.RootCAs(expandHomedir(c.config.CA))) + } + + if c.config.SocksProxy != "" { + nopts = append(nopts, nats.SetCustomDialer(c.SOCKSDialer())) + } + + if c.config.InboxPrefix != "" { + nopts = append(nopts, nats.CustomInboxPrefix(c.config.InboxPrefix)) + } + + if c.config.TLSFirst { + nopts = append(nopts, nats.TLSHandshakeFirst()) + } + + if c.config.WinCertStoreType != "" { + return nil, fmt.Errorf("windows cert stores are not supported") + } + + nopts = append(nopts, opts...) + + return nopts, nil +} + +func expandHomedir(path string) string { + if path[0] != '~' { + return path + } + + usr, err := user.Current() + if err != nil { + return path + } + + return strings.Replace(path, "~", usr.HomeDir, 1) +} + +func newFromName(name string) (*context, error) { + c := &context{ + Name: name, + config: &Settings{}, + } + + err := c.loadActiveContext() + if err != nil { + return nil, err + } + + return c, nil +} + +func newFromFile(filename string) (*context, error) { + c := &context{ + Name: strings.TrimSuffix(filepath.Base(filename), filepath.Ext(filename)), + config: &Settings{}, + path: filename, + } + + err := c.loadActiveContext() + if err != nil { + return nil, err + } + + return c, nil +} + +func readCtxFromFile(file string) string { + parent, err := parentDir() + if err != nil { + return "" + } + + currentFile := filepath.Join(parent, "nats", file) + + _, err = os.Stat(currentFile) + if os.IsNotExist(err) { + return "" + } + + fc, err := os.ReadFile(currentFile) + if err != nil { + return "" + } + + return strings.TrimSpace(string(fc)) +} + +func selectedContext() string { + return readCtxFromFile("context.txt") +} + +func validName(name string) bool { + return name != "" && !strings.Contains(name, "..") && !strings.Contains(name, string(os.PathSeparator)) +} + +func ctxDir(parent string) string { + return filepath.Join(parent, "nats", "context") +} + +func knownContext(parent string, name string) bool { + if !validName(name) { + return false + } + + _, err := os.Stat(filepath.Join(ctxDir(parent), name+".json")) + return !os.IsNotExist(err) +} + +func (c *context) loadActiveContext() error { + if c.path == "" { + parent, err := parentDir() + if err != nil { + return err + } + + // none given, lets try to find it via the fs + if c.Name == "" { + c.Name = selectedContext() + if c.Name == "" { + return nil + } + } + + if !validName(c.Name) { + return fmt.Errorf("invalid context name %s", c.Name) + } + + if !knownContext(parent, c.Name) { + return fmt.Errorf("unknown context %q", c.Name) + } + + c.path = filepath.Join(parent, "nats", "context", c.Name+".json") + } + + ctxContent, err := os.ReadFile(c.path) + if err != nil { + return err + } + + err = json.Unmarshal(ctxContent, c.config) + if err != nil { + return err + } + + // performing environment variable expansion for the path of the cerds. + c.config.Creds = os.ExpandEnv(c.config.Creds) + + if c.config.NSCLookup != "" { + err := c.resolveNscLookup() + if err != nil { + return err + } + } + + return nil +} + +func (c *context) resolveNscLookup() error { + if c.config.NSCLookup == "" { + return nil + } + + path, err := exec.LookPath("nsc") + if err != nil { + return fmt.Errorf("cannot find 'nsc' in user path") + } + + cmd := exec.Command(path, "generate", "profile", c.config.NSCLookup) + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("nsc invoke failed: %s", string(out)) + } + + var result nscCreds + err = json.Unmarshal(out, &result) + if err != nil { + return fmt.Errorf("could not parse nsc output: %s", err) + } + + if result.UserCreds != "" { + c.config.nscCreds = result.UserCreds + } + + if len(result.Operator.Service) > 0 { + c.config.nscUrl = strings.Join(result.Operator.Service, ",") + } + + return nil +} + +func parentDir() (string, error) { + parent := os.Getenv("XDG_CONFIG_HOME") + if parent != "" { + return parent, nil + } + + u, err := user.Current() + if err != nil { + return "", err + } + + if u.HomeDir == "" { + return "", fmt.Errorf("cannot determine home directory") + } + + return filepath.Join(u.HomeDir, parent, ".config"), nil +} diff --git a/natscontext/dialer.go b/natscontext/dialer.go new file mode 100644 index 0000000..392452a --- /dev/null +++ b/natscontext/dialer.go @@ -0,0 +1,52 @@ +// Copyright 2022-2024 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package natscontext + +import ( + "net" + "net/url" + + "github.com/nats-io/nats.go" + "golang.org/x/net/proxy" +) + +// In context.go we have the regular accessors for the context, +// WithSocksProxy and SocksProxy. +// +// The NATSOptions builder will call SOCKSDialer to get our custom dialer, +// to pass along, if the proxy is set. The result has to satisfy the +// nats.CustomDialer interface. + +// SocksDialer should satisfy the NATS CustomDialer interface +type SocksDialer struct { + proxy string +} + +var _ nats.CustomDialer = SocksDialer{} + +func (c *context) SOCKSDialer() SocksDialer { + return SocksDialer{proxy: c.config.SocksProxy} +} + +func (sd SocksDialer) Dial(network, address string) (net.Conn, error) { + u, err := url.Parse(sd.proxy) + if err != nil { + return nil, err + } + dialer, err := proxy.FromURL(u, proxy.Direct) + if err != nil { + return nil, err + } + return dialer.Dial(network, address) +} diff --git a/natscontext/expansion_test.go b/natscontext/expansion_test.go new file mode 100644 index 0000000..860a3d2 --- /dev/null +++ b/natscontext/expansion_test.go @@ -0,0 +1,36 @@ +// Copyright 2022 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package natscontext + +import "testing" + +func TestContext(t *testing.T) { + // Do nothing if the string does not start with ~ + t1 := expandHomedir("foo") + if t1 != "foo" { + t.Fatalf("failed to expand home directory for string 'foo': %s", t1) + } + + // Expand ~ to HOMEDIR if string starts with ~ + t2 := expandHomedir("~/foo") + if t2 == "~/foo" { + t.Fatalf("failed to expand home directory for string 'foo': %s", t2) + } + + // Do nothing if there is a ~ but it is not the first character of the string + t3 := expandHomedir("/~/foo") + if t3 != "/~/foo" { + t.Fatalf("failed to expand home directory for string 'foo': %s", t3) + } +} diff --git a/natscontext/go.mod b/natscontext/go.mod new file mode 100644 index 0000000..ceaf3cb --- /dev/null +++ b/natscontext/go.mod @@ -0,0 +1,16 @@ +module github.com/synadia-io/orbit.go/natscontext + +go 1.22.0 + +require ( + github.com/nats-io/nats.go v1.37.0 + golang.org/x/net v0.31.0 +) + +require ( + github.com/klauspost/compress v1.17.11 // indirect + github.com/nats-io/nkeys v0.4.7 // indirect + github.com/nats-io/nuid v1.0.1 // indirect + golang.org/x/crypto v0.29.0 // indirect + golang.org/x/sys v0.27.0 // indirect +) diff --git a/natscontext/go.sum b/natscontext/go.sum new file mode 100644 index 0000000..3736a01 --- /dev/null +++ b/natscontext/go.sum @@ -0,0 +1,14 @@ +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE= +github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= +github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= +github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/natscontext/go.work b/natscontext/go.work new file mode 100644 index 0000000..080652d --- /dev/null +++ b/natscontext/go.work @@ -0,0 +1,6 @@ +go 1.22.1 + +use ( + . + ./test +) diff --git a/natscontext/go.work.sum b/natscontext/go.work.sum new file mode 100644 index 0000000..b4b2a84 --- /dev/null +++ b/natscontext/go.work.sum @@ -0,0 +1,13 @@ +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= +golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= diff --git a/natscontext/test/context_test.go b/natscontext/test/context_test.go new file mode 100644 index 0000000..11bc469 --- /dev/null +++ b/natscontext/test/context_test.go @@ -0,0 +1,100 @@ +package test + +import ( + "encoding/json" + "github.com/nats-io/nats-server/v2/server" + natsserver "github.com/nats-io/nats-server/v2/test" + "github.com/nats-io/nats.go" + "github.com/synadia-io/orbit.go/natscontext" + "os" + "testing" + "time" +) + +func TestContext(t *testing.T) { + srv, _ := RunServerWithConfig("testdata/server.conf") + defer srv.Shutdown() + + nctx := natscontext.Settings{ + URL: srv.ClientURL(), + User: "test", + Password: "s3cret", + } + + j, _ := json.Marshal(nctx) + tf, err := os.CreateTemp("", "") + if err != nil { + t.Fatalf("creating temp file failed: %v", err) + } + defer os.Remove(tf.Name()) + + _, err = tf.Write(j) + if err != nil { + t.Fatalf("writing temp file failed: %v", err) + } + tf.Close() + + nc, _, err := natscontext.Connect(tf.Name(), nats.Name("unit tests")) + if err != nil { + t.Fatalf("connecting to nats server failed: %v", err) + } + + resp, err := nc.Request("$SYS.REQ.SERVER.PING.CONNZ", nil, 2*time.Second) + if err != nil { + t.Fatalf("user info failed: %v", err) + } + + type connz struct { + Data struct { + Connections server.ConnInfos `json:"connections"` + } `json:"data"` + } + + var nfo connz + err = json.Unmarshal(resp.Data, &nfo) + if err != nil { + t.Fatalf("invalid user info: %v", err) + } + + if len(nfo.Data.Connections) != 1 { + t.Fatalf("invalid number of connections: %d", len(nfo.Data.Connections)) + } + + if nfo.Data.Connections[0].Name != "unit tests" { + t.Fatalf("invalid connection name: %q", nfo.Data.Connections[0].Name) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Running nats server in separate Go routines +//////////////////////////////////////////////////////////////////////////////// + +// RunDefaultServer will run a server on the default port. +func RunDefaultServer() *server.Server { + return RunServerOnPort(nats.DefaultPort) +} + +// RunServerOnPort will run a server on the given port. +func RunServerOnPort(port int) *server.Server { + opts := natsserver.DefaultTestOptions + opts.Port = port + opts.Cluster.Name = "testing" + return RunServerWithOptions(&opts) +} + +// RunServerWithOptions will run a server with the given options. +func RunServerWithOptions(opts *server.Options) *server.Server { + return natsserver.RunServer(opts) +} + +// RunServerWithConfig will run a server with the given configuration file. +func RunServerWithConfig(configFile string) (*server.Server, *server.Options) { + return natsserver.RunServerWithConfig(configFile) +} + +func RunBasicJetStreamServer() *server.Server { + opts := natsserver.DefaultTestOptions + opts.Port = -1 + opts.JetStream = true + return RunServerWithOptions(&opts) +} diff --git a/natscontext/test/go.mod b/natscontext/test/go.mod new file mode 100644 index 0000000..f18ad5b --- /dev/null +++ b/natscontext/test/go.mod @@ -0,0 +1,17 @@ +module github.com/synadia-io/orbit.go/natscontext/test + +go 1.22.1 + +require github.com/nats-io/nats-server/v2 v2.10.22 + +require ( + github.com/klauspost/compress v1.17.11 // indirect + github.com/minio/highwayhash v1.0.3 // indirect + github.com/nats-io/jwt/v2 v2.5.8 // indirect + github.com/nats-io/nats.go v1.37.0 // indirect + github.com/nats-io/nkeys v0.4.7 // indirect + github.com/nats-io/nuid v1.0.1 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/time v0.7.0 // indirect +) diff --git a/natscontext/test/go.sum b/natscontext/test/go.sum new file mode 100644 index 0000000..3db62c5 --- /dev/null +++ b/natscontext/test/go.sum @@ -0,0 +1,23 @@ +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= +github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= +github.com/nats-io/jwt/v2 v2.5.8 h1:uvdSzwWiEGWGXf+0Q+70qv6AQdvcvxrv9hPM0RiPamE= +github.com/nats-io/jwt/v2 v2.5.8/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A= +github.com/nats-io/nats-server/v2 v2.10.22 h1:Yt63BGu2c3DdMoBZNcR6pjGQwk/asrKU7VX846ibxDA= +github.com/nats-io/nats-server/v2 v2.10.22/go.mod h1:X/m1ye9NYansUXYFrbcDwUi/blHkrgHh2rgCJaakonk= +github.com/nats-io/nats.go v1.36.0 h1:suEUPuWzTSse/XhESwqLxXGuj8vGRuPRoG7MoRN/qyU= +github.com/nats-io/nats.go v1.36.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= +github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE= +github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= +github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= +github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= diff --git a/natscontext/test/testdata/server.conf b/natscontext/test/testdata/server.conf new file mode 100644 index 0000000..4d48013 --- /dev/null +++ b/natscontext/test/testdata/server.conf @@ -0,0 +1,6 @@ +port: -1 + +authorization { + user: "test" + password: "s3cret" +} \ No newline at end of file diff --git a/natsext/go.work.sum b/natsext/go.work.sum index ce021a9..0e53159 100644 --- a/natsext/go.work.sum +++ b/natsext/go.work.sum @@ -1,17 +1,21 @@ github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/nats-io/nats.go v1.36.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=