Рубрики
Uncategorized

Планирование функционального переключения с использованием кода с приложениями Azure Logic

Я использую запуск Darkly, чтобы переключать функции в приложении. Существует одна сторонняя зависимость, которая имеет правила … Tagged with Nocode, Azure, DevOps.

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

Запуск Darkly имеет встроенное планирование для обработки этого сценария, но вы должны быть на плане предприятия, чтобы использовать его. План предприятия слишком дорогой, чтобы обновить только для планирования, поэтому мне нужно было найти другой способ автоматизировать это.

Не-код на лазуре

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

  1. Мне нужно быстро это реализовать и перейти к продукту Особенности
  2. Он должен был встроить планирование
  3. Это нужно было легко поддерживать кем -либо
  4. Нужно было иметь легкий доступ, чтобы изменить график любым (графический интерфейс для параметров)
  5. Я должен был быть в состоянии обеспечить запуск Key Darkly API, потому что мы пишем значения функций

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

Резюме

Я опишу каждый из шагов в деталях, чтобы вы поняли, почему я должен был их добавить.

Я предоставил полную конфигурацию JSON в конце этой статьи, поэтому легко воссоздать логическое приложение.

Все это можно настроить с использованием графического интерфейса Azure Logic Apps. Я добавлю конфигурацию JSON для шагов, где вы не можете увидеть все входы на скриншотах.

Помните, что копирование этой конфигурации не будет работать сразу для вас. Вам придется настроить собственные подключения для Vault Vault и Slack Integrations, используя веб -интерфейс приложения Logic.

Полное представление о дизайнере приложений логики

Вот все вместе!

Верхняя половина дизайнера приложений

Нижняя половина дизайнера приложений

Хорошо, начнем!

Добавить логическое приложение

Создайте новое логическое приложение в группе ресурсов, и вы можете использовать план потребления для этого.

Добавить параметры логического приложения нам понадобится

Откройте редактор параметров и добавьте все эти параметры. Они все струны.

LdenvironmentKey Ваша среда, например производство
ldfeatureKey Функция, например моя третья вечеринка
LDProjectKey Проект, например мой проект
Lduserkey Идентификатор пользователя, например me@mycompany.com
PREDALESTART местное время, например 2021-04-29T19: 30: 00
расписание местное время, например 2021-04-29T19: 30: 00

LSUSERKEY используется для проверки, фактически ли наш триггер отключил вариант для пользователя.

Добавить триггер повторения

Это приложение будет проверять, требуется ли обновление каждые 30 минут. Логические приложения обеспечивают очень простой в использовании триггер рецидивов для такого рода вещей.

Добавить триггер повторения

{
  "triggers": {
    "Recurrence_Trigger": {
        "recurrence": {
        "frequency": "Minute",
        "interval": 30
    },
    "type": "Recurrence"
   }
}

Получить запуск Darkly API -ключа от Vault Azure Key

Для безопасности мы храним запуск Darkly API -ключа в существующем хранилище Azure Key. Создайте новое хранилище ключа, если вам это нужно, а затем добавьте соединение из App App для чтения секретов Vault.

Получите запуск Darkly API -ключа

Получите текущее состояние запуска темно характерная черта

Нам нужно сделать вызов HTTP на запуск Darkly API. Мы используем параметры для создания URL -адреса и добавляем заголовок авторизации из секрета Vault Vault Azure.

Получить текущий статус

"GET_current_feature_status": {
        "inputs": {
          "headers": {
            "Authorization": "@body('Get_Launch_Darkly_API_key_secret')?['value']"
          },
          "method": "GET",
          "uri": "https://app.launchdarkly.com/api/v2/users/@{parameters('ldProjectKey')}/@{parameters('ldEnvironmentKey')}/@{parameters('ldUserKey')}/flags/@{parameters('ldFeatureKey')} "
        },
        "runAfter": {
          "Get_Launch_Darkly_API_key_secret": ["Succeeded"]
        },
        "type": "Http"
      },

Разобрать ответ http http

Действие вызова HTTP не анализирует ответ. Мы должны использовать действие приложения JSON Parser Logic для выполнения анализа. Все свойства будут доступны в последующих шагах.

Вам нужен пример ответа, чтобы SADERSER создал для вас схему JSON. Вы можете использовать почтальон или скручивание, чтобы запрашивать один раз из запуска Darkly.

Разобрать ответ http http

"Parse_LD_Response_Body": {
        "inputs": {
          "content": "@body('GET_current_feature_status')",
          "schema": {
            "properties": {
              "_links": {
                "properties": {
                  "self": {
                    "properties": {
                      "href": {
                        "type": "string"
                      },
                      "type": {
                        "type": "string"
                      }
                    },
                    "type": "object"
                  }
                },
                "type": "object"
              },
              "_value": {
                "type": "boolean"
              },
              "setting": {}
            },
            "type": "object"
          }
        },
        "runAfter": {
          "GET_current_feature_status": ["Succeeded"]
        },
        "type": "ParseJson"
      }
    },

