【问题标题】:How can I change a VM/Instance label with GCP Cloud Function using node.js?如何使用 node.js 更改带有 GCP Cloud Function 的 VM/实例标签?
【发布时间】:2020-01-14 04:03:29
【问题描述】:

我正在 GCP 中测试一个云函数,我想用云函数修改我的计算实例的标签,即将标签“status=active”更改为“status=tobedeleted”。

有没有办法用 Cloud Function 和 node.js 做到这一点?

看起来方法 compute.instances.setLabels 需要额外的库?

我已经创建了 Cloud Function 来停止/启动实例。

这是错误:

资源:{…}
严重性:“错误”
textPayload:“{错误:需要登录 在 Gaxios.request (/srv/node_modules/googleapis-common/node_modules/gaxios/build/src/gaxios.js:70:23) 在 在 process._tickDomainCallback (internal/process/next_tick.js:229:7) 回复: {配置: { 网址:'https://www.googleapis.com/compute/v1/projects/wpress-v1/zones/us-central1-a/instances/instance-1/setLabels?labels%5Bis-scheduled%5D=manual', 方法:'POST', 参数序列化器:[功能], 标题:[对象], 参数:[对象], 验证状态:[功能], 重试:真, 响应类型:'json', 重试配置:[对象]}, 数据:{错误:[对象]},

然后这是我的代码:

const Compute = require('@google-cloud/compute');
/*const compute = new Compute();*/
const {google} = require('googleapis');
/*const google = require('@google-cloud/googleapis');*/
var compute = google.compute('v1');

exports.setInstanceScheduleMode = (event, context, callback) => {
  try {

    const payload = _validatePayload(
      JSON.parse(Buffer.from(event.data, 'base64').toString())
    );

    var request = {
    project: 'wpress-v1',  
    zone: 'us-central1-a', 
    instance: 'instance-1', 
    labels: {
    "is-scheduled": "manual"
  },
    auth: google.authClient,
    };
    compute.instances.setLabels(request, function(err, response) {
    if (err) {
      console.error(err);
      return;
    }


    console.log(JSON.stringify(response, null, 2));
  });
  } catch (err) {
    console.log(err);
    callback(err);
  }
};
// [END functions_start_instance_pubsub]
function _validatePayload(payload) {
  if (!payload.zone) {
    throw new Error(`Attribute 'zone' missing from payload`);
  } else if (!payload.label) {
    throw new Error(`Attribute 'label' missing from payload`);
  }
  else if (!payload.instance) {
    throw new Error(`Attribute 'instance' missing from payload`);
  }
  return payload;
}
function authorize(callback) {
  google.auth.getClient({
    scopes: ['https://www.googleapis.com/auth/cloud-platform']
  }).then(client => {
    callback(client);
  }).catch(err => {
    console.error('authentication failed: ', err);
  });
}

【问题讨论】:

  • 我知道这看起来像是一个授权问题,但我试图找出除了授权功能之外我应该在哪里

标签: node.js google-cloud-platform google-cloud-functions google-compute-engine


【解决方案1】:

Google Cloud Platform documentation 提供了 instances.setLabels 方法的详细概述,该方法是 Google 的 Node.js client library 的一部分。

请参阅下面 GCP 文档中提到的 Node.js 代码示例

// BEFORE RUNNING:
// ---------------
// 1. If not already done, enable the Compute Engine API
//    and check the quota for your project at
//    https://console.developers.google.com/apis/api/compute
// 2. This sample uses Application Default Credentials for authentication.
//    If not already done, install the gcloud CLI from
//    https://cloud.google.com/sdk and run
//    `gcloud beta auth application-default login`.
//    For more information, see
//    https://developers.google.com/identity/protocols/application-default-credentials
// 3. Install the Node.js client library by running
//    `npm install googleapis --save`

const {google} = require('googleapis');
var compute = google.compute('v1');

authorize(function(authClient) {
  var request = {
    // Project ID for this request.
    project: 'my-project',  // TODO: Update placeholder value.

    // The name of the zone for this request.
    zone: 'my-zone',  // TODO: Update placeholder value.

    // Name of the instance scoping this request.
    instance: 'my-instance',  // TODO: Update placeholder value.

    resource: {
      // TODO: Add desired properties to the request body.
    },

    auth: authClient,
  };

  compute.instances.setLabels(request, function(err, response) {
    if (err) {
      console.error(err);
      return;
    }

    // TODO: Change code below to process the `response` object:
    console.log(JSON.stringify(response, null, 2));
  });
});

function authorize(callback) {
  google.auth.getClient({
    scopes: ['https://www.googleapis.com/auth/cloud-platform']
  }).then(client => {
    callback(client);
  }).catch(err => {
    console.error('authentication failed: ', err);
  });
}

请记住在编写代码时将request body 作为参数发送。

使用此方法时请注意以下事项:

  • 您需要在 labelFingerprint 下指定您的实例具有的当前标签
  • 您的实例的标签将被覆盖,因此请确保在请求正文中包含您想要保留的任何标签。

【讨论】:

  • 感谢您的信息。但我仍然有麻烦。我将粘贴我的代码,如果有人有想法,将不胜感激。
  • 请注意,上面的代码几乎肯定会因条件错误而失败,因为它不提供setLabels 请求中当前标签的指纹。
【解决方案2】:

这段代码中发生了很多事情。这不是一个简单的操作,我希望文档中有更多示例说明如何执行此操作。

首先,@google-cloud/compute 惯用库似乎在其VMs object 上不支持setLabels 函数,因此我们不得不使用node REST library,它不太容易使用.您编写的代码似乎以某种令人困惑的方式混合了两者,但大部分已经在使用 REST API,因此我们可以从那里开始。供参考,setLabels REST API documentation

其次,您遇到的身份验证错误是因为您没有为 REST API 正确集成 authClient,特别是通过授予它正确的范围。 (值得注意的是,与示例代码不同,从未调用过authorize() 方法)。这需要调用以至少请求@987654324@ 范围,尽管cloud-platform 范围也可以工作,因为它具有更多特权。这就是导致您立即发生身份验证错误的原因。

您也有可能在没有必要角色的情况下将云功能作为 IAM 帐户运行,但默认计算引擎和默认应用程序引擎帐户都应该能够执行此操作,因此范围似乎是'没有要求。

最后,即使这样有效,您会发现 setLabels 方法需要当前标签值的指纹,否则它会返回 CONDITION_FAILURE——本质上,当您调用 setLabels 时,您将完全替换上的标签实例,因此 API 希望确保两个调用者不会同时竞争。

总而言之,这导致我们这样做(为简单起见,我使用了一个 HTTP 函数,但当然您也可以使用现有的触发器):

const { google } = require('googleapis');
const computeClient = google.compute('v1');

exports.labelInstance = async (req, res) => {
  // First, get the auth scope we need.  Thankfully cloud functions runs with 
  // application default credentials, so we don't need to do anything with keys, etc
  // as long as the service account we are configured to run as has the right permissions.
  //
  // We only need the compute scope, we don't need all of cloud-platform, so limit ourselves to that.

  const auth = new google.auth.GoogleAuth({
    scopes: ['https://www.googleapis.com/auth/compute']
  });
  const authClient = await auth.getClient();

  // Build our request
  var baseRequest = {
    project: 'YOUR-PROJECT-NAME',  
    zone: 'us-central1-a', 
    instance: 'instance-1', 
    auth: authClient
  };

  // We need to get the existing labels and fingerprint first. 
  return computeClient.instances.get(baseRequest).then(result => {
    // We need all the fields from baseRequest again, and we want to keep the old labels.
    // I'm sort of cheating here, since it isn't a deep copy, but it works within the
    // scope of this function.
    setRequest = baseRequest;

    // As setLabels is a POST request, we need to put the parameters in the requestBody.
    setRequest.requestBody = {
      labels: result.data.labels || {},
      labelFingerprint: result.data.labelFingerprint  // Needed to avoid CONDITION_FAILURE
    };

    // And add our new label...
    setRequest.requestBody.labels['my-new-label'] = 'my-new-value';

    return computeClient.instances.setLabels(setRequest);
  }).then(result => {
    console.log('set done');
    console.log(result);
    return res.send('ok');
  }).catch(error => {
    console.error('Error!');
    console.error(error);
    return res.send('error');
  });
};

在您最初的问题中,您想更改标签。显然,您可以调整上面的代码,从使用您喜欢的指纹检索到的集合中删除任何标签,您不必全部复制它们。

另外请注意,上述代码实际上并没有等待操作完成(因为操作是异步的——返回的结果可能处于 RUNNING 状态),您需要进一步使用 REST API检查操作的状态。我没有这样做,因为它有点超出了这个问题的范围,但你可以read about it here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-02-17
    • 2020-11-02
    • 2021-06-12
    • 2020-09-25
    • 1970-01-01
    • 2021-10-24
    • 2018-04-24
    • 1970-01-01
    相关资源
    最近更新 更多