How to authorize a web-socket connection in kubernetes in Go? - go

so I am trying to implement Use a WebSocket client to exec commands in a Kubernetes pod in Go (so I create a WebSocket app to remotely run commands in a container's pod for debugging/monitoring/code-practice) but I am getting:
dial:tls: first record does not look like a TLS handshake exit status
1
What am I missing? I am using this as an example cluster. There is a way to do it by passing a bearer token but that doesn't look very safe.
package main
import (
"io/ioutil"
"log"
"github.com/gorilla/websocket"
)
func main() {
ca, err := ioutil.ReadFile("/home/george/.minikube/ca.crt")
if err != nil {
log.Fatal(err)
}
// read client cert
clientCert, err := ioutil.ReadFile("/home/george/.minikube/profiles/minikube/client.crt")
if err != nil {
log.Fatal("Error loading client cert", err)
}
// read client key
clientKey, err := ioutil.ReadFile("/home/george/.minikube/profiles/minikube/client.key")
if err != nil {
log.Fatal("Error loading client key", err)
}
value1 := "ca:" + string(ca)
value2 := "cert: " + string(clientCert)
value3 := "key: " + string(clientKey)
dialer := websocket.DefaultDialer // use default dialer
dialer.Subprotocols = []string{value1, value2, value3}
//dialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
url := "wss://192.168.49.2:30110/api/v1/namespaces/default/pods/hello-minikube-6ddfcc9757-g4484/exec?command=echo&command=ls&stderr=true&stdout=true"
//dial websocket
c, _, err := dialer.Dial(url, nil)
if err != nil {
// print response body
//fmt.Println(response.Body)
log.Fatal("dial:", err)
}
// receive websocket message
defer c.Close()
}

Related

Not able to access gcp secret manager from standalone go application

I am trying to access GCP secret manager from google cloud function. Below is the code snippet
// Package p contains an HTTP Cloud Function.
package p
import (
"context"
"fmt"
secretmanager "cloud.google.com/go/secretmanager/apiv1"
secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
)
func main() {
ctx := context.Background()
client, err := secretmanager.NewClient(ctx)
if err != nil {
fmt.Errorf("failed to create secretmanager client: %v", err)
}
defer client.Close()
name := "projects/485126440943/secrets/APIAdminServiceAccountLookupKey"
// Build the request.
req := &secretmanagerpb.GetSecretRequest{
Name: name,
}
// Call the API.
result, err := client.GetSecret(ctx, req)
if err != nil {
fmt.Errorf("failed to get secret: %v", err)
}
replication := result.Replication.Replication
fmt.Printf("Found secret %s with replication policy %s\n", result.Name, replication)
//return nil
}
But i am getting secretManagerClient as Nil ]. Thats why after running code I am getting below error:
panic: runtime error: invalid memory address or nil pointer dereference
panic: runtime error: invalid memory address or nil pointer dereference
How to solve this issue?
Thanks in advance!!
From your codes, we can see that you forgot to stop the program when getting an error. It will lead you to the nil pointer panic because you access the method of nil *secretmanager.Client in client.GetSecret(ctx, req). To solve this, you just need to add panic or return statement. After implementing it, you will no longer get a nil pointer panic and you will face an error from secretmanager.NewClient(ctx).
package main
import (
"context"
"fmt"
secretmanager "cloud.google.com/go/secretmanager/apiv1"
secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
)
func main() {
ctx := context.Background()
client, err := secretmanager.NewClient(ctx)
if err != nil {
// stop your program from executing the next lines
panic(fmt.Errorf("failed to create secretmanager client: %v", err))
// you can also use return statement
//
// log.Println(fmt.Errorf("failed to create secretmanager client: %v", err))
// return
}
defer client.Close()
name := "projects/485126440943/secrets/APIAdminServiceAccountLookupKey"
// Build the request.
req := &secretmanagerpb.GetSecretRequest{
Name: name,
}
// Call the API.
result, err := client.GetSecret(ctx, req)
if err != nil {
panic(fmt.Errorf("failed to get secret: %v", err))
}
replication := result.Replication.Replication
fmt.Printf("Found secret %s with replication policy %s\n", result.Name, replication)
//return nil
}