Преобразовать время начала и окончания в логические

Это одно и то же, повторялось дважды, поэтому я просто опишу PradeLestart. Расписание — это один и тот же шаблон с разными именами!

Чтобы начать использовать конверсию часового пояса в UTC, потому что все другие действия логики приложения используют UTC.

Преобразовать в UTC

Инициализировать логическую переменную

Это будет удерживать результат от тестирования, если пройдет запланированное время.

Инициализировать переменную

Проверьте, прошло ли запланированное время

Здесь мы проверяем, больше ли сейчас запланированное время.

Контрольная работа Если прошло время

      "Detect_if_start_time_has_passed": {
        "inputs": {
          "name": "isScheduledStartTimePassed",
          "value": "@greater(ticks(utcNow()),ticks(body('Convert_schedule_start_to_UTC_time_zone')))"
        },
        "runAfter": {
          "Initialize_start_time_variable": ["Succeeded"]
        },
        "type": "SetVariable"
      },

Теперь Сделайте те же три шага, кроме как использовать параметр графика конечного графика.

Проверьте, есть ли мы в запланированном периоде

Так что проверьте, если мы после начала и до окончания времени. Мы можем сделать это на основе предыдущих переменных, которые мы создали. Посмотрите на скриншот, как это настроено.

Мы в течение запланированного периода

Если мы находимся в расписании, проверьте ли функция в настоящее время

_value Параметр здесь от действия Parse Json, который мы сделали вверх в начале. Это состояние запуска Darkly Feature прямо сейчас.

Если мы находимся в расписании, и он в настоящее время включен (правда), тогда нам нужно отключить его!

Это функция в настоящее время на

Позвоните в запуск Darkly API, чтобы отключить функцию

Здесь мы снова используем параметры из приложения Logic для генерации URL. Мы используем авторизацию из Key Vault. Здесь есть дополнительный тип контента, потому что API запуска Darkly использует формат патча JSON.

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

Выключите функцию в Darkly Darkly

Получите последнее состояние функции, чтобы проверить наш звонок работал

Мы снова получаем состояние ключа, чтобы убедиться, что мы действительно переключали эту функцию, как и ожидалось.

Получите статус функции от запуска Darkly

Разобрать этот ответ с запуска мрачно

Это то же самое, что и предыдущий шаг синхронизации! Мы хотим иметь _value Доступно позже.

Анализировать ответ

Отправить сообщение на канал Slack

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

Вам нужно быть администратором на своем слабом, чтобы добавить интеграцию.

Как только интеграция добавлена, вы можете установить его на «Отправить сообщение на канал».

Чем больше информации вы добавляете здесь, тем лучше! Вы можете изменить значок и имя бота, но используя выбор «Добавить новый параметр».

Отправить сообщение Slack

Теперь делайте случай, когда мы за пределами графика и функция выключены

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

Здесь вы можете увидеть, что инструкция — «Путешественник».

Включите эту функцию в запуске Darkly

Проверьте это!

Установите параметры расписания на некоторое время близко к текущему времени. Закройте запись параметров, нажав X в правом верхнем углу.

Затем Нажмите «Сохранить» в верхнем левом углу и нажмите «Запуск»!

Вывод

Это было очень легко настроить! Я наверняка попытаюсь перенести больше DevOps Type Work в логические приложения.

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

Другая вещь, которая была бы замечательной, — это попросить приложение прочитать веб -сайт или API, чтобы получить графики от третьей стороны. Прямо сейчас мы должны вручную устанавливать графики каждый раз.

