Node.js : Ожидание запроса

#javascript #node.js #asynchronous #azure-keyvault

#javascript #node.js #асинхронный #azure-keyvault

Вопрос:

Я новичок Node.js , работал с образцом проекта стороннего поставщика и пытаюсь использовать Azure Key Vault для хранения значений конфигурации.

У меня возникли проблемы с запуском процесса wait перед выполнением остальных. Я постараюсь описать все подробно, насколько мне известно.

Пример проекта имеет файл с именем agent.js , который является начальной страницей / файлом. В строке 16 ( agent_config = require('./config/config.js')[process.env.LP_ACCOUNT][process.env.LP_USER] ) он вызывает конфигурационный файл со значениями. Я пытаюсь установить эти значения с помощью хранилища ключей. Я перепробовал множество комбинаций вызывающих функций и даже их реализацию, async / await но значение для agent_config всегда содержит [Promise] объект, а не данные, возвращаемые Key Vault.

Если я прав, это потому, что само хранилище ключей также использует async / await и файл конфигурации возвращается до того, как будут возвращены значения хранилища ключей.

Как можно добавить / реализовать хранилище ключей в подобной ситуации?

Вот что я пробовал:

Первое обновление agent.js для

 let agent_config = {};
try {
  agent_config = require('./config/config.js')['123']['accountName'];
} catch (ex) {
  log.warn(`[agent.js] Error loading config: ${ex}`)
}

console.log(agent_config);
  

Тест 1

. /config/config.js

 const KeyVault = require('azure-keyvault');
const msRestAzure = require('ms-rest-azure');
const KEY_VAULT_URI = 'https://'   '{my vault}'   '.vault.azure.net/' || process.env['KEY_VAULT_URI'];

function getValue(secretName, secretVersion) {
  msRestAzure.loginWithAppServiceMSI({ resource: 'https://vault.azure.net' }).then((credentials) => {
    const client = new KeyVault.KeyVaultClient(credentials);
    client.getSecret(KEY_VAULT_URI, secretName, secretVersion).then(
      function (response) {
        return response.Value;
      });
  });
}

module.exports = {
    '123': {
        'accountName': {
            accountId: getValue('mySecretName', '')
         }
     }
};
  

Результаты

{ Идентификатор учетной записи: не определен }

Тест 2

Создал GetValue в async функции и обернул ее вокруг другой функции (пробовал без переноса, но тоже не сработало)

. /config/config.js

 const KeyVault = require('azure-keyvault');
const msRestAzure = require('ms-rest-azure');
const KEY_VAULT_URI = 'https://'   '{my vault}'   '.vault.azure.net/' || process.env['KEY_VAULT_URI'];

async function getValue(secretName, secretVersion) {
  msRestAzure.loginWithAppServiceMSI({ resource: 'https://vault.azure.net' }).then((credentials) => {
    const client = new KeyVault.KeyVaultClient(credentials);
    client.getSecret(KEY_VAULT_URI, secretName, secretVersion).then(
      function (response) {
        return response.Value;
      });
  });
}

async function config() {
    module.exports = {
        '123': {
            'accountName': {
                accountId: await getValue('mySecretName', '')
             }
         }
    };
}

config();
  

Результаты

{}

Тест 3

Создал GetValue в async функции и обернул ее вокруг другой функции (пробовал без переноса, но тоже не сработало)

. /config/config.js

 const KeyVault = require('azure-keyvault');
const msRestAzure = require('ms-rest-azure');
const KEY_VAULT_URI = 'https://'   '{my vault}'   '.vault.azure.net/' || process.env['KEY_VAULT_URI'];

async function getValue(secretName, secretVersion) {
  return msRestAzure.loginWithAppServiceMSI({ resource: 'https://vault.azure.net' })
    .then((credentials) => {
      const client = new KeyVault.KeyVaultClient(credentials);
      return client.getSecret(KEY_VAULT_URI, secretName, secretVersion).then(
        function (response) {
          return response.Value;
        });
    });
}

module.exports = {
    '123': {
        'accountName': {
            accountId: getValue('mySecretName', '')
        }
    }
};

config();
  

Результаты

{ AccountId: { <ожидающий> } }

Другое

