如何使用node.js通过GCP Cloud Function更改VM /实例标签?

时间:2019-09-12 09:43:00

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

我正在GCP中测试一个Cloud Function,我想使用Cloud Function修改我的计算实例的标签,即将标签“ 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(内部/进程/next_tick.js:229:7)     响应:      {配置:         {url:'https://www.googleapis.com/compute/v1/projects/wpress-v1/zones/us-central1-a/instances/instance-1/setLabels?labels%5Bis-scheduled%5D=manual',           方法:“ POST”,           paramsSerializer:[功能],           标头:[Object],           参数:[Object],           validateStatus:[功能],           重试:是的,           responseType:'json',           retryConfig:[Object]},        数据:{错误:[对象]},

然后这是我的代码:

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

2 个答案:

答案 0 :(得分:0)

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 下指定实例的当前标签
  • 您实例的标签将被覆盖,因此请确保在请求正文中包含您要保留的所有标签。

答案 1 :(得分:0)

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

首先,似乎@google-cloud/compute惯用库在其VMs object上不支持setLabels函数,因此我们被迫使用node REST library,不太容易使用。您编写的代码似乎以一种令人困惑的方式将两者混为一谈,但是大多数时候已经在使用REST API,因此我们可以从那里开始。供参考,setLabels REST API documentation

第二,您收到的身份验证错误是因为您没有正确利用REST API的authClient,特别是通过授予它正确的作用域。 (值得注意的是,与示例代码不同,从未调用authorize()方法)。尽管https://www.googleapis.com/auth/compute范围也可以工作,因为它具有更高的特权,但是至少需要请求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