首頁 >後端開發 >Golang >簡化對嵌入式 Instruqt 實驗室的訪問

簡化對嵌入式 Instruqt 實驗室的訪問

Patricia Arquette
Patricia Arquette原創
2024-10-03 20:07:301078瀏覽

您如何向潛在客戶和現有客戶教授技術性很強的主題?如何讓旅途順利?

在 Isovalent,我們熱衷於為使用者提供盡可能無縫的學習體驗。 Isovalent 是 Cilium 的創建者,Cilium 是事實上的 Kubernetes 雲端網路平台。雖然我們熱愛網路和安全,但我們很高興人們可能會覺得這是一個困難的話題。我們認為我們會讓學習 Kubernetes 網路變得有趣,因此我們將學習體驗遊戲化。
Instruqt 提供了一個很棒的平台來建造實作實驗室,既具有先進的技術,又對使用者有吸引力。

我們也相信使用者體驗應該流暢且流程應該完全自動化。
幸運的是,利用 Instruqt graphQL API 可以做很多事情。
為此,我們編寫了自己的 instruqt-go 庫,並決定將其開源。該程式庫旨在幫助開發人員輕鬆自動化並與 Instruqt 平台整合。

發布 Instruqt 實驗室的問題之一是將 Instruqt 中的使用者資訊與您自己的資料庫或 CRM 的使用者資訊連結。
在第一篇文章中,我們將指導您使用 instruqt-go 建立代理:

  • 收集使用者識別碼(例如 HubSpot 代幣);
  • 驗證使用者身分;
  • 將使用者重新導向到具有透過 Instruqt API 產生的唯一存取權杖的實驗室。

然後我們將在 Google Cloud Functions 上發布該函數。

為什麼需要代理

在實驗室收集使用者資訊的原因有很多:

  • 完成實驗後能夠產生徽章(我們喜歡徽章)非常有用(在以後的帖子中會詳細介紹)。
  • 它可以向使用者展示他們在實驗室中的進度,以便他們知道要進行哪些實驗(例如請參閱 Cilium 實驗室地圖)。

Streamlining Access to Embedded Instruqt Labs

如何傳遞用戶數據

有多種方法可以將使用者資料傳遞到 Instruqt 軌道。

自訂參數

Instruqt 自訂參數對於在啟動曲目時傳遞任何類型的信息非常有用。這些欄位只是作為查詢參數添加到 URL 中,並以 icp_ 為前綴。這些參數也可以在 Instruqt webhooks 以及 Instruqt GraphQL API 中檢索,使其實用起來。

直到最近,Instruqt 也鼓勵賽道開發人員使用自訂參數傳遞使用者資訊(例如姓名、電子郵件或令牌)。

但是,使用自訂參數有一些缺點:

  1. 它們沒有標準化,Instruqt 不會解釋它們。這意味著使用者會話將在 Instruqt 報告中顯示為匿名(並且唯一使用者計數可能是錯誤的)。
  2. 預設情況下它們不加密。您當然可以使用自己的金鑰對它們進行加密,但 Instruqt 會在播放報告中向您顯示加密的值。
  3. 我多次看到當使用者重新啟動實驗室時自訂參數遺失。我實際上啟動了自己的快取資料庫來解決這個問題。

邀請

Instruqt 邀請允許建立曲目清單並產生可與使用者共用以便於存取的邀請連結。邀請可以設定為透過表單收集使用者資料。

然後,此使用者資料將會新增至 Instruqt 上的使用者詳細資料(使用者詳細資料附加至使用者帳戶,但每個 Instruqt 團隊都是唯一的)。

這對研討會來說非常實用,但也有一些限制:

  1. 使用邀請來訪問所有實驗室意味著邀請必須包含所有已發布的實驗室。
  2. 邀請有自己的登陸頁面,因此它不適用於我們的 Cilium Labs 地圖或其他資訊亭方法。

注意:Instruqt 最近推出了登陸頁面,這是一種邀請形式,可以調整登陸頁面,具有相同的優點和限制。

第三方表格

最近,Instruqt 增加了另一種傳遞使用者資訊的方式,它結合了先前兩種方法的優點。

加密的 PII 方法允許將 pii_tpg 查詢參數傳遞到嵌入 URL。這意味著:

  1. 資料使用 Instruqt 提供的公鑰進行加密,因此 URL 不包含可讀的使用者資訊。
  2. Instruqt 理解 pii_tpg 資料並擁有解密它的私鑰。這些資訊用於填寫用戶的詳細信息,就像他們接受了邀請一樣。
  3. 這不連結到邀請,因此它可以與任何曲目一起使用。

我們將在本例中使用這種新方法,因為它是當今最通用的以安全可靠的方式向 Instruqt 傳遞訊息的方法。

關於嵌入令牌的註釋

當您造訪 Instruqt 上的曲目頁面時,可以選擇嵌入曲目。
這將為您提供一個 URL,其中包含該曲目獨有的代幣。

雖然使用該 URL 是完全有效的,但這也意味著任何有權訪問此令牌的人都可以隨時開始曲目。

Instruqt 最近新增了一個 API 呼叫來為曲目產生一次性令牌,這樣使用此類令牌的 URL 只能使用一次。

我們正在編碼的代理程式將使用一次性令牌,因為我們可以存取 API 並且可以輕鬆產生它們。

創建代理

初步步驟

首先,為您的函數建立目錄:

mkdir instruqt-proxy

移動到目錄並初始化Go環境:

# Replace example.com with the prefix of your choice
go mod init example.com/labs

谷歌雲端功能利用

對於本機測試,建立一個cmd目錄:

mkdir cmd

