【问题标题】:How to authorize an HTTP POST request to execute dataflow template with REST API如何授权 HTTP POST 请求以使用 REST API 执行数据流模板
【发布时间】:2019-08-09 15:44:10
【问题描述】:

我正在尝试通过 NodeJS 后端服务器中的 REST API 将 Cloud Bigtable 执行到 Cloud Storage SequenceFile 模板。

我正在使用 axios 0.17.1 发送请求,我得到 401 状态。 我试图按照谷歌文档进行操作,但是我不知道如何授权 HTTP 请求来运行数据流模板。 我想作为服务帐户进行身份验证,并且成功生成并下载了包含私钥的 json 文件。 任何人都可以通过向https://dataflow.googleapis.com/v1b3/projects/[YOUR_PROJECT_ID]/templates:launch?gcsPath=gs://dataflow-templates/latest/ 发送 HTTP POST 请求的示例来帮助我

【问题讨论】:

  • 我现在也在做同样的事情。这些文档对于通过 HTTP 执行此操作不是很有帮助。我认为您可以从服务帐户凭据生成 JWT,将 JWT 交换为访问令牌,然后使用它来发出请求。我收到 403,但可能是权限错误。试试看:gist.github.com/adrice727/1199e1e3df87018058926fec6447e583
  • 更新:可以确认以上方法有效。

标签: node.js rest google-cloud-platform authorization dataflow


【解决方案1】:

您需要从您的服务帐户凭据生成jwt。可以将 jwt 交换为访问令牌,然后该令牌可用于发出执行 Dataflow 作业的请求。完整示例:

import axios from "axios";
import jwt from "jsonwebtoken";
import mem from "mem";
import fs from "fs";

const loadServiceAccount = mem(function(){
    // This is a string containing service account credentials
    const serviceAccountJson = process.env.GOOGLE_APPLICATION_CREDENTIALS;
    if (!serviceAccountJson) {
      throw new Error("Missing GCP Credentials");
    }
})

const loadCredentials = mem(function() {

  loadServiceAccount();

  const credentials = JSON.parse(fs.readFileSync("key.json").toString());

  return {
    projectId: credentials.project_id,
    privateKeyId: credentials.private_key_id,
    privateKey: credentials.private_key,
    clientEmail: credentials.client_email,
  };
});

interface ProjectCredentials {
  projectId: string;
  privateKeyId: string;
  privateKey: string;
  clientEmail: string;
}

function generateJWT(params: ProjectCredentials) {
  const scope = "https://www.googleapis.com/auth/cloud-platform";
  const authUrl = "https://www.googleapis.com/oauth2/v4/token";
  const issued = new Date().getTime() / 1000;
  const expires = issued + 60;

  const payload = {
    iss: params.clientEmail,
    sub: params.clientEmail,
    aud: authUrl,
    iat: issued,
    exp: expires,
    scope: scope,
  };

  const options = {
    keyid: params.privateKeyId,
    algorithm: "RS256",
  };

  return jwt.sign(payload, params.privateKey, options);
}

async function getAccessToken(credentials: ProjectCredentials): Promise<string> {
  const jwt = generateJWT(credentials);
  const authUrl = "https://www.googleapis.com/oauth2/v4/token";
  const params = {
    grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
    assertion: jwt,
  };
  try {
    const response = await axios.post(authUrl, params);
    return response.data.access_token;
  } catch (error) {
    console.error("Failed to get access token", error);
    throw error;
  }
}

function buildTemplateParams(projectId: string, table: string) {
  return {
    jobName: `[job-name]`,
    parameters: {
      bigtableProjectId: projectId,
      bigtableInstanceId: "[table-instance]",
      bigtableTableId: table,
      outputDirectory: `[gs://your-instance]`,
      filenamePrefix: `${table}-`,
    },
    environment: {
      zone: "us-west1-a" // omit or define your own,
      tempLocation: `[gs://your-instance/temp]`,
    },
  };
}

async function backupTable(table: string) {
  console.info(`Executing backup template for table=${table}`);
  const credentials = loadCredentials();
  const { projectId } = credentials;
  const accessToken = await getAccessToken(credentials);
  const baseUrl = "https://dataflow.googleapis.com/v1b3/projects";
  const templatePath = "gs://dataflow-templates/latest/Cloud_Bigtable_to_GCS_Avro";
  const url = `${baseUrl}/${projectId}/templates:launch?gcsPath=${templatePath}`;
  const template = buildTemplateParams(projectId, table);
  try {
    const response = await axios.post(url, template, {
      headers: { Authorization: `Bearer ${accessToken}` },
    });
    console.log("GCP Response", response.data);
  } catch (error) {
    console.error(`Failed to execute template for ${table}`, error.message);
  }
}

async function run() {
  await backupTable("my-table");
}

try {
  run();
} catch (err) {
  process.exit(1);
}

【讨论】:

  • 感谢您的回复。实际上,模板需要更多范围,所以我将它们添加为单个空格分隔的字符串。不幸的是,JSON.parse() 引发了异常Unexpected token \ in JSON at position 1 我用空格替换了\\n,发送了启动模板的调用但我得到了400 bad request。你能帮我调试一下错误的请求吗?
  • 我收到了错误的请求,因为我使用了错误的模板参数
  • @MelekKaroui 答案已更新以解决凭据 json 的问题。
猜你喜欢
  • 2015-03-08
  • 2016-05-31
  • 2020-09-18
  • 1970-01-01
  • 2018-01-05
  • 1970-01-01
  • 2018-06-29
  • 1970-01-01
  • 2011-07-29
相关资源
最近更新 更多