Статьи

Бессерверный API с функциями Azure

В этом посте я собираюсь поработать над довольно простым вариантом использования. Во время выполнения конвейера развертывания FlexDeploy может создавать некоторые неавтоматизированные задачи, которые должны быть либо утверждены, либо отклонены. Например, кто-то должен одобрить развертывание в производственной среде. Это можно сделать либо в пользовательском интерфейсе FlexDeploy, либо с некоторыми внешними каналами связи. Сегодня я собираюсь сосредоточиться на сценарии, когда человеческая задача FlexDeploy одобрена / отклонена с помощью Slack :

Безсерверный API

Есть несколько требований и соображений, которые я хотел бы принять во внимание:

  • Я не хочу учить FlexDeploy общаться со Slack
  • Я не хочу предоставлять Slack подробности API FlexDeploy
  • Я не хочу показывать FlexDeploy API публично
  • Я хочу иметь возможность легко изменить Slack на что-то другое или добавить другие инструменты коммуникации, не касаясь FlexDeploy

По сути, я хочу отделить FlexDeploy от деталей механизма внешней связи. По этой причине я собираюсь представить дополнительный слой, API между FlexDeploy и Slack . Похоже, что серверная парадигма является очень привлекательным подходом для реализации этого API . Сегодня я собираюсь создать его с помощью функций Azure , потому что … почему бы и нет?

Таким образом, технически версия решения выглядит следующим образом:

Безсерверный API

Как только появляется новая человеческая задача, FlexDeploy уведомляет бессерверный API об этом, предоставляя внутренний идентификатор задачи и описание задачи. Существует функция SaveTask, которая сохраняет предоставленные сведения о задаче вместе с сгенерированным токеном (всего лишь uid) в хранилище Azure Table . Этот токен имеет время истечения, что означает, что его следует использовать до того времени, чтобы утвердить / отклонить задачу.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
const azure = require('azure-storage');
const uuidv1 = require('uuid/v1');
 
module.exports = async function (context, taskid) {  
    var tableSvc = azure.createTableService('my_account', 'my_key');
    var entGen = azure.TableUtilities.entityGenerator;
    var token = uuidv1();
    var tokenEntity = {
        PartitionKey: entGen.String('tokens'),
        RowKey: entGen.String(token),
        TaskId: entGen.String(taskid),
        dueDate: entGen.DateTime(new Date(Date.now() + 24 * 60 * 60 * 1000))
      };
      
      tableSvc.insertEntity('tokens',tokenEntity, function (error, result, response) { });
 
    return token;
};

После сохранения токена вызывается функция PostToSlack, которая отправляет сообщение на канал Slack . Функции SaveTask и PostTo Slack объединены в долговременную функцию NotifyOnTask, которая фактически вызывается FlexDeploy:

1
2
3
4
5
6
7
const df = require("durable-functions");
 
module.exports = df.orchestrator(function*(context){  
    var task = context.df.getInput()
    var token = yield context.df.callActivity("SaveTask",  task.taskid)
    return yield context.df.callActivity("PostToSlack",  {"token": token, "description": task.description})
});

Сообщение в Slack содержит две кнопки для подтверждения и отклонения задачи.

Безсерверный API

Кнопки относятся к веб- зацепкам, указывающим на долговременную функцию ActionOnToken :

01
02
03
04
05
06
07
08
09
10
11
const df = require("durable-functions");
 
module.exports = df.orchestrator(function*(context){  
    var input = context.df.getInput()
    var taskId = yield context.df.callActivity("GetTaskId",  input.token)
    if (input.action == 'approve') {
        yield context.df.callActivity("ApproveTask",  taskId)
    } else if (input.action == 'reject') {
        yield context.df.callActivity("RejectTask",  taskId)
    }
});

ActionOnToken вызывает функцию GetTaskId, получая идентификатор задачи из хранилища по указанному токену:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const azure = require('azure-storage');
 
module.exports = async function (context, token) {
    var tableSvc = azure.createTableService('my_account', 'my_key');
 
    function queryTaskID(token) {
        return new Promise(function (resolve, reject) {
            tableSvc.retrieveEntity('tokens', 'tokens', token, 
             function (error, result, response) {
                if (error) {
                    reject(error)
                } else {
                    resolve(result)
                }
            });
        });
    }
 
    var tokenEntity = await queryTaskID(token);
    if (tokenEntity) {
        var dueDate = tokenEntity.dueDate._
        if (dueDate > Date.now()) {
            return tokenEntity.TaskId._
        }
    }
};

Сделав это, он либо одобряет, либо отклоняет задачу, вызывая либо
Функции ApproveTask или RejectTask . Эти функции, в свою очередь, выполняют соответствующие вызовы FlexDeploy REST API.

01
02
03
04
05
06
07
08
09
10
const request = require('sync-request');
 
module.exports = async function (context, taskid) {  
    var taskid = taskid;
    var res = request('PUT',
              fd_url+'/flexdeploy/rest/v1/tasks/approval/approve/'+taskid,{       
                    });
 
};

Я мог бы начать разработку своего безсерверного приложения прямо в облаке на
Портал Azure , но я решил все реализовать и поиграть с ним локально, а потом перейти к облаку. То, что я могу это делать, разрабатывать и тестировать свои функции локально, на самом деле очень круто, не каждая серверная платформа предоставляет вам такую ​​возможность. Единственное, что я настроил в облаке, это
Учетная запись хранения Azure Table с таблицей для хранения моих токенов и сведений о задании.

Удобный способ начать работу с функциями Azure локально — использовать
Visual Studio Code как инструмент разработки. Я работаю на Mac, поэтому я скачал и установил версию для Mac OS X. VS Code посвящен расширениям, поскольку для каждой технологии, с которой вы работаете, вы устанавливаете одно или несколько расширений. То же самое относится и к функциям Azure. Для этого есть расширение:

Безсерверный API

Сделав это, вы получаете новую вкладку, где вы можете создать новое функциональное приложение и начать реализацию своих функций:

Безсерверный API

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

Безсерверный API

Несмотря на то, что я люблю Java, я выбрал JavaScript, потому что помимо обычных функций я хотел реализовать долговременные функции, и они поддерживают C # ,
Только F # и JavaScript . На момент написания этого поста JavaScript был мне ближе всего.

Отдыхай как обычно. Вы создаете функции, пишете код, отлаживаете, тестируете, исправляете и все заново. Вы просто нажимаете F5 и VS Code запускает все приложение в режиме отладки для вас:

Безсерверный API

Когда вы запускаете приложение в первый раз, VS Code предложит вам установить исполняемые функции на ваш компьютер, если его там нет. Таким образом, в основном, предполагая, что у вас есть на вашем ноутбуке среда выполнения вашего предпочтительного языка (Node.js), вам просто нужно иметь VS Code с расширением функций, чтобы начать работать с функциями Azure . Он сделает все остальное за вас.

Итак, после запуска приложения я могу проверить его, вызвав функцию NotifyOnTask, которая запускает весь цикл:

1
curl -X POST --data '{"taskid":"8900","description":"DiPocket v.1.0.0.1 is about to be deployed to PROD"}'  -H "Content-type: application/json" http://localhost:7071/api/orchestrators/NotifyOnTask

Исходный код приложения доступен на GitHub .

Что ж, общее мнение о функциях Azure до сих пор … это хорошо. Это просто работает. Я не сталкивался ни с одной раздражающей проблемой (пока) при реализации этого решения (за исключением некоторых глупых ошибок, которые я сделал, потому что я не читал руководство внимательно). Я определенно буду продолжать играть и публиковать в Azure Functions, чтобы обогатить и перенести это решение в облако и, возможно, реализовать что-то другое.

Это оно!

Опубликовано на Java Code Geeks с разрешения Евгения Федоренко, партнера нашей программы JCG . См. Оригинальную статью здесь: Бессерверный API с функциями Azure.

Мнения, высказанные участниками Java Code Geeks, являются их собственными.