Testing NATS-streaming in Kubernetes with minimal effort - go

I wanted to test a very basic application for NATS-streaming on Kubernetes. To do so, I followed the commands from the official NATS-docs.
It basically comes down to running
kubectl apply -f https://raw.githubusercontent.com/nats-io/k8s/master/nats-server/single-server-nats.yml
kubectl apply -f https://raw.githubusercontent.com/nats-io/k8s/master/nats-streaming-server/single-server-stan.yml
in a terminal with access to the cluster (it's a kind-cluster in my case).
I used stan.go as the NATS-streaming-client. Here is the code I tried to connect to the NATS-streaming-server:
package main
import stan "github.com/nats-io/stan.go"
func main() {
sc, err := stan.Connect("stan", "test-client")
if err != nil {
panic(err)
}
if err := sc.Publish("test-subject", []byte("This is a test-message!")); err != nil {
panic(err)
}
}
and this is the error I'm getting:
panic: nats: no servers available for connection
goroutine 1 [running]:
main.main()
/Users/thilt/tmp/main.go:9 +0x15d
exit status 2
so I think another name was used for the cluster or something. If I use the provided example with nats-box from the docs.nats-link above, it also doesn't work! Where did I go wrong here?
I will happily provide more information, if needed.

There is a great example in stan.go docs:
// Connect to NATS
nc, err := nats.Connect(URL, opts...)
if err != nil {
log.Fatal(err)
}
defer nc.Close()
sc, err := stan.Connect(clusterID, clientID, stan.NatsConn(nc))
if err != nil {
log.Fatalf("Can't connect: %v.\nMake sure a NATS Streaming Server is running at: %s", err, URL)
}
defer sc.Close()
Your error happens because by default stan connects to localhost address (source code):
// DefaultNatsURL is the default URL the client connects to
DefaultNatsURL = "nats://127.0.0.1:4222"
Notice that povided above example overwrite this default connection.
Stan source code is short and easy to analyze. I really recommend you to try to analyze it and figure out what it does.
Now let's put it all together; here is a working example:
package main
import (
nats "github.com/nats-io/nats.go"
stan "github.com/nats-io/stan.go"
)
func main() {
// Create a NATS connection
nc, err := nats.Connect("nats://nats:4222")
if err != nil {
panic(err)
}
// Then pass it to the stan.Connect() call.
sc, err := stan.Connect("stan", "me", stan.NatsConn(nc))
if err != nil {
panic(err)
}
if err := sc.Publish("test-subject", []byte("This is a test-message!")); err != nil {
panic(err)
}
}

Related

How to use kubernetes go-client on amazon eks service?

I've been looking for documentation for a long time and still couldn't find any clear connection procedure.
I came up with this code sample :
package aws
import (
"fmt"
"net/http"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/eks"
"github.com/joho/godotenv"
)
func Connect() {
godotenv.Load(".env")
session := session.Must(session.NewSession())
svc := eks.New(session)
clusters, err := svc.ListClusters(&eks.ListClustersInput{})
if err != nil {
fmt.Println(err.Error())
}
fmt.Println(clusters)
}
i mean, this still returns a 403 forbidden error because of env variable mess, but the code is valid i guess. My question is, having this connection established : how to convert this svc variable into the *kubernetes.Clientset one from the go driver ?
Have you had a look at the client-go example on how to authenticate in-cluster?
Code that authenticate to the Kubernetes API typically start like this:
// creates the in-cluster config
config, err := rest.InClusterConfig()
if err != nil {
panic(err.Error())
}
// creates the clientset
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err.Error())
}
I use the following code to automatically detect where its running from local machine or any kubernetes cluster.
var config *rest.Config
if _, err := os.Stat("/var/run/secrets/kubernetes.io/serviceaccount/token"); err == nil {
config, err = rest.InClusterConfig()
if err != nil {
log.Fatal(err)
}
} else if os.IsNotExist(err) {
config, err = clientcmd.BuildConfigFromFlags("", *kubeConfig)
if err != nil {
log.Fatal("No serviceaccount mounted or -kubeconfig flag passed or .kube/config file \n " ,err)
}
}
// Create an rest client not targeting specific API version
clientSet, err := kubernetes.NewForConfig(config)
if err != nil {
log.Fatal(err)
}

How can I create eth account via go-ethereum?

I'm running local ethereum node on my localhost on http://127.0.0.1:7545 (using ganache). I create a new account with keystore as below snippet. But, how can my local ethereum node can be aware of that new account? Normally, I can get balances, transactions etc... But I couldn't achieve to awareness of new account and managing them over my network via go-ethereum SDK.
func CreateAccount() {
password := "secret"
ks := keystore.NewKeyStore("./wallets", keystore.StandardScryptN, keystore.StandardScryptP)
account, err := ks.NewAccount(password)
if err != nil {
log.Fatal(err)
}
fmt.Println(account.Address.Hex())
}
In order to make go-ethereum talk to your Ganache client, you need to call Dial, which accepts a provider URL. In the case described, that would be done as follows:
client, err := ethclient.Dial("http://localhost:7545")
if err != nil {
log.Fatal(err)
}
So all together, you would have something like this along with what you are trying to accomplish in creating a new account and having Ganache see it:
func main() {
client, err := ethclient.Dial("http://localhost:7545")
if err != nil {
log.fatal(err)
}
fmt.Println("we have a connection")
}
func CreateAccount() {
ks := keystore.NewKeyStore("./wallets", keystore.StandardScryptN, keystore.StandardScryptP)
password := "secret"
account, err := ks.NewAccount(password)
if err != nil {
log.Fatal(err)
}
fmt.Println(account.Address.Hex())
}
A really great reference for all things go-ethereum is https://goethereumbook.org which walks through this and more step-by-step with full code examples.

