Azure Logic 애플리케이션에서 코드 스케줄링 기능을 사용하지 않고 전환

29981 단어 devopsnocodeazure
나는 launch darkly를 사용하여 응용 프로그램의 기능을 전환한다.제3자 의존항이 정기적으로 유지보수를 하고 있기 때문에 저는 계획에 따라 이 기능을 켜고 닫아야 합니다.
Launch Darkly 에는 이 장면을 처리할 계획이 내장되어 있지만, 기업 계획에 따라 사용해야 합니다.기업 계획의 업그레이드 원가가 너무 높아서 단독으로 스케줄링에 사용할 수 없기 때문에 나는 자동화를 실현하는 다른 방법을 찾아야 한다.

Azure에 코드가 없습니다.


나는 몇 가지 비기능적인 수요가 있어서 나를 코드 없는 해결 방안으로 인도한다.
  • 이 문제를 신속하게 해결하고
  • 에 대해 논의해야 합니다.
  • 내장형
  • 프로그램 필요
  • 누구나 손쉽게 관리할 수 있는
  • 모든 사용자(매개변수의 그래픽 사용자 인터페이스)는 쉽게 일정을 변경해야 합니다.
  • Launch Darkly api 키를 보호할 수 있어야 합니다. 기능 값
  • 을 작성하고 있기 때문입니다.
    Azure는 Logic Apps라는 무코드 플랫폼을 제공하는데 이런 작업 흐름 문제에 매우 적합하다고 들린다.

    요약


    나는 당신이 내가 왜 그것들을 추가해야 하는지 이해할 수 있도록 모든 절차를 상세하게 설명할 것이다.
    나는 본문 말미에 완전한 JSON 설정을 제공했기 때문에 logic 응용 프로그램을 다시 만들기 쉽다.
    이러한 모든 작업은 Azure Logic Apps GUI를 사용하여 설정할 수 있습니다.화면 캡처에서 입력한 모든 절차를 볼 수 있도록 json 설정을 추가합니다.
    이 설정을 복사하는 것은 즉시 당신에게 도움이 되지 않는다는 것을 기억하십시오.logic app 웹 UI를 사용하여 Azure Key Vault 및 Slack 통합을 위한 연결을 설정해야 합니다.

    전체 논리 응용 프로그램 디자이너 보기


    모든 게 함께 있어!
    어플리케이션 디자이너의 상단
    응용 프로그램 디자이너의 하반부
    자, 우리 시작합시다!

    논리적 애플리케이션 추가


    자원 그룹에 새로운 논리 프로그램을 만들면 소비 계획을 사용할 수 있습니다.

    필요한 논리 응용 프로그램 매개 변수 추가


    매개변수 편집기를 열고 모든 매개변수를 추가합니다.그것들은 모두 줄이다.
    매개변수 이름
    기본값 설명
    ldEnvironmentKey
    당신의 환경, 예를 들면 생산 환경
    ldFeatureKey
    기능(예: 내 타사 서비스 등)
    ldProjectKey
    내 프로젝트
    ldUserKey
    사용자 id(예: [email protected])
    계획 시작
    현지 시간(예: 2021-04-29T19:30:00)
    일정이 끝나다
    현지 시간(예: 2021-04-29T19:30:00)
    lsUserKey 는 트리거가 사용자에게 변수를 설정했는지 테스트하는 데 사용됩니다.

    반복 트리거 추가


    이 프로그램은 30분마다 업데이트가 필요한지 확인합니다.논리 응용 프로그램은 이런 일에 아주 쉽게 사용할 수 있는 중복 트리거를 제공했다.
    중복 트리거 추가
    {
      "triggers": {
        "Recurrence_Trigger": {
            "recurrence": {
            "frequency": "Minute",
            "interval": 30
        },
        "type": "Recurrence"
       }
    }
    

    Azure 키 라이브러리에서 Launch Darkly API 키 가져오기


    보안을 위해 Launch Darkly API 키를 기존 Azure 키 라이브러리에 저장합니다.필요한 경우 새 키 라이브러리를 만들고 Logic Appaction에서 키 라이브러리 기밀을 읽는 연결을 추가합니다.
    검정색 부팅 API 키 얻기

    Launch Darkly 기능의 현재 상태 가져오기


    launch darkly api를 http로 호출해야 합니다.매개변수를 사용하여 URL을 만들고 Azure Key Vault secret에서 헤더를 추가합니다.
    현재 상태 가져오기
    "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"
          },
    

    JSON http 응답 확인


    http 호출 동작은 응답을 해석하지 않습니다.JSON 파서 논리 응용 프로그램 작업을 사용하여 분석을 수행해야 합니다.모든 속성은 다음 단계에서 제공될 것입니다.
    파서가 JSON 모드를 만들 수 있도록 응답 예시가 필요합니다.postman이나curl을 사용하여launchdarkly에서 요청할 수 있습니다.
    해석 JSON 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"
          }
        },
    

    시작 및 종료 시간을 부울 값으로 변환


    같은 일이 두 번 반복되었기 때문에 나는 schedule Start만 묘사했다.scheduleEnd는 같은 모드이고 이름이 다릅니다!
    시작하려면 다른 모든 논리 응용 프로그램 작업에서 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"
          },
    
    이제 END schedule 매개변수를 사용하는 것 외에 동일한 세 단계를 수행합니다.

    우리가 예정된 시간 내에


    그래서 우리가 시작 시간 이후와 종료 시간 전에우리는 이전에 만들어진 변수에 근거하여 이 점을 실현할 수 있다.화면 캡처를 보고 어떻게 설정하는지 알 수 있습니다.
    예정 기간 내에

    만약 우리가 계획 중이라면, 이 기능이 현재 사용 중인지 확인하십시오


    이곳의 _value 매개 변수는 우리가 처음에 한Parse JSON 작업에서 나온 것이다.'블랙 발사'기능의 현재 상태다.
    만약 우리가 계획 중이고, 그것이 현재 열려 있는 상태라면, 우리는 그것을 닫아야 한다.
    은 현재 활성화된 기능

    Launch Darkly API를 호출하여 기능을 종료합니다.


    여기서 Logic 응용 프로그램의 매개 변수를 사용하여 URL을 생성합니다.우리는 열쇠 창고의 권한을 사용한다.Launch Darkly의api는 JSON 패치 형식을 사용하기 때문에 추가 내용 형식이 있습니다.
    본문에는 우리가 변경하고자 하는 키, 어두운 시작에 대한 구체적인 명령, 그리고 우리가 진행하고 있는 감사를 기록하는 데 필요한 매개 변수가 있습니다.
    Launch Darkly 기능 해제

    통화가 정상적인지 확인하기 위해 최신 기능 상태를 가져옵니다


    우리는 이 기능이 예상대로 바뀌었는지 확인하기 위해 키의 상태를 다시 얻었다.
    Launch Darkly에서 기능 상태 가져오기

    Launch Darkly에서 응답 확인


    이것은 앞의 해석 절차와 같다!우리는 앞으로 _value이 있기를 바란다.
    분석 응답

    slack channel에 메시지 보내기


    내가 일하는 곳에서는 모든 통신이 slack을 사용하기 때문에 우리는 내장 조작을 사용하여 메시지를 보내고 팀에 이 기능이 전환되었음을 알립니다.
    슬랙 관리자가 되어야만 통합을 추가할 수 있습니다.
    통합을 추가하면 채널로 메시지 보내기로 설정할 수 있습니다.
    네가 여기에 추가한 정보는 많을수록 좋다!아이콘과 로봇 이름을 변경할 수 있지만 "새 파라미터 추가"드롭다운 메뉴를 사용하십시오.
    슬랙에 메시지 보내기

    현재, 만약 우리가 계획을 초과하여 기능이 닫혔다면, 이 조작을 실행하십시오


    이것은 우리가 이 기능을 켜야 한다는 것을 의미한다.아래의 완전한 논리 응용 프로그램 그림 캡처에서 이것이 내 응용 프로그램에서 어떤 모습인지 볼 수 있다.이것은 매우 중복된 것이기 때문에 나는 한 걸음 한 걸음 반복하지 않을 것이다. 그러나 이번에 네가 이 기능을 켰으니, 네가 슬랙에 준 정보는 반드시 이 점을 반영해야 한다.
    여기서 너는 지시가 신호등이라는 것을 볼 수 있다.
    Launch Darkly에서 이 기능을 엽니다.

    해봐!


    일정 매개 변수를 현재 시간에 가까운 시간으로 설정합니다.오른쪽 위의 x 를 클릭하여 매개변수 항목을 닫습니다.
    그리고 왼쪽 상단에 있는 '저장' 을 누르고 '실행' 을 누르십시오!

    결론


    이것은 매우 설치하기 쉽다!더 많은 devops 형식의 작업을 논리 프로그램으로 옮기려고 합니다.
    여기에 약간의 개선을 할 수 있다.단계 열기/닫기 및 느슨함 메시지는 변수를 더 나은 방법으로 사용하도록 변경할 수 있으며 각 변수는 하나의 인스턴스만 있습니다.
    또 다른 좋은 일은 응용 프로그램이 사이트나api를 읽고 제3자로부터 시간표를 얻는 것이다.지금 우리는 매번 수동으로 시간표를 설정해야 한다.

    전체 논리 응용 프로그램 코드 보기


    {
      "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": "[email protected]",
            "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": "<YOUR_CONNECTION>/providers/Microsoft.Web/connections/keyvault",
              "connectionName": "keyvault",
              "id": "<YOUR_CONNECTION>/managedApis/keyvault"
            },
            "slack": {
              "connectionId": "<YOUR_CONNECTION>/providers/Microsoft.Web/connections/slack",
              "connectionName": "slack",
              "id": "<YOUR_CONNECTION>/managedApis/slack"
            }
          }
        }
      }
    }
    

    좋은 웹페이지 즐겨찾기