Google Cloud Vertex AI with Golang: rpc error: code = Unimplemented desc = unexpected HTTP status code received from server: 404 (Not Found)

I have a Vertex AI model deployed on an endpoint and want to do some prediction from my app in Golang.
To do this I create code inspired by this example : https://cloud.google.com/go/docs/reference/cloud.google.com/go/aiplatform/latest/apiv1?hl=en
const file = "MY_BASE64_IMAGE"
func main() {
ctx := context.Background()
c, err := aiplatform.NewPredictionClient(cox)
if err != nil {
log.Printf("QueryVertex NewPredictionClient - Err:%s", err)
}
defer c.Close()
parameters, err := structpb.NewValue(map[string]interface{}{
"confidenceThreshold": 0.2,
"maxPredictions": 5,
})
if err != nil {
log.Printf("QueryVertex structpb.NewValue parameters - Err:%s", err)
}
instance, err := structpb.NewValue(map[string]interface{}{
"content": file,
})
if err != nil {
log.Printf("QueryVertex structpb.NewValue instance - Err:%s", err)
}
reqP := &aiplatformpb.PredictRequest{
Endpoint: "projects/PROJECT_ID/locations/LOCATION_ID/endpoints/ENDPOINT_ID",
Instances: []*structpb.Value{instance},
Parameters: parameters,
}
resp, err := c.Predict(cox, reqP)
if err != nil {
log.Printf("QueryVertex Predict - Err:%s", err)
}
log.Printf("QueryVertex Res:%+v", resp)
}
I put the path to my service account JSON file on GOOGLE_APPLICATION_CREDENTIALS environment variable.
But when I run my test app I obtain this error message:
QueryVertex Predict - Err:rpc error: code = Unimplemented desc = unexpected HTTP status code received from server: 404 (Not Found); transport: received unexpected content-type "text/html; charset=UTF-8"
QueryVertex Res:<nil>
As #DazWilkin suggested, configure the client option to specify the specific regional endpoint with a port 443:
option.WithEndpoint("<region>-aiplatform.googleapis.com:443")
Try like below:
func main() {
ctx := context.Background()
c, err := aiplatform.NewPredictionClient(
ctx,
option.WithEndpoint("<region>-aiplatform.googleapis.com:443"),
)
if err != nil {
log.Printf("QueryVertex NewPredictionClient - Err:%s", err)
}
defer c.Close()
.
.
I'm unfamiliar with Google's (Vertex?) AI Platform and unable to test this hypothesis but it appears that the API uses location-specific endpoints.
Can you try configuring the client's ClientOption to specify the specific regional endpoint, i.e.:
url := fmt.Sprintf("https://%s-aiplatform.googleapis.com", location)
opts := []option.ClientOption{
option.WithEndpoint(url),
}
And:
package main
import (
"context"
"fmt"
"log"
"os"
aiplatform "cloud.google.com/go/aiplatform/apiv1"
"google.golang.org/api/option"
aiplatformpb "google.golang.org/genproto/googleapis/cloud/aiplatform/v1"
"google.golang.org/protobuf/types/known/structpb"
)
const file = "MY_BASE64_IMAGE"
func main() {
// Values from the environment
project := os.Getenv("PROJECT")
location := os.Getenv("LOCATION")
endpoint := os.Getenv("ENDPOINT")
ctx := context.Background()
// Configure the client with a region-specific endpoint
url := fmt.Sprintf("https://%s-aiplatform.googleapis.com", location)
opts := []option.ClientOption{
option.WithEndpoint(url),
}
c, err := aiplatform.NewPredictionClient(ctx, opts...)
if err != nil {
log.Fatal(err)
}
defer c.Close()
parameters, err := structpb.NewValue(map[string]interface{}{
"confidenceThreshold": 0.2,
"maxPredictions": 5,
})
if err != nil {
log.Fatal(err)
}
instance, err := structpb.NewValue(map[string]interface{}{
"content": file,
})
if err != nil {
log.Printf("QueryVertex structpb.NewValue instance - Err:%s", err)
}
rqst := &aiplatformpb.PredictRequest{
Endpoint: fmt.Sprintf("projects/%s/locations/%s/endpoints/%s",
project,
location,
endpoint,
),
Instances: []*structpb.Value{
instance,
},
Parameters: parameters,
}
resp, err := c.Predict(ctx, rqst)
if err != nil {
log.Fatal(err)
}
log.Printf("QueryVertex Res:%+v", resp)
}
Try to do something like this
[...]
url := fmt.Sprintf("%s-aiplatform.googleapis.com:443", location)
[..]

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 to make send message to particular URI after successfull webscoket connection?

I have a secure websocket server running on localhost:443/server-demo ( jetty websocket server).
Now I am writing a go client that can communicate with the websocket server. I am able to connect to the websocket server using right certificates. Here is the sample code.
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"log"
)
func main() {
cert, err := tls.LoadX509KeyPair("nifi-1.10.0-bin/nifi-1.10.0/extras/gen-certs/certs/admin.pem", "nifi-1.10.0-bin/nifi-1.10.0/extras/gen-certs/certs/admin-key.pem")
if err != nil {
log.Fatalf("server: loadkeys: %s", err)
}
config := tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true}
conn, err := tls.Dial("tcp", "127.0.0.1:443", &config)
if err != nil {
log.Fatalf("client: dial: %s", err)
}
defer conn.Close()
log.Println("client: connected to: ", conn.RemoteAddr())
state := conn.ConnectionState()
for _, v := range state.PeerCertificates {
fmt.Println(x509.MarshalPKIXPublicKey(v.PublicKey))
fmt.Println(v.Subject)
}
log.Println("client: handshake: ", state.HandshakeComplete)
log.Println("client: mutual: ", state.NegotiatedProtocolIsMutual)
message := "Hello\n"
n, err := io.WriteString(conn, message)
if err != nil {
log.Fatalf("client: write: %s", err)
}
log.Printf("client: wrote %q (%d bytes)", message, n)
reply := make([]byte, 256)
n, err = conn.Read(reply)
log.Printf("client: read %q (%d bytes)", string(reply[:n]), n)
log.Print("client: exiting")
}
The above code throws this error:
"HTTP/1.1 400 No URI\r\nContent-Type: text/html;charset=iso-8859-1\r\nContent-Length: 49\r\nConnection: close\r\nServer: Jetty(9.4.19.v20190610)\r\n\r\n<h1>Bad Message 400</h1><pre>reason: No URI</pre>" (188 bytes)
My question is after making the connection how can I send message to particular URI? i.e I want to send a message to wss://localhost:443/server-demo.
The code in a question does not establish a WebSocket connection to the server.
To establish the WebSocket connection, the application must write a WebSocket handshake to conn and receive the handshake response. See the RFC for the details.
Most applications use a websocket package than handles all of these details. The gorilla/websocket package is a popular choice.
This code should get you started with gorilla:
cert, err := tls.LoadX509KeyPair("nifi-1.10.0-bin/nifi-1.10.0/extras/gen-certs/certs/admin.pem", "nifi-1.10.0-bin/nifi-1.10.0/extras/gen-certs/certs/admin-key.pem")
if err != nil {
log.Fatalf("server: loadkeys: %s", err)
}
config := tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true}
d := websocket.Dialer{
TLSClientConfig: &config,
}
c, _, err := d.Dial("wss://localhost:443/server-demo", nil)
if err != nil {
log.Fatal(err)
}
defer c.Close()
// Use `c` to send and receive messages

