I have been trying to create a grpc client in Go and I have followed the correct instructions as shown in the official grpc site. When I start my grpc server written in node.js, the connection works well but upon compiling the protocol buffer in Go and creating a client interface with the correct grpc client configurations, I run into an error.
Here is my what I have in my identity.pb.go.
type IdentityServiceClient interface {
CreateUser(ctx context.Context, in *GoogleIdToken, opts ...grpc.CallOption) (error, *UserInfo)
}
type simpleServerClient struct {
connection *grpc.ClientConn
}
func NewSimpleServerClient(connection *grpc.ClientConn) IdentityServiceClient {
return &simpleServerClient{connection}
}
func (simpleClient *simpleServerClient) CreateUser(ctx context.Context, in *GoogleIdToken, opts ...grpc.CallOption) (error, *UserInfo) {
out := new(UserInfo)
err := simpleClient.connection.Invoke(ctx, "/protobuf.IdentityService/CreateUser", in, out, opts...)
if err != nil {
return err, nil
}
return nil, out
}
here is the identity.proto
syntax="proto3";
package protobuf;
service IdentityService {
rpc CreateUser (GoogleIdToken) returns (UserInfo) {}
}
message GoogleIdToken {
string token = 1;
}
message UserInfo {
string name = 1;
string email = 2;
message Profile {
string imageUrl = 1;
string lastUpdated = 2;
};
Profile profile = 3;
string token = 4;
}
here is my main.go
import pb "github.com/Duncanian/iam-gateway/server/protobuf"
func grpcConnection() {
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
if err != nil {
log.Fatalf("Failed to start gRPC connection: %v", err)
}
defer conn.Close()
client := pb.NewSimpleServerClient(conn)
err, _ = client.CreateUser(context.Background(), &pb.GoogleIdToken{Token: "tevgvybububvvg"})
if err != nil {
log.Fatalf("Failed to create user: %v", err)
}
log.Println("Created user!")
}
I expect the output of passing a correct google auth token to get me the correct user details which are
name: user,
email: user.email#user.com,
profile: {
imageUrl: myimageUrl,
lastUpdated: mylastUpdatedTime,
},
token,
but got
rpc error: code = Unimplemented desc = RPC method not implemented /protobuf.IdentityService/CreateUser
Here are my github repos:
Go grpc client &
Node JS grpc server
I had the same problem.
Here is my solution:
After compiling .proto I created 2 files: client.go and server.go.
In client.go I implemented my methods (rpc in terms of protobuffers) and had a main function.
In server.go I defined a server struct server with one field: Unimplemented*ServiceName*Server. After that I also implemented the mentioned above methods, but those had a receiver type: func (s *server) Foo(ctx context.Context, *other params*)
That worked for me, hopefully it will help you!
The error indicates that the /protobuf.IdentityService/CreateUser method is not registered at the server side. And I didn't see any service registration code in your linked server code. Please take a look at the node.js guide here.
Using GUI of grpc try to send your protos to your both servers and check for upcoming errors/correct endpoints.
In my case java proto had a package inside which was added to the endpoint.
Had package com.example.grpc;
instead of
option java_package = "com.example.grpc";
Related
I'm currently working on an api gateway that forwards requests to an grpc client. The routes use a middleware. Within that middleware the Validate function from the dialed grpc client is used. The function is in the last code snippet at the end.
The gateway is based on a gin router, which, as far as I understand handles each request in a separat go routine. I can send as many requests simultaneously to either to endpoint /protected/1 or to endpoint /protected/2 and they get handled correctly.
The Validate function is having problems handling request sent simultaneously to /protected/1 and /protected/2. Sending two request to both endpoints simultaneously results in that either both, none or only one of the requests is handled correctly. The error message that I receive is actually from the gorm function trying to query the user in the Validate function (ERROR: relation "users" does not exist (SQLSTATE 42P01)). I use a gorm database object that is connected to a postgres database. This behaviour suggests that some how a resource is shared. Using run -race does not give any insights.
So my questions are, why does this setup not work to send requests simultaneously to both endpoints?
I tried to make a working minimal example, but somehow I could nor reproduce the error in a minimal setting, hence I would like to share my code snippets here.
Important snippets of the the Api Gateway implementation
func main() {
c, err := config.LoadConfig()
...
r := gin.Default()
auth.RegisterRoutes(r, &c)
r.Run(":" + c.Port)
}
whereas the routes are defined as
func RegisterRoutes(r *gin.Engine, c *config.Config){
svc := &ServiceClient{
Client: InitServiceClient(c),
}
a := InitAuthMiddleware(svc)
// routes
protected := r.Group("/protected")
protected.Use(a.AuthRequired)
protected.GET("/1", svc.DoStuff)
protected.GET("/2", svc.DoStuff)
}
// dummy handler
func (svc *ServiceClient) DoStuff(ctx *gin.Context) {
handler.DoStuff(ctx, svc.Client)
}
The grpc client is dialed with the following function
type ServiceClient struct {
Client pb.AuthServiceClient
}
func InitServiceClient(c *config.Config) pb.AuthServiceClient {
// using WithInsecure() because no SSL running
cc, err := grpc.Dial(c.AuthSvcUrl, grpc.WithInsecure())
if err != nil {
fmt.Println("Could not connect:", err)
}
return pb.NewAuthServiceClient(cc)
}
and the middleware is implemented in the following way:
type AuthMiddlewareConfig struct {
svc *ServiceClient
}
func InitAuthMiddleware(svc *ServiceClient) AuthMiddlewareConfig {
return AuthMiddlewareConfig{svc}
}
func (c *AuthMiddlewareConfig) AuthRequired(ctx *gin.Context) {
access_token, _ := ctx.Cookie("access_token")
res, err := c.svc.Client.Validate(context.Background(), &pb.ValidateRequest{
Token: access_token,
TokenType: "ACCESS_TOKEN",
Url: ctx.Request.URL.String(),
})
if err != nil || res.Status != http.StatusOK {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"status": http.StatusUnauthorized, "error": res.Error})
return
}
ctx.Set("userId", res.UserId)
ctx.Next()
}
Important snippets of the Auth Service:
func serve(c *conf.Configuration) {
/*
Dial returns:
type Repository struct {
DB *gorm.DB
}
*/
h := storage.Dial(&c.DB)
serviceUri := fmt.Sprintf(":%s", c.API.Port)
lis, err := net.Listen("tcp", serviceUri)
if err != nil {
logrus.Fatal("Failed to listen on: ", err)
}
s := api.Server{
R: h,
//
}
grpcServer := grpc.NewServer()
pb.RegisterAuthServiceServer(grpcServer, &s)
if err := grpcServer.Serve(lis); err != nil {
logrus.Fatalln("Failed to serve:", err)
}
}
and the validate function is implemented by
type Server struct {
R storage.Repository
//
// #https://github.com/grpc/grpc-go/issues/3794:
pb.UnimplementedAuthServiceServer
}
func (s *Server) Validate(ctx context.Context, req *pb.ValidateRequest) (*pb.ValidateResponse, error) {
// token validation stuff works
var user models.User
// causes error sometimes
if result := s.R.DB.Where(&models.User{Id: claims.Id}).First(&user); result.Error != nil {
return &pb.ValidateResponse{
Status: http.StatusNotFound,
Error: "User not found",
}, nil
}
// send response
}
EDIT
For completeness here the function that connects to the postgres client.
func openDb(c *conf.PostgresConfiguration, gormConfig *gorm.Config, database string) *gorm.DB {
connectionString := fmt.Sprintf("postgres://%s:%s#%s:%s/%s", c.User, c.Password, c.Host, c.Port, database)
db, err := gorm.Open(postgres.Open(connectionString), gormConfig)
if err != nil {
logrus.Fatal(fmt.Sprintf("Unable to connect to database '%s' given url: ", database), err)
}
logrus.Info(fmt.Sprintf("Connected to database %s", database))
return db
}
func Dial(c *conf.PostgresConfiguration) Repository {
gormDefaultConfig := &gorm.Config{}
gormAppConfig := &gorm.Config{}
// connect to default database
logrus.Info(fmt.Sprintf("Use default database %s for initialization", c.DefaultDatabase))
defaultDb := openDb(c, gormDefaultConfig, c.DefaultDatabase)
// check if database exists and create it if necessary
var dbexists bool
dbSQL := fmt.Sprintf("SELECT EXISTS (SELECT FROM pg_database WHERE datname = '%s') AS dbexists;", c.AppDatabase)
defaultDb.Raw(dbSQL).Row().Scan(&dbexists)
if !dbexists {
logrus.Info(fmt.Sprintf("Created database %s", c.AppDatabase))
db := defaultDb.Exec(fmt.Sprintf("CREATE DATABASE %s;", c.AppDatabase))
if db.Error != nil {
logrus.Fatal("Unable to create app database: ", db.Error)
}
}
// connect to app databse
appDb := openDb(c, gormAppConfig, c.AppDatabase)
// check if schema exists and create it if necessary
var schemaexists bool
schemaSQL := fmt.Sprintf("SELECT EXISTS(SELECT FROM pg_namespace WHERE nspname = '%s') AS schemaexisits;", c.AppDatabaseSchema)
appDb.Raw(schemaSQL).Row().Scan(&schemaexists)
// create app specfic database, if not already existing
if !schemaexists {
// create service specific schema
db := appDb.Exec(fmt.Sprintf("CREATE SCHEMA %s;", c.AppDatabaseSchema))
if db.Error != nil {
logrus.Fatal(fmt.Sprintf("Unable to create database schema %s", c.AppDatabaseSchema), db.Error)
}
logrus.Info(fmt.Sprintf("Created database schema %s", c.AppDatabaseSchema))
}
db := appDb.Exec(fmt.Sprintf(`set search_path='%s';`, c.AppDatabaseSchema))
logrus.Info(fmt.Sprintf("Use existing database schema %s", c.AppDatabaseSchema))
if db.Error != nil {
logrus.Fatal(fmt.Sprintf("Unable to set search_path for database to schema %s", c.AppDatabaseSchema), db.Error)
}
// migrate table
appDb.AutoMigrate(&models.User{})
return Repository{appDb}
}
Edit2
Changing the connection string in the openDb function to connectionString := fmt.Sprintf("postgres://%s:%s#%s:%s/%s?search_path=%s", c.User, c.Password, c.Host, c.Port, database, c.AppDatabaseSchema) fixes the issue (see how the search_path is part of the url).
Thank you!
I am new Stack Overflow and this is my first question so I'm very open and happy to make any improvements to it :)
I'm having an issue when I run a test method to unlike an artwork. I have a method that enables a user to like a specific artwork which is categorised by their own separate artwork uuid's, and the method works perfectly. I have a console.proto file where I specify the necessary request and response messages for both the LikeArtwork and UnlikeArtwork methods, and have created the methods themselves in a methods.go file. I have other methods that I am testing and they all seem to work perfectly except for the UnlikeArtwork method.
I have a followUser and UnfollowUser method, which both work in exactly the same way, except in the UnfollowUser method, a user is just removed from the "followers" slice/array that contains all of the followers. The concept is exactly the same for the likeArtwork and UnlikeArtwork methods where users are either added or removed from the "likes" slice/array, which is why I am so confused as to why it is causing my User's server to crash with the error:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x58 pc=0x104d0b844]
My console.proto is defined as follows:
service Service {
// Registers a user on the hockney platform/ This entails creating the user
// using the CreateUser method and uploading their profile picture using the
// UploadProfilePicture method/ Down the line, this could also entail the
// creation of a custodian wallet for users as well/
rpc RegisterUser (RegisterUserRequest) returns (RegisterUserResponse) {}
// Follow user takes the user of the current session and updates their details
// and that of the followee to reflect the following
rpc FollowUser (FollowUserRequest) returns (FollowUserResponse) {}
// Like an artwork
rpc LikeArtwork (LikeArtworkRequest) returns (LikeArtworkResponse) {}
// Unfollow a user
rpc UnfollowUser (UnfollowUserRequest) returns (UnfollowUserResponse) {}
// Unlike an artwork
rpc UnlikeArtwork (UnlikeArtworkRequest) returns (UnlikeArtworkResponse) {}
// New UnLike an artwork method
rpc UnLikeArtwork (UnLikeArtworkRequest) returns (UnLikeArtworkResponse) {}
}
and,
// Request message for the FollowUser method
message FollowUserRequest {
// The user of the current session
// Format: users/{username}
string current_user = 1;
// The user to follow
// Format: users/{username}
string user_to_follow = 2;
}
// Response message for the FollowUser method
// Reserved for future use...
message FollowUserResponse {}
// Request message for LikeArtwork
message LikeArtworkRequest {
// The user that likes
// Format: users/{username}
string user = 1;
// The artwork that has been liked
// Format: artworks/{uuid}
string artwork = 2;
}
// Response message for LikeArtwork
message LikeArtworkResponse {}
// Request message for the UnfollowUser method
message UnfollowUserRequest {
// The user of the current session
// Format: users/{username}
string current_user = 1;
// The user to unfollow
// Format: users/{username}
string user_to_unfollow = 2;
}
// Response message for UnfollowUser method
message UnfollowUserResponse {}
// Request message for the UnlikeArtwork method
message UnlikeArtworkRequest {
// The user that unlikes
// Format: users/{username}
string user = 1;
// The artwork that has been unliked
// Format: artworks/{uuid}
string artwork_to_unlike = 2;
}
// Response message for the UnlikeArtwork method
message UnlikeArtworkResponse {}
The method I wrote to unlike an artwork works in exactly the same way as the LikeArtwork method, however, a user is removed from the "likes" slice. This occurs when currentUser.Likes = append(...)... . I don't think thats where the issue lies, since running this code through the Goland debugger, the error seems to occur before it gets to this part of the function. The function for this method is shown below:
func (s *myService) UnlikeArtwork(ctx context.Context, req *pb.UnlikeArtworkRequest) (*pb.UnlikeArtworkResponse, error) {
currentUser, err := clients.Users.GetUser(ctx, &pbUsers.GetUserRequest{
Name: req.GetUser(),
ReadMask: &fieldmaskpb.FieldMask{Paths: []string{"likes"}},
})
if err != nil {
return nil, err
}
// Set new likes
//currentUser.Following = append(currentUser.Following[:sort.StringSlice(currentUser.Following).Search(req.GetUserToUnfollow())], currentUser.Following[sort.StringSlice(currentUser.Following).Search(req.GetUserToUnfollow())+1:]...)
currentUser.Likes = append(currentUser.Likes[:sort.StringSlice(currentUser.Likes).Search(req.GetUser())], currentUser.Likes[sort.StringSlice(currentUser.Likes).Search(req.GetUser())+1:]...)
// Update the current_user
_, err = clients.Users.UpdateUser(ctx, &pbUsers.UpdateUserRequest{
User: currentUser,
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"likes"}},
})
if err != nil {
return nil, err
}
currentArtwork, err := clients.Artworks.GetArtwork(ctx, &pbArtworks.GetArtworkRequest{
Name: req.GetArtworkToUnlike(),
ReadMask: &fieldmaskpb.FieldMask{Paths: []string{"likes"}},
})
if err != nil {
return nil, err
}
// Set the likes
//userToUnfollow.Followers = append(userToUnfollow.Followers[:sort.StringSlice(userToUnfollow.Followers).Search(req.GetCurrentUser())], userToUnfollow.Followers[sort.StringSlice(userToUnfollow.Followers).Search(req.GetCurrentUser())+1:]...)
currentArtwork.Likes = append(currentArtwork.Likes[:sort.StringSlice(currentArtwork.Likes).Search(req.GetArtworkToUnlike())], currentArtwork.Likes[sort.StringSlice(currentArtwork.Likes).Search(req.GetArtworkToUnlike())+1:]...)
// Update the current artwork
_, err = clients.Artworks.UpdateArtwork(ctx, &pbArtworks.UpdateArtworkRequest{
Artwork: currentArtwork,
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"likes"}},
})
if err != nil {
return nil, err
}
return &pb.UnlikeArtworkResponse{}, nil
}
The test was then run in a methods_test.go file where the inputs to the function and the function itself was simulated.
func TestMyService_UnlikeArtwork(t *testing.T) {
req := pbConsole.UnlikeArtworkRequest{
User: "PurpleRaine",
ArtworkToUnlike: "artworks/0cca6063-7b6f-464a-ac88-dff8679a3905",
}
// Run a method
res, err := client.UnlikeArtwork(context.Background(), &req)
if err != nil {
t.Error(err)
}
log.Println(logging.Entry{Message: res.String()})
}
The output of the test is:
methods_test.go:111: rpc error: code = Unavailable desc = error reading from server: EOF
methods_test.go:114: {"message":"\u003cnil\u003e","severity":"INFO"}
--- FAIL: TestMyService_UnLikeArtwork (0.01s)
FAIL
and this happens along with the server crash error listed at the beginning of this question.
Additional Information:
My user's server is configured as follows:
package main
import (
"context"
"fmt"
pb "github.com/jaebrownn/hockney/protobuf/go/hock/ap/resources/users/v1"
"google.golang.org/grpc"
"hock.ap.resources.users.v1/internal/logging"
"hock.ap.resources.users.v1/internal/methods"
"log"
"net"
"os"
)
// clients is a global clients, initialized once per cloud run instance.
var ()
func init() {
// Disable log prefixes such as the default timestamp.
// Prefix text prevents the message from being parsed as JSON.
// A timestamp is added when shipping logs to Cloud Logging.
log.SetFlags(0)
// TODO: implement when needed
// Ensure that required envs exist.
//if os.Getenv("ENV") == "" {
// log.Fatal("ENV env not set.")
//}
}
func main() {
log.Println(&logging.Entry{Message: "starting server...", Severity: logging.NOTICE})
port := os.Getenv("USERS_PORT")
if port == "" {
port = "8080"
log.Println(&logging.Entry{Message: "Defaulting to port " + port, Severity: logging.WARNING})
}
listener, err := net.Listen("tcp", ":"+port)
if err != nil {
log.Fatalf("net.Listen: %v", err)
}
grpcServer := grpc.NewServer(grpc.UnaryInterceptor(serverInterceptor))
pb.RegisterUsersServiceServer(grpcServer, &methods.MyService{})
if err = grpcServer.Serve(listener); err != nil {
log.Fatal(err)
}
}
// serverInterceptor is an example of a Server Interceptor which could be used to 'inject'
// for example logs and/or tracing details to incoming server requests.
// Add this method to your grpc server connection, for example
// grpcServer := grpc.NewServer(grpc.UnaryInterceptor(serverInterceptor))
//
// pb.RegisterServiceServer(grpcServer, &myService{})
func serverInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
// Calls the handler
h, err := handler(ctx, req)
if err != nil {
log.Println(&logging.Entry{Message: fmt.Sprintf("%v", req), Severity: logging.DEBUG, Ctx: ctx})
log.Println(&logging.Entry{Message: err.Error(), Severity: logging.WARNING, Ctx: ctx})
}
_ = info
return h, err
}
Edited: Here is the full stack trace when the crash occurs:
goroutine 54 [running]:
cloud.google.com/go/firestore.(*DocumentSnapshot).DataTo(0x0?, {0x104e28820?, 0x140000d02c0?})
/Users/michaelaltshuler/go/pkg/mod/cloud.google.com/go/firestore#v1.6.1/document.go:112 +0x24
hock.ap.resources.users.v1/internal/methods.(*MyService).GetUser(0x1400011f9a8?, {0x104e4ea60, 0x14000102750}, 0x14?)
/Users/michaelaltshuler/GolandProjects/hockney/products/hock/ap/resources/users/v1/internal/methods/methods.go:59 +0x84
github.com/jaebrownn/hockney/protobuf/go/hock/ap/resources/users/v1._UsersService_GetUser_Handler.func1({0x104e4ea60, 0x14000102750}, {0x104dd2d40?, 0x14000046340})
/Users/michaelaltshuler/GolandProjects/hockney/protobuf/go/hock/ap/resources/users/v1/users_grpc.pb.go:212 +0x74
main.serverInterceptor({0x104e4ea60?, 0x14000102750}, {0x104dd2d40, 0x14000046340}, 0x1400011faa8?, 0x104a98c24?)
/Users/michaelaltshuler/GolandProjects/hockney/products/hock/ap/resources/users/v1/server.go:62 +0x3c
github.com/jaebrownn/hockney/protobuf/go/hock/ap/resources/users/v1._UsersService_GetUser_Handler({0x104dd7cc0?, 0x1052cdd08}, {0x104e4ea60, 0x14000102750}, 0x14000140000, 0x104e44430)
/Users/michaelaltshuler/GolandProjects/hockney/protobuf/go/hock/ap/resources/users/v1/users_grpc.pb.go:214 +0x138
google.golang.org/grpc.(*Server).processUnaryRPC(0x140000d63c0, {0x104e52278, 0x14000003860}, 0x140005a4000, 0x140000a08a0, 0x10528e3f8, 0x0)
/Users/michaelaltshuler/go/pkg/mod/google.golang.org/grpc#v1.48.0/server.go:1295 +0x9c4
google.golang.org/grpc.(*Server).handleStream(0x140000d63c0, {0x104e52278, 0x14000003860}, 0x140005a4000, 0x0)
/Users/michaelaltshuler/go/pkg/mod/google.golang.org/grpc#v1.48.0/server.go:1636 +0x82c
google.golang.org/grpc.(*Server).serveStreams.func1.2()
/Users/michaelaltshuler/go/pkg/mod/google.golang.org/grpc#v1.48.0/server.go:932 +0x84
created by google.golang.org/grpc.(*Server).serveStreams.func1
/Users/michaelaltshuler/go/pkg/mod/google.golang.org/grpc#v1.48.0/server.go:930 +0x290
And when navigating to my getUser method from this stack trace it takes me to the line err = dsnap.DataTo(user) in the code below:
dsnap, err := clients.Firestore.Doc(req.GetName()).Get(ctx)
//user message to return
user := &pb.User{}
err = dsnap.DataTo(user) // This line
if err != nil {
return nil, err
}
I know this is a very long winded way of asking for help with this question. I have found very little resources online to deal with this issue, and I'm hoping this makes some sense and someone could guide me in the right direction.
Thank you!
I seemed to have fixed the problem and it was a lot more simple than I originally thought. When writing the test method I was specifying the user as User: "UserID", however, the server expected a path to the firestore document ref, and needed the input User: "user/UserID".
I am new in programming and have no idea about using the the token generate client api function in the source code from my client side golang program. Looking for some advice. Thank you so much.
Source code package: https://pkg.go.dev/github.com/gravitational/teleport/api/client#Client.UpsertToken
Function Source Code:
func (c *Client) UpsertToken(ctx context.Context, token types.ProvisionToken) error {
tokenV2, ok := token.(*types.ProvisionTokenV2)
if !ok {
return trace.BadParameter("invalid type %T", token)
}
_, err := c.grpc.UpsertToken(ctx, tokenV2, c.callOpts...)
return trail.FromGRPC(err)
}
My code:
package main
import (
"context"
"crypto/tls"
"fmt"
"log"
"os"
"strings"
"time"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/proto"
"google.golang.org/grpc"
)
// Client is a gRPC Client that connects to a Teleport Auth server either
// locally or over ssh through a Teleport web proxy or tunnel proxy.
//
// This client can be used to cover a variety of Teleport use cases,
// such as programmatically handling access requests, integrating
// with external tools, or dynamically configuring Teleport.
type Client struct {
// c contains configuration values for the client.
//c Config
// tlsConfig is the *tls.Config for a successfully connected client.
tlsConfig *tls.Config
// dialer is the ContextDialer for a successfully connected client.
//dialer ContextDialer
// conn is a grpc connection to the auth server.
conn *grpc.ClientConn
// grpc is the gRPC client specification for the auth server.
grpc proto.AuthServiceClient
// closedFlag is set to indicate that the connnection is closed.
// It's a pointer to allow the Client struct to be copied.
closedFlag *int32
// callOpts configure calls made by this client.
callOpts []grpc.CallOption
}
/*
type ProvisionToken interface {
Resource
// SetMetadata sets resource metatada
SetMetadata(meta Metadata)
// GetRoles returns a list of teleport roles
// that will be granted to the user of the token
// in the crendentials
GetRoles() SystemRoles
// SetRoles sets teleport roles
SetRoles(SystemRoles)
// GetAllowRules returns the list of allow rules
GetAllowRules() []*TokenRule
// GetAWSIIDTTL returns the TTL of EC2 IIDs
GetAWSIIDTTL() Duration
// V1 returns V1 version of the resource
V2() *ProvisionTokenSpecV2
// String returns user friendly representation of the resource
String() string
}
type ProvisionTokenSpecV2 struct {
// Roles is a list of roles associated with the token,
// that will be converted to metadata in the SSH and X509
// certificates issued to the user of the token
Roles []SystemRole `protobuf:"bytes,1,rep,name=Roles,proto3,casttype=SystemRole" json:"roles"`
Allow []*TokenRule `protobuf:"bytes,2,rep,name=allow,proto3" json:"allow,omitempty"`
AWSIIDTTL Duration `protobuf:"varint,3,opt,name=AWSIIDTTL,proto3,casttype=Duration" json:"aws_iid_ttl,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
*/
func main() {
ctx := context.Background()
args := os.Args[1:]
nodeType := ""
if len(args) > 0 {
nodeType = args[0]
}
proxyAddress := os.Getenv("TELEPORT_PROXY")
if len(proxyAddress) <= 0 {
proxyAddress = "proxy.teleport.example.local:443"
}
clt, err := client.New(ctx, client.Config{
Addrs: []string{
"proxy.teleport.example.local:443",
"proxy.teleport.example.local:3025",
"proxy.teleport.example.local:3024",
"proxy.teleport.example.local:3080",
},
Credentials: []client.Credentials{
client.LoadProfile("", ""),
},
})
if err != nil {
log.Fatalf("failed to create client: %v", err)
}
defer clt.Close()
ctx, err, token, err2 := clt.UpsertToken(ctx, token)
if err || err2 != nil {
log.Fatalf("failed to get tokens: %v", err)
}
now := time.Now()
t := 0
fmt.Printf("{\"tokens\": [")
for a, b := range token {
if strings.Contains(b.GetRoles(), b.Allow().String(), b.GetAWSIIDTTL(), nodeType) {
if t >= 1 {
fmt.Printf(",")
} else {
panic(err)
}
expiry := "never" //time.Now().Add(time.Hour * 8).Unix()
_ = expiry
if b.Expiry().Unix() > 0 {
exptime := b.Expiry().Format(time.RFC822)
expdur := b.Expiry().Sub(now).Round(time.Second)
expiry = fmt.Sprintf("%s (%s)", exptime, expdur.String())
}
fmt.Printf("\"count\": \"%1d\",", a)
fmt.Printf(b.Roles(), b.GetAllowRules(), b.GetAWSIIDTTL(), b.GetMetadata().Labels)
}
}
}
Output:
Syntax error instead of creating a token
It's seems your code have many mistake. And, It's very obvious you are getting syntax error. I am sure you would have got the line number in the console where actually these syntax error has occurred.
Please understand the syntax of Golang and also how to call the functions and how many parameter should i pass to those functions.
There are few mistakes i would like to point out after reviewing your code.
//It shouldn't be like this
ctx, err, token, err2 := clt.UpsertToken(ctx, token)
//Instead it should be like this
err := clt.UpsertToken(ctx, token)
//The return type of UpsertToken() method is error, you should use only one variable to receive this error.
strings.Contains() function takes two argument but you are passing four.
Refer this document for string.Contains()
You are assigning t := 0 and checking it with if condition inside for loop and never incremented.
Refer this document for fmt.Printf()
Refer this for function
Remove all the syntax error then only your code will run also cross check your logic.
If you want to see the example of syntax error then check here : https://go.dev/play/p/Hhu48UqlPRF
I've constructed a simple script which goes on a website, and does a few things on there (automating a checkout process on lego.com)
This is all done using the go http client with a cookiejar on it, but when I try to print out the cookies after all the activity on the site, nothing prints - not sure if I'm doing something wrong here.
type Program struct {
taskInfo task.Task
client http.Client
jar *cookiejar.Jar
// Task Specific Variables
maxOrderQty string
sessionID string
sku string
shipMethodUID string
}
The HTTP client is initialized below
func (pr *Program) initializeClient() {
pr.jar, _ = cookiejar.New(nil)
pr.client = http.Client{
Timeout: time.Second * 10,
Jar: pr.jar,
}
}
After that, i make a few calls to the same domain (mixture of GET and POST) using said client. When I try to print out the cookies, nothing prints.
func (pr *Program) getSessionCookies() {
log.Debug("Getting Cookies")
u, _ := url.Parse("https://www.lego.com/")
for _, cookie := range pr.jar.Cookies(u) {
fmt.Printf(" %s: %s\n", cookie.Name, cookie.Value)
}
}
Go's http package support redirects by default, but the cookie jar must be refreshed, specially if the domain changes.
I spent a great deal of time debugging it some time ago, the solution I found was to specify a redirect handler:
client.CheckRedirect = getRedirectHandler(req, &client)
Which had the following code:
func getRedirectHandler(req *Request, client *http.Client) redirectHandler {
fn := func(request *http.Request, via []*http.Request) error {
if req.FollowRedirect {
log.Debug(fmt.Sprintf("Redirecting to %s", request.URL.String()))
jar, err := getCookieJarWithURL(request.URL.String(), req.Cookies)
if err != nil {
return err
}
client.Jar = jar
return nil
}
return errors.New("Redirect not allowed")
}
return fn
}
The full code can be found in this gist:
I hope that saves you some time :)
I'm trying to implement a websocket proxy server for GlassFish. If I try to connect more than one client I'm getting error:
ReadMessage Failed: websocket: close 1007 Illegal UTF-8 Sequence.
I'm sure the GlassFish server sending right data, because the same server works properly with another proxy server implemented with node.js.
func GlassFishHandler(conn *websocket.Conn){
defer conn.Close()
conn.SetReadDeadline(time.Now().Add(1000 * time.Second))
conn.SetWriteDeadline(time.Now().Add(1000 * time.Second))
fmt.Println("WS-GOLANG PROXY SERVER: Connected to GlassFish")
for {
messageType, reader, err := conn.NextReader()
if err != nil {
fmt.Println("ReadMessage Failed: ", err) // <- error here
} else {
message, err := ioutil.ReadAll(reader)
if (err == nil && messageType == websocket.TextMessage){
var dat map[string]interface{}
if err := json.Unmarshal(message, &dat); err != nil {
panic(err)
}
// get client destination id
clientId := dat["target"].(string)
fmt.Println("Msg from GlassFish for Client: ", dat);
// pass through
clients[clientId].WriteMessage(websocket.TextMessage, message)
}
}
}
}
Summing up my comments as an answer:
When you are writing to the client, you are taking the clientId from the GlassFish message, fetching the client from a map, and then writing to it - basically clients[clientId].WriteMessage(...).
While your map access can be thread safe, writing is not, as this can be seen as:
// map access - can be safe if you're using a concurrent map
client := clients[clientId]
// writing to a client, not protected at all
client.WriteMessage(...)
So what's probably happening is that two separate goroutines are writing to the same client at the same time. You should protect your client from it by adding a mutex in the WriteMessage method implementation.
BTW actually instead of protecting this method with a mutex, a better, more "go-ish" approach would be to use a channel to write the message, and a goroutine per client that consumes from the channel and writes to the actual socket.
So in the client struct I'd do something like this:
type message struct {
msgtype string
msg string
}
type client struct {
...
msgqueue chan *message
}
func (c *client)WriteMessage(messageType, messageText string) {
// I'm simplifying here, but you get the idea
c.msgqueue <- &message{msgtype: messageType, msg: messageText}
}
func (c *client)writeLoop() {
go func() {
for msg := ragne c.msgqueue {
c.actuallyWriteMessage(msg)
}
}()
}
and when creating a new client instance, just launch the write loop