Полное представление кода приложения логики

{
  "definition": {
    "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
    "actions": {
      "Convert_schedule_end_to_UTC_time_zone": {
        "inputs": {
          "baseTime": "@parameters('scheduleEnd')",
          "destinationTimeZone": "UTC",
          "formatString": "o",
          "sourceTimeZone": "AUS Eastern Standard Time"
        },
        "kind": "ConvertTimeZone",
        "runAfter": {
          "Detect_if_start_time_has_passed": ["Succeeded"]
        },
        "type": "Expression"
      },
      "Convert_schedule_start_to_UTC_time_zone": {
        "inputs": {
          "baseTime": "@parameters('scheduleStart')",
          "destinationTimeZone": "UTC",
          "formatString": "o",
          "sourceTimeZone": "AUS Eastern Standard Time"
        },
        "kind": "ConvertTimeZone",
        "runAfter": {
          "Parse_LD_Response_Body": ["Succeeded"]
        },
        "type": "Expression"
      },
      "Detect_if_end_time_has_passed": {
        "inputs": {
          "name": "isScheduledEndTimePassed",
          "value": "@greater(ticks(utcNow()),ticks(body('Convert_schedule_end_to_UTC_time_zone')))"
        },
        "runAfter": {
          "Initialize_end_time_variable": ["Succeeded"]
        },
        "type": "SetVariable"
      },
      "Detect_if_start_time_has_passed": {
        "inputs": {
          "name": "isScheduledStartTimePassed",
          "value": "@greater(ticks(utcNow()),ticks(body('Convert_schedule_start_to_UTC_time_zone')))"
        },
        "runAfter": {
          "Initialize_start_time_variable": ["Succeeded"]
        },
        "type": "SetVariable"
      },
      "GET_current_feature_status": {
        "inputs": {
          "headers": {
            "Authorization": "@body('Get_Launch_Darkly_API_key_secret')?['value']"
          },
          "method": "GET",
          "uri": "https://app.launchdarkly.com/api/v2/users/@{parameters('ldProjectKey')}/@{parameters('ldEnvironmentKey')}/@{parameters('ldUserKey')}/flags/@{parameters('ldFeatureKey')} "
        },
        "runAfter": {
          "Get_Launch_Darkly_API_key_secret": ["Succeeded"]
        },
        "type": "Http"
      },
      "Get_Launch_Darkly_API_key_secret": {
        "inputs": {
          "host": {
            "connection": {
              "name": "@parameters('$connections')['keyvault']['connectionId']"
            }
          },
          "method": "get",
          "path": "/secrets/@{encodeURIComponent('launchDarklyApiWriteKey')}/value"
        },
        "runAfter": {},
        "type": "ApiConnection"
      },
      "Initialize_end_time_variable": {
        "inputs": {
          "variables": [
            {
              "name": "isScheduledEndTimePassed",
              "type": "boolean",
              "value": false
            }
          ]
        },
        "runAfter": {
          "Convert_schedule_end_to_UTC_time_zone": ["Succeeded"]
        },
        "type": "InitializeVariable"
      },
      "Initialize_start_time_variable": {
        "inputs": {
          "variables": [
            {
              "name": "isScheduledStartTimePassed",
              "type": "boolean",
              "value": false
            }
          ]
        },
        "runAfter": {
          "Convert_schedule_start_to_UTC_time_zone": ["Succeeded"]
        },
        "type": "InitializeVariable"
      },
      "Is_current_time_within_desired_OFF_schedule": {
        "actions": {
          "Is_feature_turned_on": {
            "actions": {
              "GET_after_off_feature_status": {
                "inputs": {
                  "headers": {
                    "Authorization": "@body('Get_Launch_Darkly_API_key_secret')?['value']"
                  },
                  "method": "GET",
                  "uri": "https://app.launchdarkly.com/api/v2/users/@{parameters('ldProjectKey')}/@{parameters('ldEnvironmentKey')}/@{parameters('ldUserKey')}/flags/@{parameters('ldFeatureKey')} "
                },
                "runAfter": {
                  "Turn_LD_feature_OFF": ["Succeeded"]
                },
                "type": "Http"
              },
              "Parse_after_off_response": {
                "inputs": {
                  "content": "@body('GET_after_off_feature_status')",
                  "schema": {
                    "properties": {
                      "_links": {
                        "properties": {
                          "self": {
                            "properties": {
                              "href": {
                                "type": "string"
                              },
                              "type": {
                                "type": "string"
                              }
                            },
                            "type": "object"
                          }
                        },
                        "type": "object"
                      },
                      "_value": {
                        "type": "boolean"
                      },
                      "setting": {}
                    },
                    "type": "object"
                  }
                },
                "runAfter": {
                  "GET_after_off_feature_status": ["Succeeded"]
                },
                "type": "ParseJson"
              },
              "Post_message_(V2)": {
                "inputs": {
                  "body": {
                    "channel": "your-development-channel",
                    "icon_emoji": ":red_circle:",
                    "text": "Turned OFF @{parameters('ldFeatureKey')} on [env: @{parameters('ldEnvironmentKey')}, project: @{parameters('ldProjectKey')}] for schedule (@{parameters('scheduleStart')} --> @{parameters('scheduleEnd')}) - test retreived variation result: @{body('Parse_after_off_response')?['_value']}",
                    "username": "DanBot"
                  },
                  "host": {
                    "connection": {
                      "name": "@parameters('$connections')['slack']['connectionId']"
                    }
                  },
                  "method": "post",
                  "path": "/v2/chat.postMessage"
                },
                "runAfter": {
                  "Parse_after_off_response": ["Succeeded"]
                },
                "type": "ApiConnection"
              },
              "Turn_LD_feature_OFF": {
                "inputs": {
                  "body": {
                    "comment": "set state OFF using logic app",
                    "environmentKey": "@{parameters('ldEnvironmentKey')}",
                    "instructions": [
                      {
                        "kind": "turnFlagOff"
                      }
                    ]
                  },
                  "headers": {
                    "Authorization": "@body('Get_Launch_Darkly_API_key_secret')?['value']",
                    "Content-Type": "application/json; domain-model=launchdarkly.semanticpatch"
                  },
                  "method": "PATCH",
                  "uri": "https://app.launchdarkly.com/api/v2/flags/@{parameters('ldProjectKey')}/@{parameters('ldFeatureKey')}"
                },
                "runAfter": {},
                "type": "Http"
              }
            },
            "expression": {
              "and": [
                {
                  "equals": [
                    "@body('Parse_LD_Response_Body')?['_value']",
                    "@true"
                  ]
                }
              ]
            },
            "runAfter": {},
            "type": "If"
          }
        },
        "else": {
          "actions": {
            "Is_feature_turned_off": {
              "actions": {
                "GET_after_on_feature_status": {
                  "inputs": {
                    "headers": {
                      "Authorization": "@body('Get_Launch_Darkly_API_key_secret')?['value']"
                    },
                    "method": "GET",
                    "uri": "https://app.launchdarkly.com/api/v2/users/@{parameters('ldProjectKey')}/@{parameters('ldEnvironmentKey')}/@{parameters('ldUserKey')}/flags/@{parameters('ldFeatureKey')} "
                  },
                  "runAfter": {
                    "Turn_LD_feature_ON": ["Succeeded"]
                  },
                  "type": "Http"
                },
                "Parse_after_on": {
                  "inputs": {
                    "content": "@body('GET_after_on_feature_status')",
                    "schema": {
                      "properties": {
                        "_links": {
                          "properties": {
                            "self": {
                              "properties": {
                                "href": {
                                  "type": "string"
                                },
                                "type": {
                                  "type": "string"
                                }
                              },
                              "type": "object"
                            }
                          },
                          "type": "object"
                        },
                        "_value": {
                          "type": "boolean"
                        },
                        "setting": {}
                      },
                      "type": "object"
                    }
                  },
                  "runAfter": {
                    "GET_after_on_feature_status": ["Succeeded"]
                  },
                  "type": "ParseJson"
                },
                "Post_message_(V2)_2": {
                  "inputs": {
                    "body": {
                      "channel": "your-development-channel",
                      "icon_emoji": ":green_heart:",
                      "text": "Turned ON @{parameters('ldFeatureKey')} on [env: @{parameters('ldEnvironmentKey')}, project: @{parameters('ldProjectKey')}] for schedule (@{parameters('scheduleStart')} --> @{parameters('scheduleEnd')}) - test retrieved variation result: @{body('Parse_after_on')?['_value']}",
                      "username": "DanBot"
                    },
                    "host": {
                      "connection": {
                        "name": "@parameters('$connections')['slack']['connectionId']"
                      }
                    },
                    "method": "post",
                    "path": "/v2/chat.postMessage"
                  },
                  "runAfter": {
                    "Parse_after_on": ["Succeeded"]
                  },
                  "type": "ApiConnection"
                },
                "Turn_LD_feature_ON": {
                  "inputs": {
                    "body": {
                      "comment": "set state ON using logic app",
                      "environmentKey": "@{parameters('ldEnvironmentKey')}",
                      "instructions": [
                        {
                          "kind": "turnFlagOn"
                        }
                      ]
                    },
                    "headers": {
                      "Authorization": "@body('Get_Launch_Darkly_API_key_secret')?['value']",
                      "Content-Type": "application/json; domain-model=launchdarkly.semanticpatch"
                    },
                    "method": "PATCH",
                    "uri": "https://app.launchdarkly.com/api/v2/flags/@{parameters('ldProjectKey')}/@{parameters('ldFeatureKey')}"
                  },
                  "runAfter": {},
                  "type": "Http"
                }
              },
              "expression": {
                "and": [
                  {
                    "equals": [
                      "@body('Parse_LD_Response_Body')?['_value']",
                      "@false"
                    ]
                  }
                ]
              },
              "runAfter": {},
              "type": "If"
            }
          }
        },
        "expression": {
          "and": [
            {
              "equals": ["@variables('isScheduledStartTimePassed')", "@true"]
            },
            {
              "equals": ["@variables('isScheduledEndTimePassed')", "@false"]
            }
          ]
        },
        "runAfter": {
          "Detect_if_end_time_has_passed": ["Succeeded"]
        },
        "type": "If"
      },
      "Parse_LD_Response_Body": {
        "inputs": {
          "content": "@body('GET_current_feature_status')",
          "schema": {
            "properties": {
              "_links": {
                "properties": {
                  "self": {
                    "properties": {
                      "href": {
                        "type": "string"
                      },
                      "type": {
                        "type": "string"
                      }
                    },
                    "type": "object"
                  }
                },
                "type": "object"
              },
              "_value": {
                "type": "boolean"
              },
              "setting": {}
            },
            "type": "object"
          }
        },
        "runAfter": {
          "GET_current_feature_status": ["Succeeded"]
        },
        "type": "ParseJson"
      }
    },
    "contentVersion": "1.0.0.0",
    "outputs": {},
    "parameters": {
      "$connections": {
        "defaultValue": {},
        "type": "Object"
      },
      "ldEnvironmentKey": {
        "defaultValue": "production",
        "type": "String"
      },
      "ldFeatureKey": {
        "defaultValue": "my-third-party-service",
        "type": "String"
      },
      "ldProjectKey": {
        "defaultValue": "my-project",
        "type": "String"
      },
      "ldUserKey": {
        "defaultValue": "me@mycompany.com",
        "type": "String"
      },
      "scheduleEnd": {
        "defaultValue": "2021-04-30T06:00:00",
        "type": "String"
      },
      "scheduleStart": {
        "defaultValue": "2021-04-29T19:30:00",
        "type": "String"
      }
    },
    "triggers": {
      "Recurrence_Trigger": {
        "recurrence": {
          "frequency": "Minute",
          "interval": 30
        },
        "type": "Recurrence"
      }
    }
  },
  "parameters": {
    "$connections": {
      "value": {
        "keyvault": {
          "connectionId": "/providers/Microsoft.Web/connections/keyvault",
          "connectionName": "keyvault",
          "id": "/managedApis/keyvault"
        },
        "slack": {
          "connectionId": "/providers/Microsoft.Web/connections/slack",
          "connectionName": "slack",
          "id": "/managedApis/slack"
        }
      }
    }
  }
}

Оригинал: «https://dev.to/darraghor/scheduling-a-feature-toggle-using-no-code-with-azure-logic-apps-3laf»