Complete tracing using zipkin-go

I have some services. I want to trace those services using zipkin-go. In every service, I am calling some of my other internal services or db calls. I want to trace every activity like how much time it has taken to call internal services or db. I have implemented using available tutorials on internet. Below is my code:
package main
import (
"fmt"
"os"
"net/http"
"log"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"Upload-Image-API/services/controllers/updatemodrefid"
"Upload-Image-API/services/controllers/uploadimage"
"Upload-Image-API/services/utils"
"github.com/openzipkin/zipkin-go"
"github.com/openzipkin/zipkin-go/model"
zipkinhttp "github.com/openzipkin/zipkin-go/middleware/http"
reporterhttp "github.com/openzipkin/zipkin-go/reporter/http"
)
const endpointURL = "http://localhost:9411/api/v2/spans"
func newTracer() (*zipkin.Tracer, error) {
// The reporter sends traces to zipkin server
reporter := reporterhttp.NewReporter(endpointURL)
port := utils.GetString("serviceListeningPort")
if port == "" {
fmt.Println("Port is not defined in configuration ...!!!")
os.Exit(1)
}
// Local endpoint represent the local service information
localEndpoint := &model.Endpoint{ServiceName: "Upload-Image-API", Port: 7795}
// Sampler tells you which traces are going to be sampled or not. In this case we will record 100% (1.00) of traces.
sampler, err := zipkin.NewCountingSampler(1)
if err != nil {
return nil, err
}
t, err := zipkin.NewTracer(
reporter,
zipkin.WithSampler(sampler),
zipkin.WithLocalEndpoint(localEndpoint),
)
if err != nil {
return nil, err
}
return t, err
}
func main() {
var err error
var environment = os.Args[1]
err = utils.LoadDefaultConfig(environment)
tracer, err := newTracer()
if err != nil {
log.Fatal(err)
}
// We add the instrumented transport to the defaultClient
// that comes with the zipkin-go library
http.DefaultClient.Transport, err = zipkinhttp.NewTransport(
tracer,
zipkinhttp.TransportTrace(true),
)
if err != nil {
log.Fatal(err)
}
if err != nil {
fmt.Println("Could not load the configuration because of following err " + err.Error() + " ...!!!")
os.Exit(1)
} else {
fmt.Println("Configurations sucessfully loaded for environment " + environment + " ...!!!")
}
allowedHeaders := handlers.AllowedHeaders([]string{"X-Requested-With"})
allowedOrigins := handlers.AllowedOrigins([]string{"*"})
allowedMethods := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"})
port := utils.GetString("serviceListeningPort")
if port == "" {
fmt.Println("Port is not defined in configuration ...!!!")
os.Exit(1)
}
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/uploadimage", uploadimage.UploadImages).Methods("POST", "GET")
router.HandleFunc("/updatemodrefid", updatemodrefid.UpdateModRefId).Methods("POST", "GET")
router.Use(zipkinhttp.NewServerMiddleware(
tracer,
zipkinhttp.SpanName("request")), // name for request span
)
err = http.ListenAndServe(port, handlers.CORS(allowedHeaders, allowedOrigins, allowedMethods)(router))
if err != nil {
fmt.Println("Count not start the server because of following err " + err.Error())
return
}
}
I am getting my request traced but I am not able to trace what is happening inside the uploadimage controller. Below is the screenshot of my zipkin UI:
I want to trace all the activities happening inside uploadimage controller. What should I need to pass so that I can trace all.
I am new to zipkin and golang, If you want to trace internal process, then you can create span from context
example: say you have api called Login, inside login you might perform database operation or any other operations
func Login(res http.ResponseWriter, req *http.Request) {
span, _ := tracer.StartSpanFromContext(req.Context(), "database")
databaseOperation()
span.Finish()
}

Resources