在該目錄中建立一個 main.go 文件,內容如下:

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)
    }
}

建立函數

返回 instruqt-proxy 目錄,建立一個 proxy.go 文件,然後向其中添加 init() 函數以及我們將使用的 Go 套件:

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)
}

這將允許 Google Cloud Functions 在初始化時呼叫 instruqtProxy 函數。

讓我們來寫這個函數:

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)
}

在此函數中,我們:

  1. 從 INSTRUQT_TOKEN 環境變數取得 Instruqt 令牌
  2. 為令牌和團隊初始化 Instruqt API 客戶端
  3. 從 URL 參數中檢索 utk 參數以驗證使用者
  4. 根據該UTK取得使用者資訊
  5. 從 URL 參數取得 lab slug
  6. 檢索重定向的實驗室 URL
  7. 使用 http.Redirect 函數重定向使用者

實作 getLabURL()

getLabURL 函數將根據使用者資訊、請求的實驗室 slug 以及來自 Instruqt API 的動態資訊產生實驗室的重定向 URL。

我們來寫:

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
}

首先,請注意,我們定義了一些您可以調整的新常數:

  • labSignupPage 是您網站上的 URL,未經驗證的使用者將被重新導向到該 URL。它包含實驗室蛞蝓的變數。
  • finishBtnText 是實驗室完成按鈕上顯示的文字。
  • finishBtnURL 是實驗結束時按鈕的操作。它還包含實驗室蛞蝓的變數。

現在我們來解釋一下getLabURL()函數的步驟:

  1. 從Instruqt API中獲取曲目信息,如果找不到則報錯。
  2. 如果使用者未知,則重定向到註冊頁面。
  3. 為嵌入式軌道存取產生一次性令牌。
  4. 產生重定向 URL。
  5. 使用 Instruqt API 中的 PII 金鑰加密使用者資訊。
  6. 將所有參數(一次性令牌、加密的使用者資訊、完成按鈕選項)新增至重定向 URL。
  7. 對 URL 進行編碼。
  8. 傳回結果 URL。

getUser() 函數

此代理程式中缺少的最後一個部分是 getUser() 函數。我在這裡幫不了你太多,因為這部分是你插入你自己的邏輯的地方。您可能正在使用像 Hubspot 這樣的 CRM 從 UTK 或其他資料庫檢索聯絡資訊,這取決於您!

我將在此處向您展示的程式碼僅傳回範例使用者:

/*
 * 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
}

測試程式碼

現在我們已經有了完整的 proxy.go 功能,讓我們來測試一下!

首先,使用以下內容更新您的 go.mod 和 go.sum 檔案:

go get ./...
go mod tidy

在 Instruqt 儀表板中,前往「API 金鑰」並取得 API 金鑰的值。將其匯出為 shell 中的變數:

export INSTRUQT_TOKEN=<your_instruqt_token>

接下來,在本機上啟動該功能:

FUNCTION_TARGET=InstruqtProxy go run ./cmd/main.go

最後,在另一個終端中,向 localhost:8080 發出測試請求,您的函數將在其中運行(如果需要,您可以傳遞上面的 PORT 環境變數來更改連接埠):

curl  -i "localhost:8080/?slug=cilium-getting-started&utk=someUtkValue"

適應使用 Instruqt 組織中存在的軌道 slug。如果該曲目存在,您應該會收到一個302 回應,其中包含用於存取的一次性令牌的重定向URL,以及使用PII 金鑰加密的John Doe 的資訊和一次性令牌(以ott_ 開頭) !

替代測試:使用 Docker

如果您想使用 Docker 在本機測試您的功能,您可以在目前目錄中建立一個 Dockerfile:

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"]

新增 docker-compose.yaml 檔案:

version: '3'
services:
  proxy:
    build: ./
    ports:
      - "8080:8080"
    environment:
      INSTRUQT_TOKEN: ${INSTRUQT_TOKEN}
      FUNCTION_TARGET: InstruqtProxy

最後,建置並啟動您的容器:

docker-compose up --build

並且您可以像以前一樣向 localhost:8080 發送請求!

Hosting the Proxy on Google Cloud Functions

In order to deploy to Google Cloud, first make sure you are logged in to your Google Cloud project:

gcloud auth application-default login

Create a Secret for the API Token

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!

Deploy 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-.cloudfunctions.net/instruqt-proxy.
You can then test the function using that URL instead of localhost:8080!

Further Considerations

This is a simplified approach to the lab proxy we use at Isovalent. There are things you might want to consider with this implementation:

  • If you have actual user (instead of marketing contact), switch to a proper authentication system (e.g. JWT) instead of UTK.
  • The current implementation will give access to any lab in your collection if you know its slug. You might want to filter them out (using for example track tags).
  • This implementation manages errors but is very basic in logging. We would recommend using Google Cloud logging to easily audit function runs.
  • You might want to pass extra information as custom parameters. For example, we like to pass some form of even or campaign ID. These can then be retrieved via the API as part or the Track structure.
  • If you're using a custom form and redirecting to the proxy, you might want to be sure your CRM/database has already gotten the user information. You can for example implement a retry logic in the proxy function.
  • Invite embed URLs contain the invite ID. If you want to support invites, the proxy could take an optional invite parameter and add it to the URL.

Using the Proxy

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:

  1. Set up lab sign-up pages with the form system of your choice (e.g. using Hubspot forms).
  2. Retrieve a user identifier from the page context (e.g. a Hubspot cookie token).
  3. Redirect users to the proxy, passing the user identifier and lab slug as parameters.

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.

以上是簡化對嵌入式 Instruqt 實驗室的訪問的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn