首页 > 后端开发 > Golang > 正文

简化对嵌入式 Instruqt 实验室的访问

Patricia Arquette
发布: 2024-10-03 20:07:30
原创
1066 人浏览过

您如何向潜在客户和现有客户教授技术性很强的主题?如何让旅途顺利?

在 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中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板