Я пробовал много других способов, таких как module.exports = async (value) =< {...} (найдено в других вопросах / решениях, но безуспешно.

Я начинаю думать, что мне нужно немного «подождать» agent.js , но я не нашел хорошей информации по этому поводу.

Любая помощь была бы отличной!

Ответ №1:

Одна из проблем заключается в том, что ваша getValue функция ничего не возвращает, поскольку ваши возвраты должны быть явными.

(и без возврата обещания ждать нечего)

 async function getValue(secretName, secretVersion) {
  return msRestAzure.loginWithAppServiceMSI({ resource: 'https://vault.azure.net' })
    .then((credentials) => {
      const client = new KeyVault.KeyVaultClient(credentials);
      return client.getSecret(KEY_VAULT_URI, secretName, secretVersion).then(
        function (response) {
          return response.Value;
        });
    });
}
  

Вы также могли бы избежать менее явных возвратов, используя функции со стрелками..

 const getValue = async (secretName, secretVersion) => 
  msRestAzure.loginWithAppServiceMSI({ resource: 'https://vault.azure.net' })
    .then(credentials => {
      const client = new KeyVault.KeyVaultClient(credentials);
      return client.getSecret(KEY_VAULT_URI, secretName, secretVersion)
        .then(response => response.Value);
    });
  

Асинхронное чтение Azure Key Vault означает, что все ваши данные конфигурации будут асинхронными. Вы ничего не можете сделать, чтобы обойти это. Это будет означать, что код, использующий конфигурацию, должен будет обработать ее соответствующим образом. Вы начинаете с экспорта асинхронной функции, которая вернет конфигурацию..

 async function getConfig() {
  return {
    '123': {
      'accountName': {
        accountId: await getValue('mySecretName', '')
      }
    }
  };
}

module.exports = getConfig;
  

В вашем коде агента вы вызываете эту функцию. Это будет означать, что ваш код агента тоже нужно будет обернуть в функцию, так что, возможно, что-то вроде этого..

 const Bot = require('./bot/bot.js');
const getConfig = require('./config/config.js');

getConfig().then(agentConfig => {
    const agent = new Bot(agentConfig);

    agent.on(Bot.const.CONNECTED, data => {
        log.info(`[agent.js] CONNECTED ${JSON.stringify(data)}`);
    });
});
  

Комментарии:

1. Вы делаете это неправильно в тесте 3. Вы должны ожидать на getValue . Но это приведет к тому, что ваша конфигурация должна быть обернута внутри асинхронной функции, что-то вроде test 1 . Кстати, вы делаете это неправильно в тесте 1: не устанавливайте module.exports там, вместо этого возвращайте объект, которому будет присвоен module.exports .

2. добавлен пример использования к моему ответу

3. @RoLYroLLs можете ли вы проверить, сработает ли это? gist.github.com/Pritilender/7ae1d68331b5f76c818e601760bd2db2

4. упс, спасибо, исправлена опечатка. Не уверен насчет неопределенного AccountId сейчас. Я бы добавил ведение журнала в GetValue, чтобы проверить, credentials и response являются ли они ожидаемыми.

5. response => response.Value похоже, «значение» должно быть в нижнем регистре

Ответ №2:

Пакет azure-keyvault устарел в пользу новых пакетов, позволяющих обрабатывать ключи Keyvault, секреты и сертификаты отдельно. Для вашего сценария вы можете использовать новый пакет @azure/keyvault-secrets для взаимодействия с хранилищем ключей и новый пакет @azure/identity для создания учетных данных.

 const { SecretClient } = require("@azure/keyvault-secrets");
const { DefaultAzureCredential } = require("@azure/identity");

async function getValue(secretName, secretVersion) {
  const credential = new DefaultAzureCredential();
  const client = new SecretClient(KEY_VAULT_URI, credential);
  const secret = await client.getSecret(secretName);
  return secret.value;
}
  

DefaultAzureCredential Предполагается, что вы установили приведенные ниже переменные env

  • AZURE_TENANT_ID: идентификатор клиента в Azure Active Directory
  • AZURE_CLIENT_ID: приложение (клиент) ИДЕНТИФИКАТОР, зарегистрированный в клиенте AAD
  • AZURE_CLIENT_SECRET: секрет клиента для зарегистрированного приложения

Чтобы попробовать другие учетные данные, обратитесь к readme для @azure/identity Если вы переходите из более старого пакета azure-keyvault, ознакомьтесь с руководством по миграции, чтобы понять основные изменения