Golang grpc: how to determine when the server has started listening?

So I have the following:
type Node struct {
Table map[string]string
thing.UnimplementedGreeterServer
address string
}
func (n *Node) Start() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
thing.RegisterGreeterServer(s, n)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
In my main function I'll spin up mulitple nodes like so:
func main() {
n :=Node{Table: map[string]string{}}
go n.Start()
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
}
The problem is, because I'm spinning up the node concurrently, there's a chance the dial up connection might not work because the node might not have been setup yet.
Ideally, I'd like a done channel that tells me when the grpc server has actually started listening. How do I accomplish this?
This is essntially the same problem as How to add hook on golang grpc server start? which doesn't have an answer
s.Serve(listener) blocks, so you can't achieve your purpose by having a done chan, instead you have to implement the healthcheck and readiness for your service, and check those before performing any request by the client.
The server should implement the following proto:
syntax = "proto3";
package grpc.health.v1;
message HealthCheckRequest {
string service = 1;
}
message HealthCheckResponse {
enum ServingStatus {
UNKNOWN = 0;
SERVING = 1;
NOT_SERVING = 2;
SERVICE_UNKNOWN = 3; // Used only by the Watch method.
}
ServingStatus status = 1;
}
service Health {
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse);
}
For example, the envoy proxy grpc_health_check works with the above proto.
Read GRPC Health Checking Protocol for more information.
The server can be Dialed as soon as net.Listen returns a nil error. Dial will block until the server calls Accept (which will happen somewhere in s.Serve in this case).
Either move creation of the listener into the caller and pass it as an argument:
func (n *Node) Start(lis net.Listener) {
s := grpc.NewServer()
thing.RegisterGreeterServer(s, n)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
n := Node{Table: map[string]string{}}
go n.Start(lis)
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
}
Or signal that the listener is up after Listen returns:
func (n *Node) Start(up chan struct{}) {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
if up != nil {
close(up)
}
s := grpc.NewServer()
thing.RegisterGreeterServer(s, n)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
func main() {
n := Node{Table: map[string]string{}}
up := make(chan struct{})
go n.Start(up)
<-up
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
}
For all those who are still looking for an answer to this, here is another simple way to do it. Start the server in a child routine. Here is a code snippet:
// Start the server in a child routine
go func() {
if err := s.Serve(listener); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}()
fmt.Println("Server succesfully started on port :50051")
In my case I am using MongoDB as well, so when you run it, you get:
grpc-go-mongodb-cobra>go run server/main.go
Starting server on port :50051...
Connecting to MongoDB...
Connected to MongoDB
Server succesfully started on port :50051
I have also written a Blog post on this, with working code in GitHub. Here is the link: https://softwaredevelopercentral.blogspot.com/2021/03/golang-grpc-microservice.html

TCP connection between different hosts on different connections

I am trying to make a TCP connection where 2 hosts are on different networks. I have made a client and server in go lang
Server.go
func main() {
l, err := net.Listen("tcp",":8000")
if err != nil{
log.Fatal(err)
}
for{
conn,err := l.Accept()
if err != nil{
log.Println(err)
return
}
go serve(conn)
}
}
func serve(connection net.Conn){
defer connection.Close()
for{
buffer := make([]byte,1024)
_,err := connection.Read(buffer[:])
if err != nil{
log.Println(err)
return
}
fmt.Println(string(buffer))
}
}
client.go
func main() {
c, err := net.Dial("tcp",":8000")
if err != nil{
log.Fatal(err)
}
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan(){
_,err := c.Write([]byte(scanner.Text()))
if err != nil{
log.Fatal(err)
}
}
}
when I run my server.go and use telnet on a device that is on the same network to send messages to the server, it works without any problem. I can send messages and it will show up in the console.
But when I try to run telnet on a device with a different network, it does not work. I try to send a message and nothing appears in the console. I then get a message something along the lines of connection closed by the foreign host.
I am a bit of a newbie in networking, I don't understand why is it not able to show any messages from another device on another network.
How do I achieve a TCP connection between 2 different hosts on different connections in go lang?
thanks.
You should call through an external address, not a local address:
Find the external address of your server application host. Put it in client code:
For example:
c, err := net.Dial("tcp","45.44.230.205:8000")
That's it

How to make a websocket client wait util the server is running?

I want to create a websocket client that waits until the server is running. If the connection is closed by the server it should reconnect.
What I tried does not work and my code exits with a runtime error:
panic: runtime error: invalid memory address or nil pointer dereference
func run() {
origin := "http://localhost:8080/"
url := "ws://localhost:8080/ws"
ws, err := websocket.Dial(url, "", origin)
if err != nil {
fmt.Println("Connection fails, is being re-connection")
main()
}
if _, err := ws.Write([]byte("something")); err != nil {
log.Fatal(err)
}
}
Your example looks like a code snippet. It's difficult to say why you're getting that error without seeing all the code. As were pointed out in the comments to your post, you can't call main() again from your code and including the line numbers from the panic report would be helpful as well.
Usually minimizing your program to a minimal case that anyone can run and reproduce the error is the fastest way to get help. I've reconstructed yours for you in such fashion. Hopefully you can use it to fix your own code.
package main
import (
"websocket"
"fmt"
"log"
"time"
)
func main() {
origin := "http://localhost:8080/"
url := "ws://localhost:8080/ws"
var err error
var ws *websocket.Conn
for {
ws, err = websocket.Dial(url, "", origin)
if err != nil {
fmt.Println("Connection fails, is being re-connection")
time.Sleep(1*time.Second)
continue
}
break
}
if _, err := ws.Write([]byte("something")); err != nil {
log.Fatal(err)
}
}
To run this, just copy it into a file called main.go on your system and then run:
go run main.go

Resources