Home >Backend Development >Golang >Streamlining Access to Embedded Instruqt Labs
Bagaimanakah anda mengajar topik yang sangat teknikal kepada prospek dan pelanggan? Bagaimanakah anda menjadikannya perjalanan yang lancar?
Di Isovalent, kami bersemangat untuk menjadikan pengalaman pembelajaran selancar mungkin untuk pengguna kami. Isovalent ialah pencipta Cilium, platform rangkaian awan de facto untuk Kubernetes. Walaupun kami suka rangkaian dan keselamatan, kami menghargai orang yang mungkin menganggapnya sebagai topik yang sukar. Kami fikir kami akan menjadikan pembelajaran rangkaian Kubernetes menyeronokkan, jadi kami menjadikannya satu titik untuk memperagakan pengalaman pembelajaran.
Instruqt menyediakan platform yang hebat untuk membina makmal praktikal yang boleh maju dari segi teknikal dan menarik untuk pengguna.
Kami juga percaya pengalaman pengguna harus lancar dan prosesnya automatik sepenuhnya.
Nasib baik, banyak perkara boleh dilakukan dengan memanfaatkan Instruqt graphQL API.
Untuk tujuan itu, kami menulis perpustakaan instruqt-go kami sendiri, yang kami telah memutuskan untuk membuka sumber. Perpustakaan ini direka bentuk untuk membantu pembangun mengautomasikan dan menyepadukan dengan platform Instruqt dengan mudah.
Salah satu isu dalam penerbitan makmal Instruqt ialah memautkan maklumat pengguna daripada Instruqt dengan pangkalan data atau CRM anda sendiri.
Dalam siaran pertama ini, kami akan membimbing anda membina proksi menggunakan instruqt-go bahawa:
Kami kemudiannya akan menerbitkan fungsi tersebut pada Fungsi Awan Google.
Terdapat pelbagai sebab untuk mengumpul maklumat pengguna dalam makmal:
Terdapat beberapa kaedah untuk menghantar data pengguna ke runut Instruqt.
Parameter tersuai Instruqt sangat berguna untuk menghantar sebarang jenis maklumat semasa memulakan trek. Medan ini hanya ditambahkan pada URL sebagai parameter pertanyaan, diawali dengan icp_. Parameter ini juga boleh diambil dalam webhooks Instruqt serta melalui Instruqt GraphQL API, menjadikannya praktikal untuk digunakan.
Sehingga baru-baru ini, Instruqt menggalakkan pembangun jejak untuk menyampaikan maklumat pengguna (seperti nama, e-mel atau token) menggunakan parameter tersuai.
Walau bagaimanapun, terdapat beberapa kelemahan untuk menggunakan parameter tersuai:
Jemputan Instruqt membolehkan anda membuat senarai lagu dan menjana pautan jemputan yang boleh dikongsi dengan pengguna untuk akses mudah. Jemputan boleh ditetapkan untuk mengumpul data pengguna melalui borang.
Data pengguna ini kemudiannya ditambahkan pada butiran pengguna pada Instruqt (butiran pengguna dilampirkan pada akaun pengguna, tetapi unik bagi setiap pasukan Instruqt).
Ini sangat praktikal untuk bengkel, tetapi terdapat beberapa batasan lagi:
Nota: Instruqt baru-baru ini memperkenalkan halaman pendaratan, yang merupakan satu bentuk jemputan dengan cara untuk menala halaman pendaratan, dengan kelebihan dan had yang sama.
Baru-baru ini, Instruqt menambah cara lain untuk menyampaikan maklumat pengguna, yang menggabungkan faedah kedua-dua kaedah sebelumnya.
Kaedah PII yang disulitkan membenarkan untuk menghantar parameter pertanyaan pii_tpg ke URL benam. Ini bermakna:
Kami akan menggunakan kaedah baharu ini dalam contoh ini, kerana kaedah ini paling serba boleh hari ini untuk menyampaikan maklumat kepada Instruqt dengan cara yang selamat dan boleh dipercayai.
When you visit a track page on Instruqt, there is an option to embed the track.
This gives you a URL which contains a token unique to the track.
While it is perfectly valid to use that URL, it also means that whoever has access to this token can start the track whenever they want.
Instruqt has recently added an API call to generate one-time tokens for tracks, such that URLs using such tokens can only be used once.
The proxy we're coding will use one-time tokens, since we have access to the API and can easily generate them.
First, create a directory for your function:
mkdir instruqt-proxy
Move to this directory and initialize the Go environment:
# Replace example.com with the prefix of your choice go mod init example.com/labs
For local testing, create a cmd directory:
mkdir cmd
Create a main.go file in that directory, with the following content:
package main import ( "log" "os" // Blank-import the function package so the init() runs // Adapt if you replaced example.com earlier _ "example.com/labs" "github.com/GoogleCloudPlatform/functions-framework-go/funcframework" ) func main() { // Use PORT environment variable, or default to 8080. port := "8080" if envPort := os.Getenv("PORT"); envPort != "" { port = envPort } if err := funcframework.Start(port); err != nil { log.Fatalf("funcframework.Start: %v\n", err) } }
Back to the instruqt-proxy directory, create a proxy.go file and start by adding the init() function to it, along with the Go packages we will be using:
package labs import ( "fmt" "net/http" "net/url" "os" "github.com/GoogleCloudPlatform/functions-framework-go/functions" "github.com/isovalent/instruqt-go/instruqt" ) func init() { functions.HTTP("InstruqtProxy", instruqtProxy) }
This will allow Google Cloud Functions to call the instruqtProxy function when it is initialized.
Let's write that function:
const ( // Replace team name with yours instruqtTeam = "isovalent" ) func instruqtProxy(w http.ResponseWriter, r *http.Request) { instruqtToken := os.Getenv("INSTRUQT_TOKEN") if instruqtToken == "" { w.WriteHeader(http.StatusInternalServerError) return } instruqtClient := instruqt.NewClient(instruqtToken, instruqtTeam) // Get user from passed token utk := r.URL.Query().Get("utk") if utk == "" { w.WriteHeader(http.StatusUnauthorized) return } user, err := getUser(utk) if err != nil { w.WriteHeader(http.StatusUnauthorized) return } labSlug := r.URL.Query().Get("slug") url, err := getLabURL(instruqtClient, user, labSlug) if err != nil { w.WriteHeader(http.StatusNotFound) return } http.Redirect(w, r, url, http.StatusFound) }
In this function, we:
The getLabURL function will generate the redirect URL for the lab based on user information, the requested lab slug, and dynamic information from the Instruqt API.
Let's write it:
const ( // Replace with your sign-up page format labSignupPage = "https://isovalent.com/labs/%s" // Adapt to your values finishBtnText = "Try your next Lab!" finishBtnURL = "https://labs-map.isovalent.com/map?lab=%s&showInfo=true" ) func getLabURL(instruqtClient *instruqt.Client, u user, slug string) (string, error) { track, err := instruqtClient.GetTrackBySlug(slug) if err != nil { return "", err } // Unknown user if u.Email == "" { url := fmt.Sprintf(labSignupPage, slug) return url, nil } // Get one-time token token, err := instruqtClient.GenerateOneTimePlayToken(track.Id) if err != nil { return "", err } labURL, err := url.Parse(fmt.Sprintf("https://play.instruqt.com/embed/%s/tracks/%s", instruqtTeam, track.Slug)) if err != nil { return "", err } // Prepare the fields to encrypt encryptedPII, err := instruqtClient.EncryptUserPII(u.FirstName, u.LastName, u.Email) if err != nil { return "", err } // Add params params := map[string]string{ "token": token, "pii_tpg": encryptedPII, "show_challenges": "true", "finish_btn_target": "_blank", "finish_btn_text": finishBtnText, "finish_btn_url": fmt.Sprintf(finishBtnURL, track.Slug), } q := labURL.Query() for key, value := range params { q.Set(key, value) } // Encode the parameters labURL.RawQuery = q.Encode() return labURL.String(), nil }
First, note that we have defined some new constants that you can tune:
Now let's explain the getLabURL() function steps:
The last piece missing in this proxy is the getUser() function. I can't help you much here, since this part is where you plug your own logic. You might be using a CRM like Hubspot to retrieve contact information from the UTK, or another database, it's up to you!
The code I'll show you here simply returns a sample user:
/* * This is where you add the logic to get user information from your CRM/database. */ type user struct { FirstName string LastName string Email string } func getUser(utk string) (u user, err error) { // Implement the logic to get your user information from UTK u = user{ FirstName: "John", LastName: "Doe", Email: "john@doe.com", } return u, err }
Now that we have our whole proxy.go function, let's test it!
First, update your go.mod and go.sum files with:
go get ./... go mod tidy
In your Instruqt dashboard, go to "API keys" and get the value of your API key. Export it as a variable in your shell:
export INSTRUQT_TOKEN=<your_instruqt_token>
Next, launch the function on your local machine:
FUNCTION_TARGET=InstruqtProxy go run ./cmd/main.go
Finally, in another terminal, make test requests to localhost:8080 where your function will be running (you can pass a PORT environment variable above to change the port if necessary):
curl -i "localhost:8080/?slug=cilium-getting-started&utk=someUtkValue"
Adapt to use a track slug that exists in your Instruqt organization. If the track exists, you should get a 302 response with the redirect URL containing a one-time token for access, as well as John Doe's information encrypted with the PII key, and a one-time token (starting with ott_)!
If you'd like to use Docker to test your function locally, you can create a Dockerfile in your current directory:
FROM golang:1.23 WORKDIR /app COPY . . RUN go build -o myapp ./cmd/main.go ENV DEV=true ENV PORT=8080 EXPOSE $PORT CMD ["./myapp"]
Add a docker-compose.yaml file:
version: '3' services: proxy: build: ./ ports: - "8080:8080" environment: INSTRUQT_TOKEN: ${INSTRUQT_TOKEN} FUNCTION_TARGET: InstruqtProxy
Finally, build and launch your container:
docker-compose up --build
And you can send requests to localhost:8080 just the same as before!
In order to deploy to Google Cloud, first make sure you are logged in to your Google Cloud project:
gcloud auth application-default login
Next, let's create a new secret for the Instruqt token:
echo -n "$INSTRUQT_TOKEN" | gcloud secrets create instruqt-token --data-file=-
In order to adjust the permissions on this secret, you will need to get your project ID:
PROJECT_NUMBER=$(gcloud projects describe $(gcloud config get-value project) --format="value(projectNumber)")
Then, add the "Secret Manager Secret Accessor" role for the default Compute Engine service account for the project:
gcloud secrets add-iam-policy-binding instruqt-token \ --member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \ --role="roles/secretmanager.secretAccessor"
Your secret is now ready to be used by the function!
You can then deploy the function (adapt the region if needed):
gcloud functions deploy "instruqt-proxy" \ --gen2 --runtime=go122 --region=europe-west1 --source=. \ --entry-point="InstruqtProxy" --trigger-http --allow-unauthenticated \ --set-secrets="INSTRUQT_TOKEN=instruqt-token:latest"
This will upload and build your project, and return the URL to access the function.
This URL will look something like https://europe-west1-
You can then test the function using that URL instead of localhost:8080!
This is a simplified approach to the lab proxy we use at Isovalent. There are things you might want to consider with this implementation:
This proxy can typically be used to give access to authenticated users in a safe way, while preserving user information in Instruqt reports and making sure embed URLs are not reusable.
Here is an example of usage of this proxy:
This can allow you to build a series of public sign-up pages for your labs, similar to what we have built on the Isovalent website. It can also be used to build a Kiosk interface, or even a more creative landing page such as the Cilium Labs map, where clicks on the map redirect to the lab proxy.
By making a complex networking technology like Cilium fun with our labs, we have made it the experience for users less intimidating and more approachable. Using our proxy can help you provide a similar user experience to your prospects. Please get in touch if you have any questions.
The above is the detailed content of Streamlining Access to Embedded Instruqt Labs. For more information, please follow other related articles on the PHP Chinese website!